diff --git a/DEPS b/DEPS
index a961eaa..a5700cc 100644
--- a/DEPS
+++ b/DEPS
@@ -171,11 +171,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': 'a6572f78d084aa3ca3e6f174a24e753a45ea1fc3',
+  'skia_revision': 'f8ef81b35e8f91360d034a5283052071c07f7d22',
   # 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': '156331aba1a4cf85b73fede1ec8973b53052d9cd',
+  'v8_revision': '57083a22efbe63d75af8adb979583fa3aa0ff0d4',
   # 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.
@@ -183,11 +183,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'aa09ca69e4173cb14261e39be3b7bdf56bbd3840',
+  'angle_revision': '5b7ce876b01ca7d227e6e5d8aee072b5a2505e44',
   # 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': 'e162b023f53956b05b26972e9ecb75985b2f2c27',
+  'swiftshader_revision': '4cc54b409febf31c04e684f78a57e7170b952251',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -234,7 +234,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '3eb0df2c199c5959af69a93614a7ed456986b49a',
+  'catapult_revision': 'd2b5e7801939660ffef29da4a049b350381f2ebc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -242,7 +242,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '384e239655c4a424eae5b6c2627953acefc59acc',
+  'devtools_frontend_revision': '0fc949c8b168dea42d12432f4591ee4c5dabb9a5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -298,7 +298,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '5a921f8fadbaef0660024f6ac2fe3b4f9bdb02ea',
+  'dawn_revision': '6c1d646ee9d4120dcdf344b3fee01f3fdf79390c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1242,7 +1242,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b9ee1cfe480f7b4f36fdd265a23bf4e5d6d4793c',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f2c28cd7657fd0a59dd25b555f5f5803450fc096',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1443,7 +1443,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ec18cc3262922e7dcdbe70243c6f40606f979144',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1dea1ea412021819bddbc9400dcca7d9dd2eba74',
+    Var('webrtc_git') + '/src.git' + '@' + 'd6fb409d463dc3eb1f09dc4c6f879d15e5503ed0',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1518,7 +1518,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b5cd962aec5daa72cb82ad3b24fb2ce7cba18e7f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4be48f5a3603af1cdc75e562fe3797b8667cf537',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java
index 1fa4105b..40c5a0e 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsAnchorViewTest.java
@@ -102,7 +102,7 @@
 
             // Add anchor view
             View anchorView = addAnchorView();
-            LayoutParams layoutParams = anchorView.getLayoutParams();
+            LayoutParams layoutParams = new LayoutParams(anchorView.getLayoutParams());
 
             // Replace container view
             FrameLayout updatedContainerView = updateContainerView();
@@ -156,7 +156,7 @@
 
             // Add anchor view
             View anchorView = addAnchorView();
-            LayoutParams layoutParams = anchorView.getLayoutParams();
+            LayoutParams layoutParams = new LayoutParams(anchorView.getLayoutParams());
 
             // Replace container view
             FrameLayout updatedContainerView = updateContainerView();
@@ -213,7 +213,7 @@
         float scaledDimension = (float) dimension * scale;
         mViewDelegate.setViewPosition(
                 anchorView, scaledCoords, scaledCoords, scaledDimension, scaledDimension, 10, 10);
-        return anchorView.getLayoutParams();
+        return new LayoutParams(anchorView.getLayoutParams());
     }
 
     private FrameLayout updateContainerView() {
diff --git a/ash/app_list/app_list_controller_impl_unittest.cc b/ash/app_list/app_list_controller_impl_unittest.cc
index 4d97ce6..39fa3ea4 100644
--- a/ash/app_list/app_list_controller_impl_unittest.cc
+++ b/ash/app_list/app_list_controller_impl_unittest.cc
@@ -44,6 +44,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/with_feature_override.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -525,32 +526,20 @@
   EXPECT_FALSE(GetExpandArrowViewVisibility());
 }
 
-class HotseatAppListControllerImplTest
-    : public AppListControllerImplTest,
-      public testing::WithParamInterface<bool> {
+class HotseatAppListControllerImplTest : public base::test::WithFeatureOverride,
+                                         public AppListControllerImplTest {
  public:
-  HotseatAppListControllerImplTest() = default;
+  HotseatAppListControllerImplTest()
+      : WithFeatureOverride(chromeos::features::kShelfHotseat) {}
   ~HotseatAppListControllerImplTest() override = default;
 
-  // AshTestBase:
-  void SetUp() override {
-    if (GetParam()) {
-      feature_list_.InitAndEnableFeature(chromeos::features::kShelfHotseat);
-    } else {
-      feature_list_.InitAndDisableFeature(chromeos::features::kShelfHotseat);
-    }
-    AppListControllerImplTest::SetUp();
-  }
-
  private:
   base::test::ScopedFeatureList feature_list_;
   DISALLOW_COPY_AND_ASSIGN(HotseatAppListControllerImplTest);
 };
 
 // Tests with both hotseat disabled and enabled.
-INSTANTIATE_TEST_SUITE_P(All,
-                         HotseatAppListControllerImplTest,
-                         testing::Bool());
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(HotseatAppListControllerImplTest);
 
 // Verifies that the pinned app should still show after canceling the drag from
 // AppsGridView to Shelf (https://crbug.com/1021768).
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 4c68072f..b978acc 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -2858,7 +2858,8 @@
         compositor->refresh_rate(), IsTabletMode());
   }
   // Gradient mask is no longer necessary once transition is finished.
-  layer()->SetMaskLayer(nullptr);
+  if (layer()->layer_mask_layer())
+    layer()->SetMaskLayer(nullptr);
 }
 
 void AppsGridView::ScrollStarted() {
@@ -2879,8 +2880,9 @@
 void AppsGridView::ScrollEnded() {
   // Scroll can end without triggering state animation.
   presentation_time_recorder_.reset();
-  // No need to reset the mask because transition will happen in almost all
-  // cases.
+  // Need to reset the mask because transition will not happen in some
+  // cases. (See https://crbug.com/1049275)
+  layer()->SetMaskLayer(nullptr);
 }
 
 void AppsGridView::OnAppListModelStatusChanged() {
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index d74cbb8..1ed871c 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -1859,6 +1859,59 @@
       1);
 }
 
+// Make sure that a folder icon resets background blur after scrolling the apps
+// grid without completing any transition (See https://crbug.com/1049275). The
+// background blur is masked by the apps grid's layer mask.
+TEST_F(AppsGridViewTabletTest, EnsureBlurAfterScrollingWithoutTransition) {
+  // Create a folder with 2 apps. Then add apps until a second page is created.
+  model_->CreateAndPopulateFolderWithApps(2);
+  model_->PopulateApps(GetTilesPerPage(0));
+  EXPECT_EQ(2, GetPaginationModel()->total_pages());
+
+  gfx::Point apps_grid_view_origin =
+      apps_grid_view_->GetBoundsInScreen().origin();
+  ui::GestureEvent scroll_begin(
+      apps_grid_view_origin.x(), apps_grid_view_origin.y(), 0,
+      base::TimeTicks(),
+      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, -1));
+  ui::GestureEvent scroll_update_upwards(
+      apps_grid_view_origin.x(), apps_grid_view_origin.y(), 0,
+      base::TimeTicks(),
+      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 0, -10));
+  ui::GestureEvent scroll_update_downwards(
+      apps_grid_view_origin.x(), apps_grid_view_origin.y(), 0,
+      base::TimeTicks(),
+      ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 0, 15));
+  ui::GestureEvent scroll_end(
+      apps_grid_view_origin.x(), apps_grid_view_origin.y(), 0,
+      base::TimeTicks(), ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
+
+  AppListItemView* folder_view = GetItemViewAt(0);
+  ASSERT_TRUE(folder_view->is_folder());
+  ASSERT_FALSE(apps_grid_view_->layer()->layer_mask_layer());
+
+  // On the first page drag upwards, there should not be a page switch and the
+  // layer mask should make the folder lose blur.
+  ASSERT_EQ(0, GetPaginationModel()->selected_page());
+  apps_grid_view_->OnGestureEvent(&scroll_begin);
+  EXPECT_TRUE(scroll_begin.handled());
+  apps_grid_view_->OnGestureEvent(&scroll_update_upwards);
+  EXPECT_TRUE(scroll_update_upwards.handled());
+
+  ASSERT_EQ(0, GetPaginationModel()->selected_page());
+  ASSERT_TRUE(apps_grid_view_->layer()->layer_mask_layer());
+
+  // Continue drag, now switching directions and release. There shouldn't be any
+  // transition and the mask layer should've been reset.
+  apps_grid_view_->OnGestureEvent(&scroll_update_downwards);
+  EXPECT_TRUE(scroll_update_downwards.handled());
+  apps_grid_view_->OnGestureEvent(&scroll_end);
+  EXPECT_TRUE(scroll_end.handled());
+
+  EXPECT_FALSE(GetPaginationModel()->has_transition());
+  EXPECT_FALSE(apps_grid_view_->layer()->layer_mask_layer());
+}
+
 INSTANTIATE_TEST_SUITE_P(All, AppsGridViewTabletTest, testing::Bool());
 
 // Test various dragging behaviors only allowed when apps grid gap (part of
diff --git a/ash/assistant/assistant_alarm_timer_controller.cc b/ash/assistant/assistant_alarm_timer_controller.cc
index 845a94b..99cf85f8 100644
--- a/ash/assistant/assistant_alarm_timer_controller.cc
+++ b/ash/assistant/assistant_alarm_timer_controller.cc
@@ -346,18 +346,12 @@
                    << "duration.";
         return;
       }
-      // Verify the timer is ringing.
-      DCHECK(model_.GetAlarmTimerById(alarm_timer_id.value()));
-      // LibAssistant doesn't currently support adding time to an ringing timer.
-      // We'll create a new one with the duration specified. Note that we
-      // currently only support this deep link for an alarm/timer that is
-      // ringing.
-      assistant_->StopAlarmTimerRinging();
-      assistant_->CreateTimer(duration.value());
+      assistant_->AddTimeToTimer(alarm_timer_id.value(), duration.value());
       break;
     case assistant::util::AlarmTimerAction::kStopRinging:
       assistant_->StopAlarmTimerRinging();
       break;
   }
 }
+
 }  // namespace ash
diff --git a/ash/assistant/test/test_assistant_service.cc b/ash/assistant/test/test_assistant_service.cc
index abbccbef..65f5aa0d 100644
--- a/ash/assistant/test/test_assistant_service.cc
+++ b/ash/assistant/test/test_assistant_service.cc
@@ -320,11 +320,12 @@
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
-void TestAssistantService::StopAlarmTimerRinging() {
+void TestAssistantService::AddTimeToTimer(const std::string& id,
+                                          base::TimeDelta duration) {
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
-void TestAssistantService::CreateTimer(base::TimeDelta duration) {
+void TestAssistantService::StopAlarmTimerRinging() {
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
diff --git a/ash/assistant/test/test_assistant_service.h b/ash/assistant/test/test_assistant_service.h
index aeec6aa..6f3910a 100644
--- a/ash/assistant/test/test_assistant_service.h
+++ b/ash/assistant/test/test_assistant_service.h
@@ -109,8 +109,8 @@
       chromeos::assistant::mojom::AssistantFeedbackPtr feedback) override;
   void NotifyEntryIntoAssistantUi(
       chromeos::assistant::mojom::AssistantEntryPoint entry_point) override;
+  void AddTimeToTimer(const std::string& id, base::TimeDelta duration) override;
   void StopAlarmTimerRinging() override;
-  void CreateTimer(base::TimeDelta duration) override;
 
  private:
   void StartInteraction(
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 75d10ef6..f88dd2a0 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -44,7 +44,6 @@
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/views/accessible_pane_view.h"
 #include "ui/views/focus/focus_search.h"
-#include "ui/views/layout/fill_layout.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_delegate.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -119,6 +118,14 @@
 
   void SetParentLayer(ui::Layer* layer);
 
+  // Adds the login shelf view as a child of this view.
+  // Returns a pointer to the login shelf view passed in as an argument.
+  LoginShelfView* AddLoginShelfView(
+      std::unique_ptr<LoginShelfView> login_shelf_view) {
+    login_shelf_view_ = AddChildView(std::move(login_shelf_view));
+    return login_shelf_view_;
+  }
+
   void set_default_last_focusable_child(bool default_last_focusable_child) {
     default_last_focusable_child_ = default_last_focusable_child;
   }
@@ -147,6 +154,7 @@
 
   // views::AccessiblePaneView:
   views::View* GetDefaultFocusableChild() override;
+  void Layout() override;
 
   // ShelfBackgroundAnimatorObserver:
   void UpdateShelfBackground(SkColor color) override;
@@ -174,6 +182,11 @@
   bool hide_background_for_transitions_ = false;
   ShelfWidget* shelf_widget_;
   FocusCycler* focus_cycler_;
+
+  // Pointer to the login shelf view - visible only when the session is
+  // inactive. The view is owned by this view's hierarchy.
+  LoginShelfView* login_shelf_view_ = nullptr;
+
   // A background layer that may be visible depending on a
   // ShelfBackgroundAnimator.
   ui::LayerOwner opaque_background_;
@@ -211,7 +224,6 @@
   DCHECK(shelf_widget_);
   set_owned_by_client();  // Deleted by DeleteDelegate().
 
-  SetLayoutManager(std::make_unique<views::FillLayout>());
   set_allow_deactivate_on_esc(true);
 
   // |animating_background_| will be made visible during hotseat animations.
@@ -281,7 +293,7 @@
   // This widget only contains anything interesting to activate in login/lock
   // screen mode. Only allow activation from the focus cycler, not from mouse
   // events, etc.
-  return shelf_widget_->login_shelf_view_->GetVisible() && focus_cycler_ &&
+  return login_shelf_view_->GetVisible() && focus_cycler_ &&
          focus_cycler_->widget_activating() == GetWidget();
 }
 
@@ -375,15 +387,8 @@
     drag_handle_->SetVisible(false);
     return;
   }
-  drag_handle_->SetVisible(true);
 
-  const int x = (shelf_widget_->GetClientAreaBoundsInScreen().width() -
-                 kDragHandleSize.width()) /
-                2;
-  const int y =
-      (ShelfConfig::Get()->in_app_shelf_size() - kDragHandleSize.height()) / 2;
-  drag_handle_->SetBounds(x, y, kDragHandleSize.width(),
-                          kDragHandleSize.height());
+  drag_handle_->SetVisible(true);
 }
 
 void ShelfWidget::DelegateView::OnBoundsChanged(const gfx::Rect& old_bounds) {
@@ -407,8 +412,8 @@
   if (!IsUsingViewsShelf())
     return GetFirstFocusableChild();
 
-  if (shelf_widget_->login_shelf_view_->GetVisible()) {
-    return FindFirstOrLastFocusableChild(shelf_widget_->login_shelf_view_,
+  if (login_shelf_view_->GetVisible()) {
+    return FindFirstOrLastFocusableChild(login_shelf_view_,
                                          default_last_focusable_child_);
   }
   // If the login shelf view is not visible, there is nothing else to focus
@@ -416,6 +421,14 @@
   return nullptr;
 }
 
+void ShelfWidget::DelegateView::Layout() {
+  login_shelf_view_->SetBoundsRect(GetLocalBounds());
+
+  gfx::Rect drag_handle_bounds = GetLocalBounds();
+  drag_handle_bounds.ClampToCenteredSize(kDragHandleSize);
+  drag_handle_->SetBoundsRect(drag_handle_bounds);
+}
+
 void ShelfWidget::DelegateView::UpdateShelfBackground(SkColor color) {
   opaque_background()->SetColor(color);
   UpdateOpaqueBackground();
@@ -457,8 +470,8 @@
   // This should only get called when the login shelf is visible, i.e. not
   // during an active session. In an active session, hit test rects should be
   // calculated higher up in the class hierarchy by |EasyResizeWindowTargeter|.
-  // When in OOBE or locked/login screen, let events pass through empty parts
-  // of the shelf.
+  // When in OOBE or locked/login screen, let events pass through empty parts of
+  // the shelf.
   DCHECK(login_shelf_view_->GetVisible());
   gfx::Rect login_view_button_bounds =
       login_shelf_view_->ConvertRectToWidget(login_shelf_view_->GetMirroredRect(
@@ -528,8 +541,9 @@
   DCHECK(shelf_container);
 
   login_shelf_view_ =
-      new LoginShelfView(RootWindowController::ForWindow(shelf_container)
-                             ->lock_screen_action_background_controller());
+      delegate_view_->AddLoginShelfView(std::make_unique<LoginShelfView>(
+          RootWindowController::ForWindow(shelf_container)
+              ->lock_screen_action_background_controller()));
 
   views::Widget::InitParams params(
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
@@ -543,10 +557,8 @@
 
   // The shelf should not take focus when initially shown.
   set_focus_on_creation(false);
-  SetContentsView(delegate_view_);
   delegate_view_->SetParentLayer(GetLayer());
-
-  GetContentsView()->AddChildView(login_shelf_view_);
+  SetContentsView(delegate_view_);
 
   shelf_layout_manager_->AddObserver(this);
   shelf_container->SetLayoutManager(shelf_layout_manager_);
@@ -854,14 +866,7 @@
     hotseat_transition_animator_->SetAnimationsEnabledInSessionState(
         show_hotseat);
     login_shelf_view()->SetVisible(!show_hotseat);
-    delegate_view_->SetLayoutManager(
-        show_hotseat ? nullptr : std::make_unique<views::FillLayout>());
-
-    // When FillLayout is no longer the layout manager, ensure the correct size
-    // for the drag handle is set.
-    if (show_hotseat)
-      delegate_view_->UpdateDragHandle();
-
+    delegate_view_->UpdateDragHandle();
     ShowIfHidden();
   }
   shelf_layout_manager_->SetDimmed(false);
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 8308352..7fdc1098 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -53,6 +53,13 @@
   # file name) is saved.
   enable_location_source = true
 
+  # Whether or not the FROM_HERE macro uses base::Location::Current(). This
+  # allows the implementation to be reverted if needed while validating its
+  # replacement base::Location::Current(). On by default in non-official builds
+  # for testing purposes.
+  # TODO(https://crbug.com/974061): remove this eventually.
+  from_here_uses_location_builtins = !is_official_build
+
   # Unsafe developer build. Has developer-friendly features that may weaken or
   # disable security measures like sandboxing or ASLR.
   # IMPORTANT: Unsafe developer builds should never be distributed to end users.
@@ -2103,6 +2110,7 @@
 
   flags = [
     "ENABLE_LOCATION_SOURCE=$enable_location_source",
+    "FROM_HERE_USES_LOCATION_BUILTINS=$from_here_uses_location_builtins",
     "ENABLE_PROFILING=$enable_profiling",
     "CAN_UNWIND_WITH_FRAME_POINTERS=$can_unwind_with_frame_pointers",
     "UNSAFE_DEVELOPER_BUILD=$is_unsafe_developer_build",
diff --git a/base/location.cc b/base/location.cc
index cf189341..e18e661 100644
--- a/base/location.cc
+++ b/base/location.cc
@@ -57,11 +57,16 @@
 #define RETURN_ADDRESS() nullptr
 #endif
 
+#if !BUILDFLAG(FROM_HERE_USES_LOCATION_BUILTINS)
+#if !BUILDFLAG(ENABLE_LOCATION_SOURCE)
+
 // static
 NOINLINE Location Location::CreateFromHere(const char* file_name) {
   return Location(file_name, RETURN_ADDRESS());
 }
 
+#else
+
 // static
 NOINLINE Location Location::CreateFromHere(const char* function_name,
                                            const char* file_name,
@@ -69,6 +74,9 @@
   return Location(function_name, file_name, line_number, RETURN_ADDRESS());
 }
 
+#endif
+#endif
+
 #if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
 // static
 NOINLINE Location Location::Current(const char* function_name,
diff --git a/base/location.h b/base/location.h
index bcc3ca0..c9057b2 100644
--- a/base/location.h
+++ b/base/location.h
@@ -83,10 +83,15 @@
   // are not available, this will return "pc:<hex address>".
   std::string ToString() const;
 
+#if !BUILDFLAG(FROM_HERE_USES_LOCATION_BUILTINS)
+#if !BUILDFLAG(ENABLE_LOCATION_SOURCE)
   static Location CreateFromHere(const char* file_name);
+#else
   static Location CreateFromHere(const char* function_name,
                                  const char* file_name,
                                  int line_number);
+#endif
+#endif
 
 #if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
   static Location Current(const char* function_name = __builtin_FUNCTION(),
@@ -107,20 +112,20 @@
 
 BASE_EXPORT const void* GetProgramCounter();
 
+#if BUILDFLAG(FROM_HERE_USES_LOCATION_BUILTINS)
+
+#define FROM_HERE ::base::Location::Current()
+
 // The macros defined here will expand to the current function.
-#if BUILDFLAG(ENABLE_LOCATION_SOURCE)
+#elif BUILDFLAG(ENABLE_LOCATION_SOURCE)
 
 // Full source information should be included.
-#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__func__)
-#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \
-  ::base::Location::CreateFromHere(function_name, __FILE__, __LINE__)
+#define FROM_HERE ::base::Location::CreateFromHere(__func__, __FILE__, __LINE__)
 
 #else
 
 // TODO(http://crbug.com/760702) remove the __FILE__ argument from these calls.
 #define FROM_HERE ::base::Location::CreateFromHere(__FILE__)
-#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \
-  ::base::Location::CreateFromHere(function_name, __FILE__, -1)
 
 #endif
 
diff --git a/base/location_unittest.cc b/base/location_unittest.cc
index d421027..d5dd7004 100644
--- a/base/location_unittest.cc
+++ b/base/location_unittest.cc
@@ -34,6 +34,8 @@
 #endif
 #elif defined(OFFICIAL_BUILD)
 #error Location builtins must be supported in official builds.
+#elif BUILDFLAG(FROM_HERE_USES_LOCATION_BUILTINS)
+#error FROM_HERE requires location builtins to be supported.
 #endif
   ALLOW_UNUSED_LOCAL(previous_line);
 }
diff --git a/base/task/cancelable_task_tracker.cc b/base/task/cancelable_task_tracker.cc
index 2dac43c..a0689d2 100644
--- a/base/task/cancelable_task_tracker.cc
+++ b/base/task/cancelable_task_tracker.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/sequenced_task_runner.h"
+#include "base/task/scoped_set_task_priority_for_current_thread.h"
 #include "base/task_runner.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 
@@ -31,8 +32,43 @@
     task_runner->PostTask(FROM_HERE, std::move(closure));
 }
 
-// TODO(https://crbug.com/1009795): Remove this once we have established whether
-// off-sequence cancelation is worthwhile.
+// TODO(https://crbug.com/1009795): Remove these once we have established
+// whether off-sequence cancelation is worthwhile.
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class TaskStatus {
+  kSameSequenceLive = 0,
+  kOffSequenceLive = 1,
+  kSameSequenceCanceled = 2,
+  kOffSequenceCanceled = 3,
+  kMaxValue = kOffSequenceCanceled,
+};
+
+void UmaRecordTaskDuration(bool same_sequence,
+                           bool background,
+                           bool canceled,
+                           TimeDelta duration) {
+#define DECLARE_HISTOGRAM(suffix)                                     \
+  Histogram::FactoryTimeGet(                                          \
+      "Scheduler.CancelableTaskTracker.TaskDuration2_" suffix,        \
+      TimeDelta::FromMilliseconds(1), TimeDelta::FromSeconds(10), 50, \
+      Histogram::kUmaTargetedHistogramFlag)
+
+  static HistogramBase* histograms[] = {
+      DECLARE_HISTOGRAM("LiveForegroundOffSequence"),
+      DECLARE_HISTOGRAM("LiveForegroundSameSequence"),
+      DECLARE_HISTOGRAM("LiveBackgroundOffSequence"),
+      DECLARE_HISTOGRAM("LiveBackgroundSameSequence"),
+      DECLARE_HISTOGRAM("CanceledForegroundOffSequence"),
+      DECLARE_HISTOGRAM("CanceledForegroundSameSequence"),
+      DECLARE_HISTOGRAM("CanceledBackgroundOffSequence"),
+      DECLARE_HISTOGRAM("CanceledBackgroundSameSequence")};
+
+  int i = (same_sequence ? 1 : 0) + (background ? 2 : 0) + (canceled ? 4 : 0);
+  histograms[i]->AddTimeMillisecondsGranularity(duration);
+}
+
 const base::Feature kAllowOffSequenceTaskCancelation{
     "AllowOffSequenceTaskCancelation", base::FEATURE_ENABLED_BY_DEFAULT};
 
@@ -168,18 +204,37 @@
     const scoped_refptr<SequencedTaskRunner>& origin_task_runner,
     const scoped_refptr<TaskCancellationFlag>& flag,
     OnceClosure task) {
-  const bool task_canceled = flag->data.IsSet();
-  UMA_HISTOGRAM_BOOLEAN("Scheduler.CancelableTaskTracker.TaskCanceled",
-                        task_canceled);
-  // TODO(https://crbug.com/1009795): Ignore off-sequence cancellation, to
-  // evaluate whether it is a worthwhile optimization.
+  // TODO(https://crbug.com/1009795): Record durations for executed tasks,
+  // correlated with whether the task runs on a background or foreground
+  // sequence, and whether it is the same sequence as the CancelableTaskTracker.
+  // Also correlate with whether the task was run despite being canceled, to
+  // allow an experiment to assess the value of off-sequence cancelation.
+
+  // Record canceled & off-sequence status for all tasks.
+  const bool was_canceled = flag->data.IsSet();
+  const bool same_sequence = origin_task_runner->RunsTasksInCurrentSequence();
+  const TaskStatus task_status =
+      was_canceled ? (same_sequence ? TaskStatus::kSameSequenceCanceled
+                                    : TaskStatus::kOffSequenceCanceled)
+                   : (same_sequence ? TaskStatus::kSameSequenceLive
+                                    : TaskStatus::kOffSequenceLive);
+  UMA_HISTOGRAM_ENUMERATION("Scheduler.CancelableTaskTracker.TaskStatus",
+                            task_status);
+
+  // Skip tasks if they are canceled, taking into account the off-sequence
+  // cancelation experiment.
   const bool skip_task =
-      task_canceled && (AllowOffSequenceTaskCancelation() ||
-                        origin_task_runner->RunsTasksInCurrentSequence());
+      was_canceled && (AllowOffSequenceTaskCancelation() || same_sequence);
   if (skip_task)
     return;
-  SCOPED_UMA_HISTOGRAM_TIMER("Scheduler.CancelableTaskTracker.TaskDuration");
+
+  // Run the task and record its duration.
+  const TimeTicks before_task_ticks = TimeTicks::Now();
   std::move(task).Run();
+  const TimeDelta duration = TimeTicks::Now() - before_task_ticks;
+  const bool is_background =
+      internal::GetTaskPriorityForCurrentThread() < TaskPriority::USER_VISIBLE;
+  UmaRecordTaskDuration(same_sequence, is_background, was_canceled, duration);
 }
 
 // static
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 450b68e8..941b1af 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -142,6 +142,8 @@
     "trace_to_file.h",
     "values_test_util.cc",
     "values_test_util.h",
+    "with_feature_override.cc",
+    "with_feature_override.h",
   ]
 
   configs += [ "//build/config:precompiled_headers" ]
diff --git a/base/test/with_feature_override.cc b/base/test/with_feature_override.cc
new file mode 100644
index 0000000..7a9d7870
--- /dev/null
+++ b/base/test/with_feature_override.cc
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/with_feature_override.h"
+#include "base/task/thread_pool/thread_pool_instance.h"
+
+namespace base {
+namespace test {
+
+WithFeatureOverride::WithFeatureOverride(const base::Feature& feature) {
+  // Most other classes that tests inherit from start task environments. Verify
+  // that has not happened yet.
+  DCHECK(base::ThreadPoolInstance::Get() == nullptr)
+      << "WithFeatureOverride should be the first class a test inherits from "
+         "so it sets the features before any other setup is done.";
+
+  if (GetParam()) {
+    scoped_feature_list_.InitAndEnableFeature(feature);
+  } else {
+    scoped_feature_list_.InitAndDisableFeature(feature);
+  }
+}
+
+bool WithFeatureOverride::IsParamFeatureEnabled() {
+  return GetParam();
+}
+
+WithFeatureOverride::~WithFeatureOverride() = default;
+
+}  // namespace test
+}  // namespace base
diff --git a/base/test/with_feature_override.h b/base/test/with_feature_override.h
new file mode 100644
index 0000000..9a88253c
--- /dev/null
+++ b/base/test/with_feature_override.h
@@ -0,0 +1,55 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TEST_WITH_FEATURE_OVERRIDE_H_
+#define BASE_TEST_WITH_FEATURE_OVERRIDE_H_
+
+#include "base/feature_list.h"
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace test {
+
+#define INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(test_name) \
+  INSTANTIATE_TEST_SUITE_P(All, test_name, testing::Values(false, true))
+
+// Base class for a test fixture that must run with a feature enabled and
+// disabled. Must be the first base class of the test fixture to take effect
+// during the construction of the test fixture itself.
+//
+// Example usage:
+//
+//  class MyTest : public base::WithFeatureOverride, public testing::Test {
+//   public:
+//    MyTest() : WithFeatureOverride(kMyFeature){}
+//  };
+//
+//  TEST_P(MyTest, FooBar) {
+//    This will run with both the kMyFeature enabled and disabled.
+//  }
+//
+//  INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(MyTest);
+
+class WithFeatureOverride : public testing::WithParamInterface<bool> {
+ public:
+  explicit WithFeatureOverride(const base::Feature& feature);
+  ~WithFeatureOverride();
+
+  WithFeatureOverride(const WithFeatureOverride&) = delete;
+  WithFeatureOverride& operator=(const WithFeatureOverride&) = delete;
+
+  // Use to know if the configured feature provided in the ctor is enabled or
+  // not.
+  bool IsParamFeatureEnabled();
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+}  // namespace test
+}  // namespace base
+
+#endif  // BASE_TEST_WITH_FEATURE_OVERRIDE_H_
diff --git a/base/win/scoped_variant_unittest.cc b/base/win/scoped_variant_unittest.cc
index 441ba37f..3157584 100644
--- a/base/win/scoped_variant_unittest.cc
+++ b/base/win/scoped_variant_unittest.cc
@@ -16,13 +16,16 @@
 
 namespace {
 
-static const wchar_t kTestString1[] = L"Used to create BSTRs";
-static const wchar_t kTestString2[] = L"Also used to create BSTRs";
+constexpr wchar_t kTestString[] = L"Test string for BSTRs.";
 
-void GiveMeAVariant(VARIANT* ret) {
-  EXPECT_TRUE(ret != nullptr);
-  ret->vt = VT_BSTR;
-  V_BSTR(ret) = ::SysAllocString(kTestString1);
+void InitializeVariantWithBstr(VARIANT* var) {
+  if (!var) {
+    ADD_FAILURE() << "|var| cannot be null.";
+    return;
+  }
+
+  var->vt = VT_BSTR;
+  V_BSTR(var) = ::SysAllocString(kTestString);
 }
 
 // An unimplemented IDispatch subclass.
@@ -68,184 +71,287 @@
   EXPECT_EQ(expected_refcount, object->Release());
 }
 
+void ExpectVariantType(VARENUM var_type, const ScopedVariant& var) {
+  EXPECT_EQ(var_type, var.type());
+  EXPECT_EQ(var_type, V_VT(var.ptr()));
+}
+
 }  // namespace
 
-TEST(ScopedVariantTest, ScopedVariant) {
+TEST(ScopedVariantTest, Empty) {
   ScopedVariant var;
-  EXPECT_TRUE(var.type() == VT_EMPTY);
-  // V_BSTR(var.ptr()) = NULL;  <- NOTE: Assignment like that is not supported.
+  ExpectVariantType(VT_EMPTY, var);
+}
 
-  ScopedVariant var_bstr(L"VT_BSTR");
-  EXPECT_EQ(VT_BSTR, V_VT(var_bstr.ptr()));
-  EXPECT_TRUE(V_BSTR(var_bstr.ptr()) !=
-              nullptr);  // can't use EXPECT_NE for BSTR
-  var_bstr.Reset();
-  EXPECT_NE(VT_BSTR, V_VT(var_bstr.ptr()));
-  var_bstr.Set(kTestString2);
-  EXPECT_EQ(VT_BSTR, V_VT(var_bstr.ptr()));
+TEST(ScopedVariantTest, ConstructBstr) {
+  ScopedVariant var(kTestString);
+  ExpectVariantType(VT_BSTR, var);
+  EXPECT_STREQ(kTestString, V_BSTR(var.ptr()));
+}
 
-  VARIANT tmp = var_bstr.Release();
-  EXPECT_EQ(VT_EMPTY, V_VT(var_bstr.ptr()));
-  EXPECT_EQ(VT_BSTR, V_VT(&tmp));
-  EXPECT_EQ(0, lstrcmp(V_BSTR(&tmp), kTestString2));
+TEST(ScopedVariantTest, SetBstr) {
+  ScopedVariant var;
+  var.Set(kTestString);
+  ExpectVariantType(VT_BSTR, var);
+  EXPECT_STREQ(kTestString, V_BSTR(var.ptr()));
+}
 
-  var.Reset(tmp);
-  EXPECT_EQ(VT_BSTR, V_VT(var.ptr()));
-  EXPECT_EQ(0, lstrcmpW(V_BSTR(var.ptr()), kTestString2));
+TEST(ScopedVariantTest, ReleaseBstr) {
+  ScopedVariant var;
+  var.Set(kTestString);
+  VARIANT released_variant = var.Release();
+  ExpectVariantType(VT_EMPTY, var);
+  EXPECT_EQ(VT_BSTR, V_VT(&released_variant));
+  EXPECT_STREQ(kTestString, V_BSTR(&released_variant));
+  ::VariantClear(&released_variant);
+}
 
-  var_bstr.Swap(var);
-  EXPECT_EQ(VT_EMPTY, V_VT(var.ptr()));
-  EXPECT_EQ(VT_BSTR, V_VT(var_bstr.ptr()));
-  EXPECT_EQ(0, lstrcmpW(V_BSTR(var_bstr.ptr()), kTestString2));
-  var_bstr.Reset();
+TEST(ScopedVariantTest, ResetToEmptyBstr) {
+  ScopedVariant var(kTestString);
+  ExpectVariantType(VT_BSTR, var);
+  var.Reset();
+  ExpectVariantType(VT_EMPTY, var);
+}
 
-  // Test the Compare and Copy routines.
-  GiveMeAVariant(var_bstr.Receive());
-  ScopedVariant var_bstr2(V_BSTR(var_bstr.ptr()));
-  EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+TEST(ScopedVariantTest, TakeOwnershipBstr) {
+  VARIANT bstr_variant;
+  bstr_variant.vt = VT_BSTR;
+  bstr_variant.bstrVal = ::SysAllocString(kTestString);
+
+  ScopedVariant var;
+  var.Reset(bstr_variant);
+  ExpectVariantType(VT_BSTR, var);
+  EXPECT_EQ(bstr_variant.bstrVal, V_BSTR(var.ptr()));
+}
+
+TEST(ScopedVariantTest, SwapBstr) {
+  ScopedVariant from(kTestString);
+  ScopedVariant to;
+  to.Swap(from);
+  ExpectVariantType(VT_EMPTY, from);
+  ExpectVariantType(VT_BSTR, to);
+  EXPECT_STREQ(kTestString, V_BSTR(to.ptr()));
+}
+
+TEST(ScopedVariantTest, CompareBstr) {
+  ScopedVariant var_bstr1;
+  InitializeVariantWithBstr(var_bstr1.Receive());
+  ScopedVariant var_bstr2(V_BSTR(var_bstr1.ptr()));
+  EXPECT_EQ(0, var_bstr1.Compare(var_bstr2));
+
   var_bstr2.Reset();
-  EXPECT_NE(0, var_bstr.Compare(var_bstr2));
-  var_bstr2.Reset(var_bstr.Copy());
-  EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
-  var_bstr2.Reset();
-  var_bstr2.Set(V_BSTR(var_bstr.ptr()));
-  EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
-  var_bstr2.Reset();
-  var_bstr.Reset();
+  EXPECT_NE(0, var_bstr1.Compare(var_bstr2));
+}
 
-  // Test for the SetDate setter.
+TEST(ScopedVariantTest, ReceiveAndCopyBstr) {
+  ScopedVariant var_bstr1;
+  InitializeVariantWithBstr(var_bstr1.Receive());
+  ScopedVariant var_bstr2;
+  var_bstr2.Reset(var_bstr1.Copy());
+  EXPECT_EQ(0, var_bstr1.Compare(var_bstr2));
+}
+
+TEST(ScopedVariantTest, SetBstrFromBstrVariant) {
+  ScopedVariant var_bstr1;
+  InitializeVariantWithBstr(var_bstr1.Receive());
+  ScopedVariant var_bstr2;
+  var_bstr2.Set(V_BSTR(var_bstr1.ptr()));
+  EXPECT_EQ(0, var_bstr1.Compare(var_bstr2));
+}
+
+TEST(ScopedVariantTest, SetDate) {
+  ScopedVariant var;
   SYSTEMTIME sys_time;
   ::GetSystemTime(&sys_time);
   DATE date;
   ::SystemTimeToVariantTime(&sys_time, &date);
-  var.Reset();
   var.SetDate(date);
-  EXPECT_EQ(VT_DATE, var.type());
+  ExpectVariantType(VT_DATE, var);
   EXPECT_EQ(date, V_DATE(var.ptr()));
+}
 
-  // Simple setter tests.  These do not require resetting the variant
-  // after each test since the variant type is not "leakable" (i.e. doesn't
-  // need to be freed explicitly).
-
-  // We need static cast here since char defaults to int (!?).
+TEST(ScopedVariantTest, SetSigned1Byte) {
+  ScopedVariant var;
   var.Set(static_cast<int8_t>('v'));
-  EXPECT_EQ(VT_I1, var.type());
+  ExpectVariantType(VT_I1, var);
   EXPECT_EQ('v', V_I1(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetSigned2Byte) {
+  ScopedVariant var;
   var.Set(static_cast<short>(123));
-  EXPECT_EQ(VT_I2, var.type());
+  ExpectVariantType(VT_I2, var);
   EXPECT_EQ(123, V_I2(var.ptr()));
+}
 
-  var.Set(static_cast<int32_t>(123));
-  EXPECT_EQ(VT_I4, var.type());
+TEST(ScopedVariantTest, SetSigned4Byte) {
+  ScopedVariant var;
+  var.Set(123);
+  ExpectVariantType(VT_I4, var);
   EXPECT_EQ(123, V_I4(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetSigned8Byte) {
+  ScopedVariant var;
   var.Set(static_cast<int64_t>(123));
-  EXPECT_EQ(VT_I8, var.type());
+  ExpectVariantType(VT_I8, var);
   EXPECT_EQ(123, V_I8(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetUnsigned1Byte) {
+  ScopedVariant var;
   var.Set(static_cast<uint8_t>(123));
-  EXPECT_EQ(VT_UI1, var.type());
+  ExpectVariantType(VT_UI1, var);
   EXPECT_EQ(123u, V_UI1(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetUnsigned2Byte) {
+  ScopedVariant var;
   var.Set(static_cast<unsigned short>(123));
-  EXPECT_EQ(VT_UI2, var.type());
+  ExpectVariantType(VT_UI2, var);
   EXPECT_EQ(123u, V_UI2(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetUnsigned4Byte) {
+  ScopedVariant var;
   var.Set(static_cast<uint32_t>(123));
-  EXPECT_EQ(VT_UI4, var.type());
+  ExpectVariantType(VT_UI4, var);
   EXPECT_EQ(123u, V_UI4(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetUnsigned8Byte) {
+  ScopedVariant var;
   var.Set(static_cast<uint64_t>(123));
-  EXPECT_EQ(VT_UI8, var.type());
+  ExpectVariantType(VT_UI8, var);
   EXPECT_EQ(123u, V_UI8(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetReal4Byte) {
+  ScopedVariant var;
   var.Set(123.123f);
-  EXPECT_EQ(VT_R4, var.type());
+  ExpectVariantType(VT_R4, var);
   EXPECT_EQ(123.123f, V_R4(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetReal8Byte) {
+  ScopedVariant var;
   var.Set(static_cast<double>(123.123));
-  EXPECT_EQ(VT_R8, var.type());
+  ExpectVariantType(VT_R8, var);
   EXPECT_EQ(123.123, V_R8(var.ptr()));
+}
 
+TEST(ScopedVariantTest, SetBooleanTrue) {
+  ScopedVariant var;
   var.Set(true);
-  EXPECT_EQ(VT_BOOL, var.type());
+  ExpectVariantType(VT_BOOL, var);
   EXPECT_EQ(VARIANT_TRUE, V_BOOL(var.ptr()));
+}
+
+TEST(ScopedVariantTest, SetBooleanFalse) {
+  ScopedVariant var;
   var.Set(false);
-  EXPECT_EQ(VT_BOOL, var.type());
+  ExpectVariantType(VT_BOOL, var);
   EXPECT_EQ(VARIANT_FALSE, V_BOOL(var.ptr()));
+}
 
-  // Com interface tests
-
-  var.Set(static_cast<IDispatch*>(nullptr));
-  EXPECT_EQ(VT_DISPATCH, var.type());
-  EXPECT_EQ(nullptr, V_DISPATCH(var.ptr()));
-  var.Reset();
-
-  var.Set(static_cast<IUnknown*>(nullptr));
-  EXPECT_EQ(VT_UNKNOWN, var.type());
-  EXPECT_EQ(nullptr, V_UNKNOWN(var.ptr()));
-  var.Reset();
-
+TEST(ScopedVariantTest, SetComIDispatch) {
+  ScopedVariant var;
   Microsoft::WRL::ComPtr<IDispatch> dispatch_stub =
       Microsoft::WRL::Make<DispatchStub>();
   ExpectRefCount(1U, dispatch_stub.Get());
   var.Set(dispatch_stub.Get());
-  EXPECT_EQ(VT_DISPATCH, var.type());
+  ExpectVariantType(VT_DISPATCH, var);
   EXPECT_EQ(dispatch_stub.Get(), V_DISPATCH(var.ptr()));
   ExpectRefCount(2U, dispatch_stub.Get());
   var.Reset();
   ExpectRefCount(1U, dispatch_stub.Get());
+}
 
-  // A separate instance to handle IUnknown makes refcount checking easier.
+TEST(ScopedVariantTest, SetComNullIDispatch) {
+  ScopedVariant var;
+  var.Set(static_cast<IDispatch*>(nullptr));
+  ExpectVariantType(VT_DISPATCH, var);
+  EXPECT_EQ(nullptr, V_DISPATCH(var.ptr()));
+}
+
+TEST(ScopedVariantTest, SetComIUnknown) {
+  ScopedVariant var;
   Microsoft::WRL::ComPtr<IUnknown> unknown_stub =
       Microsoft::WRL::Make<DispatchStub>();
   ExpectRefCount(1U, unknown_stub.Get());
   var.Set(unknown_stub.Get());
-  EXPECT_EQ(VT_UNKNOWN, var.type());
+  ExpectVariantType(VT_UNKNOWN, var);
   EXPECT_EQ(unknown_stub.Get(), V_UNKNOWN(var.ptr()));
   ExpectRefCount(2U, unknown_stub.Get());
   var.Reset();
   ExpectRefCount(1U, unknown_stub.Get());
+}
 
+TEST(ScopedVariantTest, SetComNullIUnknown) {
+  ScopedVariant var;
+  var.Set(static_cast<IUnknown*>(nullptr));
+  ExpectVariantType(VT_UNKNOWN, var);
+  EXPECT_EQ(nullptr, V_UNKNOWN(var.ptr()));
+}
+
+TEST(ScopedVariant, ScopedComIDispatchConstructor) {
+  Microsoft::WRL::ComPtr<IDispatch> dispatch_stub =
+      Microsoft::WRL::Make<DispatchStub>();
   {
-    ScopedVariant disp_var(dispatch_stub.Get());
-    EXPECT_EQ(VT_DISPATCH, disp_var.type());
-    EXPECT_EQ(dispatch_stub.Get(), V_DISPATCH(disp_var.ptr()));
+    ScopedVariant var(dispatch_stub.Get());
+    ExpectVariantType(VT_DISPATCH, var);
+    EXPECT_EQ(dispatch_stub.Get(), V_DISPATCH(var.ptr()));
     ExpectRefCount(2U, dispatch_stub.Get());
   }
   ExpectRefCount(1U, dispatch_stub.Get());
+}
 
+TEST(ScopedVariant, ScopedComIDispatchMove) {
+  Microsoft::WRL::ComPtr<IDispatch> dispatch_stub =
+      Microsoft::WRL::Make<DispatchStub>();
   {
-    ScopedVariant ref1(dispatch_stub.Get());
+    ScopedVariant var1(dispatch_stub.Get());
     ExpectRefCount(2U, dispatch_stub.Get());
-    ScopedVariant ref2(std::move(ref1));
+    ScopedVariant var2(std::move(var1));
     ExpectRefCount(2U, dispatch_stub.Get());
-    ScopedVariant ref3;
-    ref3 = std::move(ref2);
+    ScopedVariant var3;
+    var3 = std::move(var2);
     ExpectRefCount(2U, dispatch_stub.Get());
   }
   ExpectRefCount(1U, dispatch_stub.Get());
+}
 
+TEST(ScopedVariant, ScopedComIDispatchCopy) {
+  Microsoft::WRL::ComPtr<IDispatch> dispatch_stub =
+      Microsoft::WRL::Make<DispatchStub>();
   {
-    ScopedVariant ref1(dispatch_stub.Get());
+    ScopedVariant var1(dispatch_stub.Get());
     ExpectRefCount(2U, dispatch_stub.Get());
-    ScopedVariant ref2(static_cast<const VARIANT&>(ref1));
+    ScopedVariant var2(static_cast<const VARIANT&>(var1));
     ExpectRefCount(3U, dispatch_stub.Get());
-    ScopedVariant ref3;
-    ref3 = static_cast<const VARIANT&>(ref2);
+    ScopedVariant var3;
+    var3 = static_cast<const VARIANT&>(var2);
     ExpectRefCount(4U, dispatch_stub.Get());
   }
   ExpectRefCount(1U, dispatch_stub.Get());
+}
 
+TEST(ScopedVariant, ScopedComIUnknownConstructor) {
+  Microsoft::WRL::ComPtr<IUnknown> unknown_stub =
+      Microsoft::WRL::Make<DispatchStub>();
   {
     ScopedVariant unk_var(unknown_stub.Get());
-    EXPECT_EQ(VT_UNKNOWN, unk_var.type());
+    ExpectVariantType(VT_UNKNOWN, unk_var);
     EXPECT_EQ(unknown_stub.Get(), V_UNKNOWN(unk_var.ptr()));
     ExpectRefCount(2U, unknown_stub.Get());
   }
   ExpectRefCount(1U, unknown_stub.Get());
+}
 
+TEST(ScopedVariant, ScopedComIUnknownWithRawVariant) {
+  ScopedVariant var;
+  Microsoft::WRL::ComPtr<IUnknown> unknown_stub =
+      Microsoft::WRL::Make<DispatchStub>();
   VARIANT raw;
   raw.vt = VT_UNKNOWN;
   raw.punkVal = unknown_stub.Get();
@@ -254,25 +360,25 @@
   ExpectRefCount(2U, unknown_stub.Get());
   var.Reset();
   ExpectRefCount(1U, unknown_stub.Get());
+}
 
-  {
-    ScopedVariant number(123);
-    EXPECT_EQ(VT_I4, number.type());
-    EXPECT_EQ(123, V_I4(number.ptr()));
-  }
-
-  // SAFEARRAY tests
-  var.Set(static_cast<SAFEARRAY*>(nullptr));
-  EXPECT_EQ(VT_EMPTY, var.type());
-
+TEST(ScopedVariant, SetSafeArray) {
   SAFEARRAY* sa = ::SafeArrayCreateVector(VT_UI1, 0, 100);
-  ASSERT_TRUE(sa != nullptr);
+  ASSERT_TRUE(sa);
 
+  ScopedVariant var;
   var.Set(sa);
   EXPECT_TRUE(ScopedVariant::IsLeakableVarType(var.type()));
-  EXPECT_EQ(VT_ARRAY | VT_UI1, var.type());
+  ExpectVariantType(static_cast<VARENUM>(VT_ARRAY | VT_UI1), var);
   EXPECT_EQ(sa, V_ARRAY(var.ptr()));
   // The array is destroyed in the destructor of var.
+  sa = nullptr;
+}
+
+TEST(ScopedVariant, SetNullSafeArray) {
+  ScopedVariant var;
+  var.Set(static_cast<SAFEARRAY*>(nullptr));
+  ExpectVariantType(VT_EMPTY, var);
 }
 
 }  // namespace win
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn
index 0548eff..9a84d39 100644
--- a/build/android/BUILD.gn
+++ b/build/android/BUILD.gn
@@ -8,8 +8,6 @@
 import("//build_overrides/build.gni")
 
 if (enable_java_templates) {
-  sun_tools_jar_path = "$root_gen_dir/sun_tools_jar/tools.jar"
-
   # Create or update the API versions cache if necessary by running a
   # functionally empty lint task. This prevents racy creation of the
   # cache while linting java targets in android_lint.
@@ -18,23 +16,6 @@
     create_cache = true
   }
 
-  action("find_sun_tools_jar") {
-    script = "//build/android/gyp/find_sun_tools_jar.py"
-    depfile = "$target_gen_dir/$target_name.d"
-    outputs = [ sun_tools_jar_path ]
-    args = [
-      "--depfile",
-      rebase_path(depfile, root_build_dir),
-      "--output",
-      rebase_path(sun_tools_jar_path, root_build_dir),
-    ]
-  }
-
-  java_prebuilt("sun_tools_java") {
-    jar_path = sun_tools_jar_path
-    deps = [ ":find_sun_tools_jar" ]
-  }
-
   # Write to a file some GN vars that are useful to scripts that use the output
   # directory. Format is chosen as easliy importable by both python and bash.
   _lines = [
diff --git a/build/android/gyp/compile_java.py b/build/android/gyp/compile_java.py
index 4cba0aa..85d66984 100755
--- a/build/android/gyp/compile_java.py
+++ b/build/android/gyp/compile_java.py
@@ -421,6 +421,7 @@
       build_utils.CheckOutput(
           cmd,
           print_stdout=options.chromium_code,
+          stdout_filter=ProcessJavacOutput,
           stderr_filter=ProcessJavacOutput)
       logging.info('Finished build command')
 
diff --git a/build/android/gyp/find_sun_tools_jar.py b/build/android/gyp/find_sun_tools_jar.py
deleted file mode 100755
index 7cd4c3398..0000000
--- a/build/android/gyp/find_sun_tools_jar.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""This finds the java distribution's tools.jar and copies it somewhere.
-"""
-
-import argparse
-import os
-import re
-import shutil
-import sys
-
-from util import build_utils
-
-RT_JAR_FINDER = re.compile(r'\[Opened (.*)/jre/lib/rt.jar\]')
-
-def main():
-  parser = argparse.ArgumentParser(description='Find Sun Tools Jar')
-  parser.add_argument('--depfile',
-                      help='Path to depfile. This must be specified as the '
-                           'action\'s first output.')
-  parser.add_argument('--output', required=True)
-  args = parser.parse_args()
-
-  sun_tools_jar_path = FindSunToolsJarPath()
-
-  if sun_tools_jar_path is None:
-    raise Exception("Couldn\'t find tools.jar")
-
-  # Using copyfile instead of copy() because copy() calls copymode()
-  # We don't want the locked mode because we may copy over this file again
-  shutil.copyfile(sun_tools_jar_path, args.output)
-
-  if args.depfile:
-    build_utils.WriteDepfile(args.depfile, args.output, [sun_tools_jar_path])
-
-
-def FindSunToolsJarPath():
-  # This works with at least openjdk 1.6, 1.7 and sun java 1.6, 1.7
-  stdout = build_utils.CheckOutput(
-      ["java", "-verbose", "-version"], print_stderr=False)
-  for ln in stdout.splitlines():
-    match = RT_JAR_FINDER.match(ln)
-    if match:
-      return os.path.join(match.group(1), 'lib', 'tools.jar')
-
-  return None
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py
index 514ba0b..84c6872 100755
--- a/build/android/gyp/write_build_config.py
+++ b/build/android/gyp/write_build_config.py
@@ -1104,6 +1104,7 @@
   if options.base_module_build_config:
     with open(options.base_module_build_config, 'r') as f:
       base_module_build_config = json.load(f)
+      all_inputs.append(options.base_module_build_config)
 
   # Initialize some common config.
   # Any value that needs to be queryable by dependents must go within deps_info.
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 67c7b196..07cd2e0 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -2783,11 +2783,13 @@
         "--generated-dir=$_rebased_generated_dir",
         "--jar-path=$_rebased_javac_jar_path",
         "--java-srcjars=$_rebased_java_srcjars",
-        "--java-version=1.8",
         "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)",
         "--processorpath=@FileArg($_rebased_build_config:javac:processor_classpath)",
         "--processors=@FileArg($_rebased_build_config:javac:processor_classes)",
       ]
+      if (invoker.supports_android) {
+        args += [ "--java-version=1.8" ]
+      }
       if (use_java_goma) {
         args += [ "--gomacc-path=$goma_dir/gomacc" ]
       }
@@ -3333,6 +3335,7 @@
           }
           srcjar_deps = _srcjar_deps
           chromium_code = _chromium_code
+          supports_android = _supports_android
           requires_android = _requires_android
           deps = _accumulated_deps + _accumulated_public_deps
 
diff --git a/build/config/chromeos/BUILD.gn b/build/config/chromeos/BUILD.gn
index ecadcf6..1e9f783 100644
--- a/build/config/chromeos/BUILD.gn
+++ b/build/config/chromeos/BUILD.gn
@@ -11,9 +11,6 @@
   # https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf?
   # to a file, used for generating orderfiles in Chrome OS
   dump_call_chain_clustering_order = ""
-
-  # Enable pattern initialization. Disabled by default. http://crbug.com/966551
-  enable_pattern_initialization = false
 }
 
 declare_args() {
@@ -34,8 +31,4 @@
     cflags = [ "-fexperimental-new-pass-manager" ]
     ldflags = [ "-fexperimental-new-pass-manager" ]
   }
-
-  if (enable_pattern_initialization) {
-    cflags = [ "-ftrivial-auto-var-init=pattern" ]
-  }
 }
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 01709544..ed11a70 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1998,8 +1998,6 @@
     # Favor size over speed, /O1 must be before the common flags.
     # /O1 implies /Os and /GF.
     cflags = [ "/O1" ] + common_optimize_on_cflags + [ "/Oi" ]
-    # TODO(thakis): Use -flto in official builds here eventually,
-    # https://crbug.com/598772
   } else if (optimize_for_size && !is_nacl) {
     # Favor size over speed.
     # TODO(crbug.com/718650): Fix -Os in PNaCl compiler and remove the is_nacl
@@ -2067,13 +2065,6 @@
       # Favor speed over size, /O2 must be before the common flags.
       # /O2 implies /Ot, /Oi, and /GF.
       cflags = [ "/O2" ] + common_optimize_on_cflags
-
-      if (is_official_build) {
-        if (!is_clang) {
-          cflags += [ "/GL" ]  # Whole program optimization.
-        }
-        # TODO(crbug.com/598772): Enable -flto for Clang.
-      }
     } else if (optimize_for_fuzzing) {
       cflags = [ "-O1" ] + common_optimize_on_cflags
     } else {
@@ -2106,11 +2097,6 @@
       # Favor speed over size, /O2 must be before the common flags.
       # /O2 implies /Ot, /Oi, and /GF.
       cflags = [ "/O2" ] + common_optimize_on_cflags
-
-      # TODO(thakis): Remove is_clang here, https://crbug.com/598772
-      if (is_official_build && !is_clang) {
-        cflags += [ "/GL" ]  # Whole program optimization.
-      }
     } else if (optimize_for_fuzzing) {
       cflags = [ "-O1" ] + common_optimize_on_cflags
     } else {
@@ -2237,24 +2223,13 @@
 # Full symbols.
 config("symbols") {
   if (is_win) {
-    if (use_goma || is_clang) {
-      # Note that with VC++ this requires is_win_fastlink, enforced elsewhere.
+    if (is_clang) {
       cflags = [ "/Z7" ]  # Debug information in the .obj files.
     } else {
       cflags = [ "/Zi" ]  # Produce PDB file, no edit and continue.
     }
 
-    if (is_win_fastlink && !use_lld) {
-      # TODO(hans): is_win_fastlink=true is incompatible with use_lld. However,
-      # some developers might have enabled it manually, so to ease the
-      # transition to lld, just ignore it rather than asserting. Eventually we
-      # want to assert instead.
-
-      # Tell VS 2015+ to create a PDB that references debug
-      # information in .obj and .lib files instead of copying
-      # it all. This flag is incompatible with /PROFILE
-      ldflags = [ "/DEBUG:FASTLINK" ]
-    } else if (is_clang && use_lld && use_ghash) {
+    if (is_clang && use_lld && use_ghash) {
       cflags += [ "-gcodeview-ghash" ]
       ldflags = [ "/DEBUG:GHASH" ]
     } else {
@@ -2265,19 +2240,13 @@
     configs = [ ":win_pdbaltpath" ]
 
     if (is_clang) {
-      # /DEBUG:FASTLINK requires every object file to have standalone debug
-      # information.
-      if (is_win_fastlink && !use_lld) {
-        cflags += [ "-fstandalone-debug" ]
-      } else {
-        # Use constructor debug mode. This option reduces debug info more than
-        # -fno-standalone-debug by emitting class type information only when
-        # constructors are emitted.
-        cflags += [
-          "-Xclang",
-          "-debug-info-kind=constructor",
-        ]
-      }
+      # Use constructor debug mode. This option reduces debug info more than
+      # -fno-standalone-debug by emitting class type information only when
+      # constructors are emitted.
+      cflags += [
+        "-Xclang",
+        "-debug-info-kind=constructor",
+      ]
     }
   } else {
     cflags = []
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 365ee5c..7e7b290 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -78,11 +78,6 @@
   # to use (for linkers that support this).
   max_jobs_per_link = 8
 
-  # Tell VS to create a PDB that references information in .obj files rather
-  # than copying it all. This should improve linker performance. mspdbcmf.exe
-  # can be used to convert a fastlink pdb to a normal one.
-  is_win_fastlink = false
-
   # Whether we're using a sample profile collected on an architecture different
   # than the one we're compiling for.
   #
@@ -254,13 +249,6 @@
   } else {
     symbol_level = 0
   }
-} else if (symbol_level == 2) {
-  if (is_win) {
-    # See crbug.com/630074
-    assert(is_win_fastlink || use_lld || !use_goma || is_clang,
-           "VC++ goma builds that use symbol_level 2 must use " +
-               "is_win_fastlink or use_lld.")
-  }
 }
 
 # Non-component debug builds with symbol_level = 2 are an undesirable (very slow
diff --git a/build/config/ui.gni b/build/config/ui.gni
index ce69f96..3a515a3b 100644
--- a/build/config/ui.gni
+++ b/build/config/ui.gni
@@ -21,8 +21,7 @@
 
 declare_args() {
   # Indicates if Ozone is enabled. Ozone is a low-level library layer for Linux
-  # that does not require X11. Enabling this feature disables use of glib, x11,
-  # Pango, and Cairo.
+  # that does not require X11. Enabling this feature disables use of x11.
   use_ozone = is_chromeos || (is_chromecast && !is_android) || is_fuchsia
 
   # Indicates if Aura is enabled. Aura is a low-level windowing library, sort
@@ -35,7 +34,7 @@
   toolkit_views = is_mac || is_win || is_linux || (is_fuchsia && is_chromecast)
 
   # Whether we should use glib, a low level C utility library.
-  use_glib = is_linux && !use_ozone
+  use_glib = is_desktop_linux && !is_chromecast
 }
 
 # Additional dependent variables -----------------------------------------------
@@ -49,7 +48,7 @@
 assert(!use_glib || (is_linux && !is_chromeos && !is_chromecast))
 
 # Whether to use atk, the Accessibility ToolKit library
-use_atk = is_desktop_linux && use_x11
+use_atk = is_desktop_linux && !is_chromecast
 # =============================================
 #   PLEASE DO NOT ADD MORE FLAGS TO THIS FILE
 # =============================================
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 37a710e..29b9f11 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -152,8 +152,7 @@
 
   if (!is_debug && !is_component_build) {
     # Enable standard linker optimizations like GC (/OPT:REF) and ICF in static
-    # release builds. These are implied by /PROFILE below, but /PROFILE is
-    # incompatible with /debug:fastlink.
+    # release builds.
     # Release builds always want these optimizations, so enable them explicitly.
     ldflags += [
       "/OPT:REF",
@@ -173,9 +172,7 @@
     # PDB file by about 5%) but does not otherwise alter the output binary. It
     # is enabled opportunistically for builds where it is not prohibited (not
     # supported when incrementally linking, or using /debug:fastlink).
-    if (!is_win_fastlink) {
-      ldflags += [ "/PROFILE" ]
-    }
+    ldflags += [ "/PROFILE" ]
   }
 
   # arflags apply only to static_libraries. The normal linker configs are only
@@ -217,10 +214,6 @@
     "_SECURE_ATL",
   ]
 
-  # This is required for ATL to use XP-safe versions of its functions.
-  # TODO(thakis): We no longer support XP; try removing this.
-  defines += [ "_USING_V110_SDK71_" ]
-
   if (current_os == "winuwp") {
     # When targeting Windows Runtime, certain compiler/linker flags are
     # necessary.
diff --git a/build/fuchsia/common_args.py b/build/fuchsia/common_args.py
index 038ccdf8..9646452 100644
--- a/build/fuchsia/common_args.py
+++ b/build/fuchsia/common_args.py
@@ -78,10 +78,11 @@
   common_args.add_argument('--memory', type=int, default=2048,
                            help='Sets the RAM size (MB) if launching in a VM'),
   common_args.add_argument(
-      '--no-kvm',
-      action='store_true',
-      default=False,
-      help='Disable KVM virtualization.')
+      '--allow-no-kvm',
+      action='store_false',
+      dest='require_kvm',
+      default=True,
+      help='Do not require KVM acceleration for emulators.')
   common_args.add_argument(
       '--os_check', choices=['check', 'update', 'ignore'],
       default='update',
@@ -120,7 +121,7 @@
       logging.DEBUG if args.verbose else logging.WARN)
 
 
-def GetDeploymentTargetForArgs(args, require_kvm=False):
+def GetDeploymentTargetForArgs(args):
   """Constructs a deployment target object using parameters taken from
   command line arguments."""
   if args.system_log_file == '-':
@@ -150,10 +151,12 @@
                          'os_check':args.os_check })
     return DeviceTarget(**target_args)
   else:
-    target_args.update({ 'cpu_cores':args.qemu_cpu_cores,
-                         'require_kvm':not args.no_kvm,
-                         'emu_type':args.device,
-                         'ram_size_mb':args.memory })
+    target_args.update({
+        'cpu_cores': args.qemu_cpu_cores,
+        'require_kvm': args.require_kvm,
+        'emu_type': args.device,
+        'ram_size_mb': args.memory
+    })
     if args.device == 'qemu':
       return QemuTarget(**target_args)
     else:
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a3290da5..aedf5f2 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200225.0.1
\ No newline at end of file
+0.20200225.1.1
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a3290da5..aedf5f2 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200225.0.1
\ No newline at end of file
+0.20200225.1.1
\ No newline at end of file
diff --git a/build/fuchsia/qemu_target.py b/build/fuchsia/qemu_target.py
index 14fe109..bdc397e 100644
--- a/build/fuchsia/qemu_target.py
+++ b/build/fuchsia/qemu_target.py
@@ -20,6 +20,7 @@
 from common import GetHostArchFromPlatform, GetEmuRootForPlatform
 from common import EnsurePathExists
 from qemu_image import ExecQemuImgWithRetry
+from target import FuchsiaTargetException
 
 
 # Virtual networking configuration data for QEMU.
@@ -46,14 +47,17 @@
     return self._emu_type
 
   def _IsKvmEnabled(self):
-    if self._require_kvm:
-      if (sys.platform.startswith('linux') and
-          os.access('/dev/kvm', os.R_OK | os.W_OK)):
-        if self._target_cpu == 'arm64' and platform.machine() == 'aarch64':
-          return True
-        if self._target_cpu == 'x64' and platform.machine() == 'x86_64':
-          return True
-    return False
+    kvm_supported = sys.platform.startswith('linux') and \
+                    os.access('/dev/kvm', os.R_OK | os.W_OK)
+    same_arch = \
+        (self._target_cpu == 'arm64' and platform.machine() == 'aarch64') or \
+        (self._target_cpu == 'x64' and platform.machine() == 'x86_64')
+    if kvm_supported and same_arch:
+      return True
+    elif self._require_kvm:
+      raise FuchsiaTargetException('KVM required but unavailable.')
+    else:
+      return False
 
   def _BuildQemuConfig(self):
     boot_data.AssertBootImagesExist(self._GetTargetSdkArch(), 'qemu')
@@ -210,5 +214,3 @@
     blobstore_hash_file.write(current_blobstore_hash)
 
   return qcow_path
-
-
diff --git a/build/fuchsia/test_runner.py b/build/fuchsia/test_runner.py
index 845d866..f60e4f0 100755
--- a/build/fuchsia/test_runner.py
+++ b/build/fuchsia/test_runner.py
@@ -116,10 +116,7 @@
   if args.child_args:
     child_args.extend(args.child_args)
 
-  # KVM is required on x64 test bots.
-  require_kvm = args.test_launcher_bot_mode and args.target_cpu == 'x64'
-
-  with GetDeploymentTargetForArgs(args, require_kvm=require_kvm) as target:
+  with GetDeploymentTargetForArgs(args) as target:
     target.Start()
 
     if args.test_launcher_filter_file:
diff --git a/cc/scheduler/begin_frame_tracker.h b/cc/scheduler/begin_frame_tracker.h
index 81c27d5a..2f5f3b1 100644
--- a/cc/scheduler/begin_frame_tracker.h
+++ b/cc/scheduler/begin_frame_tracker.h
@@ -8,14 +8,11 @@
 #include <set>
 #include <string>
 
-#include "base/location.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "cc/cc_export.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 
-#define BEGINFRAMETRACKER_FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION("")
-
 namespace perfetto {
 namespace protos {
 namespace pbzero {
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 3c37539..96b6ca8c77 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -8,6 +8,7 @@
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "base/trace_event/trace_event.h"
@@ -39,7 +40,7 @@
       layer_tree_host_id_(layer_tree_host_id),
       task_runner_(task_runner),
       compositor_timing_history_(std::move(compositor_timing_history)),
-      begin_impl_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE),
+      begin_impl_frame_tracker_(FROM_HERE),
       state_machine_(settings) {
   TRACE_EVENT1("cc", "Scheduler::Scheduler", "settings", settings_.AsValue());
   DCHECK(client_);
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 5ee19bf..2cbd02e 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -18,6 +18,7 @@
 #include "base/containers/flat_map.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/dump_without_crashing.h"
+#include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/metrics/histogram.h"
@@ -259,7 +260,7 @@
     : client_(client),
       scheduling_client_(scheduling_client),
       task_runner_provider_(task_runner_provider),
-      current_begin_frame_tracker_(BEGINFRAMETRACKER_FROM_HERE),
+      current_begin_frame_tracker_(FROM_HERE),
       compositor_frame_reporting_controller_(
           std::make_unique<CompositorFrameReportingController>(
               settings.single_thread_proxy_scheduler)),
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index fb9f477..ce15747 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -437,7 +437,6 @@
   "javatests/src/org/chromium/chrome/browser/share/ShareButtonControllerTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareDelegateImplTest.java",
-  "javatests/src/org/chromium/chrome/browser/share/ShareHelperTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareSheetCoordinatorTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareSheetPropertyModelBuilderTest.java",
   "javatests/src/org/chromium/chrome/browser/share/ShareUrlTest.java",
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml
index 4bc6b90..ef60622 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/address_accessory_sheet.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<android.support.v7.widget.RecyclerView
+<androidx.recyclerview.widget.RecyclerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/addresses_sheet"
     android:fillViewport="true"
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
index 97ee1dbd..6c9289e 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/credit_card_accessory_sheet.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<android.support.v7.widget.RecyclerView
+<androidx.recyclerview.widget.RecyclerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/credit_card_sheet"
     android:fillViewport="true"
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory.xml
index fe4e134..32d2c77 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory.xml
@@ -40,7 +40,7 @@
 
         <View style="@style/VerticalDivider" />
 
-        <android.support.v7.widget.RecyclerView
+        <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/bar_items_view"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"/>
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml
index 8950ce51..4062f56 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/keyboard_accessory_modern.xml
@@ -58,7 +58,7 @@
             android:background="?android:attr/selectableItemBackground"
             android:visibility="gone"/>
 
-        <android.support.v7.widget.RecyclerView
+        <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/bar_items_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml b/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml
index 3151229a..ee63478 100644
--- a/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml
+++ b/chrome/android/features/keyboard_accessory/internal/java/res/layout/password_accessory_sheet.xml
@@ -3,7 +3,7 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<android.support.v7.widget.RecyclerView
+<androidx.recyclerview.widget.RecyclerView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/passwords_sheet"
     android:fillViewport="true"
diff --git a/chrome/android/features/start_surface/internal/java/res/layout/ss_bottom_bar_layout.xml b/chrome/android/features/start_surface/internal/java/res/layout/ss_bottom_bar_layout.xml
index ed85e9d..08cecdb 100644
--- a/chrome/android/features/start_surface/internal/java/res/layout/ss_bottom_bar_layout.xml
+++ b/chrome/android/features/start_surface/internal/java/res/layout/ss_bottom_bar_layout.xml
@@ -19,7 +19,7 @@
         android:scaleType="fitXY"
         android:scaleY="-1"
         android:importantForAccessibility="no" />
-    <android.support.design.widget.TabLayout
+    <com.google.android.material.tabs.TabLayout
         android:id="@+id/bottom_tab_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -28,9 +28,9 @@
         app:tabGravity="fill"
         app:tabMode="fixed"
         app:tabMaxWidth="2000dp">
-        <android.support.design.widget.TabItem
+        <com.google.android.material.tabs.TabItem
             android:layout="@layout/ss_home_button"/>
-        <android.support.design.widget.TabItem
+        <com.google.android.material.tabs.TabItem
             android:layout="@layout/ss_explore_button"/>
-    </android.support.design.widget.TabLayout>
+    </com.google.android.material.tabs.TabLayout>
 </org.chromium.chrome.features.start_surface.BottomBarView>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
index cdb38b6e..c19f7e0 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiFeatureUtilities.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 
 /**
@@ -11,11 +13,12 @@
  */
 public class TabUiFeatureUtilities {
     private static Boolean sSearchTermChipEnabledForTesting;
+    private static Boolean sTabManagementModuleSupportedForTesting;
 
     /**
      * Set whether the search term chip in Grid tab switcher is enabled for testing.
      */
-    public static void setSearchTermChipEnabledForTesting(Boolean enabled) {
+    public static void setSearchTermChipEnabledForTesting(@Nullable Boolean enabled) {
         sSearchTermChipEnabledForTesting = enabled;
     }
 
@@ -27,4 +30,22 @@
         return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
                 ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, "enable_search_term_chip", false);
     }
+
+    /**
+     * Set whether the tab management module is supported for testing.
+     */
+    public static void setTabManagementModuleSupportedForTesting(@Nullable Boolean enabled) {
+        sTabManagementModuleSupportedForTesting = enabled;
+    }
+
+    /**
+     * @return Whether the tab management module is supported.
+     */
+    public static boolean isTabManagementModuleSupported() {
+        if (sTabManagementModuleSupportedForTesting != null) {
+            return sTabManagementModuleSupportedForTesting;
+        }
+
+        return TabManagementModuleProvider.isTabManagementModuleSupported();
+    }
 }
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
index 9571487..1c35021 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogTest.java
@@ -106,6 +106,7 @@
     public void setUp() {
         CachedFeatureFlags.setGridTabSwitcherEnabledForTesting(true);
         CachedFeatureFlags.setTabGroupsAndroidEnabledForTesting(true);
+        TabUiFeatureUtilities.setTabManagementModuleSupportedForTesting(true);
         mActivityTestRule.startMainActivityFromLauncher();
         Layout layout = mActivityTestRule.getActivity().getLayoutManager().getOverviewLayout();
         assertTrue(layout instanceof StartSurfaceLayout);
@@ -119,6 +120,7 @@
     public void tearDown() {
         CachedFeatureFlags.setGridTabSwitcherEnabledForTesting(null);
         CachedFeatureFlags.setTabGroupsAndroidEnabledForTesting(null);
+        TabUiFeatureUtilities.setTabManagementModuleSupportedForTesting(null);
     }
 
     @Test
diff --git a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageCardRenderTest.java b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageCardRenderTest.java
index 24c7deb..3cd7eb7 100644
--- a/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageCardRenderTest.java
+++ b/chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/FeedNewTabPageCardRenderTest.java
@@ -52,7 +52,8 @@
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags
         .Add(ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE)
-        @Features.EnableFeatures(ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS)
+        @Features.EnableFeatures({ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS,
+                ChromeFeatureList.OMNIBOX_SEARCH_ENGINE_LOGO})
         public class FeedNewTabPageCardRenderTest {
     private static final String TEST_FEED_DATA_BASE_PATH = "/chrome/test/data/android/feed/";
 
diff --git a/chrome/android/java/res/layout/radio_button_group_homepage_preference.xml b/chrome/android/java/res/layout/radio_button_group_homepage_preference.xml
index ec5c722..56d9fbc7 100644
--- a/chrome/android/java/res/layout/radio_button_group_homepage_preference.xml
+++ b/chrome/android/java/res/layout/radio_button_group_homepage_preference.xml
@@ -8,8 +8,6 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:focusable="true"
-    android:focusableInTouchMode="true"
     android:orientation="vertical">
 
   <!-- TODO(crbug.com/1048859): Change to the exact style TextMessagePreference is using. -->
@@ -27,7 +25,8 @@
   <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionLayout
       android:id="@+id/radio_button_group"
       android:layout_width="match_parent"
-      android:layout_height="wrap_content">
+      android:layout_height="wrap_content"
+      android:focusableInTouchMode="true">
 
     <!-- TODO(crbug.com/1048863): Remove android:background for radio buttons here and below after enhancement. -->
     <org.chromium.components.browser_ui.widget.RadioButtonWithDescription
diff --git a/chrome/android/java/res/xml/homepage_preferences.xml b/chrome/android/java/res/xml/homepage_preferences.xml
index 57996e6..9b841a5b 100644
--- a/chrome/android/java/res/xml/homepage_preferences.xml
+++ b/chrome/android/java/res/xml/homepage_preferences.xml
@@ -21,6 +21,7 @@
     <org.chromium.chrome.browser.settings.homepage.RadioButtonGroupHomepagePreference
         android:key="homepage_radio_group"
         android:visibility="gone"
+        android:selectable="false"
         app:allowDividerBelow="false" />
 
     <org.chromium.chrome.browser.settings.TextMessagePreference
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
index 3b9ef759..af267d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/AppHooks.java
@@ -53,7 +53,8 @@
 import org.chromium.chrome.browser.usage_stats.DigitalWellbeingClient;
 import org.chromium.chrome.browser.webapps.GooglePlayWebApkInstallDelegate;
 import org.chromium.chrome.browser.webauth.Fido2ApiHandler;
-import org.chromium.chrome.browser.xsurface.SurfaceAdapterFactory;
+import org.chromium.chrome.browser.xsurface.SurfaceDependencyProvider;
+import org.chromium.chrome.browser.xsurface.SurfaceRenderer;
 import org.chromium.components.browser_ui.widget.FeatureHighlightProvider;
 import org.chromium.components.download.DownloadCollectionBridge;
 import org.chromium.components.signin.AccountManagerDelegate;
@@ -380,10 +381,11 @@
     }
 
     /**
-     * Returns a new {@link SurfaceAdapterFactory} if the xsurface implementation is included in the
+     * Returns a new {@link SurfaceRenderer} if the xsurface implementation is included in the
      * apk. Otherwise null is returned.
      */
-    public @Nullable SurfaceAdapterFactory createExternalSurfaceAdapterFactory() {
+    public @Nullable SurfaceRenderer createExternalSurfaceRenderer(
+            SurfaceDependencyProvider dependencies) {
         return null;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
index f3acf164..28ae9bb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivitySessionTracker.java
@@ -34,7 +34,7 @@
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.ProfileManagerUtils;
 import org.chromium.chrome.browser.settings.privacy.BrowsingDataBridge;
-import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.share.ShareImageFileUtils;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.translate.TranslateBridge;
 import org.chromium.ui.base.ResourceBundle;
@@ -181,7 +181,7 @@
         if (ApplicationStatus.isEveryActivityDestroyed()) {
             // These will all be re-initialized when a new Activity starts / upon next use.
             PartnerBrowserCustomizations.destroy();
-            ShareHelper.clearSharedImages();
+            ShareImageFileUtils.clearSharedImages();
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java
index 4b49dfe..78d25c3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeCachedFlags.java
@@ -41,7 +41,6 @@
         List<String> featuresToCache = Arrays.asList(ChromeFeatureList.HOMEPAGE_LOCATION_POLICY,
                 ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED, ChromeFeatureList.CHROME_DUET,
                 ChromeFeatureList.CHROME_DUET_ADAPTIVE, ChromeFeatureList.CHROME_DUET_LABELED,
-                ChromeFeatureList.ANDROID_NIGHT_MODE_CCT,
                 ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE,
                 ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS,
                 ChromeFeatureList.INTEREST_FEED_CONTENT_SUGGESTIONS,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index 87d83b1a..d9c818d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -27,6 +27,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver;
 import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.share.ShareImageFileUtils;
 import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
@@ -272,7 +273,7 @@
             public void onResult(byte[] result) {
                 if (mActivity == null) return;
 
-                ShareHelper.generateUriFromData(mActivity, result, callback);
+                ShareImageFileUtils.generateTemporaryUriFromData(mActivity, result, callback);
             }
         };
 
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 809e3d3..0af4b21 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
@@ -12,7 +12,6 @@
 import androidx.browser.customtabs.CustomTabsIntent;
 
 import org.chromium.base.ObserverList;
-import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
@@ -57,8 +56,7 @@
      * @param intent  The {@link Intent} to retrieve information about the initial state.
      */
     void initialize(AppCompatDelegate delegate, Intent intent) {
-        if (!NightModeUtils.isNightModeSupported()
-                || !CachedFeatureFlags.isNightModeForCustomTabsAvailable()) {
+        if (!NightModeUtils.isNightModeSupported()) {
             // Always stay in light mode if night mode is not available.
             mRequestedColorScheme = CustomTabsIntent.COLOR_SCHEME_LIGHT;
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
index 7210a76..56b55b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.prerender.ExternalPrerenderHandler;
-import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.share.ShareImageFileUtils;
 import org.chromium.chrome.browser.ssl.SecurityStateModel;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -211,7 +211,7 @@
         if (!mCustomTabsConnection.shouldSendNavigationInfoForSession(mSession)) return;
         if (tab.getWebContents() == null) return;
 
-        ShareHelper.captureScreenshotForContents(tab.getWebContents(), mContentBitmapWidth,
+        ShareImageFileUtils.captureScreenshotForContents(tab.getWebContents(), mContentBitmapWidth,
                 mContentBitmapHeight, (Uri snapshotPath) -> {
                     if (TextUtils.isEmpty(tab.getTitle()) && snapshotPath == null) return;
                     mCustomTabsConnection.sendNavigationInfo(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 2c40dd0..4a703c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -16,9 +16,10 @@
 import org.chromium.chrome.browser.device.DeviceClassManager;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
+import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.ui.base.DeviceFormFactor;
 
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -63,9 +64,11 @@
             put(ChromeFeatureList.PRIORITIZE_BOOTSTRAP_TASKS, true);
             put(ChromeFeatureList.IMMERSIVE_UI_MODE, false);
             put(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT, true);
-            put(ChromeFeatureList.ANDROID_NIGHT_MODE_CCT, true);
             put(ChromeFeatureList.START_SURFACE_ANDROID, false);
             put(ChromeFeatureList.PAINT_PREVIEW_TEST, false);
+            put(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, false);
+            put(ChromeFeatureList.TAB_GROUPS_ANDROID, false);
+            put(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID, false);
         }
     };
 
@@ -100,12 +103,16 @@
             put(ChromeFeatureList.SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT,
                     ChromePreferenceKeys
                             .FLAGS_CACHED_SWAP_PIXEL_FORMAT_TO_FIX_CONVERT_FROM_TRANSLUCENT);
-            put(ChromeFeatureList.ANDROID_NIGHT_MODE_CCT,
-                    ChromePreferenceKeys.FLAGS_CACHED_NIGHT_MODE_CCT_AVAILABLE);
             put(ChromeFeatureList.START_SURFACE_ANDROID,
                     ChromePreferenceKeys.FLAGS_CACHED_START_SURFACE_ENABLED);
             put(ChromeFeatureList.PAINT_PREVIEW_TEST,
                     ChromePreferenceKeys.FLAGS_CACHED_PAINT_PREVIEW_TEST_ENABLED_KEY);
+            put(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
+                    ChromePreferenceKeys.FLAGS_CACHED_GRID_TAB_SWITCHER_ENABLED);
+            put(ChromeFeatureList.TAB_GROUPS_ANDROID,
+                    ChromePreferenceKeys.FLAGS_CACHED_TAB_GROUPS_ANDROID_ENABLED);
+            put(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID,
+                    ChromePreferenceKeys.FLAGS_CACHED_DUET_TABSTRIP_INTEGRATION_ANDROID_ENABLED);
         }
     };
 
@@ -274,22 +281,6 @@
         return isEnabled(ChromeFeatureList.CHROME_DUET_LABELED) && isBottomToolbarEnabled();
     }
 
-    /**
-     * @return Whether or not night mode experiment is enabled (i.e. night mode experiment is
-     *         enabled) for custom tabs.
-     */
-    public static boolean isNightModeForCustomTabsAvailable() {
-        return isEnabled(ChromeFeatureList.ANDROID_NIGHT_MODE_CCT);
-    }
-
-    /**
-     * Toggles whether the night mode for custom tabs experiment is enabled. Must only be used for
-     * testing. Should be reset back to NULL after the test has finished.
-     */
-    public static void setNightModeForCustomTabsAvailableForTesting(Boolean available) {
-        setForTesting(ChromeFeatureList.ANDROID_NIGHT_MODE_CCT, available);
-    }
-
     public static boolean isCommandLineOnNonRootedEnabled() {
         return isEnabled(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED);
     }
@@ -318,10 +309,11 @@
 
     @VisibleForTesting
     static void cacheNativeTabSwitcherUiFlags() {
-        if (isEligibleForTabUiExperiments()) {
-            cacheGridTabSwitcherEnabled();
-            cacheTabGroupsAndroidEnabled();
-            cacheDuetTabStripIntegrationAndroidEnabled();
+        if (isEligibleForTabUiExperiments() && !DeviceClassManager.enableAccessibilityLayout()) {
+            List<String> featuresToCache = Arrays.asList(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
+                    ChromeFeatureList.TAB_GROUPS_ANDROID,
+                    ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID);
+            cacheNativeFlags(featuresToCache);
         }
     }
 
@@ -334,22 +326,6 @@
                         ChromePreferenceKeys.START_SURFACE_SINGLE_PANE_ENABLED_KEY, false);
     }
 
-    @VisibleForTesting
-    static void cacheGridTabSwitcherEnabled() {
-        SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getInstance();
-        String featureKey = ChromePreferenceKeys.FLAGS_CACHED_GRID_TAB_SWITCHER_ENABLED;
-        boolean shouldQueryFeatureFlag = !DeviceClassManager.enableAccessibilityLayout();
-        if (!shouldQueryFeatureFlag) {
-            sharedPreferencesManager.writeBoolean(featureKey, false);
-            return;
-        }
-        boolean queriedFlagValue =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID);
-        boolean gridTabSwitcherEnabled =
-                queriedFlagValue && TabManagementModuleProvider.isTabManagementModuleSupported();
-        sharedPreferencesManager.writeBoolean(featureKey, gridTabSwitcherEnabled);
-    }
-
     /**
      * @return Whether the Grid Tab Switcher UI is enabled and available for use.
      */
@@ -358,8 +334,8 @@
         // changing that setting while Chrome is alive.
         // Having Tab Groups or Start implies Grid Tab Switcher.
         return !(isTabGroupsAndroidContinuationChromeFlagEnabled() && SysUtils.isLowEndDevice())
-                && getConsistentBooleanValue(
-                        ChromePreferenceKeys.FLAGS_CACHED_GRID_TAB_SWITCHER_ENABLED, false)
+                && isEnabled(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID)
+                && TabUiFeatureUtilities.isTabManagementModuleSupported()
                 || isTabGroupsAndroidEnabled() || isStartSurfaceEnabled();
     }
 
@@ -369,48 +345,15 @@
      */
     @VisibleForTesting
     public static void setGridTabSwitcherEnabledForTesting(@Nullable Boolean enabled) {
-        sBoolValuesReturned.put(
-                ChromePreferenceKeys.FLAGS_CACHED_GRID_TAB_SWITCHER_ENABLED, enabled);
-    }
-
-    @VisibleForTesting
-    static void cacheTabGroupsAndroidEnabled() {
-        SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getInstance();
-        String featureKey = ChromePreferenceKeys.FLAGS_CACHED_TAB_GROUPS_ANDROID_ENABLED;
-        boolean shouldQueryFeatureFlag = !DeviceClassManager.enableAccessibilityLayout();
-        if (!shouldQueryFeatureFlag) {
-            sharedPreferencesManager.writeBoolean(featureKey, false);
-            return;
-        }
-        boolean queriedFlagValue =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_GROUPS_ANDROID);
-        boolean tabGroupsEnabled =
-                queriedFlagValue && TabManagementModuleProvider.isTabManagementModuleSupported();
-        sharedPreferencesManager.writeBoolean(featureKey, tabGroupsEnabled);
-    }
-
-    private static void cacheDuetTabStripIntegrationAndroidEnabled() {
-        SharedPreferencesManager sharedPreferencesManager = SharedPreferencesManager.getInstance();
-        String featureKey =
-                ChromePreferenceKeys.FLAGS_CACHED_DUET_TABSTRIP_INTEGRATION_ANDROID_ENABLED;
-        boolean shouldQueryFeatureFlag = !DeviceClassManager.enableAccessibilityLayout();
-        if (!shouldQueryFeatureFlag) {
-            sharedPreferencesManager.writeBoolean(featureKey, false);
-            return;
-        }
-        boolean queriedFlagValue =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID);
-        boolean duetTabStripIntegrationEnabled =
-                queriedFlagValue && TabManagementModuleProvider.isTabManagementModuleSupported();
-        sharedPreferencesManager.writeBoolean(featureKey, duetTabStripIntegrationEnabled);
+        setForTesting(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, enabled);
     }
 
     /**
      * @return Whether the tab group feature is enabled and available for use.
      */
     public static boolean isTabGroupsAndroidEnabled() {
-        return getConsistentBooleanValue(
-                ChromePreferenceKeys.FLAGS_CACHED_TAB_GROUPS_ANDROID_ENABLED, false);
+        return isEnabled(ChromeFeatureList.TAB_GROUPS_ANDROID)
+                && TabUiFeatureUtilities.isTabManagementModuleSupported();
     }
 
     /**
@@ -419,8 +362,7 @@
      */
     @VisibleForTesting
     public static void setTabGroupsAndroidEnabledForTesting(@Nullable Boolean available) {
-        sBoolValuesReturned.put(
-                ChromePreferenceKeys.FLAGS_CACHED_TAB_GROUPS_ANDROID_ENABLED, available);
+        setForTesting(ChromeFeatureList.TAB_GROUPS_ANDROID, available);
     }
 
     /**
@@ -440,9 +382,7 @@
     @VisibleForTesting
     public static void setDuetTabStripIntegrationAndroidEnabledForTesting(
             @Nullable Boolean isEnabled) {
-        sBoolValuesReturned.put(
-                ChromePreferenceKeys.FLAGS_CACHED_DUET_TABSTRIP_INTEGRATION_ANDROID_ENABLED,
-                isEnabled);
+        setForTesting(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID, isEnabled);
     }
 
     private static boolean isEligibleForTabUiExperiments() {
@@ -478,10 +418,9 @@
      * @return Whether the tab strip and duet integration feature is enabled and available for use.
      */
     public static boolean isDuetTabStripIntegrationAndroidEnabled() {
-        return isTabGroupsAndroidEnabled()
-                && getConsistentBooleanValue(
-                        ChromePreferenceKeys.FLAGS_CACHED_DUET_TABSTRIP_INTEGRATION_ANDROID_ENABLED,
-                        false);
+        return isEnabled(ChromeFeatureList.TAB_GROUPS_ANDROID)
+                && isEnabled(ChromeFeatureList.DUET_TABSTRIP_INTEGRATION_ANDROID)
+                && TabUiFeatureUtilities.isTabManagementModuleSupported();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 22539d8..98658d79 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -74,7 +74,7 @@
 import org.chromium.chrome.browser.rlz.RevenueStats;
 import org.chromium.chrome.browser.searchwidget.SearchWidgetProvider;
 import org.chromium.chrome.browser.services.GoogleServicesManager;
-import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.share.ShareImageFileUtils;
 import org.chromium.chrome.browser.sharing.shared_clipboard.SharedClipboardShareActivity;
 import org.chromium.chrome.browser.sync.SyncController;
 import org.chromium.chrome.browser.util.ConversionUtils;
@@ -321,7 +321,7 @@
 
                 PowerMonitor.create();
 
-                ShareHelper.clearSharedImages();
+                ShareImageFileUtils.clearSharedImages();
 
                 SelectFileDialog.clearCapturedCameraFiles();
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java
index bb54227..a7f9ec1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareDelegateImpl.java
@@ -181,7 +181,7 @@
         if (sScreenshotCaptureSkippedForTesting) {
             callback.onResult(null);
         } else {
-            ShareHelper.captureScreenshotForContents(webContents, 0, 0, callback);
+            ShareImageFileUtils.captureScreenshotForContents(webContents, 0, 0, callback);
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
index f53ddaf..12956cd4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/ShareHelper.java
@@ -23,7 +23,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
-import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
@@ -40,30 +39,17 @@
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.ApplicationState;
-import org.chromium.base.ApplicationStatus;
-import org.chromium.base.Callback;
-import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
 import org.chromium.base.PackageManagerUtils;
-import org.chromium.base.StreamUtil;
 import org.chromium.base.StrictModeContext;
 import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.task.AsyncTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.content_public.browser.RenderWidgetHostView;
-import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.UiUtils;
-import org.chromium.ui.base.Clipboard;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.base.WindowAndroid.IntentCallback;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
 
@@ -95,18 +81,10 @@
     /** The task ID of the activity that triggered the share action. */
     public static final String EXTRA_TASK_ID = "org.chromium.chrome.extra.TASK_ID";
 
-    private static final String JPEG_EXTENSION = ".jpg";
     private static final String PACKAGE_NAME_KEY_SUFFIX = "last_shared_package_name";
     private static final String CLASS_NAME_KEY_SUFFIX = "last_shared_class_name";
     private static final String EXTRA_SHARE_SCREENSHOT_AS_STREAM = "share_screenshot_as_stream";
 
-    /**
-     * Directory name for shared images.
-     *
-     * Named "screenshot" for historical reasons as we only initially shared screenshot images.
-     */
-    private static final String SHARE_IMAGES_DIRECTORY_NAME = "screenshot";
-
     /** Force the use of a Chrome-specific intent chooser, not the system chooser. */
     private static boolean sForceCustomChooserForTesting;
     private static boolean sIgnoreActivityNotFoundException;
@@ -148,67 +126,6 @@
     }
 
     /**
-     * Delete the |file|, if the |file| is a directory, delete the files and directories in the
-     * directory recursively.
-     *
-     * @param file The {@link File} or directory to be deleted.
-     * @param reservedFilepath The filepath should not to be deleted.
-     * @return Whether the |folder| has file to keep/reserve.
-     */
-    private static boolean deleteFiles(File file, @Nullable String reservedFilepath) {
-        if (!file.exists()) return false;
-        if (reservedFilepath != null && file.isFile()
-                && file.getPath().endsWith(reservedFilepath)) {
-            return true;
-        }
-
-        boolean anyChildKept = false;
-        if (file.isDirectory()) {
-            for (File child : file.listFiles()) {
-                anyChildKept |= deleteFiles(child, reservedFilepath);
-            }
-        }
-
-        // file.delete() will fail if |file| is a directory and has a file need to keep. In this
-        // case, the log should not been recorded since it is correct.
-        if (!anyChildKept && !file.delete()) {
-            Log.w(TAG, "Failed to delete share image file: %s", file.getAbsolutePath());
-            return true;
-        }
-        return anyChildKept;
-    }
-
-    /**
-     * Check if the file related to |fileUri| is in the |folder|.
-     *
-     * @param fileUri The {@link Uri} related to the file to be checked.
-     * @param folder The folder that may contain the |fileUrl|.
-     * @return Whether the |fileUri| is in the |folder|.
-     */
-    private static boolean isUriInDirectory(Uri fileUri, File folder) {
-        if (fileUri == null) return false;
-
-        Uri chromeUriPrefix = ContentUriUtils.getContentUriFromFile(folder);
-        if (chromeUriPrefix == null) return false;
-
-        return fileUri.toString().startsWith(chromeUriPrefix.toString());
-    }
-
-    /**
-     * Check if the system clipboard contains a Uri that comes from Chrome. If yes, return the file
-     * name from the Uri, otherwise return null.
-     *
-     * @return The file name if system clipboard contains a Uri from Chrome, otherwise return null.
-     */
-    private static String getClipboardCurrentFilepath() throws IOException {
-        Uri clipboardUri = Clipboard.getInstance().getImageUri();
-        if (isUriInDirectory(clipboardUri, getSharedFilesDirectory())) {
-            return clipboardUri.getPath();
-        }
-        return null;
-    }
-
-    /**
      * Force the use of a Chrome-specific intent chooser, not the system chooser.
      *
      * This emulates the behavior on pre Lollipop-MR1 systems, where the system chooser is not
@@ -351,30 +268,6 @@
         }
     }
 
-    /**
-     * Returns the directory where temporary files are stored to be shared with external
-     * applications. These files are deleted on startup and when there are no longer any active
-     * Activities.
-     *
-     * @return The directory where shared files are stored.
-     */
-    public static File getSharedFilesDirectory() throws IOException {
-        File imagePath = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext());
-        return new File(imagePath, SHARE_IMAGES_DIRECTORY_NAME);
-    }
-
-    /**
-     * Clears all shared image files.
-     */
-    public static void clearSharedImages() {
-        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
-            try {
-                deleteFiles(getSharedFilesDirectory(), getClipboardCurrentFilepath());
-            } catch (IOException ie) {
-                // Ignore exception.
-            }
-        });
-    }
 
     /**
      * Share directly with the last used share target.
@@ -388,61 +281,6 @@
         makeIntentAndShare(params, component);
     }
 
-    /**
-     * Generate a temporary URI for a set of JPEG bytes and provide that URI to a callback for
-     * sharing.
-     * @param activity The activity used to trigger the share action.
-     * @param jpegImageData The image data to be shared in jpeg format.
-     * @param callback A provided callback function which will act on the generated URI.
-     */
-    public static void generateUriFromData(
-            final Activity activity, final byte[] jpegImageData, Callback<Uri> callback) {
-        if (jpegImageData.length == 0) {
-            Log.w(TAG, "Share failed -- Received image contains no data.");
-            return;
-        }
-
-        new AsyncTask<Uri>() {
-            @Override
-            protected Uri doInBackground() {
-                FileOutputStream fOut = null;
-                try {
-                    File path = new File(UiUtils.getDirectoryForImageCapture(activity),
-                            SHARE_IMAGES_DIRECTORY_NAME);
-                    if (path.exists() || path.mkdir()) {
-                        File saveFile = File.createTempFile(
-                                String.valueOf(System.currentTimeMillis()), JPEG_EXTENSION, path);
-                        fOut = new FileOutputStream(saveFile);
-                        fOut.write(jpegImageData);
-                        fOut.flush();
-
-                        return ContentUriUtils.getContentUriFromFile(saveFile);
-                    } else {
-                        Log.w(TAG, "Share failed -- Unable to create share image directory.");
-                    }
-                } catch (IOException ie) {
-                    // Ignore exception.
-                } finally {
-                    StreamUtil.closeQuietly(fOut);
-                }
-
-                return null;
-            }
-
-            @Override
-            protected void onPostExecute(Uri imageUri) {
-                if (imageUri == null) {
-                    return;
-                }
-                if (ApplicationStatus.getStateForApplication()
-                        == ApplicationState.HAS_DESTROYED_ACTIVITIES) {
-                    return;
-                }
-
-                callback.onResult(imageUri);
-            }
-        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
-    }
 
     /**
      * Share an image URI with an activity identified by the provided Component Name.
@@ -488,67 +326,6 @@
         }
     }
 
-    private static class ExternallyVisibleUriCallback implements Callback<String> {
-        private Callback<Uri> mComposedCallback;
-        ExternallyVisibleUriCallback(Callback<Uri> cb) {
-            mComposedCallback = cb;
-        }
-
-        @Override
-        public void onResult(final String path) {
-            if (TextUtils.isEmpty(path)) {
-                mComposedCallback.onResult(null);
-                return;
-            }
-
-            new AsyncTask<Uri>() {
-                @Override
-                protected Uri doInBackground() {
-                    return ContentUriUtils.getContentUriFromFile(new File(path));
-                }
-
-                @Override
-                protected void onPostExecute(Uri uri) {
-                    mComposedCallback.onResult(uri);
-                }
-            }
-                    .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-        }
-    }
-
-    // TODO(yfriedman): Remove after internal tree is updated.
-    public static void saveScreenshotToDisk(
-            Bitmap screenshot, Context context, Callback<Uri> callback) {}
-
-    /**
-     * Captures a screenshot for the provided web contents, persists it and notifies the file
-     * provider that the file is ready to be accessed by the client.
-     *
-     * The screenshot is compressed to JPEG before being written to the file.
-     *
-     * @param contents The WebContents instance for which to capture a screenshot.
-     * @param width    The desired width of the resulting screenshot, or 0 for "auto."
-     * @param height   The desired height of the resulting screenshot, or 0 for "auto."
-     * @param callback The callback that will be called once the screenshot is saved.
-     */
-    public static void captureScreenshotForContents(
-            WebContents contents, int width, int height, Callback<Uri> callback) {
-        RenderWidgetHostView rwhv = contents.getRenderWidgetHostView();
-        if (rwhv == null) {
-          callback.onResult(null);
-          return;
-        }
-        try {
-            String path = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext())
-                    + File.separator + SHARE_IMAGES_DIRECTORY_NAME;
-            rwhv.writeContentBitmapToDiskAsync(
-                    width, height, path, new ExternallyVisibleUriCallback(callback));
-        } catch (IOException e) {
-            Log.e(TAG, "Error getting content bitmap: ", e);
-            callback.onResult(null);
-        }
-    }
-
     /**
      * Creates and shows a share intent picker dialog.
      *
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java
index 73d38cd..6172722 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java
@@ -15,9 +15,10 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
+import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.SyncController;
-import org.chromium.components.signin.ChromeSigninController;
+import org.chromium.components.signin.base.CoreAccountInfo;
 
 /**
  * This activity is used for requesting a sync passphrase from the user. Typically,
@@ -46,7 +47,8 @@
     @Override
     protected void onResume() {
         super.onResume();
-        Account account = ChromeSigninController.get().getSignedInUser();
+        Account account = CoreAccountInfo.getAndroidAccountFrom(
+                IdentityServicesProvider.get().getIdentityManager().getPrimaryAccountInfo());
         if (account == null) {
             finish();
             return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
index 8e0be87..bdd13012 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webshare/ShareServiceImpl.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.safe_browsing.SafeBrowsingBridge;
 import org.chromium.chrome.browser.share.ShareHelper;
+import org.chromium.chrome.browser.share.ShareImageFileUtils;
 import org.chromium.chrome.browser.share.ShareParams;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.mojo.system.MojoException;
@@ -224,7 +225,7 @@
                 ArrayList<Uri> fileUris = new ArrayList<>(files.length);
                 ArrayList<BlobReceiver> blobReceivers = new ArrayList<>(files.length);
                 try {
-                    File sharePath = ShareHelper.getSharedFilesDirectory();
+                    File sharePath = ShareImageFileUtils.getSharedFilesDirectory();
 
                     if (!sharePath.exists() && !sharePath.mkdir()) {
                         throw new IOException("Failed to create directory for shared file.");
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 04e674ef..70ed63f 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -13,6 +13,7 @@
     "//chrome/android/features/start_surface/internal/junit/start_surface_junit_java_sources.gni")
 import("//chrome/android/features/tab_ui/tab_management_java_sources.gni")
 import("//chrome/android/feed/feed_java_sources.gni")
+import("//chrome/browser/share/android/test_java_sources.gni")
 import("//components/feed/features.gni")
 import("//components/offline_pages/buildflags/features.gni")
 import("//device/vr/buildflags/buildflags.gni")
@@ -38,6 +39,7 @@
 chrome_junit_test_java_sources += start_surface_junit_java_sources
 chrome_junit_test_java_sources += tab_management_junit_java_sources
 chrome_test_java_sources += tab_management_test_java_sources
+chrome_test_java_sources += share_test_java_sources
 
 if (enable_arcore) {
   chrome_java_sources += [
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 956c37c3..663d3c4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -93,7 +93,6 @@
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.firstrun.FirstRunStatus;
-import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
 import org.chromium.chrome.browser.history.HistoryItem;
 import org.chromium.chrome.browser.history.TestBrowsingHistoryObserver;
@@ -2326,8 +2325,6 @@
     @SmallTest
     @DisableIf.Build(sdk_is_greater_than = 20)
     public void testLaunchCustomTabWithColorSchemeDark_Kitkat() {
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(true);
-
         Intent intent = createMinimalCustomTabIntent();
         addColorSchemeToIntent(intent, CustomTabsIntent.COLOR_SCHEME_DARK);
 
@@ -2338,16 +2335,12 @@
         Assert.assertNotNull(cctActivity.getNightModeStateProvider());
         Assert.assertFalse("Night mode should be disabled on K with dark color scheme set.",
                 cctActivity.getNightModeStateProvider().isInNightMode());
-
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(null);
     }
 
     @Test
     @SmallTest
     @DisableIf.Build(sdk_is_less_than = 21)
     public void testLaunchCustomTabWithColorSchemeDark_PostKitkat() {
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(true);
-
         Intent intent = createMinimalCustomTabIntent();
         addColorSchemeToIntent(intent, CustomTabsIntent.COLOR_SCHEME_DARK);
 
@@ -2359,15 +2352,11 @@
 
         Assert.assertTrue("Night mode should be enabled on K+ with dark color scheme set.",
                 cctActivity.getNightModeStateProvider().isInNightMode());
-
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(null);
     }
 
     @Test
     @SmallTest
     public void testLaunchCustomTabWithColorSchemeLight() {
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(true);
-
         Intent intent = createMinimalCustomTabIntent();
         addColorSchemeToIntent(intent, CustomTabsIntent.COLOR_SCHEME_LIGHT);
 
@@ -2377,8 +2366,6 @@
 
         Assert.assertNotNull(cctActivity.getNightModeStateProvider());
         Assert.assertFalse(cctActivity.getNightModeStateProvider().isInNightMode());
-
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(null);
     }
 
     private void addColorSchemeToIntent(Intent intent, int colorScheme) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java
index 92bd9526..06121840 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabNightModeStateControllerTest.java
@@ -21,7 +21,6 @@
 
 import androidx.browser.customtabs.CustomTabsIntent;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +31,6 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.flags.CachedFeatureFlags;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
 import org.chromium.chrome.browser.night_mode.PowerSavingModeMonitor;
@@ -68,12 +66,6 @@
                 mPowerSavingObserverCaptor.capture());
         mNightModeController = new CustomTabNightModeStateController(mActivityLifecycleDispatcher,
                 mSystemNightModeMonitor, mPowerSavingModeMonitor);
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(true);
-    }
-
-    @After
-    public void tearDown() {
-        CachedFeatureFlags.setNightModeForCustomTabsAvailableForTesting(false);
     }
 
     @Test
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index cbf93a59..e2b3974 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4034,6 +4034,8 @@
       sources += [
         "component_updater/third_party_module_list_component_installer_win.cc",
         "component_updater/third_party_module_list_component_installer_win.h",
+        "google/google_update_policy_fetcher_win.cc",
+        "google/google_update_policy_fetcher_win.h",
         "google/google_update_win.cc",
         "google/google_update_win.h",
         "win/conflicts/incompatible_applications_updater.cc",
@@ -5824,6 +5826,8 @@
       "sessions/session_service_test_helper.h",
       "sessions/tab_loader_tester.cc",
       "sessions/tab_loader_tester.h",
+      "sessions/tab_restore_service_load_waiter.cc",
+      "sessions/tab_restore_service_load_waiter.h",
       "ui/tabs/tab_activity_simulator.cc",
       "ui/tabs/tab_activity_simulator.h",
     ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ca3b6965..fa6688a 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1764,10 +1764,6 @@
     {"enable-webassembly-baseline", flag_descriptions::kEnableWasmBaselineName,
      flag_descriptions::kEnableWasmBaselineDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kWebAssemblyBaseline)},
-    {"enable-webassembly-code-cache",
-     flag_descriptions::kEnableWasmCodeCacheName,
-     flag_descriptions::kEnableWasmCodeCacheDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kWasmCodeCache)},
     {"enable-webassembly-code-gc", flag_descriptions::kEnableWasmCodeGCName,
      flag_descriptions::kEnableWasmCodeGCDescription, kOsAll,
      FEATURE_VALUE_TYPE(features::kWebAssemblyCodeGC)},
@@ -4948,6 +4944,13 @@
      flag_descriptions::kPasswordChangeDescription, kOsAll,
      FEATURE_VALUE_TYPE(password_manager::features::kPasswordChange)},
 
+#if defined(OS_ANDROID)
+    {"context-menu-performance-info",
+     flag_descriptions::kContextMenuPerformanceInfoName,
+     flag_descriptions::kContextMenuPerformanceInfoDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(chrome::android::kContextMenuPerformanceInfo)},
+#endif  // !defined(OS_ANDROID)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/omnibox/autocomplete_controller_android.cc b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
index fdef6458..f8ae6d6 100644
--- a/chrome/browser/android/omnibox/autocomplete_controller_android.cc
+++ b/chrome/browser/android/omnibox/autocomplete_controller_android.cc
@@ -532,8 +532,8 @@
   ScopedJavaLocalRef<jstring> image_url;
   ScopedJavaLocalRef<jstring> image_dominant_color;
 
-  if (!match.image_url.empty()) {
-    image_url = ConvertUTF8ToJavaString(env, match.image_url);
+  if (!match.image_url.is_empty()) {
+    image_url = ConvertUTF8ToJavaString(env, match.image_url.spec());
   }
 
   if (!match.image_dominant_color.empty()) {
diff --git a/chrome/browser/apps/platform_apps/app_browsertest.cc b/chrome/browser/apps/platform_apps/app_browsertest.cc
index 7dad960..a262206 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest.cc
@@ -454,10 +454,12 @@
       << message_;
   observer.Wait();
   ASSERT_EQ(kExpectedNumberOfTabs, observer.tabs().size());
-  content::WaitForLoadStop(observer.tabs()[kExpectedNumberOfTabs - 1]);
+  EXPECT_FALSE(
+      content::WaitForLoadStop(observer.tabs()[kExpectedNumberOfTabs - 1]));
   EXPECT_EQ(GURL(kChromiumURL),
             observer.tabs()[kExpectedNumberOfTabs - 1]->GetURL());
-  content::WaitForLoadStop(observer.tabs()[kExpectedNumberOfTabs - 2]);
+  EXPECT_FALSE(
+      content::WaitForLoadStop(observer.tabs()[kExpectedNumberOfTabs - 2]));
   EXPECT_EQ(GURL(kChromiumURL),
             observer.tabs()[kExpectedNumberOfTabs - 2]->GetURL());
 }
diff --git a/chrome/browser/bluetooth/web_bluetooth_browsertest.cc b/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
index 0ea6d2ca..038f4a6 100644
--- a/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
+++ b/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
@@ -398,8 +398,8 @@
 
   // Reload tab.
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(
-      browser()->tab_strip_model()->GetActiveWebContents());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetActiveWebContents()));
 
   // Use Web Bluetooth again.
   std::string result_after_crash;
diff --git a/chrome/browser/chrome_back_forward_cache_browsertest.cc b/chrome/browser/chrome_back_forward_cache_browsertest.cc
index 40be8c0..5271b0e 100644
--- a/chrome/browser/chrome_back_forward_cache_browsertest.cc
+++ b/chrome/browser/chrome_back_forward_cache_browsertest.cc
@@ -106,7 +106,7 @@
 
   // 3) Navigate back.
   web_contents()->GetController().GoBack();
-  content::WaitForLoadStop(web_contents());
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
 
   // A is restored, B is stored.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
@@ -116,7 +116,7 @@
 
   // 4) Navigate forward.
   web_contents()->GetController().GoForward();
-  content::WaitForLoadStop(web_contents());
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
 
   // A is stored, B is restored.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
@@ -142,7 +142,7 @@
     iframe.url = url;
     document.body.appendChild(iframe);
   )"));
-  content::WaitForLoadStop(web_contents());
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
 
   content::RenderFrameHost* rfh_b = nullptr;
   for (content::RenderFrameHost* rfh : web_contents()->GetAllFrames()) {
@@ -163,7 +163,7 @@
 
   // 3) Navigate back.
   web_contents()->GetController().GoBack();
-  content::WaitForLoadStop(web_contents());
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
 
   // The page A(B) is restored.
   EXPECT_FALSE(delete_observer_rfh_a.deleted());
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 48eea89..96662df 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -427,7 +427,9 @@
       FROM_HERE, base::BindOnce(&InitializeModuleDatabase,
                                 third_party_blocking_policy_enabled));
 
-  *module_watcher = ModuleWatcher::Create(base::BindRepeating(&OnModuleEvent));
+  *module_watcher =
+      ModuleWatcher::Create(base::BindRepeating(&OnModuleEvent),
+                            /* report_background_loaded_modules = */ true);
 }
 
 void ShowCloseBrowserFirstMessageBox() {
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index 93ba7c69..f220f266 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -160,7 +160,7 @@
   content::WebContents* new_web_contents =
       browser()->tab_strip_model()->GetWebContentsAt(1);
   ASSERT_NE(new_web_contents, web_contents);
-  WaitForLoadStop(new_web_contents);
+  EXPECT_TRUE(WaitForLoadStop(new_web_contents));
 
   GURL view_frame_source_url(content::kViewSourceScheme + std::string(":") +
                              iframe_target_url.spec());
@@ -400,7 +400,7 @@
   int index_of_new_tab = browser()->tab_strip_model()->count() - 1;
   content::WebContents* new_web_contents =
       browser()->tab_strip_model()->GetWebContentsAt(index_of_new_tab);
-  WaitForLoadStop(new_web_contents);
+  EXPECT_TRUE(WaitForLoadStop(new_web_contents));
 
   // Verify that the final URL after the redirects gets committed.
   EXPECT_EQ(GURL("http://bar.com/title2.html"),
@@ -541,7 +541,7 @@
   GURL error_url(content::kUnreachableWebDataURL);
   EXPECT_TRUE(ExecuteScript(web_contents,
                             "location.href = '" + error_url.spec() + "';"));
-  content::WaitForLoadStop(web_contents);
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
   EXPECT_EQ(url, web_contents->GetLastCommittedURL());
 
   // Also ensure that a page can't embed an iframe for an error page URL.
@@ -549,7 +549,7 @@
                             "var frame = document.createElement('iframe');\n"
                             "frame.src = '" + error_url.spec() + "';\n"
                             "document.body.appendChild(frame);"));
-  content::WaitForLoadStop(web_contents);
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
   content::RenderFrameHost* subframe_host =
       ChildFrameAt(web_contents->GetMainFrame(), 0);
   // The new subframe should remain blank without a committed URL.
@@ -1031,7 +1031,7 @@
   content::WebContents* popup =
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_NE(popup, opener);
-  WaitForLoadStop(popup);
+  EXPECT_TRUE(WaitForLoadStop(popup));
 
   content::ConsoleObserverDelegate console_observer(
       opener,
@@ -1083,7 +1083,7 @@
   content::WebContents* popup =
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_NE(popup, opener);
-  WaitForLoadStop(popup);
+  EXPECT_TRUE(WaitForLoadStop(popup));
 
   content::DownloadTestObserverInProgress observer(
       content::BrowserContext::GetDownloadManager(browser()->profile()),
@@ -1419,7 +1419,7 @@
 
   ASSERT_TRUE(chrome::CanGoBack(browser()));
   chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(main_contents);
+  EXPECT_TRUE(content::WaitForLoadStop(main_contents));
   ASSERT_EQ(pdf_url, main_contents->GetLastCommittedURL());
 }
 
diff --git a/chrome/browser/chrome_security_exploit_browsertest.cc b/chrome/browser/chrome_security_exploit_browsertest.cc
index 2641a05..887d66d 100644
--- a/chrome/browser/chrome_security_exploit_browsertest.cc
+++ b/chrome/browser/chrome_security_exploit_browsertest.cc
@@ -301,7 +301,8 @@
 
   // Now navigate to |target_url| in a new tab. It should not contain |payload|.
   AddTabAtIndex(0, target_url, ui::PAGE_TRANSITION_TYPED);
-  content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(0));
+  EXPECT_FALSE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetWebContentsAt(0)));
   rfh = browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
 
   // If the attack is unsuccessful, the navigation ends up in an error
@@ -378,7 +379,8 @@
 
   // Now navigate to |target_url| in a new tab. It should not contain |payload|.
   AddTabAtIndex(0, target_url, ui::PAGE_TRANSITION_TYPED);
-  content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(0));
+  EXPECT_FALSE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetWebContentsAt(0)));
   rfh = browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
 
   // If the attack is unsuccessful, the navigation ends up in an error
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.cc b/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.cc
index d0cace9..66924a7 100644
--- a/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.cc
@@ -379,7 +379,8 @@
     if (is_web && base::Contains(app_limits, GetChromeAppId()))
       new_limit = app_limits.at(GetChromeAppId());
 
-    // TODO(yilkal): Clean up the following if statement after bug bash.
+    // In Family Link app Chrome on Chrome OS is combined together with Android
+    // Chrome. Therefore, use Android Chrome's time limit.
     if (is_web && !base::Contains(app_limits, GetChromeAppId()) &&
         base::Contains(app_limits, GetAndroidChromeAppId())) {
       new_limit = app_limits.at(GetAndroidChromeAppId());
diff --git a/chrome/browser/chromeos/login/kiosk_browsertest.cc b/chrome/browser/chromeos/login/kiosk_browsertest.cc
index a414709b..c3b4b65 100644
--- a/chrome/browser/chromeos/login/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/kiosk_browsertest.cc
@@ -1021,7 +1021,8 @@
   WaitForAppLaunchSuccess();
 }
 
-IN_PROC_BROWSER_TEST_F(KioskTest, LaunchAppUserCancel) {
+// TODO(https://crbug.com/964333): Flakily seg faults.
+IN_PROC_BROWSER_TEST_F(KioskTest, DISABLED_LaunchAppUserCancel) {
   // Make fake_cws_ return empty update response.
   set_test_app_version("");
   OobeScreenWaiter splash_waiter(AppLaunchSplashScreenView::kScreenId);
diff --git a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
index 5e5f713e..9567a32 100644
--- a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
+++ b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
@@ -805,7 +805,9 @@
 
 // crbug.com/997987. Disabled on MSAN since they time out. crbug.com/1004327
 // crbug.com/1054935: EndToEnd is flaky on ChromeOS.
-#if defined(MEMORY_SANITIZER) || defined(OS_CHROMEOS)
+// crbug.com/1055853: EndToEnd is flaky on Linux Chromium OS ASan LSan
+#if defined(MEMORY_SANITIZER) || defined(OS_CHROMEOS) || \
+    defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER)
 #define MAYBE_EndToEnd DISABLED_EndToEnd
 #else
 #define MAYBE_EndToEnd EndToEnd
diff --git a/chrome/browser/chromeos/login/reset_browsertest.cc b/chrome/browser/chromeos/login/reset_browsertest.cc
index 7844017..683ca300 100644
--- a/chrome/browser/chromeos/login/reset_browsertest.cc
+++ b/chrome/browser/chromeos/login/reset_browsertest.cc
@@ -47,15 +47,28 @@
 constexpr char kTestUser1[] = "test-user1@gmail.com";
 constexpr char kTestUser1GaiaId[] = "test-user1@gmail.com";
 
+// HTML Elements
+constexpr char kResetScreen[] = "reset";
+constexpr char kConfirmationDialog[] = "confirmationDialog";
+constexpr char kHelpDialog[] = "helpDialog";
+constexpr char kTpmUpdate[] = "tpmFirmwareUpdate";
+constexpr char kTpmUpdateCheckbox[] = "tpmFirmwareUpdateCheckbox";
+
 void InvokeRollbackOption() {
   test::ExecuteOobeJS("cr.ui.Oobe.handleAccelerator('reset');");
 }
 
-void CloseResetScreen() {
+void RequestCloseResetScreen() {
   test::ExecuteOobeJS(
       "chrome.send('login.ResetScreen.userActed', ['cancel-reset']);");
 }
 
+void CloseResetScreen() {
+  RequestCloseResetScreen();
+  OobeScreenExitWaiter(ResetView::kScreenId).Wait();
+  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
+}
+
 void ClickResetButton() {
   test::ExecuteOobeJS(
       "chrome.send('login.ResetScreen.userActed', ['powerwash-pressed']);");
@@ -77,6 +90,27 @@
       "['reset-confirm-dismissed']);");
 }
 
+void WaitForConfirmationDialogToOpen() {
+  test::OobeJS()
+      .CreateAttributePresenceWaiter(
+          "open", true /*present*/,
+          {kResetScreen, kConfirmationDialog, kHelpDialog})
+      ->Wait();
+}
+
+void WaitForConfirmationDialogToClose() {
+  test::OobeJS()
+      .CreateAttributePresenceWaiter(
+          "open", false /*present*/,
+          {kResetScreen, kConfirmationDialog, kHelpDialog})
+      ->Wait();
+}
+
+void ExpectConfirmationDialogClosed() {
+  test::OobeJS().ExpectHasNoAttribute(
+      "open", {kResetScreen, kConfirmationDialog, kHelpDialog});
+}
+
 // Helper class that tracks whether 'login-prompt-visible' signal was requested
 // from the session manager service.
 class LoginPromptVisibleObserver : public SessionManagerClient::Observer {
@@ -135,7 +169,10 @@
   // Simulates reset screen request from views based login.
   void InvokeResetScreen() {
     chromeos::LoginDisplayHost::default_host()->ShowResetScreen();
+    EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
     OobeScreenWaiter(ResetView::kScreenId).Wait();
+    EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+    ExpectConfirmationDialogClosed();
   }
 
   FakeUpdateEngineClient* update_engine_client_ = nullptr;
@@ -174,6 +211,8 @@
   void InvokeResetScreen() {
     test::ExecuteOobeJS("cr.ui.Oobe.handleAccelerator('reset');");
     OobeScreenWaiter(ResetView::kScreenId).Wait();
+    EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+    ExpectConfirmationDialogClosed();
   }
 
   FakeUpdateEngineClient* update_engine_client_ = nullptr;
@@ -268,14 +307,7 @@
 IN_PROC_BROWSER_TEST_F(ResetTest, ShowAndCancel) {
   EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
   InvokeResetScreen();
-  EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
-
-  test::OobeJS().ExpectVisible("reset");
-
   CloseResetScreen();
-  test::OobeJS().CreateVisibilityWaiter(false, {"reset"})->Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 }
 
 IN_PROC_BROWSER_TEST_F(ResetTest, RestartBeforePowerwash) {
@@ -283,8 +315,6 @@
   PrefService* prefs = g_browser_process->local_state();
 
   InvokeResetScreen();
-  EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
@@ -301,10 +331,6 @@
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   InvokeResetScreen();
 
-  OobeScreenWaiter(ResetView::kScreenId).Wait();
-  test::OobeJS().ExpectVisible("reset");
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
-
   ClickResetButton();
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
   EXPECT_EQ(1, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
@@ -316,13 +342,9 @@
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   InvokeResetScreen();
 
-  OobeScreenWaiter(ResetView::kScreenId).Wait();
-  test::OobeJS().ExpectVisible("reset");
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
-
-  CloseResetScreen();
+  RequestCloseResetScreen();
   OobeScreenWaiter(WelcomeView::kScreenId).Wait();
-  test::OobeJS().ExpectHidden("reset");
+  test::OobeJS().ExpectHidden(kResetScreen);
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
@@ -352,43 +374,36 @@
   // Rollback unavailable. Show and cancel.
   update_engine_client_->set_can_rollback_check_result(false);
   InvokeResetScreen();
-  EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
-
-  test::OobeJS().CreateVisibilityWaiter(true, {"reset"})->Wait();
-  test::OobeJS().ExpectHidden("overlay-reset");
   CloseResetScreen();
-  test::OobeJS().CreateVisibilityWaiter(false, {"reset"})->Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   // Go to confirmation phase, cancel from there in 2 steps.
   prefs->SetBoolean(prefs::kFactoryResetRequested, true);
   InvokeResetScreen();
-  test::OobeJS().CreateVisibilityWaiter(false, {"overlay-reset"})->Wait();
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
+
   ClickToConfirmButton();
-  test::OobeJS().CreateVisibilityWaiter(true, {"overlay-reset"})->Wait();
+  WaitForConfirmationDialogToOpen();
+
   ClickDismissConfirmationButton();
-  test::OobeJS().CreateVisibilityWaiter(false, {"overlay-reset"})->Wait();
-  test::OobeJS().CreateVisibilityWaiter(true, {"reset"})->Wait();
+  WaitForConfirmationDialogToClose();
+
+  test::OobeJS().ExpectVisible(kResetScreen);
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   CloseResetScreen();
-  test::OobeJS().CreateVisibilityWaiter(false, {"reset"})->Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   // Rollback available. Show and cancel from confirmation screen.
   update_engine_client_->set_can_rollback_check_result(true);
   prefs->SetBoolean(prefs::kFactoryResetRequested, true);
   InvokeResetScreen();
   InvokeRollbackOption();
-  test::OobeJS().ExpectHidden("overlay-reset");
+
   ClickToConfirmButton();
-  test::OobeJS().CreateVisibilityWaiter(true, {"overlay-reset"})->Wait();
+  WaitForConfirmationDialogToOpen();
+
   ClickDismissConfirmationButton();
-  test::OobeJS().CreateVisibilityWaiter(false, {"overlay-reset"})->Wait();
-  test::OobeJS().ExpectVisible("reset");
+  WaitForConfirmationDialogToClose();
+
+  test::OobeJS().ExpectVisible(kResetScreen);
   CloseResetScreen();
-  test::OobeJS().CreateVisibilityWaiter(false, {"reset"})->Wait();
 }
 
 IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, PRE_ShowAfterBootIfRequested) {
@@ -401,11 +416,9 @@
   EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
-  test::OobeJS().CreateVisibilityWaiter(true, {"reset"})->Wait();
+  test::OobeJS().CreateVisibilityWaiter(true, {kResetScreen})->Wait();
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   CloseResetScreen();
-  test::OobeJS().CreateVisibilityWaiter(false, {"reset"})->Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 }
 
 IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, PRE_RollbackUnavailable) {
@@ -415,8 +428,6 @@
 
 IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, RollbackUnavailable) {
   InvokeResetScreen();
-  EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
@@ -428,8 +439,6 @@
   EXPECT_EQ(1, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
   EXPECT_EQ(0, update_engine_client_->rollback_call_count());
   CloseResetScreen();
-  OobeScreenExitWaiter(ResetView::kScreenId).Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   // Next invocation leads to rollback view.
   PrefService* prefs = g_browser_process->local_state();
@@ -471,21 +480,15 @@
   EXPECT_EQ(1, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
   EXPECT_EQ(0, update_engine_client_->rollback_call_count());
   CloseResetScreen();
-  OobeScreenExitWaiter(ResetView::kScreenId).Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   // Next invocation leads to simple reset, not rollback view.
   prefs->SetBoolean(prefs::kFactoryResetRequested, true);
   InvokeResetScreen();
-  EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   InvokeRollbackOption();  // Shows rollback.
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   ClickDismissConfirmationButton();
   EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
   CloseResetScreen();
-  OobeScreenExitWaiter(ResetView::kScreenId).Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
-
   InvokeResetScreen();
   ClickToConfirmButton();
   ClickResetButton();
@@ -493,8 +496,6 @@
   EXPECT_EQ(2, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
   EXPECT_EQ(0, update_engine_client_->rollback_call_count());
   CloseResetScreen();
-  OobeScreenExitWaiter(ResetView::kScreenId).Wait();
-  EXPECT_TRUE(ash::LoginScreenTestApi::IsGuestButtonShown());
 
   prefs->SetBoolean(prefs::kFactoryResetRequested, true);
   InvokeResetScreen();
@@ -520,7 +521,7 @@
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
   EXPECT_EQ(0, update_engine_client_->rollback_call_count());
-  test::OobeJS().ExpectHasNoClass("revert-promise-view", {"reset"});
+  test::OobeJS().ExpectHasNoClass("revert-promise-view", {kResetScreen});
 
   InvokeRollbackOption();
   ClickToConfirmButton();
@@ -529,7 +530,7 @@
   EXPECT_EQ(0, FakePowerManagerClient::Get()->num_request_restart_calls());
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
   EXPECT_EQ(1, update_engine_client_->rollback_call_count());
-  test::OobeJS().ExpectHasClass("revert-promise-view", {"reset"});
+  test::OobeJS().ExpectHasClass("revert-promise-view", {kResetScreen});
 
   update_engine::StatusResult error_update_status;
   error_update_status.set_current_operation(update_engine::Operation::ERROR);
@@ -561,36 +562,32 @@
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
   EXPECT_EQ(0, update_engine_client_->rollback_call_count());
 
-  test::OobeJS().ExpectVisible("reset");
-  test::OobeJS().ExpectHasNoClass("rollback-proposal-view", {"reset"});
+  test::OobeJS().ExpectVisible(kResetScreen);
+  test::OobeJS().ExpectHasNoClass("rollback-proposal-view", {kResetScreen});
 
   InvokeRollbackOption();
   test::OobeJS()
-      .CreateHasClassWaiter(true, "rollback-proposal-view", {"reset"})
+      .CreateHasClassWaiter(true, "rollback-proposal-view", {kResetScreen})
       ->Wait();
 
   CloseResetScreen();
-  OobeScreenExitWaiter(ResetView::kScreenId).Wait();
-
   InvokeResetScreen();
-  OobeScreenWaiter(ResetView::kScreenId).Wait();
 
   InvokeRollbackOption();
   test::OobeJS()
-      .CreateHasClassWaiter(true, "rollback-proposal-view", {"reset"})
+      .CreateHasClassWaiter(true, "rollback-proposal-view", {kResetScreen})
       ->Wait();
 }
 
 IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate,
                        PRE_ResetFromSigninWithFirmwareUpdate) {
   InvokeResetScreen();
-  EXPECT_TRUE(login_prompt_visible_observer_->signal_emitted());
 
-  test::OobeJS().ExpectHiddenPath({"oobe-reset-md", "tpmFirmwareUpdate"});
+  test::OobeJS().ExpectHiddenPath({kResetScreen, kTpmUpdate});
   ASSERT_TRUE(HasPendingTpmFirmwareUpdateCheck());
   FinishPendingTpmFirmwareUpdateCheck({tpm_firmware_update::Mode::kPowerwash});
 
-  test::OobeJS().ExpectHiddenPath({"oobe-reset-md", "tpmFirmwareUpdate"});
+  test::OobeJS().ExpectHiddenPath({kResetScreen, kTpmUpdate});
   ClickRestartButton();
 }
 
@@ -603,11 +600,10 @@
   FinishPendingTpmFirmwareUpdateCheck({tpm_firmware_update::Mode::kPowerwash});
 
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"oobe-reset-md", "tpmFirmwareUpdate"})
+      .CreateVisibilityWaiter(true, {kResetScreen, kTpmUpdate})
       ->Wait();
-  test::OobeJS().Evaluate(
-      test::GetOobeElementPath({"oobe-reset-md", "tpmFirmwareUpdateCheckbox"}) +
-      ".fire('click')");
+
+  test::OobeJS().ClickOnPath({kResetScreen, kTpmUpdateCheckbox});
 
   ClickResetButton();
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
@@ -641,7 +637,7 @@
   FinishPendingTpmFirmwareUpdateCheck({tpm_firmware_update::Mode::kPowerwash});
 
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"oobe-reset-md", "tpmFirmwareUpdate"})
+      .CreateVisibilityWaiter(true, {kResetScreen, kTpmUpdate})
       ->Wait();
 
   ClickResetButton();
@@ -667,7 +663,7 @@
 
   EXPECT_FALSE(HasPendingTpmFirmwareUpdateCheck());
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"oobe-reset-md", "tpmFirmwareUpdate"})
+      .CreateVisibilityWaiter(true, {kResetScreen, kTpmUpdate})
       ->Wait();
 
   ClickResetButton();
@@ -703,7 +699,7 @@
 
   EXPECT_FALSE(HasPendingTpmFirmwareUpdateCheck());
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"oobe-reset-md", "tpmFirmwareUpdate"})
+      .CreateVisibilityWaiter(true, {kResetScreen, kTpmUpdate})
       ->Wait();
 
   ClickResetButton();
@@ -743,12 +739,10 @@
 
   EXPECT_FALSE(HasPendingTpmFirmwareUpdateCheck());
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"oobe-reset-md", "tpmFirmwareUpdate"})
+      .CreateVisibilityWaiter(true, {kResetScreen, kTpmUpdate})
       ->Wait();
 
-  test::OobeJS().Evaluate(
-      test::GetOobeElementPath({"oobe-reset-md", "tpmFirmwareUpdateCheckbox"}) +
-      ".fire('click')");
+  test::OobeJS().ClickOnPath({kResetScreen, kTpmUpdateCheckbox});
 
   ClickResetButton();
   EXPECT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
@@ -784,7 +778,7 @@
 
   EXPECT_FALSE(HasPendingTpmFirmwareUpdateCheck());
   test::OobeJS()
-      .CreateVisibilityWaiter(true, {"oobe-reset-md", "tpmFirmwareUpdate"})
+      .CreateVisibilityWaiter(true, {kResetScreen, kTpmUpdate})
       ->Wait();
 
   ClickResetButton();
diff --git a/chrome/browser/chromeos/login/screens/reset_screen.cc b/chrome/browser/chromeos/login/screens/reset_screen.cc
index 95646d1f..33107d5 100644
--- a/chrome/browser/chromeos/login/screens/reset_screen.cc
+++ b/chrome/browser/chromeos/login/screens/reset_screen.cc
@@ -165,17 +165,12 @@
     view_->Bind(this);
     view_->SetScreenState(ResetView::State::kRestartRequired);
     view_->SetIsRollbackAvailable(false);
-    view_->SetIsRollbackChecked(false);
+    view_->SetIsRollbackRequested(false);
     view_->SetIsTpmFirmwareUpdateAvailable(false);
     view_->SetIsTpmFirmwareUpdateChecked(false);
     view_->SetIsTpmFirmwareUpdateEditable(true);
     view_->SetTpmFirmwareUpdateMode(tpm_firmware_update::Mode::kPowerwash);
-    view_->SetIsConfirmational(false);
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-    view_->SetIsGoogleBrandedBuild(true);
-#else
-    view_->SetIsGoogleBrandedBuild(false);
-#endif
+    view_->SetShouldShowConfirmationDialog(false);
   }
 }
 
@@ -312,8 +307,10 @@
     return;
   }
   // Hide Rollback view for the next show.
-  if (view_ && view_->GetIsRollbackAvailable() && view_->GetIsRollbackChecked())
+  if (view_ && view_->GetIsRollbackAvailable() &&
+      view_->GetIsRollbackRequested()) {
     OnToggleRollback();
+  }
   DBusThreadManager::Get()->GetUpdateEngineClient()->RemoveObserver(this);
   exit_callback_.Run();
 }
@@ -325,16 +322,16 @@
   }
 
   if (view_)
-    view_->SetIsConfirmational(false);
+    view_->SetShouldShowConfirmationDialog(false);
 
-  if (view_ && view_->GetIsRollbackChecked() &&
+  if (view_ && view_->GetIsRollbackRequested() &&
       !view_->GetIsRollbackAvailable()) {
     NOTREACHED()
         << "Rollback was checked but not available. Starting powerwash.";
   }
 
   if (view_ && view_->GetIsRollbackAvailable() &&
-      view_->GetIsRollbackChecked()) {
+      view_->GetIsRollbackRequested()) {
     view_->SetScreenState(ResetView::State::kRevertPromise);
     DBusThreadManager::Get()->GetUpdateEngineClient()->AddObserver(this);
     VLOG(1) << "Starting Rollback";
@@ -372,37 +369,37 @@
 void ResetScreen::OnToggleRollback() {
   // Hide Rollback if visible.
   if (view_ && view_->GetIsRollbackAvailable() &&
-      view_->GetIsRollbackChecked()) {
+      view_->GetIsRollbackRequested()) {
     VLOG(1) << "Hiding rollback view on reset screen";
-    view_->SetIsRollbackChecked(false);
+    view_->SetIsRollbackRequested(false);
     return;
   }
 
   // Show Rollback if available.
   VLOG(1) << "Requested rollback availability"
           << view_->GetIsRollbackAvailable();
-  if (view_->GetIsRollbackAvailable() && !view_->GetIsRollbackChecked()) {
+  if (view_->GetIsRollbackAvailable() && !view_->GetIsRollbackRequested()) {
     UMA_HISTOGRAM_ENUMERATION(
         "Reset.ChromeOS.PowerwashDialogShown",
         reset::DIALOG_SHORTCUT_OFFERING_ROLLBACK_AVAILABLE,
         reset::DIALOG_VIEW_TYPE_SIZE);
-    view_->SetIsRollbackChecked(true);
+    view_->SetIsRollbackRequested(true);
   }
 }
 
 void ResetScreen::OnShowConfirm() {
   reset::DialogViewType dialog_type =
-      view_->GetIsRollbackChecked()
+      view_->GetIsRollbackRequested()
           ? reset::DIALOG_SHORTCUT_CONFIRMING_POWERWASH_AND_ROLLBACK
           : reset::DIALOG_SHORTCUT_CONFIRMING_POWERWASH_ONLY;
   UMA_HISTOGRAM_ENUMERATION("Reset.ChromeOS.PowerwashDialogShown", dialog_type,
                             reset::DIALOG_VIEW_TYPE_SIZE);
 
-  view_->SetIsConfirmational(true);
+  view_->SetShouldShowConfirmationDialog(true);
 }
 
 void ResetScreen::OnConfirmationDismissed() {
-  view_->SetIsConfirmational(false);
+  view_->SetConfirmationDialogClosed();
 }
 
 void ResetScreen::ShowHelpArticle(HelpAppLauncher::HelpTopic topic) {
diff --git a/chrome/browser/chromeos/login/test/js_checker.cc b/chrome/browser/chromeos/login/test/js_checker.cc
index f43633d..404bf6fd1 100644
--- a/chrome/browser/chromeos/login/test/js_checker.cc
+++ b/chrome/browser/chromeos/login/test/js_checker.cc
@@ -131,6 +131,17 @@
   return std::make_unique<TestPredicateWaiter>(predicate);
 }
 
+std::unique_ptr<TestConditionWaiter> JSChecker::CreateAttributePresenceWaiter(
+    const std::string& attribute,
+    bool presence,
+    std::initializer_list<base::StringPiece> element_ids) {
+  std::string condition = ElementHasAttributeCondition(attribute, element_ids);
+  if (!presence) {
+    condition = "!(" + condition + ")";
+  }
+  return CreateWaiter(condition);
+}
+
 std::unique_ptr<TestConditionWaiter> JSChecker::CreateVisibilityWaiter(
     bool visibility,
     std::initializer_list<base::StringPiece> element_ids) {
diff --git a/chrome/browser/chromeos/login/test/js_checker.h b/chrome/browser/chromeos/login/test/js_checker.h
index fa4a106..cc79f97d 100644
--- a/chrome/browser/chromeos/login/test/js_checker.h
+++ b/chrome/browser/chromeos/login/test/js_checker.h
@@ -61,6 +61,15 @@
   WARN_UNUSED_RESULT std::unique_ptr<TestConditionWaiter> CreateWaiter(
       const std::string& js_condition);
 
+  // Waiter that waits until the given attribute is (not) present.
+  // WARNING! This does not cover the case where ATTRIBUTE=false.
+  // Should only be used for boolean attributes.
+  WARN_UNUSED_RESULT std::unique_ptr<TestConditionWaiter>
+  CreateAttributePresenceWaiter(
+      const std::string& attribute,
+      bool presence,
+      std::initializer_list<base::StringPiece> element_ids);
+
   // Waiter that waits until specified element is (not) hidden.
   WARN_UNUSED_RESULT std::unique_ptr<TestConditionWaiter>
   CreateVisibilityWaiter(bool visibility,
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
index c55c886..95dc0c5 100644
--- a/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
+++ b/chrome/browser/chromeos/platform_keys/platform_keys_nss.cc
@@ -529,13 +529,13 @@
         break;
     }
 
-    SECItem sign_result = {siBuffer, nullptr, 0};
+    crypto::ScopedSECItem sign_result(SECITEM_AllocItem(nullptr, nullptr, 0));
     if (SEC_SignData(
-            &sign_result,
+            sign_result.get(),
             reinterpret_cast<const unsigned char*>(state->data_.data()),
             state->data_.size(), rsa_key.get(), sign_alg_tag) == SECSuccess) {
-      signature_str.assign(sign_result.data,
-                           sign_result.data + sign_result.len);
+      signature_str.assign(sign_result->data,
+                           sign_result->data + sign_result->len);
     }
   }
 
diff --git a/chrome/browser/crash_recovery_browsertest.cc b/chrome/browser/crash_recovery_browsertest.cc
index d57bf0ba..7cab695 100644
--- a/chrome/browser/crash_recovery_browsertest.cc
+++ b/chrome/browser/crash_recovery_browsertest.cc
@@ -114,7 +114,7 @@
                                                 &title_before_crash));
   SimulateRendererCrash(browser());
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(GetActiveWebContents());
+  EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
   ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                 &title_after_crash));
   EXPECT_NE(title_before_crash, title_after_crash);
@@ -145,7 +145,7 @@
                                                 &title_before_crash));
   SimulateRendererCrash(browser());
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(GetActiveWebContents());
+  EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
   ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                 &title_after_crash));
   EXPECT_NE(title_before_crash, title_after_crash);
@@ -180,7 +180,7 @@
             GetActiveWebContents()->GetController().GetVisibleEntry()->
                 GetVirtualURL());
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(GetActiveWebContents());
+  EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
   ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(),
                                                 &title_after_crash));
   EXPECT_EQ(title_before_crash, title_after_crash);
@@ -196,11 +196,11 @@
   SimulateRendererCrash(browser());
 
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(GetActiveWebContents());
+  EXPECT_FALSE(content::WaitForLoadStop(GetActiveWebContents()));
   ASSERT_EQ(url, GetActiveWebContents()->GetVisibleURL());
 
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(GetActiveWebContents());
+  EXPECT_FALSE(content::WaitForLoadStop(GetActiveWebContents()));
   ASSERT_EQ(url, GetActiveWebContents()->GetVisibleURL());
 }
 
diff --git a/chrome/browser/data_saver/data_saver_browsertest.cc b/chrome/browser/data_saver/data_saver_browsertest.cc
index 5ea271d..c1ea78f3 100644
--- a/chrome/browser/data_saver/data_saver_browsertest.cc
+++ b/chrome/browser/data_saver/data_saver_browsertest.cc
@@ -282,16 +282,16 @@
   // correct save-data header.
   expected_save_data_header_ = "on";
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(
-      browser()->tab_strip_model()->GetActiveWebContents());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetActiveWebContents()));
 
   // Reload the webpage with data saver disabled, and expect all the resources
   // will get no save-data header.
   EnableDataSaver(false);
   expected_save_data_header_ = "";
   chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
-  content::WaitForLoadStop(
-      browser()->tab_strip_model()->GetActiveWebContents());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetActiveWebContents()));
 }
 
 // Test that the data saver receives changes in effective connection type.
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index 4f8f968..04640137 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -882,7 +882,7 @@
 
   // Now that we know the panel is loaded, switch to it.
   SwitchToExtensionPanel(window_, extension, "iframe_panel");
-  content::WaitForLoadStop(main_web_contents());
+  EXPECT_TRUE(content::WaitForLoadStop(main_web_contents()));
 
   std::vector<RenderFrameHost*> rfhs = main_web_contents()->GetAllFrames();
   EXPECT_EQ(7U, rfhs.size());
@@ -2345,7 +2345,7 @@
 
   navigation_manager.WaitForNavigationFinished();
   navigation_manager_iframe.WaitForNavigationFinished();
-  content::WaitForLoadStop(tab);
+  EXPECT_TRUE(content::WaitForLoadStop(tab));
 
   std::vector<RenderFrameHost*> frames = GetInspectedTab()->GetAllFrames();
   ASSERT_EQ(2u, frames.size());
@@ -2455,7 +2455,7 @@
 
   navigation_manager.WaitForNavigationFinished();
   navigation_manager_iframe.WaitForNavigationFinished();
-  content::WaitForLoadStop(tab);
+  EXPECT_TRUE(content::WaitForLoadStop(tab));
 
   for (auto* frame : GetInspectedTab()->GetAllFrames()) {
     content::WaitForHitTestData(frame);
diff --git a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
index 002ad9661..854e317 100644
--- a/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
+++ b/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc
@@ -141,7 +141,7 @@
 IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest,
                        VisibleSecurityStateChangedNeutralState) {
   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
-  content::WaitForLoadStop(web_contents());
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
 
   Attach();
   SendCommand("Security.enable");
diff --git a/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc b/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
index 9a0006e..787ee67 100644
--- a/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/distillable_page_utils_browsertest.cc
@@ -87,7 +87,7 @@
 
     // This blocks until the navigation has completely finished.
     ui_test_utils::NavigateToURL(browser(), article_url);
-    content::WaitForLoadStop(web_contents_);
+    EXPECT_TRUE(content::WaitForLoadStop(web_contents_));
 
     if (!test_timeout.is_zero())
       QuitAfter(test_timeout);
diff --git a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
index ee2ca85..a8e59d9 100644
--- a/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
+++ b/chrome/browser/dom_distiller/dom_distiller_viewer_source_browsertest.cc
@@ -210,9 +210,9 @@
 
   // Wait for the page load to complete as the first page completes the root
   // document.
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 
-  ASSERT_TRUE(contents != NULL);
+  ASSERT_TRUE(contents != nullptr);
   EXPECT_EQ(url, contents->GetLastCommittedURL());
 
   std::string result;
@@ -298,7 +298,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
 
   // Wait for the page load to complete (should only be template).
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
   std::string result;
   // Loading spinner should be on screen at this point.
   EXPECT_TRUE(content::ExecuteScriptAndExtractString(
@@ -324,7 +324,7 @@
   ArticleDistillationUpdate update(update_pages, true, false);
   distiller->RunDistillerUpdateCallback(update);
 
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 
   EXPECT_TRUE(
       content::ExecuteScriptAndExtractString(contents, kGetContent, &result));
@@ -342,7 +342,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
 
   // Wait for the page load to complete (this will be a distiller error page).
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 
   // Execute in isolated world; where all distiller scripts are run.
   EXPECT_EQ(true, content::EvalJsWithManualReply(
@@ -362,7 +362,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
 
   // Wait for the page load to complete (this will be a distiller error page).
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 
   bool result;
   // Execute in main world, the distiller object should not be here.
@@ -382,7 +382,7 @@
       browser()->tab_strip_model()->GetActiveWebContents();
 
   // Wait for the page load to complete.
-  content::WaitForLoadStop(contents);
+  EXPECT_FALSE(content::WaitForLoadStop(contents));
 
   bool result;
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
@@ -438,7 +438,7 @@
 
     // Wait for the page load to complete as the first page completes the root
     // document.
-    content::WaitForLoadStop(contents);
+    EXPECT_TRUE(content::WaitForLoadStop(contents));
 
     std::string result;
     EXPECT_TRUE(content::ExecuteScriptAndExtractString(
@@ -512,7 +512,7 @@
   content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ViewSingleDistilledPage(view_url, "text/html");
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
   ExpectBodyHasThemeAndFont(contents, "light", "sans-serif");
 
   DistilledPagePrefs* distilled_page_prefs =
@@ -557,7 +557,7 @@
   ui_test_utils::NavigateToURL(browser(), url);
   content::WebContents* contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 
   std::string result;
   DistilledPagePrefs* distilled_page_prefs =
@@ -589,7 +589,7 @@
   // Make sure perf persist across web pages.
   GURL url2("chrome-distiller://bad2");
   ui_test_utils::NavigateToURL(browser(), url2);
-  content::WaitForLoadStop(contents);
+  EXPECT_TRUE(content::WaitForLoadStop(contents));
 
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(
diff --git a/chrome/browser/dom_distiller/tab_utils_browsertest.cc b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
index 20a1c8e..655bd632 100644
--- a/chrome/browser/dom_distiller/tab_utils_browsertest.cc
+++ b/chrome/browser/dom_distiller/tab_utils_browsertest.cc
@@ -36,6 +36,11 @@
 
 const char* kSimpleArticlePath = "/dom_distiller/simple_article.html";
 const char* kOriginalArticleTitle = "Test Page Title";
+#if defined(OS_ANDROID)
+const char* kExpectedArticleTitle = "Test Page Title";
+#else   // Desktop. This test is in chrome/ and is not run on iOS.
+const char* kExpectedArticleTitle = "Test Page Title - Reader Mode";
+#endif  // defined(OS_ANDROID)
 
 std::unique_ptr<content::WebContents> NewContentsWithSameParamsAs(
     content::WebContents* source_web_contents) {
@@ -160,7 +165,7 @@
   EXPECT_NE(initial_web_contents, after_web_contents);
   EXPECT_TRUE(
       after_web_contents->GetLastCommittedURL().SchemeIs(kDomDistillerScheme));
-  EXPECT_EQ(kOriginalArticleTitle, GetPageTitle(after_web_contents));
+  EXPECT_EQ(kExpectedArticleTitle, GetPageTitle(after_web_contents));
 }
 
 IN_PROC_BROWSER_TEST_F(DomDistillerTabUtilsBrowserTest,
@@ -188,7 +193,7 @@
   // Verify the destination WebContents is showing distilled content.
   EXPECT_TRUE(destination_web_contents->GetLastCommittedURL().SchemeIs(
       kDomDistillerScheme));
-  EXPECT_EQ(kOriginalArticleTitle, GetPageTitle(destination_web_contents));
+  EXPECT_EQ(kExpectedArticleTitle, GetPageTitle(destination_web_contents));
 
   content::WebContentsDestroyedWatcher destroyed_watcher(
       destination_web_contents);
diff --git a/chrome/browser/downgrade/snapshot_file_collector.cc b/chrome/browser/downgrade/snapshot_file_collector.cc
index d7d54138..daec8b2 100644
--- a/chrome/browser/downgrade/snapshot_file_collector.cc
+++ b/chrome/browser/downgrade/snapshot_file_collector.cc
@@ -6,6 +6,21 @@
 
 #include <utility>
 
+#include "build/build_config.h"
+#include "chrome/browser/profiles/profile_avatar_icon_util.h"
+#include "chrome/common/chrome_constants.h"
+#include "components/autofill/core/browser/payments/strike_database.h"
+#include "components/bookmarks/common/bookmark_constants.h"
+#include "components/history/core/browser/history_constants.h"
+#include "components/password_manager/core/browser/password_manager_constants.h"
+#include "components/sessions/core/session_constants.h"
+#include "components/webdata/common/webdata_constants.h"
+
+#if defined(OS_WIN)
+#include "chrome/browser/profiles/profile_shortcut_manager_win.h"
+#include "chrome/browser/web_applications/components/web_app_file_handler_registration_win.h"
+#endif
+
 namespace downgrade {
 
 SnapshotItemDetails::SnapshotItemDetails(base::FilePath path,
@@ -15,68 +30,70 @@
 // Returns a list of items to snapshot that should be directly under the user
 // data  directory.
 std::vector<SnapshotItemDetails> CollectUserDataItems(uint16_t version) {
-  // TODO (ydago): move the logic of the component files to the component
-  // themselves.
-  return std::vector<SnapshotItemDetails>{
-      // General files to snapshot
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Last Browser")),
-                          SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Local State")),
+  std::vector<SnapshotItemDetails> user_data_items{
+      SnapshotItemDetails(base::FilePath(chrome::kLocalStateFilename),
                           SnapshotItemDetails::ItemType::kDirectory),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Avatars")),
+      SnapshotItemDetails(base::FilePath(profiles::kHighResAvatarFolderName),
                           SnapshotItemDetails::ItemType::kDirectory)};
+#if defined(OS_WIN)
+  user_data_items.emplace_back(
+      SnapshotItemDetails(base::FilePath(web_app::kLastBrowserFile),
+                          SnapshotItemDetails::ItemType::kFile));
+#endif  // defined(OS_WIN)
+  return user_data_items;
 }
 
 // Returns a list of items to snapshot that should be under a profile directory.
 std::vector<SnapshotItemDetails> CollectProfileItems(uint16_t version) {
-  // TODO (ydago): move the logic of the component files to the component
-  // themselves.
-  return std::vector<SnapshotItemDetails>{
+  std::vector<SnapshotItemDetails> profile_items{
       // General Profile files
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Preferences")),
+      SnapshotItemDetails(base::FilePath(chrome::kPreferencesFilename),
                           SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(
-          base::FilePath(FILE_PATH_LITERAL("Secure Preferences")),
-          SnapshotItemDetails::ItemType::kFile),
+      SnapshotItemDetails(base::FilePath(chrome::kSecurePreferencesFilename),
+                          SnapshotItemDetails::ItemType::kFile),
       // History files
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("History")),
+      SnapshotItemDetails(base::FilePath(history::kHistoryFilename),
                           SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Favicons")),
+      SnapshotItemDetails(base::FilePath(history::kFaviconsFilename),
                           SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("TopSites")),
+      SnapshotItemDetails(base::FilePath(history::kTopSitesFilename),
                           SnapshotItemDetails::ItemType::kFile),
       // Bookmarks
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Bookmarks")),
+      SnapshotItemDetails(base::FilePath(bookmarks::kBookmarksFileName),
                           SnapshotItemDetails::ItemType::kFile),
       // Tab Restore and sessions
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Current Tabs")),
+      SnapshotItemDetails(base::FilePath(sessions::kCurrentTabSessionFileName),
                           SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Current Session")),
+      SnapshotItemDetails(base::FilePath(sessions::kCurrentSessionFileName),
                           SnapshotItemDetails::ItemType::kFile),
       // Sign-in state
-      SnapshotItemDetails(
-          base::FilePath(FILE_PATH_LITERAL("Google Profile.ico")),
-          SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(
-          base::FilePath(FILE_PATH_LITERAL("Google Profile Picture.png")),
-          SnapshotItemDetails::ItemType::kFile),
+      SnapshotItemDetails(base::FilePath(profiles::kGAIAPictureFileName),
+                          SnapshotItemDetails::ItemType::kFile),
       // Password / Autofill
       SnapshotItemDetails(
-          base::FilePath(FILE_PATH_LITERAL("Affiliation Database")),
+          base::FilePath(password_manager::kAffiliationDatabaseFileName),
           SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Login Data")),
-                          SnapshotItemDetails::ItemType::kFile),
       SnapshotItemDetails(
-          base::FilePath(FILE_PATH_LITERAL("Login Data For Account")),
+          base::FilePath(password_manager::kLoginDataForProfileFileName),
           SnapshotItemDetails::ItemType::kFile),
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Web Data")),
-                          SnapshotItemDetails::ItemType::kFile),
       SnapshotItemDetails(
-          base::FilePath(FILE_PATH_LITERAL("AutofillStrikeDatabase")),
+          base::FilePath(password_manager::kLoginDataForAccountFileName),
           SnapshotItemDetails::ItemType::kFile),
+      SnapshotItemDetails(base::FilePath(kWebDataFilename),
+                          SnapshotItemDetails::ItemType::kFile),
+      SnapshotItemDetails(base::FilePath(autofill::kStrikeDatabaseFileName),
+                          SnapshotItemDetails::ItemType::kFile),
       // Cookies
-      SnapshotItemDetails(base::FilePath(FILE_PATH_LITERAL("Cookies")),
+      SnapshotItemDetails(base::FilePath(chrome::kCookieFilename),
                           SnapshotItemDetails::ItemType::kFile)};
+
+#if defined(OS_WIN)
+  // Sign-in state
+  profile_items.emplace_back(
+      SnapshotItemDetails(base::FilePath(profiles::kProfileIconFileName),
+                          SnapshotItemDetails::ItemType::kFile));
+#endif  // defined(OS_WIN)
+  return profile_items;
 }
 
 }  // namespace downgrade
diff --git a/chrome/browser/download/android/java/res/layout/download_home_tabs.xml b/chrome/browser/download/android/java/res/layout/download_home_tabs.xml
index 3657f5e..292c1ed 100644
--- a/chrome/browser/download/android/java/res/layout/download_home_tabs.xml
+++ b/chrome/browser/download/android/java/res/layout/download_home_tabs.xml
@@ -9,25 +9,25 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <android.support.design.widget.TabLayout
+    <com.google.android.material.tabs.TabLayout
         android:id="@+id/tabs"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         style="@style/TabLayoutStyle" >
 
-        <android.support.design.widget.TabItem
+        <com.google.android.material.tabs.TabItem
             android:id="@+id/files_tab"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/download_manager_files_tab"/>
 
-        <android.support.design.widget.TabItem
+        <com.google.android.material.tabs.TabItem
             android:id="@+id/prefetch_tab"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/download_manager_explore_offline"/>
 
-    </android.support.design.widget.TabLayout>
+    </com.google.android.material.tabs.TabLayout>
 
     <FrameLayout
         android:id="@+id/content_container"
diff --git a/chrome/browser/download/android/java/res/layout/download_manager_generic_item.xml b/chrome/browser/download/android/java/res/layout/download_manager_generic_item.xml
index 20dc7e8..09c97be 100644
--- a/chrome/browser/download/android/java/res/layout/download_manager_generic_item.xml
+++ b/chrome/browser/download/android/java/res/layout/download_manager_generic_item.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 
- <android.support.v7.widget.GridLayout
+ <androidx.gridlayout.widget.GridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -63,4 +63,4 @@
         app:layout_row="0"
         app:layout_rowSpan="2"
         app:layout_gravity="center_vertical" />
-</android.support.v7.widget.GridLayout>
+</androidx.gridlayout.widget.GridLayout>
diff --git a/chrome/browser/download/android/java/res/layout/download_manager_in_progress_item.xml b/chrome/browser/download/android/java/res/layout/download_manager_in_progress_item.xml
index aa9c4309..699afe8 100644
--- a/chrome/browser/download/android/java/res/layout/download_manager_in_progress_item.xml
+++ b/chrome/browser/download/android/java/res/layout/download_manager_in_progress_item.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 
-<android.support.v7.widget.GridLayout
+<androidx.gridlayout.widget.GridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -55,4 +55,4 @@
         android:src="@drawable/btn_close"
         app:tint="@color/default_icon_color" />
 
-</android.support.v7.widget.GridLayout>
\ No newline at end of file
+</androidx.gridlayout.widget.GridLayout>
\ No newline at end of file
diff --git a/chrome/browser/download/android/java/res/layout/download_manager_in_progress_video_item.xml b/chrome/browser/download/android/java/res/layout/download_manager_in_progress_video_item.xml
index 19fc0b3..be6377a 100644
--- a/chrome/browser/download/android/java/res/layout/download_manager_in_progress_video_item.xml
+++ b/chrome/browser/download/android/java/res/layout/download_manager_in_progress_video_item.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 
-<android.support.v7.widget.GridLayout
+<androidx.gridlayout.widget.GridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
@@ -81,4 +81,4 @@
 
     </LinearLayout>
 
-</android.support.v7.widget.GridLayout>
+</androidx.gridlayout.widget.GridLayout>
diff --git a/chrome/browser/download/android/java/res/layout/download_manager_prefetch_article.xml b/chrome/browser/download/android/java/res/layout/download_manager_prefetch_article.xml
index 8690c39..f815ac63 100644
--- a/chrome/browser/download/android/java/res/layout/download_manager_prefetch_article.xml
+++ b/chrome/browser/download/android/java/res/layout/download_manager_prefetch_article.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 
-<android.support.v7.widget.GridLayout
+<androidx.gridlayout.widget.GridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -124,4 +124,4 @@
             android:id="@+id/selection"
             style="@style/DownloadItemSelectionView"/>
     </FrameLayout>
-</android.support.v7.widget.GridLayout>
\ No newline at end of file
+</androidx.gridlayout.widget.GridLayout>
\ No newline at end of file
diff --git a/chrome/browser/download/android/java/res/layout/download_manager_video_item.xml b/chrome/browser/download/android/java/res/layout/download_manager_video_item.xml
index e99ad7a..ffad416 100644
--- a/chrome/browser/download/android/java/res/layout/download_manager_video_item.xml
+++ b/chrome/browser/download/android/java/res/layout/download_manager_video_item.xml
@@ -4,7 +4,7 @@
      found in the LICENSE file.
 -->
 
- <android.support.v7.widget.GridLayout
+ <androidx.gridlayout.widget.GridLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -82,4 +82,4 @@
 
     </LinearLayout>
 
-</android.support.v7.widget.GridLayout>
+</androidx.gridlayout.widget.GridLayout>
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc
index 4a30d8fe..539b546a 100644
--- a/chrome/browser/download/save_page_browsertest.cc
+++ b/chrome/browser/download/save_page_browsertest.cc
@@ -1145,7 +1145,7 @@
     // See https://crbug.com/948246.
     ui_test_utils::NavigateToURL(browser(), GURL("data:text/html,foo"));
     chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
-    content::WaitForLoadStop(GetCurrentTab(browser()));
+    EXPECT_TRUE(content::WaitForLoadStop(GetCurrentTab(browser())));
     AssertExpectationsAboutCurrentTab(expected_number_of_frames_in_saved_page,
                                       expected_substrings);
   }
diff --git a/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
index 679e3dd0..0b66240 100644
--- a/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/rule_indexing_unittest.cc
@@ -157,8 +157,9 @@
 
     if (persist_initial_indexed_ruleset_) {
       std::string data = "user ruleset";
-      base::WriteFile(file_util::GetIndexedRulesetPath(extension_dir_),
-                      data.c_str(), data.size());
+      base::WriteFile(
+          extension_dir_.Append(file_util::GetIndexedRulesetRelativePath()),
+          data.c_str(), data.size());
     }
   }
 
diff --git a/chrome/browser/extensions/api/permissions/permissions_apitest.cc b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
index d7f7f9c..5b4a2e5 100644
--- a/chrome/browser/extensions/api/permissions/permissions_apitest.cc
+++ b/chrome/browser/extensions/api/permissions/permissions_apitest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/extensions/api/permissions/permissions_api.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_management_test_util.h"
@@ -69,7 +70,10 @@
 }
 
 IN_PROC_BROWSER_TEST_F(PermissionsApiTest, FaviconPermission) {
+  base::HistogramTester tester;
   ASSERT_TRUE(RunExtensionTest("permissions/favicon")) << message_;
+  tester.ExpectBucketCount("Extensions.FaviconResourceRequested",
+                           Manifest::TYPE_EXTENSION, 1);
 }
 
 // Test functions and APIs that are always allowed (even if you ask for no
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index cd76b31..4a3c9400 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -516,6 +516,11 @@
     "expiry_milestone": 88
   },
   {
+    "name": "context-menu-performance-info",
+    "owners": [ "jds" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "context-menu-search-with-google-lens",
     "owners": [ "benwgold", "lens-chrome" ],
     "expiry_milestone": 88
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 0b8a6c6..075876540 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -846,10 +846,6 @@
 const char kEnableWasmBaselineDescription[] =
     "Enables WebAssembly baseline compilation and tier up.";
 
-const char kEnableWasmCodeCacheName[] = "WebAssembly compiled module cache";
-const char kEnableWasmCodeCacheDescription[] =
-    "Enables caching of compiled WebAssembly modules.";
-
 const char kEnableWasmCodeGCName[] = "WebAssembly code garbage collection";
 const char kEnableWasmCodeGCDescription[] =
     "Enables garbage collection of WebAssembly code.";
@@ -2302,6 +2298,10 @@
 const char kContextMenuCopyImageDescription[] =
     "Enable copying image to system clipboard via context menu.";
 
+const char kContextMenuPerformanceInfoName[] = "Context menu performance hints";
+const char kContextMenuPerformanceInfoDescription[] =
+    "Show link performance information in the context menu.";
+
 const char kContextualSearchDefinitionsName[] = "Contextual Search definitions";
 const char kContextualSearchDefinitionsDescription[] =
     "Enables touch-activated contextual definitions of words on a page to be "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index a1f1e9f..68afd59 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -496,9 +496,6 @@
 extern const char kEnableWasmCodeGCName[];
 extern const char kEnableWasmCodeGCDescription[];
 
-extern const char kEnableWasmCodeCacheName[];
-extern const char kEnableWasmCodeCacheDescription[];
-
 extern const char kEnableWasmLazyCompilationName[];
 extern const char kEnableWasmLazyCompilationDescription[];
 
@@ -1342,6 +1339,9 @@
 extern const char kContextMenuCopyImageName[];
 extern const char kContextMenuCopyImageDescription[];
 
+extern const char kContextMenuPerformanceInfoName[];
+extern const char kContextMenuPerformanceInfoDescription[];
+
 extern const char kContextualSearchDefinitionsName[];
 extern const char kContextualSearchDefinitionsDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 212b8c66..1843890 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -97,7 +97,6 @@
     &kAllowNewIncognitoTabIntents,
     &kAllowRemoteContextForNotifications,
     &kAndroidBlockIntentNonSafelistedHeaders,
-    &kAndroidNightModeCCT,
     &kAndroidNightModeTabReparenting,
     &kAndroidPayIntegrationV1,
     &kAndroidPayIntegrationV2,
@@ -271,9 +270,6 @@
     "AndroidBlockIntentNonSafelistedHeaders",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kAndroidNightModeCCT{"AndroidNightModeCCT",
-                                         base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kAndroidNightModeTabReparenting{
     "AndroidNightModeTabReparenting", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index 8931548..fa99c87 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -16,7 +16,6 @@
 extern const base::Feature kAllowNewIncognitoTabIntents;
 extern const base::Feature kAllowRemoteContextForNotifications;
 extern const base::Feature kAndroidBlockIntentNonSafelistedHeaders;
-extern const base::Feature kAndroidNightModeCCT;
 extern const base::Feature kAndroidNightModeTabReparenting;
 extern const base::Feature kAndroidPayIntegrationV1;
 extern const base::Feature kAndroidPayIntegrationV2;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index fccd3d1..815fe7f 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -208,7 +208,6 @@
     public static final String ADJUST_WEBAPK_INSTALLATION_SPACE = "AdjustWebApkInstallationSpace";
     public static final String ANDROID_BLOCK_INTENT_NON_SAFELISTED_HEADERS =
             "AndroidBlockIntentNonSafelistedHeaders";
-    public static final String ANDROID_NIGHT_MODE_CCT = "AndroidNightModeCCT";
     public static final String ANDROID_NIGHT_MODE_TAB_REPARENTING =
             "AndroidNightModeTabReparenting";
     public static final String ANDROID_PAY_INTEGRATION_V1 = "AndroidPayIntegrationV1";
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/StringCachedFieldTrialParameter.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/StringCachedFieldTrialParameter.java
index 00c4913..27e556c 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/StringCachedFieldTrialParameter.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/StringCachedFieldTrialParameter.java
@@ -31,6 +31,7 @@
     void cacheToDisk() {
         String value =
                 ChromeFeatureList.getFieldTrialParamByFeature(getFeatureName(), getParameterName());
-        SharedPreferencesManager.getInstance().writeString(getSharedPreferenceKey(), value);
+        SharedPreferencesManager.getInstance().writeString(
+                getSharedPreferenceKey(), value.isEmpty() ? getDefaultValue() : value);
     }
 }
diff --git a/chrome/browser/google/google_update_policy_fetcher_win.cc b/chrome/browser/google/google_update_policy_fetcher_win.cc
new file mode 100644
index 0000000..93fd1dc
--- /dev/null
+++ b/chrome/browser/google/google_update_policy_fetcher_win.cc
@@ -0,0 +1,155 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/google/google_update_policy_fetcher_win.h"
+
+#include <wrl/client.h>
+#include <utility>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/win/com_init_util.h"
+#include "base/win/scoped_bstr.h"
+#include "chrome/install_static/install_util.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/core/common/policy_types.h"
+#include "google_update/google_update_idl.h"
+
+namespace {
+
+constexpr char kAutoUpdateCheckPeriodMinutes[] = "AutoUpdateCheckPeriodMinutes";
+constexpr char kDownloadPreference[] = "DownloadPreference";
+constexpr char kInstallPolicy[] = "InstallPolicy";
+constexpr char kUpdatePolicy[] = "UpdatePolicy";
+constexpr char kUpdatesSuppressedDurationMin[] = "UpdatesSuppressedDurationMin";
+constexpr char kUpdatesSuppressedStartHour[] = "UpdatesSuppressedStartHour";
+constexpr char kUpdatesSuppressedStartMinute[] = "UpdatesSuppressedStartMinute";
+constexpr char kRollbackToTargetVersion[] = "RollbackToTargetVersion";
+constexpr char kTargetVersionPrefix[] = "TargetVersionPrefix";
+
+// Adds the |value| of |policy_name| to |policies| using a "Mandatory" level,
+// "Machine" scope and "Platform" source.
+void AddPolicy(const char* policy_name,
+               std::unique_ptr<base::Value> value,
+               policy::PolicyMap* policies) {
+  DCHECK(policies);
+  policies->Set(policy_name, policy::POLICY_LEVEL_MANDATORY,
+                policy::POLICY_SCOPE_MACHINE, policy::POLICY_SOURCE_PLATFORM,
+                std::move(value), nullptr);
+}
+
+}  // namespace
+
+base::Value GetGoogleUpdatePolicyNames() {
+  base::Value names(base::Value::Type::LIST);
+  names.Append(base::Value(kAutoUpdateCheckPeriodMinutes));
+  names.Append(base::Value(kDownloadPreference));
+  names.Append(base::Value(kInstallPolicy));
+  names.Append(base::Value(kUpdatePolicy));
+  names.Append(base::Value(kUpdatesSuppressedDurationMin));
+  names.Append(base::Value(kUpdatesSuppressedStartHour));
+  names.Append(base::Value(kUpdatesSuppressedStartMinute));
+  names.Append(base::Value(kRollbackToTargetVersion));
+  names.Append(base::Value(kTargetVersionPrefix));
+  return names;
+}
+
+std::unique_ptr<policy::PolicyMap> GetGoogleUpdatePolicies() {
+  base::win::AssertComInitialized();
+  Microsoft::WRL::ComPtr<IPolicyStatus> policy_status;
+  HRESULT last_com_res =
+      ::CoCreateInstance(CLSID_PolicyStatusClass, nullptr, CLSCTX_ALL,
+                         IID_PPV_ARGS(&policy_status));
+
+  // If IPolicyStatus could not be instantiated, assume the updater is not
+  // configured yet to return the policies.
+  if (FAILED(last_com_res))
+    return nullptr;
+
+  auto policies = std::make_unique<policy::PolicyMap>();
+  base::win::ScopedBstr app_id(install_static::GetAppGuid());
+
+  DWORD auto_update_check_period_minutes;
+  last_com_res = policy_status->get_lastCheckPeriodMinutes(
+      &auto_update_check_period_minutes);
+  if (SUCCEEDED(last_com_res)) {
+    AddPolicy(kAutoUpdateCheckPeriodMinutes,
+              std::make_unique<base::Value>(
+                  base::saturated_cast<int>(auto_update_check_period_minutes)),
+              policies.get());
+  }
+
+  base::win::ScopedBstr download_preference_group_policy;
+  last_com_res = policy_status->get_downloadPreferenceGroupPolicy(
+      download_preference_group_policy.Receive());
+  if (SUCCEEDED(last_com_res) &&
+      download_preference_group_policy.Length() > 0) {
+    AddPolicy(
+        kDownloadPreference,
+        std::make_unique<base::Value>(download_preference_group_policy.Get()),
+        policies.get());
+  }
+
+  DWORD effective_policy_for_app_installs;
+  last_com_res = policy_status->get_effectivePolicyForAppInstalls(
+      app_id.Get(), &effective_policy_for_app_installs);
+  if (SUCCEEDED(last_com_res)) {
+    AddPolicy(kInstallPolicy,
+              std::make_unique<base::Value>(
+                  base::saturated_cast<int>(effective_policy_for_app_installs)),
+              policies.get());
+  }
+
+  DWORD effective_policy_for_app_updates;
+  last_com_res = policy_status->get_effectivePolicyForAppUpdates(
+      app_id.Get(), &effective_policy_for_app_updates);
+  if (SUCCEEDED(last_com_res)) {
+    AddPolicy(kUpdatePolicy,
+              std::make_unique<base::Value>(
+                  base::saturated_cast<int>(effective_policy_for_app_updates)),
+              policies.get());
+  }
+
+  DWORD updates_suppressed_duration;
+  DWORD updates_suppressed_start_hour;
+  DWORD updates_suppressed_start_minute;
+  VARIANT_BOOL are_updates_suppressed;
+  last_com_res = policy_status->get_updatesSuppressedTimes(
+      &updates_suppressed_start_hour, &updates_suppressed_start_minute,
+      &updates_suppressed_duration, &are_updates_suppressed);
+  if (SUCCEEDED(last_com_res)) {
+    AddPolicy(kUpdatesSuppressedDurationMin,
+              std::make_unique<base::Value>(
+                  base::saturated_cast<int>(updates_suppressed_duration)),
+              policies.get());
+    AddPolicy(kUpdatesSuppressedStartHour,
+              std::make_unique<base::Value>(
+                  base::saturated_cast<int>(updates_suppressed_start_hour)),
+              policies.get());
+    AddPolicy(kUpdatesSuppressedStartMinute,
+              std::make_unique<base::Value>(
+                  base::saturated_cast<int>(updates_suppressed_start_minute)),
+              policies.get());
+  }
+
+  VARIANT_BOOL is_rollback_to_target_version_allowed;
+  last_com_res = policy_status->get_isRollbackToTargetVersionAllowed(
+      app_id.Get(), &is_rollback_to_target_version_allowed);
+  if (SUCCEEDED(last_com_res)) {
+    AddPolicy(kRollbackToTargetVersion,
+              std::make_unique<base::Value>(
+                  is_rollback_to_target_version_allowed == VARIANT_TRUE),
+              policies.get());
+  }
+
+  base::win::ScopedBstr target_version_prefix;
+  last_com_res = policy_status->get_targetVersionPrefix(
+      app_id.Get(), target_version_prefix.Receive());
+  if (SUCCEEDED(last_com_res) && target_version_prefix.Length() > 0) {
+    AddPolicy(kTargetVersionPrefix,
+              std::make_unique<base::Value>(target_version_prefix.Get()),
+              policies.get());
+  }
+
+  return policies;
+}
diff --git a/chrome/browser/google/google_update_policy_fetcher_win.h b/chrome/browser/google/google_update_policy_fetcher_win.h
new file mode 100644
index 0000000..85873c3
--- /dev/null
+++ b/chrome/browser/google/google_update_policy_fetcher_win.h
@@ -0,0 +1,27 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GOOGLE_GOOGLE_UPDATE_POLICY_FETCHER_WIN_H_
+#define CHROME_BROWSER_GOOGLE_GOOGLE_UPDATE_POLICY_FETCHER_WIN_H_
+
+#include <memory>
+
+#include "base/values.h"
+
+namespace policy {
+class PolicyMap;
+}
+
+// Returns a list of all the Google Update policies available through the
+// IPolicyStatus COM interface.
+base::Value GetGoogleUpdatePolicyNames();
+
+// Fetches all the Google Update Policies available through the IPolicyStatus
+// COM interface. Only the policies that have been set are returned by this
+// function. This function returns null if the fetch fails because IPolicyStatus
+// could not be instantiated. This function must run on a COM STA thread because
+// it makes some COM calls.
+std::unique_ptr<policy::PolicyMap> GetGoogleUpdatePolicies();
+
+#endif  // CHROME_BROWSER_GOOGLE_GOOGLE_UPDATE_POLICY_FETCHER_WIN_H_
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index d3ef5ff..3a938c40 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -768,7 +768,7 @@
   ukm_service_->RegisterMetricsProvider(
       std::make_unique<metrics::ScreenInfoMetricsProvider>());
 
-  // TODO(rkaplow): Support synthetic trials for UKM.
+  // TODO(crbug.com/754877): Support synthetic trials for UKM.
   ukm_service_->RegisterMetricsProvider(
       std::make_unique<variations::FieldTrialsProvider>(nullptr,
                                                         kUKMFieldTrialSuffix));
diff --git a/chrome/browser/password_manager/password_store_factory.cc b/chrome/browser/password_manager/password_store_factory.cc
index 572102cb..84c6bab 100644
--- a/chrome/browser/password_manager/password_store_factory.cc
+++ b/chrome/browser/password_manager/password_store_factory.cc
@@ -69,6 +69,16 @@
 }
 #endif
 
+#if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
+bool IsSignedIn(Profile* profile) {
+  auto* identity_manager =
+      IdentityManagerFactory::GetForProfileIfExists(profile);
+  return identity_manager
+             ? !identity_manager->GetAccountsWithRefreshTokens().empty()
+             : false;
+}
+#endif
+
 }  // namespace
 
 // static
@@ -163,7 +173,7 @@
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   // Prepare password hash data for reuse detection.
-  ps->PreparePasswordHashData(GetSyncUsername(profile));
+  ps->PreparePasswordHashData(GetSyncUsername(profile), IsSignedIn(profile));
 #endif
 
   auto network_context_getter = base::BindRepeating(
diff --git a/chrome/browser/payments/expandable_payment_handler_browsertest.cc b/chrome/browser/payments/expandable_payment_handler_browsertest.cc
index 217d30d..709dc5c 100644
--- a/chrome/browser/payments/expandable_payment_handler_browsertest.cc
+++ b/chrome/browser/payments/expandable_payment_handler_browsertest.cc
@@ -3,59 +3,38 @@
 // found in the LICENSE file.
 
 #include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "chrome/test/base/android/android_browser_test.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 namespace payments {
 namespace {
 
-class ExpandablePaymentHandlerBrowserTest : public PlatformBrowserTest {
+class ExpandablePaymentHandlerBrowserTest
+    : public PaymentRequestPlatformBrowserTestBase {
  public:
   ExpandablePaymentHandlerBrowserTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        http_server_(net::EmbeddedTestServer::TYPE_HTTP) {
+      : http_server_(net::EmbeddedTestServer::TYPE_HTTP) {
     scoped_feature_list_.InitWithFeatures(
         /*enabled_features=*/{chrome::android::kScrollToExpandPaymentHandler},
         /*disabled_features=*/{});
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        switches::kEnableExperimentalWebPlatformFeatures);
-  }
-
   void SetUpOnMainThread() override {
-    https_server_.ServeFilesFromSourceDirectory(
-        "components/test/data/payments/");
-    ASSERT_TRUE(https_server_.Start());
-    ASSERT_TRUE(content::NavigateToURL(
-        GetActiveWebContents(),
-        https_server_.GetURL("/maxpay.com/merchant.html")));
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
+    NavigateTo("/maxpay.com/merchant.html");
+
+    // Start http server.
     http_server_.ServeFilesFromSourceDirectory(
         "components/test/data/payments/");
     ASSERT_TRUE(http_server_.Start());
-    test_controller_.SetUpOnMainThread();
-    PlatformBrowserTest::SetUpOnMainThread();
   }
 
   GURL GetHttpPageUrl() {
     return http_server_.GetURL("/maxpay.com/merchant.html");
   }
 
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
-
-  PaymentRequestTestController test_controller_;
-
  private:
-  net::EmbeddedTestServer https_server_;
   net::EmbeddedTestServer http_server_;
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -69,9 +48,9 @@
                 GetActiveWebContents(),
                 "launchAndWaitUntilReady('./payment_handler_window.html')"));
 
-  DCHECK(test_controller_.GetPaymentHandlerWebContents());
+  DCHECK(test_controller()->GetPaymentHandlerWebContents());
   EXPECT_EQ("confirmed",
-            content::EvalJs(test_controller_.GetPaymentHandlerWebContents(),
+            content::EvalJs(test_controller()->GetPaymentHandlerWebContents(),
                             "confirm()"));
   EXPECT_EQ("success", content::EvalJs(GetActiveWebContents(), "getResult()"));
 }
@@ -85,8 +64,8 @@
                 GetActiveWebContents(),
                 "launchAndWaitUntilReady('./payment_handler_window.html')"));
 
-  DCHECK(test_controller_.GetPaymentHandlerWebContents());
-  EXPECT_TRUE(test_controller_.ClickPaymentHandlerSecurityIcon());
+  DCHECK(test_controller()->GetPaymentHandlerWebContents());
+  EXPECT_TRUE(test_controller()->ClickPaymentHandlerSecurityIcon());
 }
 
 // Make sure merchants can cancel the payment.
@@ -98,9 +77,9 @@
                 GetActiveWebContents(),
                 "launchAndWaitUntilReady('./payment_handler_window.html')"));
 
-  DCHECK(test_controller_.GetPaymentHandlerWebContents());
+  DCHECK(test_controller()->GetPaymentHandlerWebContents());
   EXPECT_EQ("canceled",
-            content::EvalJs(test_controller_.GetPaymentHandlerWebContents(),
+            content::EvalJs(test_controller()->GetPaymentHandlerWebContents(),
                             "cancel()"));
   EXPECT_EQ("unknown", content::EvalJs(GetActiveWebContents(), "getResult()"));
 }
@@ -114,9 +93,9 @@
                 GetActiveWebContents(),
                 "launchAndWaitUntilReady('./payment_handler_window.html')"));
 
-  DCHECK(test_controller_.GetPaymentHandlerWebContents());
+  DCHECK(test_controller()->GetPaymentHandlerWebContents());
   EXPECT_EQ("failed",
-            content::EvalJs(test_controller_.GetPaymentHandlerWebContents(),
+            content::EvalJs(test_controller()->GetPaymentHandlerWebContents(),
                             "fail()"));
   EXPECT_EQ("fail", content::EvalJs(GetActiveWebContents(), "getResult()"));
 }
@@ -141,9 +120,9 @@
                 GetActiveWebContents(),
                 "launchAndWaitUntilReady('./payment_handler_window.html')"));
 
-  DCHECK(test_controller_.GetPaymentHandlerWebContents());
+  DCHECK(test_controller()->GetPaymentHandlerWebContents());
   EXPECT_EQ(true,
-            content::EvalJs(test_controller_.GetPaymentHandlerWebContents(),
+            content::EvalJs(test_controller()->GetPaymentHandlerWebContents(),
                             "isWindowClientReady()"));
 }
 }  // namespace
diff --git a/chrome/browser/payments/has_enrolled_instrument_browsertest.cc b/chrome/browser/payments/has_enrolled_instrument_browsertest.cc
index 9d5629b..16c1922 100644
--- a/chrome/browser/payments/has_enrolled_instrument_browsertest.cc
+++ b/chrome/browser/payments/has_enrolled_instrument_browsertest.cc
@@ -8,26 +8,11 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "chrome/browser/payments/personal_data_manager_test_util.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/network_session_configurator/common/network_switches.h"
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 #include "components/payments/core/features.h"
 #include "components/payments/core/journey_logger.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_ANDROID)
-#include "chrome/browser/flags/android/chrome_feature_list.h"
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif
-
 namespace payments {
 namespace {
 
@@ -41,13 +26,6 @@
 #endif  // OS_ANDROID
     "User does not have valid information on file.";
 
-autofill::CreditCard GetCardWithBillingAddress(
-    const autofill::AutofillProfile& profile) {
-  autofill::CreditCard card = autofill::test::GetCreditCard();
-  card.set_billing_address_id(profile.guid());
-  return card;
-}
-
 enum HasEnrolledInstrumentMode {
   STRICT_HAS_ENROLLED_INSTRUMENT,
   LEGACY_HAS_ENROLLED_INSTRUMENT,
@@ -56,11 +34,10 @@
 // A parameterized test to test both values of
 // features::kStrictHasEnrolledAutofillInstrument.
 class HasEnrolledInstrumentTest
-    : public PlatformBrowserTest,
+    : public PaymentRequestPlatformBrowserTestBase,
       public testing::WithParamInterface<HasEnrolledInstrumentMode> {
  public:
-  HasEnrolledInstrumentTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+  HasEnrolledInstrumentTest() {
     if (GetParam() == STRICT_HAS_ENROLLED_INSTRUMENT) {
       feature_list_.InitWithFeatures(
           /*enabled_features=*/{features::kStrictHasEnrolledAutofillInstrument},
@@ -68,21 +45,11 @@
     }
   }
 
-  ~HasEnrolledInstrumentTest() override {}
+  ~HasEnrolledInstrumentTest() override = default;
 
   void SetUpOnMainThread() override {
-    https_server_.ServeFilesFromSourceDirectory(
-        "components/test/data/payments");
-    ASSERT_TRUE(https_server_.Start());
-    ASSERT_TRUE(content::NavigateToURL(
-        GetActiveWebContents(),
-        https_server_.GetURL("/has_enrolled_instrument.html")));
-    test_controller_.SetUpOnMainThread();
-    PlatformBrowserTest::SetUpOnMainThread();
-  }
-
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
+    NavigateTo("/has_enrolled_instrument.html");
   }
 
   const std::string& not_supported_message() const {
@@ -144,8 +111,6 @@
   }
 
  private:
-  PaymentRequestTestController test_controller_;
-  net::EmbeddedTestServer https_server_;
   std::string not_supported_message_ = kNotSupportedMessage;
   base::test::ScopedFeatureList feature_list_;
 
@@ -158,18 +123,15 @@
 }
 
 IN_PROC_BROWSER_TEST_P(HasEnrolledInstrumentTest, NoBillingAddress) {
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      autofill::test::GetCreditCard());
+  AddCreditCard(autofill::test::GetCreditCard());
   ExpectHasEnrolledInstrumentIs(GetParam() != STRICT_HAS_ENROLLED_INSTRUMENT);
   ExpectShowRejects();
 }
 
 IN_PROC_BROWSER_TEST_P(HasEnrolledInstrumentTest,
                        HaveShippingNoBillingAddress) {
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           autofill::test::GetFullProfile());
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      autofill::test::GetCreditCard());
+  CreateAndAddAutofillProfile();
+  AddCreditCard(autofill::test::GetCreditCard());
 
   ExpectHasEnrolledInstrumentIs(GetParam() != STRICT_HAS_ENROLLED_INSTRUMENT);
   ExpectShowRejects();
@@ -177,23 +139,18 @@
 
 IN_PROC_BROWSER_TEST_P(HasEnrolledInstrumentTest,
                        HaveShippingAndBillingAddress) {
-  autofill::AutofillProfile address = autofill::test::GetFullProfile();
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      GetCardWithBillingAddress(address));
+  CreateAndAddCreditCardForProfile(CreateAndAddAutofillProfile());
 
   ExpectHasEnrolledInstrumentIs(true);
 }
 
 IN_PROC_BROWSER_TEST_P(HasEnrolledInstrumentTest, InvalidCardNumber) {
   autofill::AutofillProfile address = autofill::test::GetFullProfile();
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  autofill::CreditCard card = GetCardWithBillingAddress(address);
+  AddAutofillProfile(address);
+  autofill::CreditCard card = CreatCreditCardForProfile(address);
   card.SetRawInfo(autofill::ServerFieldType::CREDIT_CARD_NUMBER,
                   base::ASCIIToUTF16("1111111111111111"));
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(), card);
+  AddCreditCard(card);
 
   ExpectHasEnrolledInstrumentIs(false);
   ExpectShowRejects();
@@ -201,11 +158,10 @@
 
 IN_PROC_BROWSER_TEST_P(HasEnrolledInstrumentTest, ExpiredCard) {
   autofill::AutofillProfile address = autofill::test::GetFullProfile();
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  autofill::CreditCard card = GetCardWithBillingAddress(address);
+  AddAutofillProfile(address);
+  autofill::CreditCard card = CreatCreditCardForProfile(address);
   card.SetExpirationYear(2000);
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(), card);
+  AddCreditCard(card);
 
   ExpectHasEnrolledInstrumentIs(GetParam() != STRICT_HAS_ENROLLED_INSTRUMENT);
   ExpectShowRejects();
@@ -216,15 +172,11 @@
 IN_PROC_BROWSER_TEST_P(HasEnrolledInstrumentTest,
                        HaveNoNameShippingAndBillingAddress) {
   autofill::AutofillProfile address = autofill::test::GetFullProfile();
-
   address.SetRawInfo(autofill::ServerFieldType::NAME_FIRST, base::string16());
   address.SetRawInfo(autofill::ServerFieldType::NAME_MIDDLE, base::string16());
   address.SetRawInfo(autofill::ServerFieldType::NAME_LAST, base::string16());
-
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      GetCardWithBillingAddress(address));
+  AddAutofillProfile(address);
+  CreateAndAddCreditCardForProfile(address);
 
   // Recipient name is required for shipping address in strict mode.
   EXPECT_EQ(GetParam() != STRICT_HAS_ENROLLED_INSTRUMENT,
@@ -266,10 +218,8 @@
   autofill::AutofillProfile address = autofill::test::GetFullProfile();
   address.SetRawInfo(autofill::ServerFieldType::ADDRESS_HOME_STREET_ADDRESS,
                      base::string16());
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      GetCardWithBillingAddress(address));
+  AddAutofillProfile(address);
+  CreateAndAddCreditCardForProfile(address);
 
   ExpectHasEnrolledInstrumentIs(GetParam() != STRICT_HAS_ENROLLED_INSTRUMENT);
   ExpectShowRejects();
@@ -279,10 +229,8 @@
   autofill::AutofillProfile address = autofill::test::GetFullProfile();
   address.SetRawInfo(autofill::ServerFieldType::EMAIL_ADDRESS,
                      base::string16());
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      GetCardWithBillingAddress(address));
+  AddAutofillProfile(address);
+  CreateAndAddCreditCardForProfile(address);
 
   EXPECT_EQ(true,
             content::EvalJs(GetActiveWebContents(), "hasEnrolledInstrument()"));
@@ -305,10 +253,8 @@
   autofill::AutofillProfile address = autofill::test::GetFullProfile();
   address.SetRawInfo(autofill::ServerFieldType::EMAIL_ADDRESS,
                      base::ASCIIToUTF16("this-is-not-a-valid-email-address"));
-  test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                           address);
-  test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(),
-                      GetCardWithBillingAddress(address));
+  AddAutofillProfile(address);
+  CreateAndAddCreditCardForProfile(address);
 
   EXPECT_EQ(true,
             content::EvalJs(GetActiveWebContents(), "hasEnrolledInstrument()"));
diff --git a/chrome/browser/payments/has_enrolled_instrument_query_quota_browsertest.cc b/chrome/browser/payments/has_enrolled_instrument_query_quota_browsertest.cc
index 7004fe56..0520d028 100644
--- a/chrome/browser/payments/has_enrolled_instrument_query_quota_browsertest.cc
+++ b/chrome/browser/payments/has_enrolled_instrument_query_quota_browsertest.cc
@@ -2,64 +2,30 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "components/network_session_configurator/common/network_switches.h"
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 #include "components/payments/core/features.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/content_browser_test_utils.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_ANDROID)
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif
-
 namespace payments {
 namespace {
 
-class HasEnrolledInstrumentQueryQuotaTest : public PlatformBrowserTest {
+class HasEnrolledInstrumentQueryQuotaTest
+    : public PaymentRequestPlatformBrowserTestBase {
  public:
-  HasEnrolledInstrumentQueryQuotaTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+  HasEnrolledInstrumentQueryQuotaTest() = default;
 
-  ~HasEnrolledInstrumentQueryQuotaTest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // HTTPS server only serves a valid cert for localhost, so this is needed to
-    // load pages from other hosts without an error.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
+  ~HasEnrolledInstrumentQueryQuotaTest() override = default;
 
   void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-    ASSERT_TRUE(https_server_.InitializeAndListen());
-    content::SetupCrossSiteRedirector(&https_server_);
-    https_server_.ServeFilesFromSourceDirectory(
-        "components/test/data/payments");
-    https_server_.StartAcceptingConnections();
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
     // Cannot use the default localhost hostname, because Chrome turns off the
     // quota for localhost and file:/// scheme to ease web development.
-    ASSERT_TRUE(content::NavigateToURL(
-        GetActiveWebContents(),
-        https_server_.GetURL("a.com", "/has_enrolled_instrument.html")));
-    PlatformBrowserTest::SetUpOnMainThread();
-  }
-
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
+    NavigateTo("a.com", "/has_enrolled_instrument.html");
   }
 
  private:
-  net::EmbeddedTestServer https_server_;
-
   DISALLOW_COPY_AND_ASSIGN(HasEnrolledInstrumentQueryQuotaTest);
 };
 
diff --git a/chrome/browser/payments/hybrid_request_skip_ui_browsertest.cc b/chrome/browser/payments/hybrid_request_skip_ui_browsertest.cc
index 359921d..c03274e 100644
--- a/chrome/browser/payments/hybrid_request_skip_ui_browsertest.cc
+++ b/chrome/browser/payments/hybrid_request_skip_ui_browsertest.cc
@@ -2,33 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/command_line.h"
 #include "base/macros.h"
 #include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "chrome/browser/payments/personal_data_manager_test_util.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/test_event_waiter.h"
-#include "components/network_session_configurator/common/network_switches.h"
-#include "components/payments/content/service_worker_payment_app_finder.h"
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 #include "components/payments/core/features.h"
-#include "components/payments/core/test_payment_manifest_downloader.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if defined(OS_ANDROID)
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif
-
 namespace payments {
 
 enum SkipToGPayMode {
@@ -48,21 +27,12 @@
 // - user has an incomplete autofill instrument
 // - user doesn't have any autofill instrument
 class HybridRequestSkipUITest
-    : public PlatformBrowserTest,
-      public PaymentRequestTestObserver,
+    : public PaymentRequestPlatformBrowserTestBase,
       public testing::WithParamInterface<
           std::tuple<SkipToGPayMode, SkipToGPayTestConfig>> {
  public:
-  // PaymentRequestTestObserver events that can be waited on by the EventWaiter.
-  enum TestEvent : int {
-    SHOW_APPS_READY,
-  };
-
   HybridRequestSkipUITest()
-      : gpay_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    test_controller_.SetObserver(this);
-
+      : gpay_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
     if (GetTestMode() == ALWAYS_SKIP_TO_GPAY) {
       scoped_feature_list_.InitAndEnableFeature(
           features::kPaymentRequestSkipToGPay);
@@ -73,83 +43,34 @@
     }
   }
 
-  ~HybridRequestSkipUITest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // HTTPS server only serves a valid cert for localhost, so this is needed to
-    // load pages from the fake "google.com" without an interstitial.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
+  ~HybridRequestSkipUITest() override = default;
 
   void SetUpOnMainThread() override {
-    // Map all out-going DNS lookups to the local server. This must be used in
-    // conjunction with switches::kIgnoreCertificateErrors to work.
-    host_resolver()->AddRule("*", "127.0.0.1");
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
 
     gpay_server_.ServeFilesFromSourceDirectory(
         "components/test/data/payments/google.com/");
     ASSERT_TRUE(gpay_server_.Start());
 
-    https_server_.ServeFilesFromSourceDirectory(
-        "components/test/data/payments/");
-    ASSERT_TRUE(https_server_.Start());
+    // Set up test manifest downloader that knows how to fake origin.
+    const std::string method_name = "google.com";
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting(
+        {{method_name, &gpay_server_}});
 
-    content::BrowserContext* context =
-        GetActiveWebContents()->GetBrowserContext();
-    auto downloader = std::make_unique<TestDownloader>(
-        content::BrowserContext::GetDefaultStoragePartition(context)
-            ->GetURLLoaderFactoryForBrowserProcess());
-    downloader->AddTestServerURL("https://google.com/",
-                                 gpay_server_.GetURL("google.com", "/"));
-    ServiceWorkerPaymentAppFinder::GetInstance()
-        ->SetDownloaderAndIgnorePortInOriginComparisonForTesting(
-            std::move(downloader));
-
-    ASSERT_TRUE(
-        NavigateTo(https_server_.GetURL("/hybrid_request_skip_ui_test.html")));
+    NavigateTo("/hybrid_request_skip_ui_test.html");
 
     // Inject autofill instrument based on test config.
     if (GetTestConfig() == TEST_HAS_COMPLETE_AUTOFILL_INSTRUMENT) {
-      autofill::CreditCard card = autofill::test::GetCreditCard();
-      autofill::AutofillProfile address = autofill::test::GetFullProfile();
-      test::AddAutofillProfile(GetActiveWebContents()->GetBrowserContext(),
-                               address);
-      card.set_billing_address_id(address.guid());
-      test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(), card);
+      CreateAndAddCreditCardForProfile(CreateAndAddAutofillProfile());
     } else if (GetTestConfig() == TEST_INCOMPLETE_AUTOFILL_INSTRUMENT) {
-      autofill::CreditCard card = autofill::test::GetCreditCard();
-      test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(), card);
+      AddCreditCard(autofill::test::GetCreditCard());
     }
-
-    test_controller_.SetUpOnMainThread();
-    PlatformBrowserTest::SetUpOnMainThread();
   }
 
-  // PaymentRequestTestObserver
-  void OnShowAppsReady() override {
-    if (event_waiter_)
-      event_waiter_->OnEvent(TestEvent::SHOW_APPS_READY);
-  }
-
-  void ResetEventWaiterForSequence(std::list<TestEvent> event_sequence) {
-    event_waiter_ = std::make_unique<autofill::EventWaiter<TestEvent>>(
-        std::move(event_sequence));
-  }
-
-  void WaitForObservedEvent() { event_waiter_->Wait(); }
-
   // Convenience methods for accessing the test parameterization.
   SkipToGPayMode GetTestMode() const { return std::get<0>(GetParam()); }
   SkipToGPayTestConfig GetTestConfig() const { return std::get<1>(GetParam()); }
 
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
-
-  bool NavigateTo(const GURL& url) {
-    return content::NavigateToURL(GetActiveWebContents(), url);
-  }
-
   // Runs a single test case and checks that |expected_result| is returned.
   void RunTest(const char* js_snippet, const char* expected_result) {
     if (GetTestMode() == SKIP_TO_GPAY_IF_NO_CARD &&
@@ -157,7 +78,7 @@
       // Skip-to-GPay is not activated in this combination because user has a
       // usable autofill instrument. Just verify that the payment sheet is
       // shown.
-      ResetEventWaiterForSequence({TestEvent::SHOW_APPS_READY});
+      ResetEventWaiterForSingleEvent(TestEvent::kShowAppsReady);
       EXPECT_TRUE(content::ExecJs(GetActiveWebContents(), js_snippet));
       WaitForObservedEvent();
       return;
@@ -170,10 +91,7 @@
 
  protected:
   net::EmbeddedTestServer gpay_server_;
-  net::EmbeddedTestServer https_server_;
   base::test::ScopedFeatureList scoped_feature_list_;
-  PaymentRequestTestController test_controller_;
-  std::unique_ptr<autofill::EventWaiter<TestEvent>> event_waiter_;
 };
 
 IN_PROC_BROWSER_TEST_P(HybridRequestSkipUITest, NothingRequested) {
diff --git a/chrome/browser/payments/payment_handler_change_shipping_address_option_browsertest.cc b/chrome/browser/payments/payment_handler_change_shipping_address_option_browsertest.cc
index 26ecb7ef..eb0c373 100644
--- a/chrome/browser/payments/payment_handler_change_shipping_address_option_browsertest.cc
+++ b/chrome/browser/payments/payment_handler_change_shipping_address_option_browsertest.cc
@@ -5,20 +5,8 @@
 #include <string>
 
 #include "base/strings/utf_string_conversions.h"
-#include "build/build_config.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "content/public/common/content_switches.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/re2/src/re2/re2.h"
-#include "url/gurl.h"
-
-#if defined(OS_ANDROID)
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif
 
 namespace payments {
 namespace {
@@ -46,21 +34,6 @@
     "option\",\"selected\":true}],\"total\":{\"currency\":\"GBP\",\"value\":"
     "\"0.02\"}}";
 
-// Looks for the "supportedMethods" URL and removes its port number.
-std::string ClearPortNumber(const std::string& may_contain_method_url) {
-  std::string before;
-  std::string method;
-  std::string after;
-  GURL::Replacements port;
-  port.ClearPort();
-  return re2::RE2::FullMatch(
-             may_contain_method_url,
-             "(.*\"supportedMethods\":\")(https://.*)(\",\"total\".*)", &before,
-             &method, &after)
-             ? before + GURL(method).ReplaceComponents(port).spec() + after
-             : may_contain_method_url;
-}
-
 enum class ChangeType { kAddressChange, kOptionChange };
 
 struct TestCase {
@@ -79,63 +52,41 @@
 };
 
 class PaymentHandlerChangeShippingAddressOptionTest
-    : public PlatformBrowserTest,
+    : public PaymentRequestPlatformBrowserTestBase,
       public testing::WithParamInterface<TestCase> {
  protected:
-  PaymentHandlerChangeShippingAddressOptionTest() {}
-  ~PaymentHandlerChangeShippingAddressOptionTest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        switches::kEnableExperimentalWebPlatformFeatures);
-  }
-
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
+  PaymentHandlerChangeShippingAddressOptionTest() = default;
+  ~PaymentHandlerChangeShippingAddressOptionTest() override = default;
 
   void SetUpOnMainThread() override {
-    https_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    https_server_->ServeFilesFromSourceDirectory(
-        "components/test/data/payments");
-    ASSERT_TRUE(https_server_->Start());
-    ASSERT_TRUE(content::NavigateToURL(
-        GetActiveWebContents(),
-        https_server_->GetURL("/change_shipping_address_option.html")));
-    test_controller_.SetUpOnMainThread();
-    PlatformBrowserTest::SetUpOnMainThread();
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
+    NavigateTo("/change_shipping_address_option.html");
   }
 
   std::string getTestType() {
     return GetParam().change_type == ChangeType::kOptionChange ? "option"
                                                                : "address";
   }
-
- private:
-  std::unique_ptr<net::EmbeddedTestServer> https_server_;
-  PaymentRequestTestController test_controller_;
 };
 
 IN_PROC_BROWSER_TEST_P(PaymentHandlerChangeShippingAddressOptionTest, Test) {
-  std::string actual_output;
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      GetActiveWebContents(),
-      "install('change_shipping_" + getTestType() + "_app.js');",
-      &actual_output));
-  ASSERT_EQ(actual_output, "instruments.set(): Payment handler installed.");
+  EXPECT_EQ("instruments.set(): Payment handler installed.",
+            content::EvalJsWithManualReply(
+                GetActiveWebContents(),
+                "install('change_shipping_" + getTestType() + "_app.js');"));
 
-  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
-                                     GetParam().init_test_code));
+  EXPECT_TRUE(
+      content::ExecJs(GetActiveWebContents(), GetParam().init_test_code));
 
-  ASSERT_TRUE(content::ExecuteScriptAndExtractString(
-      GetActiveWebContents(),
-      "outputChangeShippingAddressOptionReturnValue(request);",
-      &actual_output));
+  std::string actual_output =
+      content::EvalJsWithManualReply(
+          GetActiveWebContents(),
+          "outputChangeShippingAddressOptionReturnValue(request);")
+          .ExtractString();
 
   // The test expectations are hard-coded, but the embedded test server changes
   // its port number in every test, e.g., https://a.com:34548.
-  ASSERT_EQ(ClearPortNumber(actual_output), GetParam().expected_output)
+  EXPECT_EQ(ClearPortNumber(actual_output), GetParam().expected_output)
       << "When executing " << GetParam().init_test_code;
 }
 
diff --git a/chrome/browser/payments/payment_handler_enable_delegations_browsertest.cc b/chrome/browser/payments/payment_handler_enable_delegations_browsertest.cc
index 70fb38c..80436779 100644
--- a/chrome/browser/payments/payment_handler_enable_delegations_browsertest.cc
+++ b/chrome/browser/payments/payment_handler_enable_delegations_browsertest.cc
@@ -3,52 +3,23 @@
 // found in the LICENSE file.
 
 #include "base/macros.h"
-#include "build/build_config.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-
-#if defined(OS_ANDROID)
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 
 namespace payments {
 namespace {
 
-class PaymentHandlerEnableDelegationsTest : public PlatformBrowserTest {
+class PaymentHandlerEnableDelegationsTest
+    : public PaymentRequestPlatformBrowserTestBase {
  public:
-  PaymentHandlerEnableDelegationsTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
-
-  ~PaymentHandlerEnableDelegationsTest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        switches::kEnableExperimentalWebPlatformFeatures);
-  }
+  PaymentHandlerEnableDelegationsTest() = default;
+  ~PaymentHandlerEnableDelegationsTest() override = default;
 
   void SetUpOnMainThread() override {
-    https_server_.ServeFilesFromSourceDirectory(
-        "components/test/data/payments");
-    ASSERT_TRUE(https_server_.Start());
-    ASSERT_TRUE(content::NavigateToURL(
-        GetActiveWebContents(), https_server_.GetURL("/payment_handler.html")));
-    test_controller_.SetUpOnMainThread();
-    PlatformBrowserTest::SetUpOnMainThread();
-  }
-
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
+    NavigateTo("/payment_handler.html");
   }
 
  private:
-  PaymentRequestTestController test_controller_;
-  net::EmbeddedTestServer https_server_;
-
   DISALLOW_COPY_AND_ASSIGN(PaymentHandlerEnableDelegationsTest);
 };
 
diff --git a/chrome/browser/payments/payment_handler_just_in_time_installation_browsertest.cc b/chrome/browser/payments/payment_handler_just_in_time_installation_browsertest.cc
index c58cc4b..b020066 100644
--- a/chrome/browser/payments/payment_handler_just_in_time_installation_browsertest.cc
+++ b/chrome/browser/payments/payment_handler_just_in_time_installation_browsertest.cc
@@ -2,123 +2,41 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "build/build_config.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "components/autofill/core/browser/test_event_waiter.h"
-#include "components/network_session_configurator/common/network_switches.h"
-#include "components/payments/content/service_worker_payment_app_finder.h"
-#include "components/payments/core/test_payment_manifest_downloader.h"
-#include "content/public/browser/browser_context.h"
-#include "content/public/browser/storage_partition.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/common/content_switches.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "testing/gmock/include/gmock/gmock-matchers.h"
-
-#if defined(OS_ANDROID)
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 
 namespace payments {
 
 class PaymentHandlerJustInTimeInstallationTest
-    : public PlatformBrowserTest,
-      public PaymentRequestTestObserver {
+    : public PaymentRequestPlatformBrowserTestBase {
  protected:
-  // PaymentRequestTestObserver events that can be waited on by the EventWaiter.
-  enum TestEvent : int {
-    PAYMENT_COMPLETED,
-  };
-
   PaymentHandlerJustInTimeInstallationTest()
-      : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        kylepay_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
-    test_controller_.SetObserver(this);
-  }
+      : kylepay_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
 
-  ~PaymentHandlerJustInTimeInstallationTest() override {}
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        switches::kEnableExperimentalWebPlatformFeatures);
-    // HTTPS server only serves a valid cert for localhost, so this is needed to
-    // load pages from the fake "kylepay.com" without an interstitial.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
+  ~PaymentHandlerJustInTimeInstallationTest() override = default;
 
   void SetUpOnMainThread() override {
-    // Map all out-going DNS lookups to the local server. This must be used in
-    // conjunction with switches::kIgnoreCertificateErrors to work.
-    host_resolver()->AddRule("*", "127.0.0.1");
-
+    PaymentRequestPlatformBrowserTestBase::SetUpOnMainThread();
     kylepay_server_.ServeFilesFromSourceDirectory(
         "components/test/data/payments/kylepay.com/");
     ASSERT_TRUE(kylepay_server_.Start());
 
     // Set up test manifest downloader that knows how to fake origin.
-    content::BrowserContext* context =
-        GetActiveWebContents()->GetBrowserContext();
-    auto downloader = std::make_unique<TestDownloader>(
-        content::BrowserContext::GetDefaultStoragePartition(context)
-            ->GetURLLoaderFactoryForBrowserProcess());
-    downloader->AddTestServerURL("https://kylepay.com/",
-                                 kylepay_server_.GetURL("kylepay.com", "/"));
-    ServiceWorkerPaymentAppFinder::GetInstance()
-        ->SetDownloaderAndIgnorePortInOriginComparisonForTesting(
-            std::move(downloader));
+    const std::string method_name = "kylepay.com";
+    SetDownloaderAndIgnorePortInOriginComparisonForTesting(
+        {{method_name, &kylepay_server_}});
 
-    https_server_.ServeFilesFromSourceDirectory(
-        "components/test/data/payments");
-    ASSERT_TRUE(https_server_.Start());
-
-    ASSERT_TRUE(content::NavigateToURL(
-        GetActiveWebContents(),
-        https_server_.GetURL("/payment_request_bobpay_and_cards_test.html")));
-
-    test_controller_.SetUpOnMainThread();
-    PlatformBrowserTest::SetUpOnMainThread();
-  }
-
-  // PaymentRequestTestObserver implementation.
-  void OnCompleteCalled() override {
-    if (event_waiter_)
-      event_waiter_->OnEvent(TestEvent::PAYMENT_COMPLETED);
-  }
-
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
-
-  void ResetEventWaiterForSequence(std::list<TestEvent> event_sequence) {
-    event_waiter_ = std::make_unique<autofill::EventWaiter<TestEvent>>(
-        std::move(event_sequence));
-  }
-
-  void WaitForObservedEvent() { event_waiter_->Wait(); }
-
-  void ExpectBodyContains(const std::string expected_string) {
-    EXPECT_THAT(content::EvalJs(GetActiveWebContents(),
-                                "window.document.body.textContent")
-                    .ExtractString(),
-                ::testing::HasSubstr(expected_string));
+    NavigateTo("/payment_request_bobpay_and_cards_test.html");
   }
 
  private:
-  PaymentRequestTestController test_controller_;
-  net::EmbeddedTestServer https_server_;
   net::EmbeddedTestServer kylepay_server_;
-  std::unique_ptr<autofill::EventWaiter<TestEvent>> event_waiter_;
 };
 
 // kylepay.com hosts an installable payment app which handles both shipping
 // address and payer's contact information.
 IN_PROC_BROWSER_TEST_F(PaymentHandlerJustInTimeInstallationTest,
                        InstallPaymentAppAndPay) {
-  ResetEventWaiterForSequence({TestEvent::PAYMENT_COMPLETED});
+  ResetEventWaiterForSingleEvent(TestEvent::kPaymentCompleted);
   EXPECT_TRUE(content::ExecJs(
       GetActiveWebContents(),
       "testPaymentMethods([{supportedMethods: 'https://kylepay.com/webpay'}], "
@@ -131,7 +49,7 @@
 
 IN_PROC_BROWSER_TEST_F(PaymentHandlerJustInTimeInstallationTest,
                        InstallPaymentAppAndPayWithDelegation) {
-  ResetEventWaiterForSequence({TestEvent::PAYMENT_COMPLETED});
+  ResetEventWaiterForSingleEvent(TestEvent::kPaymentCompleted);
   EXPECT_TRUE(content::ExecJs(
       GetActiveWebContents(),
       "testPaymentMethods([{supportedMethods: 'https://kylepay.com/webpay'}], "
diff --git a/chrome/browser/payments/payment_request_can_make_payment_event_browsertest.cc b/chrome/browser/payments/payment_request_can_make_payment_event_browsertest.cc
index 26462d4..88437b6 100644
--- a/chrome/browser/payments/payment_request_can_make_payment_event_browsertest.cc
+++ b/chrome/browser/payments/payment_request_can_make_payment_event_browsertest.cc
@@ -3,20 +3,7 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "chrome/test/base/chrome_test_utils.h"
-#include "chrome/test/payments/payment_request_test_controller.h"
-#include "components/network_session_configurator/common/network_switches.h"
-#include "components/payments/core/features.h"
-#include "content/public/common/content_features.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/dns/mock_host_resolver.h"
-
-#if defined(OS_ANDROID)
-#include "chrome/test/base/android/android_browser_test.h"
-#else
-#include "chrome/test/base/in_process_browser_test.h"
-#endif  // defined(OS_ANDROID)
+#include "chrome/test/payments/payment_request_platform_browsertest_base.h"
 
 // This test suite verifies that the the "canmakepayment" event does not fire
 // for standardized payment methods. The test uses hasEnrolledInstrument() which
@@ -35,42 +22,14 @@
 static constexpr char kExpectedResult[] = "false";
 #endif  // defined(OS_ANDROID)
 
-class PaymentRequestCanMakePaymentEventTest : public PlatformBrowserTest {
+class PaymentRequestCanMakePaymentEventTest
+    : public PaymentRequestPlatformBrowserTestBase {
  public:
   PaymentRequestCanMakePaymentEventTest() = default;
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    // HTTPS server only serves a valid cert for localhost, so this is needed to
-    // load pages from "a.com" without an interstitial.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-  }
-
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-
-    https_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    ASSERT_TRUE(https_server_->InitializeAndListen());
-    https_server_->ServeFilesFromSourceDirectory(
-        "components/test/data/payments");
-    https_server_->StartAcceptingConnections();
-  }
-
-  content::WebContents* GetActiveWebContents() {
-    return chrome_test_utils::GetActiveWebContents(this);
-  }
-
-  void NavigateTo(const std::string& host, const std::string& file_path) {
-    EXPECT_TRUE(content::NavigateToURL(GetActiveWebContents(),
-                                       https_server_->GetURL(host, file_path)));
-  }
-
   std::string GetPaymentMethodForHost(const std::string& host) {
-    return https_server_->GetURL(host, "/").spec();
+    return https_server()->GetURL(host, "/").spec();
   }
-
- private:
-  std::unique_ptr<net::EmbeddedTestServer> https_server_;
 };
 
 // A payment handler with two standardized payment methods ("interledger" and
diff --git a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
index 8586b4f..dbbd53ba 100644
--- a/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
+++ b/chrome/browser/preferences/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceKeys.java
@@ -333,11 +333,6 @@
     public static final String FLAGS_CACHED_NETWORK_SERVICE_WARM_UP_ENABLED =
             "network_service_warm_up_enabled";
     /**
-     * Whether or not night mode is available for custom tabs.
-     * Default value is false.
-     */
-    public static final String FLAGS_CACHED_NIGHT_MODE_CCT_AVAILABLE = "night_mode_cct_available";
-    /**
      * Whether the Paint Preview Capture menu item is enabled.
      * Default value is false.
      */
@@ -734,6 +729,7 @@
                 "homepage_tile_enabled",
                 "inflate_toolbar_on_background_thread",
                 "night_mode_available",
+                "night_mode_cct_available",
                 "night_mode_default_to_light",
                 "ntp_button_enabled",
                 "ntp_button_variant",
@@ -830,7 +826,6 @@
                 FLAGS_CACHED_INTEREST_FEED_CONTENT_SUGGESTIONS,
                 FLAGS_CACHED_LABELED_BOTTOM_TOOLBAR_ENABLED,
                 FLAGS_CACHED_NETWORK_SERVICE_WARM_UP_ENABLED,
-                FLAGS_CACHED_NIGHT_MODE_CCT_AVAILABLE,
                 FLAGS_CACHED_PRIORITIZE_BOOTSTRAP_TASKS,
                 FLAGS_CACHED_SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH,
                 FLAGS_CACHED_SERVICE_MANAGER_FOR_DOWNLOAD_RESUMPTION,
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.cc b/chrome/browser/profiles/profile_avatar_icon_util.cc
index 6b69897..41ca7894 100644
--- a/chrome/browser/profiles/profile_avatar_icon_util.cc
+++ b/chrome/browser/profiles/profile_avatar_icon_util.cc
@@ -307,8 +307,10 @@
 constexpr SkColor kUserManagerBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee);
 
 constexpr char kDefaultUrlPrefix[] = "chrome://theme/IDR_PROFILE_AVATAR_";
-constexpr char kGAIAPictureFileName[] = "Google Profile Picture.png";
-constexpr char kHighResAvatarFolderName[] = "Avatars";
+constexpr base::FilePath::CharType kGAIAPictureFileName[] =
+    FILE_PATH_LITERAL("Google Profile Picture.png");
+constexpr base::FilePath::CharType kHighResAvatarFolderName[] =
+    FILE_PATH_LITERAL("Avatars");
 
 // The size of the function-static kDefaultAvatarIconResources array below.
 #if defined(OS_ANDROID)
@@ -594,8 +596,7 @@
   const char* file_name = GetDefaultAvatarIconFileNameAtIndex(index);
   base::FilePath user_data_dir;
   CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir));
-  return user_data_dir.AppendASCII(
-      kHighResAvatarFolderName).AppendASCII(file_name);
+  return user_data_dir.Append(kHighResAvatarFolderName).AppendASCII(file_name);
 }
 
 std::string GetDefaultAvatarIconUrl(size_t index) {
diff --git a/chrome/browser/profiles/profile_avatar_icon_util.h b/chrome/browser/profiles/profile_avatar_icon_util.h
index 110736d..0c2c112f 100644
--- a/chrome/browser/profiles/profile_avatar_icon_util.h
+++ b/chrome/browser/profiles/profile_avatar_icon_util.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <unordered_set>
 
+#include "base/files/file_path.h"
 #include "build/build_config.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/image/image_skia.h"
@@ -39,8 +40,8 @@
 #endif  // OS_WIN
 
 // Avatar access.
-extern const char kGAIAPictureFileName[];
-extern const char kHighResAvatarFolderName[];
+extern const base::FilePath::CharType kGAIAPictureFileName[];
+extern const base::FilePath::CharType kHighResAvatarFolderName[];
 
 // Avatar formatting.
 extern const int kAvatarIconSize;
diff --git a/chrome/browser/profiles/profile_info_cache.cc b/chrome/browser/profiles/profile_info_cache.cc
index 7171d3f..f078e49 100644
--- a/chrome/browser/profiles/profile_info_cache.cc
+++ b/chrome/browser/profiles/profile_info_cache.cc
@@ -416,7 +416,9 @@
   } else {
     // Save the new bitmap to disk.
     new_file_name =
-        old_file_name.empty() ? profiles::kGAIAPictureFileName : old_file_name;
+        old_file_name.empty()
+            ? base::FilePath(profiles::kGAIAPictureFileName).MaybeAsASCII()
+            : old_file_name;
     base::FilePath image_path = path.AppendASCII(new_file_name);
     SaveAvatarImageAtPath(
         GetPathOfProfileAtIndex(index), image, key, image_path,
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 1f9b052..5c3f536 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -550,8 +550,14 @@
                "profile_path",
                profile_path.AsUTF8Unsafe());
 
-  // Make sure that this profile is not pending deletion.
-  if (IsProfileDirectoryMarkedForDeletion(profile_path)) {
+  bool is_allowed_path = IsAllowedProfilePath(profile_path);
+
+  // Make sure the path is correct and this profile is not pending deletion.
+  if (!is_allowed_path || IsProfileDirectoryMarkedForDeletion(profile_path)) {
+    if (!is_allowed_path) {
+      LOG(ERROR) << "Cannot create profile at path "
+                 << profile_path.AsUTF8Unsafe();
+    }
     if (!callback.is_null())
       callback.Run(nullptr, Profile::CREATE_STATUS_LOCAL_FAIL);
     return;
@@ -727,6 +733,10 @@
   return profile_info ? profile_info->profile.get() : nullptr;
 }
 
+bool ProfileManager::IsAllowedProfilePath(const base::FilePath& path) const {
+  return path.DirName() == user_data_dir();
+}
+
 Profile* ProfileManager::GetProfileByPath(const base::FilePath& path) const {
   TRACE_EVENT0("browser", "ProfileManager::GetProfileByPath");
   ProfileInfo* profile_info = GetProfileInfoByPath(path);
@@ -923,7 +933,7 @@
     base::FilePath profile_path;
     bool is_valid_profile_path =
         base::GetValueAsFilePath(value, &profile_path) &&
-        profile_path.DirName() == user_data_dir();
+        IsAllowedProfilePath(profile_path);
     // Although it should never happen, make sure this is a valid path in the
     // user_data_dir, so we don't accidentially delete something else.
     if (is_valid_profile_path) {
@@ -954,7 +964,7 @@
   TRACE_EVENT0("browser", "ProfileManager::InitProfileUserPrefs");
   ProfileAttributesStorage& storage = GetProfileAttributesStorage();
 
-  if (profile->GetPath().DirName() != user_data_dir()) {
+  if (!IsAllowedProfilePath(profile->GetPath())) {
     UMA_HISTOGRAM_BOOLEAN("Profile.InitProfileUserPrefs.OutsideUserDir", true);
     return;
   }
@@ -1405,6 +1415,12 @@
     const base::FilePath& profile_dir) {
   TRACE_EVENT0("browser", "ProfileManager::CreateAndInitializeProfile");
 
+  if (!IsAllowedProfilePath(profile_dir)) {
+    LOG(ERROR) << "Cannot create profile at path "
+               << profile_dir.AsUTF8Unsafe();
+    return nullptr;
+  }
+
   // CHECK that we are not trying to load the same profile twice, to prevent
   // profile corruption. Note that this check also covers the case when we have
   // already started loading the profile but it is not fully initialized yet,
@@ -1590,7 +1606,7 @@
   TRACE_EVENT0("browser", "ProfileManager::AddProfileToCache");
   if (profile->IsGuestSession() || profile->IsSystemProfile())
     return;
-  if (profile->GetPath().DirName() != user_data_dir()) {
+  if (!IsAllowedProfilePath(profile->GetPath())) {
     UMA_HISTOGRAM_BOOLEAN("Profile.GetProfileInfoPath.OutsideUserDir", true);
     return;
   }
diff --git a/chrome/browser/profiles/profile_manager.h b/chrome/browser/profiles/profile_manager.h
index f4c3c9e2..3e1e28e 100644
--- a/chrome/browser/profiles/profile_manager.h
+++ b/chrome/browser/profiles/profile_manager.h
@@ -347,6 +347,9 @@
   // should be used carefully.
   Profile* GetProfileByPathInternal(const base::FilePath& path) const;
 
+  // Returns whether |path| is allowed for profile creation.
+  bool IsAllowedProfilePath(const base::FilePath& path) const;
+
   // Returns a ProfileInfoCache object which can be used to get information
   // about profiles without having to load them from disk.
   // Deprecated, use GetProfileAttributesStorage() instead.
diff --git a/chrome/browser/profiles/profile_manager_unittest.cc b/chrome/browser/profiles/profile_manager_unittest.cc
index 6b8d848..7314e0d5 100644
--- a/chrome/browser/profiles/profile_manager_unittest.cc
+++ b/chrome/browser/profiles/profile_manager_unittest.cc
@@ -1699,3 +1699,38 @@
   EXPECT_EQ(profile_name3, local_state->GetString(prefs::kProfileLastUsed));
 }
 #endif  // defined(OS_MACOSX)
+
+TEST_F(ProfileManagerTest, CannotCreateProfileOusideUserDir) {
+  base::ScopedTempDir non_user_dir;
+  ASSERT_TRUE(non_user_dir.CreateUniqueTempDir());
+
+  base::FilePath dest_path = non_user_dir.GetPath();
+  dest_path = dest_path.Append(FILE_PATH_LITERAL("New Profile"));
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+
+  Profile* profile = profile_manager->GetProfile(dest_path);
+  EXPECT_EQ(nullptr, profile);
+}
+
+TEST_F(ProfileManagerTest, CannotCreateProfileOusideUserDirAsync) {
+  base::ScopedTempDir non_user_dir;
+  ASSERT_TRUE(non_user_dir.CreateUniqueTempDir());
+
+  const std::string profile_name = "New Profile";
+  base::FilePath dest_path = non_user_dir.GetPath();
+  dest_path = dest_path.AppendASCII(profile_name);
+
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+
+  MockObserver mock_observer;
+  EXPECT_CALL(mock_observer,
+              OnProfileCreated(nullptr, Profile::CREATE_STATUS_LOCAL_FAIL));
+
+  profile_manager->CreateProfileAsync(
+      dest_path,
+      base::Bind(&MockObserver::OnProfileCreated,
+                 base::Unretained(&mock_observer)),
+      base::UTF8ToUTF16(profile_name), profiles::GetDefaultAvatarIconUrl(0));
+  content::RunAllTasksUntilIdle();
+}
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.cc b/chrome/browser/profiles/profile_shortcut_manager_win.cc
index 828e312..78337473 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_win.cc
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.cc
@@ -57,9 +57,6 @@
 
 namespace {
 
-// Name of the badged icon file generated for a given profile.
-const char kProfileIconFileName[] = "Google Profile.ico";
-
 // Characters that are not allowed in Windows filenames. Taken from
 // http://msdn.microsoft.com/en-us/library/aa365247.aspx
 const base::char16 kReservedCharacters[] =
@@ -617,10 +614,14 @@
 }  // namespace
 
 namespace profiles {
+
+const base::FilePath::StringPieceType kProfileIconFileName =
+    FILE_PATH_LITERAL("Google Profile.ico");
+
 namespace internal {
 
 base::FilePath GetProfileIconPath(const base::FilePath& profile_path) {
-  return profile_path.AppendASCII(kProfileIconFileName);
+  return profile_path.Append(kProfileIconFileName);
 }
 
 base::string16 GetShortcutFilenameForProfile(
diff --git a/chrome/browser/profiles/profile_shortcut_manager_win.h b/chrome/browser/profiles/profile_shortcut_manager_win.h
index 6fcb03d..6d5ad2e6 100644
--- a/chrome/browser/profiles/profile_shortcut_manager_win.h
+++ b/chrome/browser/profiles/profile_shortcut_manager_win.h
@@ -8,6 +8,7 @@
 #include <set>
 
 #include "base/callback.h"
+#include "base/files/file_path.h"
 #include "base/macros.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
@@ -15,6 +16,10 @@
 
 // Internal free-standing functions that are exported here for testing.
 namespace profiles {
+
+// Name of the badged icon file generated for a given profile.
+extern const base::FilePath::StringPieceType kProfileIconFileName;
+
 namespace internal {
 
 // Returns the full path to the profile icon file.
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_observer.h b/chrome/browser/resource_coordinator/tab_lifecycle_observer.h
index 3b46c28..f40ce594 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_observer.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_observer.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_OBSERVER_H_
 #define CHROME_BROWSER_RESOURCE_COORDINATOR_TAB_LIFECYCLE_OBSERVER_H_
 
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-forward.h"
 
 using mojom::LifecycleUnitDiscardReason;
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
index cae5e04..3187ad7 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
 #include "chrome/browser/resource_coordinator/time.h"
-#include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
+#include "components/performance_manager/public/mojom/coordination_unit.mojom-forward.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents_observer.h"
 
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
index 1bed4b4e..58a7384 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h
@@ -15,7 +15,7 @@
 #include "chrome/browser/ui/browser_tab_strip_tracker.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
-#include "components/performance_manager/public/mojom/lifecycle.mojom.h"
+#include "components/performance_manager/public/mojom/lifecycle.mojom-forward.h"
 #include "components/performance_manager/public/web_contents_proxy.h"
 
 class PrefChangeRegistrar;
diff --git a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
index 4f628ef..5239e8f 100644
--- a/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
+++ b/chrome/browser/resource_coordinator/tab_manager_delegate_chromeos.h
@@ -21,7 +21,7 @@
 #include "chrome/browser/chromeos/arc/process/arc_process.h"
 #include "chrome/browser/chromeos/arc/process/arc_process_service.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-forward.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
diff --git a/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.cc b/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.cc
index bda2e68..e2fac944 100644
--- a/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.cc
+++ b/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.cc
@@ -7,6 +7,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
 
 namespace resource_coordinator {
 
diff --git a/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.h b/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.h
index 7abe7d3..897456c 100644
--- a/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.h
+++ b/chrome/browser/resource_coordinator/tracing_lifecycle_unit_observer.h
@@ -7,7 +7,7 @@
 
 #include "base/macros.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
+#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom-forward.h"
 
 namespace resource_coordinator {
 
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index d3fc733..6c205d3 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -35,11 +35,11 @@
     ":oobe_dialog",
     ":oobe_enable_kiosk",
     ":oobe_eula",
+    ":oobe_help_dialog",
     ":oobe_i18n_behavior",
     ":oobe_i18n_dropdown",
     ":oobe_network",
     ":oobe_reset",
-    ":oobe_reset_confirmation_overlay",
     ":oobe_select",
     ":oobe_supervision_transition",
     ":oobe_update",
@@ -252,6 +252,10 @@
   ]
 }
 
+js_library("oobe_help_dialog") {
+  deps = [ ":oobe_i18n_behavior" ]
+}
+
 js_library("oobe_i18n_dropdown") {
   deps = [ ":oobe_types" ]
 }
@@ -263,10 +267,6 @@
   ]
 }
 
-js_library("oobe_reset_confirmation_overlay") {
-  deps = [ ":oobe_i18n_behavior" ]
-}
-
 js_library("oobe_reset") {
   deps = [
     ":oobe_dialog_host_behavior",
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html
index 82c1fe4c..d4788fe 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -25,7 +25,6 @@
 <include src="oobe_network_icons.html">
 <include src="oobe_enable_kiosk.html">
 <include src="oobe_reset.html">
-<include src="oobe_reset_confirmation_overlay.html">
 <include src="oobe_supervision_transition.html">
 <include src="oobe_terms_of_service.html">
 <include src="encryption_migration.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js
index 82675db..94cff01 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_login.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -34,7 +34,6 @@
 // <include src="arc_terms_of_service.js">
 // <include src="oobe_enable_kiosk.js">
 // <include src="oobe_reset.js">
-// <include src="oobe_reset_confirmation_overlay.js">
 // <include src="encryption_migration.js">
 // <include src="oobe_supervision_transition.js">
 // <include src="sync_consent.js">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
index f6150b3..09fef7a 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.html
@@ -24,7 +24,6 @@
 <include src="oobe_enable_kiosk.html">
 <include src="oobe_hid_detection.html">
 <include src="oobe_reset.html">
-<include src="oobe_reset_confirmation_overlay.html">
 <include src="oobe_terms_of_service.html">
 <include src="oobe_update.html">
 <include src="oobe_i18n_dropdown.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
index 2634bf4f..d2c8159f 100644
--- a/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
+++ b/chrome/browser/resources/chromeos/login/custom_elements_oobe.js
@@ -37,7 +37,6 @@
 // <include src="oobe_terms_of_service.js">
 // <include src="oobe_hid_detection.js">
 // <include src="oobe_reset.js">
-// <include src="oobe_reset_confirmation_overlay.js">
 // <include src="oobe_update.js">
 // <include src="oobe_i18n_dropdown.js">
 // <include src="oobe_welcome_dialog.js">
diff --git a/chrome/browser/resources/chromeos/login/md_login.html b/chrome/browser/resources/chromeos/login/md_login.html
index 0815557..3f96c869 100644
--- a/chrome/browser/resources/chromeos/login/md_login.html
+++ b/chrome/browser/resources/chromeos/login/md_login.html
@@ -49,7 +49,6 @@
 <script src="chrome://oobe/strings.js"></script>
 <link rel="stylesheet" href="api_keys_notice.css">
 <link rel="stylesheet" href="oobe_screen_enable_debugging.css">
-<link rel="stylesheet" href="oobe_screen_reset.css">
 <link rel="stylesheet" href="oobe_screen_autolaunch.css">
 <link rel="stylesheet" href="oobe_screen_auto_enrollment_check.css">
 <link rel="stylesheet" href="oobe_screen_supervision_transition.css">
diff --git a/chrome/browser/resources/chromeos/login/md_login.js b/chrome/browser/resources/chromeos/login/md_login.js
index 8e83e1a..fdde4b9 100644
--- a/chrome/browser/resources/chromeos/login/md_login.js
+++ b/chrome/browser/resources/chromeos/login/md_login.js
@@ -19,7 +19,6 @@
 // <include
 // src="../../../../../ui/login/account_picker/chromeos_user_pod_row.js">
 // <include src="cr_ui.js">
-// <include src="oobe_screen_reset.js">
 // <include src="oobe_screen_autolaunch.js">
 // <include src="oobe_screen_supervision_transition.js">
 // <include src="oobe_screen_assistant_optin_flow.js">
@@ -69,7 +68,6 @@
       cr.ui.login.DisplayManager.initialize();
       login.WrongHWIDScreen.register();
       login.AccountPickerScreen.register();
-      login.ResetScreen.register();
       login.AutolaunchScreen.register();
       login.ErrorMessageScreen.register();
       login.TPMErrorMessageScreen.register();
diff --git a/chrome/browser/resources/chromeos/login/md_login_screens.html b/chrome/browser/resources/chromeos/login/md_login_screens.html
index 91af2f8..32c2aae 100644
--- a/chrome/browser/resources/chromeos/login/md_login_screens.html
+++ b/chrome/browser/resources/chromeos/login/md_login_screens.html
@@ -1,4 +1,4 @@
-<include src="oobe_screen_reset.html">
+<oobe-reset id="reset" class="step" hidden></oobe-reset>
 <include src="oobe_screen_autolaunch.html">
 <oobe-adb-sideloading-screen id="adb-sideloading" class="step hidden">
 </oobe-adb-sideloading-screen>
diff --git a/chrome/browser/resources/chromeos/login/md_screen_container.html b/chrome/browser/resources/chromeos/login/md_screen_container.html
index 0ca9154e..41dd6e2 100644
--- a/chrome/browser/resources/chromeos/login/md_screen_container.html
+++ b/chrome/browser/resources/chromeos/login/md_screen_container.html
@@ -15,4 +15,3 @@
 <div id="bubble" class="bubble faded" hidden></div>
 <include src="md_top_header_bar.html">
 <include src="../../../../../ui/login/account_picker/chromeos_user_pod_template.html">
-<include src="oobe_screen_reset_confirmation_overlay.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe.html b/chrome/browser/resources/chromeos/login/oobe.html
index 06354b0..a6e6e81 100644
--- a/chrome/browser/resources/chromeos/login/oobe.html
+++ b/chrome/browser/resources/chromeos/login/oobe.html
@@ -54,7 +54,6 @@
 <link rel="stylesheet" href="api_keys_notice.css">
 
 <link rel="stylesheet" href="oobe_screen_enable_debugging.css">
-<link rel="stylesheet" href="oobe_screen_reset.css">
 <link rel="stylesheet" href="oobe_screen_autolaunch.css">
 <link rel="stylesheet" href="oobe_screen_auto_enrollment_check.css">
 <link rel="stylesheet" href="oobe_screen_supervision_transition.css">
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js
index 06adacf..82f9da0 100644
--- a/chrome/browser/resources/chromeos/login/oobe.js
+++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -20,7 +20,6 @@
 // <include
 // src="../../../../../ui/login/account_picker/chromeos_user_pod_row.js">
 // <include src="cr_ui.js">
-// <include src="oobe_screen_reset.js">
 // <include src="oobe_screen_autolaunch.js">
 // <include src="oobe_screen_supervision_transition.js">
 // <include src="oobe_screen_assistant_optin_flow.js">
@@ -74,7 +73,6 @@
       login.UpdateScreen.register();
       login.AutoEnrollmentCheckScreen.register();
       login.EnableDebuggingScreen.register();
-      login.ResetScreen.register();
       login.AutolaunchScreen.register();
       login.AccountPickerScreen.register();
       login.OAuthEnrollmentScreen.register();
diff --git a/chrome/browser/resources/chromeos/login/oobe_help_dialog.css b/chrome/browser/resources/chromeos/login/oobe_help_dialog.css
index cc995df7..d5dd9faf 100644
--- a/chrome/browser/resources/chromeos/login/oobe_help_dialog.css
+++ b/chrome/browser/resources/chromeos/login/oobe_help_dialog.css
@@ -3,7 +3,7 @@
  * found in the LICENSE file. */
 
 
-#help-dialog {
+#helpDialog {
   border: 0;
   border-radius: 8px;
   box-shadow: 0 1px 3px 0 rgba(var(--google-grey-800-rgb), 0.3),
@@ -14,8 +14,7 @@
   width: 512px;
 }
 
-#help-dialog-title ::slotted(h1),
-#help-dialog-title .fallback {
+#helpDialogTitle {
   color: var(--oobe-header-text-color);
   @apply --oobe-help-dialog-header-font;
   margin: 0;
@@ -23,13 +22,13 @@
   user-select: none;
 }
 
-#content-container {
+#contentContainer {
   color: var(--oobe-text-color);
   @apply --oobe-default-font;
   padding-bottom: 20px;
 }
 
-#button-container {
+#buttonContainer {
   display: flex;
   justify-content: flex-end;
   min-height: 0;
diff --git a/chrome/browser/resources/chromeos/login/oobe_help_dialog.html b/chrome/browser/resources/chromeos/login/oobe_help_dialog.html
index f37107f..f071647f 100644
--- a/chrome/browser/resources/chromeos/login/oobe_help_dialog.html
+++ b/chrome/browser/resources/chromeos/login/oobe_help_dialog.html
@@ -18,30 +18,34 @@
 
   Alternatively, one can set their own title and content into the 'title'
   and 'content' slots.
+
+  Buttons are optional and go into the 'buttons' slot. If none are specified,
+  a default button with the text 'Close' will be shown. Users might want to
+  trigger some action on their side by using 'on-close=myMethod'.
 -->
 <dom-module id="oobe-help-dialog">
   <template>
     <link rel="stylesheet" href="oobe_fonts.css">
     <link rel="stylesheet" href="oobe_help_dialog.css">
-    <dialog id="help-dialog" part="dialog" aria-labelledby="help-dialog-title">
+    <dialog id="helpDialog" part="dialog" aria-labelledby="helpDialogTitle">
       <!-- Title -->
-      <div id="help-dialog-title">
-        <slot name="title">
-            <h1 class="fallback">[[i18nDynamic(locale, titleKey)]]</h1>
-        </slot>
+      <div id="helpDialogTitle">
+        <slot name="title">[[i18nDynamic(locale, titleKey)]]</slot>
       </div>
       <!-- Content to be shown -->
-      <div id="content-container"
+      <div id="contentContainer"
           class="flex-grow layout vertical not-resizable">
         <slot name="content">
-            <h1>[[i18nDynamic(locale, contentKey)]]</h1>
+          [[i18nDynamic(locale, contentKey)]]
         </slot>
       </div>
       <!-- Close Button -->
-      <div id="button-container" class="layout horizontal">
-        <oobe-text-button inverse id="closeButton" on-click="hideDialog"
+      <div id="buttonContainer" class="layout horizontal">
+        <slot name="buttons">
+          <oobe-text-button inverse id="closeButton" on-click="hideDialog"
             text-key="oobeModalDialogClose">
-        </oobe-text-button>
+          </oobe-text-button>
+        </slot>
       </div>
     </dialog>
   </template>
diff --git a/chrome/browser/resources/chromeos/login/oobe_help_dialog.js b/chrome/browser/resources/chromeos/login/oobe_help_dialog.js
index 92f953c..af17849 100644
--- a/chrome/browser/resources/chromeos/login/oobe_help_dialog.js
+++ b/chrome/browser/resources/chromeos/login/oobe_help_dialog.js
@@ -26,12 +26,12 @@
 
   /* Shows the help dialog and changes the focus to the close button. */
   showDialog: function() {
-    this.$['help-dialog'].showModal();
+    this.$.helpDialog.showModal();
     this.$.closeButton.focus();
   },
 
   hideDialog: function() {
-    this.$['help-dialog'].close();
+    this.$.helpDialog.close();
   },
 
 });
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.css b/chrome/browser/resources/chromeos/login/oobe_reset.css
index bb23996..83120f4 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.css
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.css
@@ -19,15 +19,13 @@
 }
 
 #tpmFirmwareUpdate {
-  margin-bottom: 20px;
-  margin-inline-start: 20px;
-  margin-top: 23px; /* = 36 - font size */
-  width: 100%;
+  margin-top: 20px;
 }
 
-#tpmFirmwareUpdateCheckbox {
-  margin-top: 2px;
-  size: 16px;
+#illustration {
+  max-height: 80%;
+  max-width: 100%;
+  object-fit: contain;
 }
 
 #tpmFirmwareUpdateContainer {
@@ -35,8 +33,3 @@
   line-height: 20px;
   pointer-events: auto;
 }
-
-#illustration {
-  height: 264px;
-  width: 264px;
-}
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.html b/chrome/browser/resources/chromeos/login/oobe_reset.html
index 39f33b1..9f854caf 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.html
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.html
@@ -6,7 +6,7 @@
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 
-<dom-module id="oobe-reset-md">
+<dom-module id="oobe-reset">
   <template>
     <link rel="stylesheet" href="oobe_dialog_host.css">
     <link rel="stylesheet" href="chrome://resources/css/throbber.css">
@@ -20,28 +20,25 @@
           icon1x="oobe-32:alert" icon2x="oobe-64:alert">
       </hd-iron-icon>
       <div slot="subtitle" class="powerwash-warning">
-        <div hidden="[[!isState_(uiState_, 'powerwash-proposal-view')]]">
-          [[i18nDynamic(locale, 'resetPowerwashWarningDetails')]]
-        </div>
-        <div hidden="[[!isState_(uiState_, 'powerwash-proposal-view')]]">
-          [[i18nDynamic(locale, 'resetWarningDataDetails')]]
-        </div>
-        <div hidden="[[!isState_(uiState_, 'rollback-proposal-view')]]">
-          [[i18nDynamic(locale, 'resetPowerwashRollbackWarningDetails')]]
-        </div>
-        <div hidden="[[!isState_(uiState_, 'rollback-proposal-view')]]">
-          [[i18nDynamic(locale, 'resetWarningDataDetails')]]
-        </div>
-        <div hidden="[[!isState_(uiState_, 'restart-required-view')]]">
+        <!-- Subtitle:  Restart required  -->
+        <div hidden="[[!inRestartRequiredState_]]">
           [[i18nDynamic(locale, 'resetRestartMessage')]]
         </div>
-        <a id="powerwash-help-link-md" href="#"
-            class="oobe-local-link"
-            hidden="[[isHelpLinkHidden_(uiState_, isGoogleBranded_)]]"
-            on-tap="onLearnMoreTap_">
-          [[i18nDynamic(locale, 'learnMore')]]
-        </a>
-        <div hidden="[[!isState_(uiState_, 'revert-promise-view')]]">
+        <!-- Subtitle: Powerwash state (depends on powerwash mode) -->
+        <div hidden="[[!inPowerwashState_]]">
+          <div>[[powerwashStateSubtitle_]]</div>
+          <div>[[i18nDynamic(locale, 'resetWarningDataDetails')]]</div>
+        </div>
+        <!-- Help Link - Hidden when reverting/rolling back. -->
+        <if expr="_google_chrome">
+          <a id="powerwash-help-link-md" href="#"
+              class="oobe-local-link"
+              hidden="[[inRevertState_]]"
+             on-tap="onLearnMoreTap_">[[i18nDynamic(locale, 'learnMore')]]
+          </a>
+        </if>
+        <!-- Spinner - Shown when the revert process is ongoing -->
+        <div hidden="[[!inRevertState_]]">
           <div>
             [[i18nDynamic(locale, 'resetRevertPromise')]]
           </div>
@@ -53,49 +50,62 @@
           </div>
         </div>
       </div>
-      <div slot="footer" class="flex layout vertical center center-justified">
+      <div slot="footer" class="flex layout vertical">
+        <!-- Reset screen illustration -->
         <img id="illustration" srcset="images/1x/reset_illustration_1x.svg 1x,
                                        images/2x/reset_illustration_2x.svg 2x"
-            i18n-values="alt:resetScreenIllustrationTitle"
-            class="oobe-illustration">
+            i18n-values="alt:resetScreenIllustrationTitle">
+        <!-- TPM Update - Only shown during powerwash state -->
         <div id="tpmFirmwareUpdate" class="layout horizontal"
-            hidden="[[!tpmFirmwareUpdateAvailable_]]">
+            hidden="[[!inPowerwashState_]]">
           <cr-checkbox id="tpmFirmwareUpdateCheckbox"
-              checked="{{tpmFirmwareUpdateChecked_}}"
-              disabled="[[!tpmFirmwareUpdateEditable_]]"
+              checked="{{tpmUpdateChecked_}}"
+              disabled="[[!tpmUpdateEditable_]]"
+              hidden="[[!tpmUpdateAvailable_]]"
               on-change="onTPMFirmwareUpdateChanged_">
-              <div id="tpmFirmwareUpdateContainer">
-                <span>
-                  [[i18nDynamic(locale, 'resetTPMFirmwareUpdate')]]
-                </span>
+            <div id="tpmFirmwareUpdateContainer">
+              <span>
+                [[i18nDynamic(locale, 'resetTPMFirmwareUpdate')]]
+              </span>
+              <if expr="_google_chrome">
                 <a href="#"
-                    hidden="[[isHelpLinkHidden_(uiState_, isGoogleBranded_)]]"
-					          class="oobe-local-link"
-                    on-tap="onTPMFirmwareUpdateLearnMore_">
-                  [[i18nDynamic(locale, 'learnMore')]]
-                </a>
-              </div>
+                    class="oobe-local-link"
+                    on-tap="onTPMFirmwareUpdateLearnMore_"
+                    >[[i18nDynamic(locale, 'learnMore')]]</a>
+              </if>
+            </div>
           </cr-checkbox>
         </div>
       </div>
       <div slot="bottom-buttons" class="flex layout horizontal">
         <div class="flex"></div>
+        <!-- Cancel button, only disabled when reverting -->
         <oobe-text-button border on-tap="onCancelTap_" text-key="cancelButton"
-            hidden="[[isCancelHidden_(uiState_)]]"></oobe-text-button>
+            disabled="[[inRevertState_]]"></oobe-text-button>
+        <!-- Restart button -->
         <oobe-text-button inverse on-tap="onRestartTap_" class="focus-on-show"
             text-key="resetButtonRestart"
-            hidden="[[!isState_(uiState_, 'restart-required-view')]]">
+            hidden="[[!inRestartRequiredState_]]">
         </oobe-text-button>
+        <!-- Powerwash button (depends on powerwash mode) -->
         <oobe-text-button inverse on-tap="onPowerwashTap_" class="focus-on-show"
-            text-key="resetButtonPowerwashAndRollback"
-            hidden="[[!isState_(uiState_, 'rollback-proposal-view')]]"
-            disabled="[[tpmFirmwareUpdateChecked_]]">
-        </oobe-text-button>
-        <oobe-text-button inverse on-tap="onPowerwashTap_" class="focus-on-show"
-            text-key="resetButtonPowerwash"
-            hidden="[[!isState_(uiState_, 'powerwash-proposal-view')]]">
+            hidden="[[!inPowerwashState_]]"
+            text-key="[[powerwashButtonTextKey_]]"
+            disabled="[[powerwashButtonDisabled_]]">
         </oobe-text-button>
       </div>
     </oobe-dialog>
+
+    <!-- Powerwash confirmation dialog (depends on powerwash mode) -->
+    <oobe-help-dialog id="confirmationDialog" on-close="onDialogClosed_">
+      <div slot="title">[[confirmationDialogTitle_]]</div>
+      <div slot="content">[[confirmationDialogText_]]</div>
+      <div slot="buttons">
+        <oobe-text-button id="cancelButton" border on-tap="onDialogCancelTap_"
+            text-key="cancel"></oobe-text-button>
+        <oobe-text-button inverse on-tap="onDialogContinueTap_"
+            text-key="confirmResetButton"></oobe-text-button>
+      </div>
+    </oobe-help-dialog>
   </template>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset.js b/chrome/browser/resources/chromeos/login/oobe_reset.js
index bccf020..511e498 100644
--- a/chrome/browser/resources/chromeos/login/oobe_reset.js
+++ b/chrome/browser/resources/chromeos/login/oobe_reset.js
@@ -6,130 +6,392 @@
  * @fileoverview Polymer element for displaying material design reset screen.
  */
 
-Polymer({
-  is: 'oobe-reset-md',
+(function() {
+/** @enum {number} */
+const RESET_SCREEN_STATE = {
+  'RESTART_REQUIRED': 0,
+  'REVERT_PROMISE': 1,
+  'POWERWASH_PROPOSAL': 2,  // supports 2 ui-states - With or without rollback
+  'ERROR': 3,
+};
 
-  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
+// When the screen is in the powerwash proposal state, it depends on the mode
+/** @enum {number} */
+const POWERWASH_MODE = {
+  'POWERWASH_WITH_ROLLBACK': 0,
+  'POWERWASH_ONLY': 1,
+};
+
+// Powerwash mode details. Used by the UI for the two different modes
+/** @type {Map<POWERWASH_MODE, Object<string,string>>} */
+const POWERWASH_MODE_DETAILS = new Map([
+  [
+    POWERWASH_MODE.POWERWASH_WITH_ROLLBACK, {
+      subtitleText: 'resetPowerwashRollbackWarningDetails',
+      dialogTitle: 'confirmRollbackTitle',
+      dialogContent: 'confirmRollbackMessage',
+      buttonTextKey: 'resetButtonPowerwashAndRollback',
+    }
+  ],
+  [
+    POWERWASH_MODE.POWERWASH_ONLY, {
+      subtitleText: 'resetPowerwashWarningDetails',
+      dialogTitle: 'confirmPowerwashTitle',
+      dialogContent: 'confirmPowerwashMessage',
+      buttonTextKey: 'resetButtonPowerwash',
+    }
+  ],
+]);
+
+Polymer({
+  is: 'oobe-reset',
+
+  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior, LoginScreenBehavior],
+
+  EXTERNAL_API: [
+    'setIsRollbackAvailable',
+    'setIsRollbackRequested',
+    'setIsTpmFirmwareUpdateAvailable',
+    'setIsTpmFirmwareUpdateChecked',
+    'setIsTpmFirmwareUpdateEditable',
+    'setTpmFirmwareUpdateMode',
+    'setShouldShowConfirmationDialog',
+    'setScreenState',
+  ],
 
   properties: {
-    /**
-     * State of the screen corresponding to the css style set by
-     * oobe_screen_reset.js.
-     */
-    uiState_: String,
+
+    /* The current state of the screen as set from the C++ side. */
+    /** @type {RESET_SCREEN_STATE} */
+    screenState_: {
+      type: Number,
+      value: RESET_SCREEN_STATE.RESTART_REQUIRED,
+      observer: 'onScreenStateChanged_',
+    },
+
+    /** @type {boolean}  Whether rollback is available */
+    isRollbackAvailable_: {
+      type: Boolean,
+      value: false,
+      observer: 'updatePowerwashModeBasedOnRollbackOptions_',
+    },
 
     /**
-     * Flag that determines whether help link is shown.
+     * @type {boolean}
+     *  Whether the rollback option was chosen by the user.
      */
-    isGoogleBranded_: Boolean,
+    isRollbackRequested_: {
+      type: Boolean,
+      value: false,
+      observer: 'updatePowerwashModeBasedOnRollbackOptions_',
+    },
 
     /**
      * Whether to show the TPM firmware update checkbox.
      */
-    tpmFirmwareUpdateAvailable_: Boolean,
+    tpmUpdateAvailable_: Boolean,
 
     /**
      * If the checkbox to request a TPM firmware update is checked.
      */
-    tpmFirmwareUpdateChecked_: Boolean,
+    tpmUpdateChecked_: Boolean,
 
     /**
      * If the checkbox to request a TPM firmware update is editable.
      */
-    tpmFirmwareUpdateEditable_: Boolean,
+    tpmUpdateEditable_: Boolean,
 
     /**
-     * Reference to OOBE screen object.
-     * @type {!OobeTypes.Screen}
+     * The current TPM update mode.
      */
-    screen: {
-      type: Object,
+    tpmUpdateMode_: String,
+
+    // Title to be shown on the confirmation dialog.
+    confirmationDialogTitle_: {
+      type: String,
+      computed: 'getConfirmationDialogTitle_(locale, powerwashMode_)',
     },
+
+    // Content to be shown on the confirmation dialog.
+    confirmationDialogText_: {
+      type: String,
+      computed: 'getConfirmationDialogText_(locale, powerwashMode_)',
+    },
+
+    // The subtitle to be shown while the screen is in POWERWASH_PROPOSAL
+    powerwashStateSubtitle_: {
+      type: String,
+      computed: 'getPowerwashStateSubtitle_(locale, powerwashMode_)'
+    },
+
+    // The text shown on the powerwash button. (depends on powerwash mode)
+    powerwashButtonTextKey_: {
+      type: String,
+      computed: 'getPowerwashButtonTextKey_(locale, powerwashMode_)'
+    },
+
+    // Whether the powerwash button is disabled.
+    powerwashButtonDisabled_: {
+      type: Boolean,
+      computed: 'isPowerwashDisabled_(powerwashMode_, tpmUpdateChecked_)',
+    },
+
+    // The chosen powerwash mode
+    /**@type {POWERWASH_MODE} */
+    powerwashMode_: {
+      type: Number,
+      value: POWERWASH_MODE.POWERWASH_ONLY,
+    },
+
+    // Simple variables that reflect the current screen state
+    // Only modified by the observer of 'screenState_'
+    inRestartRequiredState_: {
+      type: Boolean,
+      value: true,
+    },
+
+    inRevertState_: {
+      type: Boolean,
+      value: false,
+    },
+
+    inPowerwashState_: {
+      type: Boolean,
+      value: false,
+    },
+  },
+
+  /** @override */
+  ready() {
+    this.initializeLoginScreen('ResetScreen', {
+      resetAllowed: false,
+      enableDebuggingAllowed: false,
+    });
   },
 
   focus() {
     this.$.resetDialog.focus();
   },
 
-  /** @private */
-  isState_(uiState_, state_) {
-    return uiState_ == state_;
-  },
-
-  /** @private */
-  isCancelHidden_(uiState_) {
-    return uiState_ == 'revert-promise-view';
-  },
-
-  /** @private */
-  isHelpLinkHidden_(uiState_, isGoogleBranded_) {
-    return !isGoogleBranded_ || (uiState_ == 'revert-promise-view');
-  },
-
-  /** @private */
-  isTPMFirmwareUpdateHidden_(uiState_, tpmFirmwareUpdateAvailable_) {
-    var inProposalView = [
-      'powerwash-proposal-view', 'rollback-proposal-view'
-    ].includes(uiState_);
-    return !(tpmFirmwareUpdateAvailable_ && inProposalView);
+  /* ---------- EXTERNAL API BEGIN ---------- */
+  /** @param {boolean} rollbackAvailable  */
+  setIsRollbackAvailable(rollbackAvailable) {
+    this.isRollbackAvailable_ = rollbackAvailable;
   },
 
   /**
+   * @param {boolean} rollbackRequested
+   */
+  setIsRollbackRequested(rollbackRequested) {
+    this.isRollbackRequested_ = rollbackRequested;
+  },
+
+  /** @param {boolean} value  */
+  setIsTpmFirmwareUpdateAvailable(value) {
+    this.tpmUpdateAvailable_ = value;
+  },
+
+  /** @param {boolean} value  */
+  setIsTpmFirmwareUpdateChecked(value) {
+    this.tpmUpdateChecked_ = value;
+  },
+
+  /** @param {boolean} value  */
+  setIsTpmFirmwareUpdateEditable(value) {
+    this.tpmUpdateEditable_ = value;
+  },
+
+  /** @param {string} value  */
+  setTpmFirmwareUpdateMode(value) {
+    this.tpmUpdateMode_ = value;
+  },
+
+  /** @param {boolean} should_show  */
+  setShouldShowConfirmationDialog(should_show) {
+    if (should_show) {
+      this.$.confirmationDialog.showDialog();
+    } else {
+      this.$.confirmationDialog.hideDialog();
+    }
+  },
+
+  /** @param {RESET_SCREEN_STATE} state  */
+  setScreenState(state) {
+    this.screenState_ = state;
+  },
+  /* ---------- EXTERNAL API END ---------- */
+
+  /**
+   *  When rollback is available and requested, the powerwash mode changes
+   *  to POWERWASH_WITH_ROLLBACK.
+   *  @private
+   */
+  updatePowerwashModeBasedOnRollbackOptions_() {
+    if (this.isRollbackAvailable_ && this.isRollbackRequested_) {
+      this.powerwashMode_ = POWERWASH_MODE.POWERWASH_WITH_ROLLBACK;
+      this.classList.add('rollback-proposal-view');
+    } else {
+      this.powerwashMode_ = POWERWASH_MODE.POWERWASH_ONLY;
+      this.classList.remove('rollback-proposal-view');
+    }
+  },
+
+  /** @private */
+  onScreenStateChanged_() {
+    if (this.screenState_ == RESET_SCREEN_STATE.REVERT_PROMISE) {
+      announceAccessibleMessage(this.i18n('resetRevertSpinnerMessage'));
+      this.classList.add('revert-promise-view');
+    } else {
+      this.classList.remove('revert-promise-view');
+    }
+
+    this.inRevertState_ =
+        (this.screenState_ == RESET_SCREEN_STATE.REVERT_PROMISE);
+    this.inRestartRequiredState_ =
+        (this.screenState_ == RESET_SCREEN_STATE.RESTART_REQUIRED);
+    this.inPowerwashState_ =
+        (this.screenState_ == RESET_SCREEN_STATE.POWERWASH_PROPOSAL);
+  },
+
+  /**
+   * Determines the subtitle based on the current powerwash mode
+   * @param {*} locale
+   * @param {POWERWASH_MODE} mode
+   * @private
+   */
+  getPowerwashStateSubtitle_(locale, mode) {
+    if (this.powerwashMode_ === undefined)
+      return '';
+    const modeDetails = POWERWASH_MODE_DETAILS.get(this.powerwashMode_);
+    return this.i18n(modeDetails.subtitleText);
+  },
+
+  /**
+   * The powerwash button text depends on the powerwash mode
+   * @param {*} locale
+   * @param {POWERWASH_MODE} mode
+   * @private
+   */
+  getPowerwashButtonTextKey_(locale, mode) {
+    if (this.powerwashMode_ === undefined)
+      return '';
+    return POWERWASH_MODE_DETAILS.get(this.powerwashMode_).buttonTextKey;
+  },
+
+  /**
+   * Cannot powerwash with rollback when the TPM update checkbox is checked
+   * @param {POWERWASH_MODE} mode
+   * @param {boolean} tpmUpdateChecked
+   * @private
+   */
+  isPowerwashDisabled_(mode, tpmUpdateChecked) {
+    return this.tpmUpdateChecked_ &&
+        (this.powerwashMode_ == POWERWASH_MODE.POWERWASH_WITH_ROLLBACK);
+  },
+
+  /* ---------- CONFIRMATION DIALOG ---------- */
+
+  /**
+   * Determines the confirmation dialog title.
+   * @param {*} locale
+   * @param {POWERWASH_MODE} mode
+   * @private
+   */
+  getConfirmationDialogTitle_(locale, mode) {
+    if (this.powerwashMode_ === undefined)
+      return '';
+    const modeDetails = POWERWASH_MODE_DETAILS.get(this.powerwashMode_);
+    return this.i18n(modeDetails.dialogTitle);
+  },
+
+  /**
+   * Determines the confirmation dialog content
+   * @param {*} locale
+   * @param {POWERWASH_MODE} mode
+   * @private
+   */
+  getConfirmationDialogText_(locale, mode) {
+    if (this.powerwashMode_ === undefined)
+      return '';
+    const modeDetails = POWERWASH_MODE_DETAILS.get(this.powerwashMode_);
+    return this.i18n(modeDetails.dialogContent);
+  },
+
+  /**
+   * On-tap event handler for confirmation dialog continue button.
+   * @private
+   */
+  onDialogContinueTap_() {
+    this.userActed('powerwash-pressed');
+  },
+
+  /**
+   * On-tap event handler for confirmation dialog cancel button.
+   * @private
+   */
+  onDialogCancelTap_() {
+    this.$.confirmationDialog.hideDialog();
+    this.userActed('reset-confirm-dismissed');
+  },
+
+  /**
+   * Catch 'close' event through escape key
+   * @private
+   */
+  onDialogClosed_() {
+    this.userActed('reset-confirm-dismissed');
+  },
+
+  /* ---------- SIMPLE EVENT HANDLERS ---------- */
+  /**
    * On-tap event handler for cancel button.
-   *
    * @private
    */
   onCancelTap_() {
-    chrome.send('login.ResetScreen.userActed', ['cancel-reset']);
+    this.userActed('cancel-reset');
   },
 
   /**
    * On-tap event handler for restart button.
-   *
    * @private
    */
   onRestartTap_() {
-    chrome.send('login.ResetScreen.userActed', ['restart-pressed']);
+    this.userActed('restart-pressed');
   },
 
   /**
    * On-tap event handler for powerwash button.
-   *
    * @private
    */
   onPowerwashTap_() {
-    chrome.send('login.ResetScreen.userActed', ['show-confirmation']);
+    this.userActed('show-confirmation');
   },
 
   /**
    * On-tap event handler for learn more link.
-   *
    * @private
    */
   onLearnMoreTap_() {
-    chrome.send('login.ResetScreen.userActed', ['learn-more-link']);
+    this.userActed('learn-more-link');
   },
 
   /**
    * Change handler for TPM firmware update checkbox.
-   *
    * @private
    */
   onTPMFirmwareUpdateChanged_() {
-    this.screen.onTPMFirmwareUpdateChanged_(
-        this.$.tpmFirmwareUpdateCheckbox.checked);
+    const checked = this.$.tpmFirmwareUpdateCheckbox.checked;
+    chrome.send('ResetScreen.setTpmFirmwareUpdateChecked', [checked]);
   },
 
   /**
    * On-tap event handler for the TPM firmware update learn more link.
-   *
    * @param {!Event} event
    * @private
    */
   onTPMFirmwareUpdateLearnMore_(event) {
-    chrome.send(
-        'login.ResetScreen.userActed', ['tpm-firmware-update-learn-more-link']);
+    this.userActed('tpm-firmware-update-learn-more-link');
     event.stopPropagation();
   },
-
 });
+})();
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.css b/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.css
deleted file mode 100644
index 7df54e0..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.css
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright 2017 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file. */
-
-.reset-popup {
-  background: white;
-  border-radius: 2px;
-  border-width: 0;
-  box-shadow: 0 0 16px rgba(0, 0, 0, .12), 0 16px 16px rgba(0, 0, 0, .24);
-  min-height: 158px;
-  padding: 0;
-  position: relative;
-  width: 512px;
-  z-index: 10;
-}
-
-.reset-popup h1 {
-  color: var(--google-grey-900);
-  font-family: var(--oobe-button-font-family);
-  font-size: 15px;
-  margin-bottom: 0;
-  margin-top: 0;
-  padding: 16px 20px;
-}
-
-.reset-popup-content-area {
-  @apply --oobe-default-font;
-  color: rgba(0, 0, 0, .54);
-  line-height: 20px;
-  padding: 0 20px 20.5px;
-}
-
-oobe-text-button {
-  color: #5a5a5a;
-  margin-bottom: 16px;
-  margin-inline-end: 16px;
-}
-
-#cancelButton {
-  margin-inline-end: 4px;
-}
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.html b/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.html
deleted file mode 100644
index f11345d..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<!-- Copyright 2017 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<dom-module id="reset-confirm-overlay-md">
-  <template>
-    <link rel="stylesheet" href="oobe_flex_layout.css">
-    <link rel="stylesheet" href="oobe_reset_confirmation_overlay.css">
-    <dialog id="dialog" class="reset-popup not-resizable">
-        <h1 hidden="[[!isPowerwashView_]]">
-          [[i18nDynamic(locale, 'confirmPowerwashTitle')]]
-        </h1>
-        <h1 hidden="[[isPowerwashView_]]">
-          [[i18nDynamic(locale, 'confirmRollbackTitle')]]
-        </h1>
-      <div class="reset-popup-content-area">
-        <div hidden="[[!isPowerwashView_]]">
-          [[i18nDynamic(locale, 'confirmPowerwashMessage')]]
-        </div>
-        <div hidden="[[isPowerwashView_]]">
-          [[i18nDynamic(locale, 'confirmRollbackMessage')]]
-        </div>
-      </div>
-      <div slot="bottom-buttons" class="flex layout horizontal">
-        <div class="flex"></div>
-        <oobe-text-button id="cancelButton" border on-tap="onCancelTap_"
-            text-key="cancel"></oobe-text-button>
-        <oobe-text-button inverse on-tap="onContinueTap_"
-            text-key="confirmResetButton"></oobe-text-button>
-      </div>
-    </dialog>
-  </template>
-</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.js b/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.js
deleted file mode 100644
index 6221bde..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_reset_confirmation_overlay.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Polymer element for displaying material design reset
- * confirmation overlay screen.
- */
-
-Polymer({
-  is: 'reset-confirm-overlay-md',
-
-  behaviors: [OobeI18nBehavior],
-
-  properties: {
-    isPowerwashView_: Boolean,
-  },
-
-  open() {
-    if (!this.$.dialog.open)
-      this.$.dialog.showModal();
-  },
-
-  close() {
-    if (this.$.dialog.open)
-      this.$.dialog.close();
-  },
-
-  /**
-   * On-tap event handler for continue button.
-   */
-  onContinueTap_() {
-    this.close();
-    chrome.send('login.ResetScreen.userActed', ['powerwash-pressed']);
-  },
-
-  /**
-   * On-tap event handler for cancel button.
-   */
-  onCancelTap_() {
-    this.close();
-    chrome.send('login.ResetScreen.userActed', ['reset-confirm-dismissed']);
-  },
-});
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_reset.css b/chrome/browser/resources/chromeos/login/oobe_screen_reset.css
deleted file mode 100644
index 3bc306f7..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_screen_reset.css
+++ /dev/null
@@ -1,10 +0,0 @@
-/* Copyright 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. */
-
-#reset {
-  display: flex;
-  font-size: 16px;
-  min-height: 0;
-  text-align: start;
-}
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_reset.html b/chrome/browser/resources/chromeos/login/oobe_screen_reset.html
deleted file mode 100644
index dca7380..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_screen_reset.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<div class="step faded hidden" id="reset" role="group"
-    i18n-values="aria-label:resetScreenAccessibleTitle" hidden>
-  <oobe-reset-md id="oobe-reset-md"></oobe-reset-md>
-</div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_reset.js b/chrome/browser/resources/chromeos/login/oobe_screen_reset.js
deleted file mode 100644
index 47cc546..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_screen_reset.js
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Device reset screen implementation.
- */
-
-login.createScreen('ResetScreen', 'reset', function() {
-  var USER_ACTION_CANCEL_RESET = 'cancel-reset';
-  var USER_ACTION_RESET_CONFIRM_DISMISSED = 'reset-confirm-dismissed';
-
-  /* Possible UI states of the reset screen. */
-  const RESET_SCREEN_UI_STATE = {
-    REVERT_PROMISE: 'ui-state-revert-promise',
-    RESTART_REQUIRED: 'ui-state-restart-required',
-    POWERWASH_PROPOSAL: 'ui-state-powerwash-proposal',
-    ROLLBACK_PROPOSAL: 'ui-state-rollback-proposal',
-    ERROR: 'ui-state-error',
-  };
-
-  const RESET_SCREEN_STATE = {
-    RESTART_REQUIRED: 0,
-    REVERT_PROMISE: 1,
-    POWERWASH_PROPOSAL: 2,  // supports 2 ui-states
-    ERROR: 3,
-  };
-
-  return {
-    EXTERNAL_API: [
-      'setIsRollbackAvailable',
-      'setIsRollbackChecked',
-      'setIsTpmFirmwareUpdateAvailable',
-      'setIsTpmFirmwareUpdateChecked',
-      'setIsTpmFirmwareUpdateEditable',
-      'setTpmFirmwareUpdateMode',
-      'setIsConfirmational',
-      'setIsGoogleBrandedBuild',
-      'setScreenState',
-    ],
-
-    /** @type {boolean} */
-    isRollbackAvailable_: false,
-    /** @type {boolean} */
-    isRollbackChecked_: false,
-    /** @type {boolean} */
-    isTpmFirmwareUpdateAvailable_: false,
-    /** @type {boolean} */
-    isTpmFirmwareUpdateChecked_: false,
-    /** @type {boolean} */
-    isTpmFirmwareUpdateEditable_: false,
-    /** @type {RESET_SCREEN_UI_STATE} */
-    tpmFirmwareUpdateMode_: RESET_SCREEN_UI_STATE.REVERT_PROMISE,
-    /** @type {boolean} */
-    isConfirmational_: false,
-    /** @type {RESET_SCREEN_STATE} */
-    screenState_: RESET_SCREEN_STATE.RESTART_REQUIRED,
-
-    setIsRollbackAvailable(rollbackAvailable) {
-      this.isRollbackAvailable_ = rollbackAvailable;
-      this.setRollbackOptionView();
-    },
-
-    setIsRollbackChecked(rollbackChecked) {
-      this.isRollbackChecked_ = rollbackChecked;
-      this.setRollbackOptionView();
-    },
-
-    setIsTpmFirmwareUpdateAvailable(value) {
-      this.isTpmFirmwareUpdateAvailable_ = value;
-      this.setTPMFirmwareUpdateView_();
-    },
-
-    setIsTpmFirmwareUpdateChecked(value) {
-      this.isTpmFirmwareUpdateChecked_ = value;
-      this.setTPMFirmwareUpdateView_();
-    },
-
-    setIsTpmFirmwareUpdateEditable(value) {
-      this.isTpmFirmwareUpdateEditable_ = value;
-      this.setTPMFirmwareUpdateView_();
-    },
-
-    setTpmFirmwareUpdateMode(value) {
-      this.tpmFirmwareUpdateMode_ = value;
-    },
-
-    setIsConfirmational(isConfirmational) {
-      this.isConfirmational_ = isConfirmational;
-      if (isConfirmational) {
-        if (this.screenState_ != RESET_SCREEN_STATE.POWERWASH_PROPOSAL)
-          return;
-        $('overlay-reset').removeAttribute('hidden');
-        $('reset-confirm-overlay-md').open();
-      } else {
-        $('overlay-reset').setAttribute('hidden', true);
-        $('reset-confirm-overlay-md').close();
-      }
-    },
-
-    setIsGoogleBrandedBuild(isGoogleBranded) {
-      $('oobe-reset-md').isGoogleBranded_ = isGoogleBranded;
-    },
-
-    setScreenState(state) {
-      this.screenState_ = state;
-
-      if (state == RESET_SCREEN_STATE.RESTART_REQUIRED)
-        this.ui_state = RESET_SCREEN_UI_STATE.RESTART_REQUIRED;
-      if (state == RESET_SCREEN_STATE.REVERT_PROMISE)
-        this.ui_state = RESET_SCREEN_UI_STATE.REVERT_PROMISE;
-      else if (state == RESET_SCREEN_STATE.POWERWASH_PROPOSAL)
-        this.ui_state = RESET_SCREEN_UI_STATE.POWERWASH_PROPOSAL;
-      this.setDialogView_();
-      if (state == RESET_SCREEN_STATE.REVERT_PROMISE) {
-        announceAccessibleMessage(
-            loadTimeData.getString('resetRevertSpinnerMessage'));
-      }
-      this.setTPMFirmwareUpdateView_();
-    },
-
-    /** @override */
-    decorate() {
-      $('oobe-reset-md').screen = this;
-    },
-
-    /**
-     * Returns a control which should receive an initial focus.
-     */
-    get defaultControl() {
-      return $('oobe-reset-md');
-    },
-
-    /**
-     * Cancels the reset and drops the user back to the login screen.
-     */
-    cancel() {
-      if (this.isConfirmational_) {
-        $('reset').send(
-            login.Screen.CALLBACK_USER_ACTED,
-            USER_ACTION_RESET_CONFIRM_DISMISSED);
-        return;
-      }
-      this.send(login.Screen.CALLBACK_USER_ACTED, USER_ACTION_CANCEL_RESET);
-    },
-
-    /**
-     * Event handler that is invoked just before the screen in shown.
-     * @param {Object} data Screen init payload.
-     */
-    onBeforeShow(data) {},
-
-    /** Event handler that is invoked after the screen is shown. */
-    onAfterShow() {
-      Oobe.resetSigninUI(false);
-    },
-
-    /**
-     * Sets css style for corresponding state of the screen.
-     * @private
-     */
-    setDialogView_(state) {
-      state = this.ui_state;
-      this.classList.toggle(
-          'revert-promise-view', state == RESET_SCREEN_UI_STATE.REVERT_PROMISE);
-      this.classList.toggle(
-          'restart-required-view',
-          state == RESET_SCREEN_UI_STATE.RESTART_REQUIRED);
-      this.classList.toggle(
-          'powerwash-proposal-view',
-          state == RESET_SCREEN_UI_STATE.POWERWASH_PROPOSAL);
-      this.classList.toggle(
-          'rollback-proposal-view',
-          state == RESET_SCREEN_UI_STATE.ROLLBACK_PROPOSAL);
-      var resetMd = $('oobe-reset-md');
-      var resetOverlayMd = $('reset-confirm-overlay-md');
-      if (state == RESET_SCREEN_UI_STATE.RESTART_REQUIRED) {
-        resetMd.uiState_ = 'restart-required-view';
-      }
-      if (state == RESET_SCREEN_UI_STATE.POWERWASH_PROPOSAL) {
-        resetMd.uiState_ = 'powerwash-proposal-view';
-        resetOverlayMd.isPowerwashView_ = true;
-      }
-      if (state == RESET_SCREEN_UI_STATE.ROLLBACK_PROPOSAL) {
-        resetMd.uiState_ = 'rollback-proposal-view';
-        resetOverlayMd.isPowerwashView_ = false;
-      }
-      if (state == RESET_SCREEN_UI_STATE.REVERT_PROMISE) {
-        resetMd.uiState_ = 'revert-promise-view';
-      }
-    },
-
-    setRollbackOptionView() {
-      if (this.isConfirmational_)
-        return;
-      if (this.screenState_ != RESET_SCREEN_STATE.POWERWASH_PROPOSAL)
-        return;
-
-      if (this.isRollbackAvailable_ && this.isRollbackChecked_) {
-        this.ui_state = RESET_SCREEN_UI_STATE.ROLLBACK_PROPOSAL;
-      } else {
-        this.ui_state = RESET_SCREEN_UI_STATE.POWERWASH_PROPOSAL;
-      }
-      this.setDialogView_();
-      this.setTPMFirmwareUpdateView_();
-    },
-
-    setTPMFirmwareUpdateView_() {
-      $('oobe-reset-md').tpmFirmwareUpdateAvailable_ =
-          this.ui_state == RESET_SCREEN_UI_STATE.POWERWASH_PROPOSAL &&
-          this.isTpmFirmwareUpdateAvailable_;
-      $('oobe-reset-md').tpmFirmwareUpdateChecked_ =
-          this.isTpmFirmwareUpdateChecked_;
-      $('oobe-reset-md').tpmFirmwareUpdateEditable_ =
-          this.isTpmFirmwareUpdateEditable_;
-    },
-
-    onTPMFirmwareUpdateChanged_(value) {
-      chrome.send('ResetScreen.setTpmFirmwareUpdateChecked', [value]);
-    },
-
-    /**
-     * Updates localized content of the screen that is not updated via template.
-     */
-    updateLocalizedContent() {
-      $('oobe-reset-md').i18nUpdateLocale();
-      $('reset-confirm-overlay-md').i18nUpdateLocale();
-    },
-  };
-});
diff --git a/chrome/browser/resources/chromeos/login/oobe_screen_reset_confirmation_overlay.html b/chrome/browser/resources/chromeos/login/oobe_screen_reset_confirmation_overlay.html
deleted file mode 100644
index 15b693c8..0000000
--- a/chrome/browser/resources/chromeos/login/oobe_screen_reset_confirmation_overlay.html
+++ /dev/null
@@ -1,4 +0,0 @@
-<div id="overlay-reset" class="popup-overlay" hidden>
-  <reset-confirm-overlay-md id="reset-confirm-overlay-md">
-  </reset-confirm-overlay-md>
-</div>
diff --git a/chrome/browser/resources/chromeos/login/oobe_screens.html b/chrome/browser/resources/chromeos/login/oobe_screens.html
index 0fab704..fa03324 100644
--- a/chrome/browser/resources/chromeos/login/oobe_screens.html
+++ b/chrome/browser/resources/chromeos/login/oobe_screens.html
@@ -5,7 +5,8 @@
 <oobe-adb-sideloading-screen id="adb-sideloading" class="step hidden">
 </oobe-adb-sideloading-screen>
 <include src="oobe_screen_enable_debugging.html">
-<include src="oobe_screen_reset.html">
+<oobe-reset class="step" id="reset" hidden>
+</oobe-reset>
 <include src="oobe_screen_autolaunch.html">
 <kiosk-enable id="kiosk-enable" class="step hidden">
 </kiosk-enable>
diff --git a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
index e39053c..b5e43ab 100644
--- a/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
+++ b/chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h
@@ -16,7 +16,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/mojom/url_loader.mojom.h"
+#include "services/network/public/mojom/url_loader.mojom-forward.h"
 
 namespace certificate_reporting_test_utils {
 
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.h b/chrome/browser/safe_browsing/client_side_detection_host.h
index 31cb9c5..1891ba4 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host.h
+++ b/chrome/browser/safe_browsing/client_side_detection_host.h
@@ -16,7 +16,6 @@
 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
 #include "chrome/browser/safe_browsing/ui_manager.h"
 #include "components/safe_browsing/content/common/safe_browsing.mojom-shared.h"
-#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/remote.h"
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
index 607bf698..42d5ada 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.cc
@@ -149,6 +149,9 @@
                            int64_t total_bytes,
                            const BinaryUploadService::Result& result,
                            const DeepScanningClientResponse& response) {
+  // Don't record UMA metrics for this result.
+  if (result == BinaryUploadService::Result::UNAUTHORIZED)
+    return;
   bool dlp_verdict_success = response.has_dlp_scan_verdict()
                                  ? response.dlp_scan_verdict().status() ==
                                        DlpDeepScanningVerdict::SUCCESS
@@ -159,39 +162,7 @@
                 MalwareDeepScanningVerdict::VERDICT_UNSPECIFIED
           : true;
   bool success = dlp_verdict_success && malware_verdict_success;
-  std::string result_value;
-  switch (result) {
-    case BinaryUploadService::Result::SUCCESS:
-      if (success)
-        result_value = "Success";
-      else
-        result_value = "FailedToGetVerdict";
-      break;
-    case BinaryUploadService::Result::UPLOAD_FAILURE:
-      result_value = "UploadFailure";
-      break;
-    case BinaryUploadService::Result::TIMEOUT:
-      result_value = "Timeout";
-      break;
-    case BinaryUploadService::Result::FILE_TOO_LARGE:
-      result_value = "FileTooLarge";
-      break;
-    case BinaryUploadService::Result::FAILED_TO_GET_TOKEN:
-      result_value = "FailedToGetToken";
-      break;
-    case BinaryUploadService::Result::UNKNOWN:
-      result_value = "Unknown";
-      break;
-    case BinaryUploadService::Result::UNAUTHORIZED:
-      // Don't record UMA metrics for this result.
-      return;
-    case BinaryUploadService::Result::FILE_ENCRYPTED:
-      result_value = "FileEncrypted";
-      break;
-    case BinaryUploadService::Result::UNSUPPORTED_FILE_TYPE:
-      result_value = "UnsupportedFileType";
-      break;
-  }
+  std::string result_value = BinaryUploadServiceResultToString(result, success);
 
   // Update |success| so non-SUCCESS results don't log the bytes/sec metric.
   success &= (result == BinaryUploadService::Result::SUCCESS);
@@ -308,4 +279,32 @@
   return response;
 }
 
+std::string BinaryUploadServiceResultToString(
+    const BinaryUploadService::Result& result,
+    bool success) {
+  switch (result) {
+    case BinaryUploadService::Result::SUCCESS:
+      if (success)
+        return "Success";
+      else
+        return "FailedToGetVerdict";
+    case BinaryUploadService::Result::UPLOAD_FAILURE:
+      return "UploadFailure";
+    case BinaryUploadService::Result::TIMEOUT:
+      return "Timeout";
+    case BinaryUploadService::Result::FILE_TOO_LARGE:
+      return "FileTooLarge";
+    case BinaryUploadService::Result::FAILED_TO_GET_TOKEN:
+      return "FailedToGetToken";
+    case BinaryUploadService::Result::UNKNOWN:
+      return "Unknown";
+    case BinaryUploadService::Result::UNAUTHORIZED:
+      return "";
+    case BinaryUploadService::Result::FILE_ENCRYPTED:
+      return "FileEncrypted";
+    case BinaryUploadService::Result::UNSUPPORTED_FILE_TYPE:
+      return "UnsupportedFileType";
+  }
+}
+
 }  // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
index 93c1e844..3afb213 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h
@@ -77,6 +77,12 @@
     base::Optional<bool> dlp_success,
     base::Optional<bool> malware_success);
 
+// Helper function to convert a BinaryUploadService::Result to a CamelCase
+// string.
+std::string BinaryUploadServiceResultToString(
+    const BinaryUploadService::Result& result,
+    bool success);
+
 }  // namespace safe_browsing
 
 #endif  // CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_DEEP_SCANNING_UTILS_H_
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
index b6fcb842..ff87d4d 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils_unittest.cc
@@ -33,33 +33,6 @@
 
 constexpr base::TimeDelta kInvalidDuration = base::TimeDelta::FromSeconds(0);
 
-std::string ResultToString(const BinaryUploadService::Result& result,
-                           bool success) {
-  switch (result) {
-    case BinaryUploadService::Result::SUCCESS:
-      if (success)
-        return "Success";
-      else
-        return "FailedToGetVerdict";
-    case BinaryUploadService::Result::UPLOAD_FAILURE:
-      return "UploadFailure";
-    case BinaryUploadService::Result::TIMEOUT:
-      return "Timeout";
-    case BinaryUploadService::Result::FILE_TOO_LARGE:
-      return "FileTooLarge";
-    case BinaryUploadService::Result::FAILED_TO_GET_TOKEN:
-      return "FailedToGetToken";
-    case BinaryUploadService::Result::UNKNOWN:
-      return "Unknown";
-    case BinaryUploadService::Result::UNAUTHORIZED:
-      return "";
-    case BinaryUploadService::Result::FILE_ENCRYPTED:
-      return "FileEncrypted";
-    case BinaryUploadService::Result::UNSUPPORTED_FILE_TYPE:
-      return "UnsupportedFileType";
-  }
-}
-
 }  // namespace
 
 class DeepScanningUtilsUMATest
@@ -81,7 +54,7 @@
   }
 
   std::string result_value(bool success) const {
-    return ResultToString(result(), success);
+    return BinaryUploadServiceResultToString(result(), success);
   }
 
   const base::HistogramTester& histograms() const { return histograms_; }
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index 3d2e8f1..92f76fd 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -1067,14 +1067,7 @@
                                 ThemeProperties::NTP_LOGO_ALTERNATE) == 1;
     theme->logo_color =
         theme_provider.GetColor(ThemeProperties::COLOR_NTP_LOGO);
-
-    // For default theme in dark mode use dark shortcuts.
-    if (native_theme_->ShouldUseDarkColors() &&
-        ThemeServiceFactory::GetForProfile(profile_)->UsingDefaultTheme()) {
-      theme->shortcut_color = gfx::kGoogleGrey900;
-    } else {
-      theme->shortcut_color =
-          theme_provider.GetColor(ThemeProperties::COLOR_NTP_SHORTCUT);
-    }
+    theme->shortcut_color =
+        theme_provider.GetColor(ThemeProperties::COLOR_NTP_SHORTCUT);
   }
 }
diff --git a/chrome/browser/search/instant_service_unittest.cc b/chrome/browser/search/instant_service_unittest.cc
index 273eae7e..fc1e39094 100644
--- a/chrome/browser/search/instant_service_unittest.cc
+++ b/chrome/browser/search/instant_service_unittest.cc
@@ -772,23 +772,25 @@
 }
 
 TEST_F(InstantServiceTest, SetNTPElementsNtpTheme) {
-  NtpTheme* theme = instant_service_->GetInitializedNtpTheme();
+  const auto& theme_provider =
+      ThemeService::GetThemeProviderForProfile(profile());
   SkColor default_text_color =
-      ThemeProperties::GetDefaultColor(ThemeProperties::COLOR_NTP_TEXT, false);
+      theme_provider.GetColor(ThemeProperties::COLOR_NTP_TEXT);
   SkColor default_logo_color =
-      ThemeProperties::GetDefaultColor(ThemeProperties::COLOR_NTP_LOGO, false);
-  SkColor default_shortcut_color = ThemeProperties::GetDefaultColor(
-      ThemeProperties::COLOR_NTP_SHORTCUT, false);
+      theme_provider.GetColor(ThemeProperties::COLOR_NTP_LOGO);
+  SkColor default_shortcut_color =
+      theme_provider.GetColor(ThemeProperties::COLOR_NTP_SHORTCUT);
 
   ASSERT_FALSE(instant_service_->IsCustomBackgroundSet());
 
   // Check defaults when no theme and no custom backgrounds is set.
+  NtpTheme* theme = instant_service_->GetInitializedNtpTheme();
   EXPECT_EQ(default_text_color, theme->text_color);
   EXPECT_FALSE(theme->logo_alternate);
   EXPECT_EQ(default_logo_color, theme->logo_color);
   EXPECT_EQ(default_shortcut_color, theme->shortcut_color);
 
-  // Install colors, theme update should trigger |SetNTPElementsNtpTheme| and
+  // Install colors, theme update should trigger SetNTPElementsNtpTheme() and
   // update NTP themed elements info.
   ThemeService* theme_service = ThemeServiceFactory::GetForProfile(profile());
   content::WindowedNotificationObserver theme_change_observer(
@@ -803,7 +805,7 @@
   EXPECT_NE(default_logo_color, theme->logo_color);
   EXPECT_NE(default_shortcut_color, theme->shortcut_color);
 
-  // Setting a custom backgrounds should call |SetNTPElementsNtpTheme| and
+  // Setting a custom background should call SetNTPElementsNtpTheme() and
   // update NTP themed elements info.
   const GURL kUrl("https://www.foo.com");
   instant_service_->AddValidBackdropUrlForTesting(kUrl);
@@ -814,5 +816,9 @@
   EXPECT_NE(default_text_color, theme->text_color);
   EXPECT_TRUE(theme->logo_alternate);
   EXPECT_EQ(default_logo_color, theme->logo_color);
-  EXPECT_EQ(default_shortcut_color, theme->shortcut_color);
+  // The shortcut color with a background set should always use the light mode
+  // default regardless of system setting.
+  EXPECT_EQ(ThemeProperties::GetDefaultColor(
+                ThemeProperties::COLOR_NTP_SHORTCUT, false),
+            theme->shortcut_color);
 }
diff --git a/chrome/browser/serial/serial_chooser_context.h b/chrome/browser/serial/serial_chooser_context.h
index 38f0eac..d416eb9 100644
--- a/chrome/browser/serial/serial_chooser_context.h
+++ b/chrome/browser/serial/serial_chooser_context.h
@@ -17,7 +17,7 @@
 #include "chrome/browser/permissions/chooser_context_base.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/device/public/mojom/serial.mojom.h"
+#include "services/device/public/mojom/serial.mojom-forward.h"
 #include "third_party/blink/public/mojom/serial/serial.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
diff --git a/chrome/browser/sessions/tab_restore_browsertest.cc b/chrome/browser/sessions/tab_restore_browsertest.cc
index 01f1ba1..cb6dcab0 100644
--- a/chrome/browser/sessions/tab_restore_browsertest.cc
+++ b/chrome/browser/sessions/tab_restore_browsertest.cc
@@ -136,7 +136,8 @@
         content::NOTIFICATION_LOAD_STOP,
         content::NotificationService::AllSources());
     {
-      TabRestoreServiceLoadWaiter waiter(browser);
+      TabRestoreServiceLoadWaiter waiter(
+          TabRestoreServiceFactory::GetForProfile(browser->profile()));
       chrome::RestoreTab(browser);
       waiter.Wait();
     }
@@ -858,13 +859,13 @@
   // automatically upon launch.
   // Wait for robustness because InProcessBrowserTest::PreRunTestOnMainThread
   // does not flush the task scheduler.
-  TabRestoreServiceLoadWaiter waiter(browser());
-  waiter.Wait();
-
-  // When we start, we should get nothing.
   sessions::TabRestoreService* service =
       TabRestoreServiceFactory::GetForProfile(browser()->profile());
   ASSERT_TRUE(service);
+  TabRestoreServiceLoadWaiter waiter(service);
+  waiter.Wait();
+
+  // When we start, we should get nothing.
   EXPECT_TRUE(service->entries().empty());
 
   // Add a tab and close it
@@ -887,13 +888,13 @@
   // automatically upon launch.
   // Wait for robustness because InProcessBrowserTest::PreRunTestOnMainThread
   // does not flush the task scheduler.
-  TabRestoreServiceLoadWaiter waiter(browser());
-  waiter.Wait();
-
-  // When we start this time we should get a Tab.
   sessions::TabRestoreService* service =
       TabRestoreServiceFactory::GetForProfile(browser()->profile());
   ASSERT_TRUE(service);
+  TabRestoreServiceLoadWaiter waiter(service);
+  waiter.Wait();
+
+  // When we start this time we should get a Tab.
   ASSERT_GE(service->entries().size(), 1u);
   EXPECT_EQ(sessions::TabRestoreService::TAB, service->entries().front()->type);
 }
diff --git a/chrome/browser/sessions/tab_restore_service_load_waiter.cc b/chrome/browser/sessions/tab_restore_service_load_waiter.cc
index d519375..7530983 100644
--- a/chrome/browser/sessions/tab_restore_service_load_waiter.cc
+++ b/chrome/browser/sessions/tab_restore_service_load_waiter.cc
@@ -4,32 +4,20 @@
 
 #include "chrome/browser/sessions/tab_restore_service_load_waiter.h"
 
-#include "chrome/browser/sessions/tab_restore_service_factory.h"
-#include "chrome/browser/ui/browser.h"
-#include "components/sessions/core/tab_restore_service.h"
-
-// Class used to run a message loop waiting for the TabRestoreService to finish
-// loading. Does nothing if the TabRestoreService was already loaded.
-TabRestoreServiceLoadWaiter::TabRestoreServiceLoadWaiter(Browser* browser)
-    : tab_restore_service_(
-          TabRestoreServiceFactory::GetForProfile(browser->profile())),
-      do_wait_(!tab_restore_service_->IsLoaded()) {
-  if (do_wait_)
-    tab_restore_service_->AddObserver(this);
+TabRestoreServiceLoadWaiter::TabRestoreServiceLoadWaiter(
+    sessions::TabRestoreService* service)
+    : service_(service) {
+  observer_.Add(service_);
 }
 
-TabRestoreServiceLoadWaiter::~TabRestoreServiceLoadWaiter() {
-  if (do_wait_)
-    tab_restore_service_->RemoveObserver(this);
-}
+TabRestoreServiceLoadWaiter::~TabRestoreServiceLoadWaiter() = default;
 
 void TabRestoreServiceLoadWaiter::Wait() {
-  if (do_wait_)
+  if (!service_->IsLoaded())
     run_loop_.Run();
 }
 
 void TabRestoreServiceLoadWaiter::TabRestoreServiceLoaded(
     sessions::TabRestoreService* service) {
-  DCHECK(do_wait_);
   run_loop_.Quit();
 }
diff --git a/chrome/browser/sessions/tab_restore_service_load_waiter.h b/chrome/browser/sessions/tab_restore_service_load_waiter.h
index a040ac7..f5b9e6f 100644
--- a/chrome/browser/sessions/tab_restore_service_load_waiter.h
+++ b/chrome/browser/sessions/tab_restore_service_load_waiter.h
@@ -6,31 +6,30 @@
 #define CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_LOAD_WAITER_H_
 
 #include "base/run_loop.h"
+#include "base/scoped_observer.h"
+#include "components/sessions/core/tab_restore_service.h"
 #include "components/sessions/core/tab_restore_service_observer.h"
 
-class Browser;
-
 // Class used to run a message loop waiting for the TabRestoreService to finish
 // loading. Does nothing if the TabRestoreService was already loaded.
 class TabRestoreServiceLoadWaiter : public sessions::TabRestoreServiceObserver {
  public:
-  explicit TabRestoreServiceLoadWaiter(Browser* browser);
-
+  explicit TabRestoreServiceLoadWaiter(sessions::TabRestoreService* service);
   ~TabRestoreServiceLoadWaiter() override;
 
   void Wait();
 
  private:
-  // Overridden from TabRestoreServiceObserver:
+  // TabRestoreServiceObserver:
   void TabRestoreServiceDestroyed(
       sessions::TabRestoreService* service) override {}
   void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override;
 
-  sessions::TabRestoreService* const tab_restore_service_;
-  const bool do_wait_;
+  sessions::TabRestoreService* const service_;
   base::RunLoop run_loop_;
-
-  DISALLOW_COPY_AND_ASSIGN(TabRestoreServiceLoadWaiter);
+  ScopedObserver<sessions::TabRestoreService,
+                 sessions::TabRestoreServiceObserver>
+      observer_{this};
 };
 
 #endif  // CHROME_BROWSER_SESSIONS_TAB_RESTORE_SERVICE_LOAD_WAITER_H_
diff --git a/chrome/browser/sessions/tab_restore_service_unittest.cc b/chrome/browser/sessions/tab_restore_service_unittest.cc
index 47daa579..32901c5 100644
--- a/chrome/browser/sessions/tab_restore_service_unittest.cc
+++ b/chrome/browser/sessions/tab_restore_service_unittest.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/sessions/session_service_factory.h"
 #include "chrome/browser/sessions/session_service_utils.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
+#include "chrome/browser/sessions/tab_restore_service_load_waiter.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/chrome_render_view_test.h"
@@ -179,8 +180,9 @@
 
   void SynchronousLoadTabsFromLastSession() {
     // Ensures that the load is complete before continuing.
+    TabRestoreServiceLoadWaiter waiter(service_.get());
     service_->LoadTabsFromLastSession();
-    content::RunAllTasksUntilIdle();
+    waiter.Wait();
   }
 
   sessions::LiveTab* live_tab() { return live_tab_.get(); }
@@ -196,32 +198,6 @@
   SessionID tab_id_;
 };
 
-namespace {
-
-class TestTabRestoreServiceObserver
-    : public sessions::TabRestoreServiceObserver {
- public:
-  TestTabRestoreServiceObserver() : got_loaded_(false) {}
-
-  void clear_got_loaded() { got_loaded_ = false; }
-  bool got_loaded() const { return got_loaded_; }
-
-  // TabRestoreServiceObserver:
-  void TabRestoreServiceDestroyed(
-      sessions::TabRestoreService* service) override {}
-  void TabRestoreServiceLoaded(sessions::TabRestoreService* service) override {
-    got_loaded_ = true;
-  }
-
- private:
-  // Was TabRestoreServiceLoaded() invoked?
-  bool got_loaded_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestTabRestoreServiceObserver);
-};
-
-}  // namespace
-
 TEST_F(TabRestoreServiceImplTest, Basic) {
   AddThreeNavigations();
 
@@ -285,6 +261,7 @@
 
   // Have the service record the tab.
   service_->CreateHistoricalTab(live_tab(), -1);
+  EXPECT_EQ(1U, service_->entries().size());
 
   // Recreate the service and have it load the tabs.
   RecreateService();
@@ -513,11 +490,7 @@
 
   EXPECT_FALSE(service_->IsLoaded());
 
-  TestTabRestoreServiceObserver observer;
-  service_->AddObserver(&observer);
   SynchronousLoadTabsFromLastSession();
-  EXPECT_TRUE(observer.got_loaded());
-  service_->RemoveObserver(&observer);
 
   // Make sure we get back one entry with one tab whose url is url1.
   ASSERT_EQ(1U, service_->entries().size());
@@ -984,11 +957,7 @@
   }
 
   EXPECT_FALSE(service_->IsLoaded());
-  TestTabRestoreServiceObserver observer;
-  service_->AddObserver(&observer);
   EXPECT_EQ(max_entries, service_->entries().size());
   SynchronousLoadTabsFromLastSession();
-  EXPECT_TRUE(observer.got_loaded());
   EXPECT_TRUE(service_->IsLoaded());
-  service_->RemoveObserver(&observer);
 }
diff --git a/chrome/browser/share/DEPS b/chrome/browser/share/DEPS
index ecb9395..a408eb45 100644
--- a/chrome/browser/share/DEPS
+++ b/chrome/browser/share/DEPS
@@ -8,4 +8,6 @@
   "+chrome/android/java/src/org/chromium/chrome/browser/document/ChromeLauncherActivity.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/modules/ModuleInstallUi.java",
   "+chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java",
+  "+content/public/android/java/src/org/chromium/content_public/browser/RenderWidgetHostView.java",
+  "+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java"
 ]
diff --git a/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml b/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml
index a1b89a1..54dad51 100644
--- a/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml
+++ b/chrome/browser/share/android/java/res/layout/qrcode_dialog.xml
@@ -9,7 +9,7 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
-    <android.support.design.widget.TabLayout
+    <com.google.android.material.tabs.TabLayout
         android:id="@+id/tab_layout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -19,16 +19,16 @@
         app:tabIndicatorFullWidth="true"
         style="@style/TabLayoutStyle" >
 
-        <android.support.design.widget.TabItem
+        <com.google.android.material.tabs.TabItem
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/qr_code_share_tab_label" />
 
-        <android.support.design.widget.TabItem
+        <com.google.android.material.tabs.TabItem
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/qr_code_scan_tab_label" />
-    </android.support.design.widget.TabLayout>
+    </com.google.android.material.tabs.TabLayout>
 
     <org.chromium.ui.widget.ChromeImageButton
         android:id="@+id/close_button"
@@ -37,7 +37,7 @@
         android:contentDescription="@string/close"
         app:tint="@color/default_icon_color_tint_list" />
 
-    <android.support.v4.view.ViewPager
+    <androidx.viewpager.widget.ViewPager
         android:id="@+id/qrcode_view_pager"
         android:layout_width="match_parent"
         android:layout_height="fill_parent"
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ShareImageFileUtils.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ShareImageFileUtils.java
new file mode 100644
index 0000000..617f22ea
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ShareImageFileUtils.java
@@ -0,0 +1,241 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.share;
+
+import android.content.Context;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.base.ApplicationState;
+import org.chromium.base.ApplicationStatus;
+import org.chromium.base.Callback;
+import org.chromium.base.ContentUriUtils;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.Log;
+import org.chromium.base.StreamUtil;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.content_public.browser.RenderWidgetHostView;
+import org.chromium.content_public.browser.WebContents;
+import org.chromium.ui.UiUtils;
+import org.chromium.ui.base.Clipboard;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Utility class for file operations for image data.
+ */
+public class ShareImageFileUtils {
+    private static final String TAG = "share";
+
+    /**
+     * Directory name for shared images.
+     *
+     * Named "screenshot" for historical reasons as we only initially shared screenshot images.
+     */
+    private static final String SHARE_IMAGES_DIRECTORY_NAME = "screenshot";
+    private static final String JPEG_EXTENSION = ".jpg";
+
+    /**
+     * Delete the |file|, if the |file| is a directory, delete the files and directories in the
+     * directory recursively.
+     *
+     * @param file The {@link File} or directory to be deleted.
+     * @param reservedFilepath The filepath should not to be deleted.
+     * @return Whether the |folder| has file to keep/reserve.
+     */
+    private static boolean deleteFiles(File file, @Nullable String reservedFilepath) {
+        if (!file.exists()) return false;
+        if (reservedFilepath != null && file.isFile()
+                && file.getPath().endsWith(reservedFilepath)) {
+            return true;
+        }
+
+        boolean anyChildKept = false;
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                anyChildKept |= deleteFiles(child, reservedFilepath);
+            }
+        }
+
+        // file.delete() will fail if |file| is a directory and has a file need to keep. In this
+        // case, the log should not been recorded since it is correct.
+        if (!anyChildKept && !file.delete()) {
+            Log.w(TAG, "Failed to delete share image file: %s", file.getAbsolutePath());
+            return true;
+        }
+        return anyChildKept;
+    }
+
+    /**
+     * Check if the file related to |fileUri| is in the |folder|.
+     *
+     * @param fileUri The {@link Uri} related to the file to be checked.
+     * @param folder The folder that may contain the |fileUrl|.
+     * @return Whether the |fileUri| is in the |folder|.
+     */
+    private static boolean isUriInDirectory(Uri fileUri, File folder) {
+        if (fileUri == null) return false;
+
+        Uri chromeUriPrefix = ContentUriUtils.getContentUriFromFile(folder);
+        if (chromeUriPrefix == null) return false;
+
+        return fileUri.toString().startsWith(chromeUriPrefix.toString());
+    }
+
+    /**
+     * Check if the system clipboard contains a Uri that comes from Chrome. If yes, return the file
+     * name from the Uri, otherwise return null.
+     *
+     * @return The file name if system clipboard contains a Uri from Chrome, otherwise return null.
+     */
+    private static String getClipboardCurrentFilepath() throws IOException {
+        Uri clipboardUri = Clipboard.getInstance().getImageUri();
+        if (isUriInDirectory(clipboardUri, getSharedFilesDirectory())) {
+            return clipboardUri.getPath();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the directory where temporary files are stored to be shared with external
+     * applications. These files are deleted on startup and when there are no longer any active
+     * Activities.
+     *
+     * @return The directory where shared files are stored.
+     */
+    public static File getSharedFilesDirectory() throws IOException {
+        File imagePath = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext());
+        return new File(imagePath, SHARE_IMAGES_DIRECTORY_NAME);
+    }
+
+    /**
+     * Clears all shared image files.
+     */
+    public static void clearSharedImages() {
+        AsyncTask.SERIAL_EXECUTOR.execute(() -> {
+            try {
+                deleteFiles(getSharedFilesDirectory(), getClipboardCurrentFilepath());
+            } catch (IOException ie) {
+                // Ignore exception.
+            }
+        });
+    }
+
+    /**
+     * Temporarily saves the given set of JPEG bytes and provides that URI to a callback for
+     * sharing.
+     * @param context The context used to trigger the share action.
+     * @param jpegImageData The image data to be shared in jpeg format.
+     * @param callback A provided callback function which will act on the generated URI.
+     */
+    public static void generateTemporaryUriFromData(
+            final Context context, final byte[] jpegImageData, Callback<Uri> callback) {
+        if (jpegImageData.length == 0) {
+            Log.w(TAG, "Share failed -- Received image contains no data.");
+            return;
+        }
+
+        new AsyncTask<Uri>() {
+            @Override
+            protected Uri doInBackground() {
+                FileOutputStream fOut = null;
+                try {
+                    File path = new File(UiUtils.getDirectoryForImageCapture(context),
+                            SHARE_IMAGES_DIRECTORY_NAME);
+                    if (path.exists() || path.mkdir()) {
+                        File saveFile = File.createTempFile(
+                                String.valueOf(System.currentTimeMillis()), JPEG_EXTENSION, path);
+                        fOut = new FileOutputStream(saveFile);
+                        fOut.write(jpegImageData);
+                        fOut.flush();
+
+                        return ContentUriUtils.getContentUriFromFile(saveFile);
+                    } else {
+                        Log.w(TAG, "Share failed -- Unable to create share image directory.");
+                    }
+                } catch (IOException ie) {
+                    // Ignore exception.
+                } finally {
+                    StreamUtil.closeQuietly(fOut);
+                }
+
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Uri imageUri) {
+                if (imageUri == null) {
+                    return;
+                }
+                if (ApplicationStatus.getStateForApplication()
+                        == ApplicationState.HAS_DESTROYED_ACTIVITIES) {
+                    return;
+                }
+
+                callback.onResult(imageUri);
+            }
+        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
+    }
+
+    /**
+     * Captures a screenshot for the provided web contents, persists it and notifies the file
+     * provider that the file is ready to be accessed by the client.
+     *
+     * The screenshot is compressed to JPEG before being written to the file.
+     *
+     * @param contents The WebContents instance for which to capture a screenshot.
+     * @param width    The desired width of the resulting screenshot, or 0 for "auto."
+     * @param height   The desired height of the resulting screenshot, or 0 for "auto."
+     * @param callback The callback that will be called once the screenshot is saved.
+     */
+    public static void captureScreenshotForContents(
+            WebContents contents, int width, int height, Callback<Uri> callback) {
+        RenderWidgetHostView rwhv = contents.getRenderWidgetHostView();
+        if (rwhv == null) {
+            callback.onResult(null);
+            return;
+        }
+        try {
+            String path = UiUtils.getDirectoryForImageCapture(ContextUtils.getApplicationContext())
+                    + File.separator + SHARE_IMAGES_DIRECTORY_NAME;
+            rwhv.writeContentBitmapToDiskAsync(
+                    width, height, path, new ExternallyVisibleUriCallback(callback));
+        } catch (IOException e) {
+            Log.e(TAG, "Error getting content bitmap: ", e);
+            callback.onResult(null);
+        }
+    }
+
+    private static class ExternallyVisibleUriCallback implements Callback<String> {
+        private Callback<Uri> mComposedCallback;
+        ExternallyVisibleUriCallback(Callback<Uri> cb) {
+            mComposedCallback = cb;
+        }
+
+        @Override
+        public void onResult(final String path) {
+            if (TextUtils.isEmpty(path)) {
+                mComposedCallback.onResult(null);
+                return;
+            }
+
+            new AsyncTask<Uri>() {
+                @Override
+                protected Uri doInBackground() {
+                    return ContentUriUtils.getContentUriFromFile(new File(path));
+                }
+
+                @Override
+                protected void onPostExecute(Uri uri) {
+                    mComposedCallback.onResult(uri);
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }
+    }
+}
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni
index a481507..c3b27ee 100644
--- a/chrome/browser/share/android/java_sources.gni
+++ b/chrome/browser/share/android/java_sources.gni
@@ -5,6 +5,7 @@
 # TODO(crbug/1022172): This should be a separate build target when circular
 # dependencies are removed.
 share_java_sources = [
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ShareImageFileUtils.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeCoordinator.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialogTab.java",
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/ShareImageFileUtilsTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/ShareImageFileUtilsTest.java
new file mode 100644
index 0000000..8c8e1f4f
--- /dev/null
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/ShareImageFileUtilsTest.java
@@ -0,0 +1,171 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.share;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.support.v4.content.FileProvider;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ContentUriUtils;
+import org.chromium.base.ContextUtils;
+import org.chromium.base.task.AsyncTask;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.ChromeActivity;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.base.Clipboard;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Tests of {@link ShareImageFileUtils}.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class ShareImageFileUtilsTest {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    private static final long WAIT_TIMEOUT_SECONDS = 5L;
+    private static final byte[] TEST_IMAGE_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+
+    private class FileProviderHelper implements ContentUriUtils.FileProviderUtil {
+        private static final String API_AUTHORITY_SUFFIX = ".FileProvider";
+
+        @Override
+        public Uri getContentUriFromFile(File file) {
+            Context appContext = ContextUtils.getApplicationContext();
+            return FileProvider.getUriForFile(
+                    appContext, appContext.getPackageName() + API_AUTHORITY_SUFFIX, file);
+        }
+    }
+
+    private class GenerateUriCallback extends CallbackHelper implements Callback<Uri> {
+        private Uri mImageUri;
+
+        public Uri getImageUri() {
+            return mImageUri;
+        }
+
+        @Override
+        public void onResult(Uri uri) {
+            mImageUri = uri;
+            notifyCalled();
+        }
+    }
+
+    private class AsyncTaskRunnableHelper extends CallbackHelper implements Runnable {
+        @Override
+        public void run() {
+            notifyCalled();
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mActivityTestRule.startMainActivityFromLauncher();
+        ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
+    }
+
+    @After
+    public void tearDown() throws TimeoutException {
+        Clipboard.getInstance().setText("");
+        clearSharedImages();
+    }
+
+    private int fileCount(File file) {
+        if (file.isFile()) {
+            return 1;
+        }
+
+        int count = 0;
+        if (file.isDirectory()) {
+            for (File f : file.listFiles()) count += fileCount(f);
+        }
+        return count;
+    }
+
+    private boolean filepathExists(File file, String filepath) {
+        if (file.isFile() && filepath.endsWith(file.getName())) {
+            return true;
+        }
+
+        if (file.isDirectory()) {
+            for (File f : file.listFiles()) {
+                if (filepathExists(f, filepath)) return true;
+            }
+        }
+        return false;
+    }
+
+    private Uri generateAnImageToClipboard() throws TimeoutException {
+        GenerateUriCallback imageCallback = new GenerateUriCallback();
+        ShareImageFileUtils.generateTemporaryUriFromData(
+                mActivityTestRule.getActivity(), TEST_IMAGE_DATA, imageCallback);
+        imageCallback.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        Clipboard.getInstance().setImageUri(imageCallback.getImageUri());
+        return imageCallback.getImageUri();
+    }
+
+    private void clearSharedImages() throws TimeoutException {
+        ShareImageFileUtils.clearSharedImages();
+
+        // ShareImageFileUtils::clearSharedImages uses AsyncTask.SERIAL_EXECUTOR to schedule a
+        // clearing the shared folder job, so schedule a new job and wait for the new job finished
+        // to make sure ShareImageFileUtils::clearSharedImages's clearing folder job finished.
+        AsyncTaskRunnableHelper runnableHelper = new AsyncTaskRunnableHelper();
+        AsyncTask.SERIAL_EXECUTOR.execute(runnableHelper);
+        runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    private int fileCountInShareDirectory() throws IOException {
+        return fileCount(ShareImageFileUtils.getSharedFilesDirectory());
+    }
+
+    private boolean fileExistsInShareDirectory(Uri fileUri) throws IOException {
+        return filepathExists(ShareImageFileUtils.getSharedFilesDirectory(), fileUri.getPath());
+    }
+
+    @Test
+    @SmallTest
+    public void clipboardUriDoNotClearTest() throws TimeoutException, IOException {
+        generateAnImageToClipboard();
+        generateAnImageToClipboard();
+        Uri clipboardUri = generateAnImageToClipboard();
+        Assert.assertEquals(3, fileCountInShareDirectory());
+
+        clearSharedImages();
+        Assert.assertEquals(1, fileCountInShareDirectory());
+        Assert.assertTrue(fileExistsInShareDirectory(clipboardUri));
+    }
+
+    @Test
+    @SmallTest
+    public void clearEverythingIfNoClipboardImageTest() throws TimeoutException, IOException {
+        generateAnImageToClipboard();
+        generateAnImageToClipboard();
+        generateAnImageToClipboard();
+        Assert.assertEquals(3, fileCountInShareDirectory());
+
+        Clipboard.getInstance().setText("");
+        clearSharedImages();
+        Assert.assertEquals(0, fileCountInShareDirectory());
+    }
+}
diff --git a/chrome/browser/share/android/test_java_sources.gni b/chrome/browser/share/android/test_java_sources.gni
new file mode 100644
index 0000000..f891735
--- /dev/null
+++ b/chrome/browser/share/android/test_java_sources.gni
@@ -0,0 +1,6 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# TODO(crbug.com/1022172): This should be a separate build target when circular dependencies are removed.
+share_test_java_sources = [ "//chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/ShareImageFileUtilsTest.java" ]
diff --git a/chrome/browser/signin/chrome_signin_client.h b/chrome/browser/signin/chrome_signin_client.h
index 7b95d616..32f71fa 100644
--- a/chrome/browser/signin/chrome_signin_client.h
+++ b/chrome/browser/signin/chrome_signin_client.h
@@ -18,7 +18,7 @@
 #include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
 #include "google_apis/gaia/gaia_oauth_client.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/mojom/network_change_manager.mojom.h"
+#include "services/network/public/mojom/network_change_manager.mojom-forward.h"
 
 #if !defined(OS_CHROMEOS)
 #include "services/network/public/cpp/network_connection_tracker.h"
diff --git a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h
index 1817e1ab..c9b6e97 100644
--- a/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h
+++ b/chrome/browser/signin/chrome_signin_proxying_url_loader_factory.h
@@ -14,7 +14,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 
 #include <memory>
 #include <set>
diff --git a/chrome/browser/spellchecker/spellcheck_service.h b/chrome/browser/spellchecker/spellcheck_service.h
index 557a0a2..803c7da 100644
--- a/chrome/browser/spellchecker/spellcheck_service.h
+++ b/chrome/browser/spellchecker/spellcheck_service.h
@@ -21,7 +21,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/spellcheck/browser/platform_spell_checker.h"
-#include "components/spellcheck/common/spellcheck.mojom.h"
+#include "components/spellcheck/common/spellcheck.mojom-forward.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_observer.cc b/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
index a153df8..a5236355 100644
--- a/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
+++ b/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
@@ -128,6 +128,9 @@
 
   content::RenderFrameHost* render_frame_host =
       navigation_handle->GetRenderFrameHost();
+  if (!render_frame_host || !render_frame_host->GetProcess())
+    return;
+
   optimization_guide_decider->CanApplyOptimizationAsync(
       navigation_handle, optimization_guide::proto::COMPRESS_PUBLIC_IMAGES,
       base::BindOnce(&OnReadyToSendResourceLoadingImageHints,
diff --git a/chrome/browser/themes/theme_properties.cc b/chrome/browser/themes/theme_properties.cc
index 84464a5..f6d5afa 100644
--- a/chrome/browser/themes/theme_properties.cc
+++ b/chrome/browser/themes/theme_properties.cc
@@ -151,6 +151,7 @@
     case ThemeProperties::COLOR_HOVER_CARD_NO_PREVIEW_FOREGROUND:
       return gfx::kGoogleGrey700;
     case ThemeProperties::COLOR_HOVER_CARD_NO_PREVIEW_BACKGROUND:
+    case ThemeProperties::COLOR_NTP_SHORTCUT:
       return gfx::kGoogleGrey900;
     case ThemeProperties::COLOR_BOOKMARK_TEXT:
     case ThemeProperties::COLOR_TAB_TEXT:
diff --git a/chrome/browser/themes/theme_properties.h b/chrome/browser/themes/theme_properties.h
index 1a9d16d0..3537d21 100644
--- a/chrome/browser/themes/theme_properties.h
+++ b/chrome/browser/themes/theme_properties.h
@@ -146,8 +146,6 @@
     COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_INCOGNITO_ACTIVE,
     COLOR_WINDOW_CONTROL_BUTTON_BACKGROUND_INCOGNITO_INACTIVE,
 
-    // These colors don't have constant default values. They are derived from
-    // the runtime value of other colors.
     COLOR_NTP_TEXT_LIGHT,
     COLOR_NTP_LOGO,
     // Color for the background of the most visited/custom link tile.
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
index b58d7d4..a7447f2 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_sheet.xml
@@ -27,7 +27,7 @@
 
     <!-- Please update R.dimens.touch_to_fill_sheet_height_second_credential
          when modifying paddingBottom. -->
-    <android.support.v7.widget.RecyclerView
+    <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/sheet_item_list"
         android:layout_width="match_parent"
         android:layout_height="0dp"
diff --git a/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.cc b/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.cc
index 2a81efce..aeb0f41 100644
--- a/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.cc
+++ b/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.cc
@@ -37,6 +37,42 @@
 }
 
 void CardNameFixFlowViewAndroid::Show() {
+  auto java_object = GetOrCreateJavaObject();
+  if (!java_object)
+    return;
+
+  java_object_.Reset(java_object);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  ui::ViewAndroid* view_android = web_contents_->GetNativeView();
+
+  Java_AutofillNameFixFlowBridge_show(
+      env, java_object_, view_android->GetWindowAndroid()->GetJavaObject());
+}
+
+void CardNameFixFlowViewAndroid::ControllerGone() {
+  controller_ = nullptr;
+  JNIEnv* env = base::android::AttachCurrentThread();
+  if (java_object_internal_) {
+    // Don't create an object just for dismiss.
+    Java_AutofillNameFixFlowBridge_dismiss(env, java_object_internal_);
+  }
+}
+
+CardNameFixFlowViewAndroid::~CardNameFixFlowViewAndroid() {
+  if (controller_)
+    controller_->OnConfirmNameDialogClosed();
+}
+
+base::android::ScopedJavaGlobalRef<jobject>
+CardNameFixFlowViewAndroid::GetOrCreateJavaObject() {
+  if (java_object_internal_)
+    return java_object_internal_;
+
+  if (web_contents_->GetNativeView() == nullptr ||
+      web_contents_->GetNativeView()->GetWindowAndroid() == nullptr)
+    return nullptr;  // No window attached (yet or anymore).
+
   JNIEnv* env = base::android::AttachCurrentThread();
   ui::ViewAndroid* view_android = web_contents_->GetNativeView();
 
@@ -50,24 +86,11 @@
   ScopedJavaLocalRef<jstring> confirm = base::android::ConvertUTF16ToJavaString(
       env, controller_->GetSaveButtonLabel());
 
-  java_object_.Reset(Java_AutofillNameFixFlowBridge_create(
-      env, reinterpret_cast<intptr_t>(this), dialog_title, inferred_name,
-      confirm, ResourceMapper::MapFromChromiumId(controller_->GetIconId()),
-      view_android->GetWindowAndroid()->GetJavaObject()));
-
-  Java_AutofillNameFixFlowBridge_show(
-      env, java_object_, view_android->GetWindowAndroid()->GetJavaObject());
-}
-
-void CardNameFixFlowViewAndroid::ControllerGone() {
-  controller_ = nullptr;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_AutofillNameFixFlowBridge_dismiss(env, java_object_);
-}
-
-CardNameFixFlowViewAndroid::~CardNameFixFlowViewAndroid() {
-  if (controller_)
-    controller_->OnConfirmNameDialogClosed();
+  return java_object_internal_ = Java_AutofillNameFixFlowBridge_create(
+             env, reinterpret_cast<intptr_t>(this), dialog_title, inferred_name,
+             confirm,
+             ResourceMapper::MapFromChromiumId(controller_->GetIconId()),
+             view_android->GetWindowAndroid()->GetJavaObject());
 }
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.h b/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.h
index 1c86345..623f6351 100644
--- a/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.h
+++ b/chrome/browser/ui/android/autofill/card_name_fix_flow_view_android.h
@@ -40,8 +40,15 @@
  private:
   ~CardNameFixFlowViewAndroid() override;
 
+  // Returns either the fully initialized java counterpart of this bridge or
+  // a is_null() reference if the creation failed. By using this method, the
+  // bridge will try to recreate the java object if it failed previously (e.g.
+  // because there was no native window available).
+  base::android::ScopedJavaGlobalRef<jobject> GetOrCreateJavaObject();
+
   // The corresponding java object.
   base::android::ScopedJavaGlobalRef<jobject> java_object_;
+  base::android::ScopedJavaGlobalRef<jobject> java_object_internal_;
 
   CardNameFixFlowController* controller_;
   content::WebContents* web_contents_;
diff --git a/chrome/browser/ui/app_list/OWNERS b/chrome/browser/ui/app_list/OWNERS
index 90b6293..6c7fc11b 100644
--- a/chrome/browser/ui/app_list/OWNERS
+++ b/chrome/browser/ui/app_list/OWNERS
@@ -1,6 +1,8 @@
 calamity@chromium.org
+dominickn@chromium.org
 jennyz@chromium.org
 khmel@chromium.org
+nancylingwang@chromium.org
 xiyuan@chromium.org
 
 per-file app_launch_event_logger*=charleszhao@chromium.org
diff --git a/chrome/browser/ui/ash/launcher/OWNERS b/chrome/browser/ui/ash/launcher/OWNERS
index 5345cbf..efb8eeb2 100644
--- a/chrome/browser/ui/ash/launcher/OWNERS
+++ b/chrome/browser/ui/ash/launcher/OWNERS
@@ -1,4 +1,6 @@
-skuhne@chromium.org
+dominickn@chromium.org
 khmel@chromium.org
+nancylingwang@chromium.org
+skuhne@chromium.org
 
 # COMPONENT: UI>Shell>Shelf
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc
index db51aa55..43efb0fef 100644
--- a/chrome/browser/ui/browser_command_controller_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -173,7 +173,8 @@
   // automatically upon launch.
   // Wait for robustness because InProcessBrowserTest::PreRunTestOnMainThread
   // does not flush the task scheduler.
-  TabRestoreServiceLoadWaiter waiter(browser());
+  TabRestoreServiceLoadWaiter waiter(
+      TabRestoreServiceFactory::GetForProfile(browser()->profile()));
   waiter.Wait();
 
   // After initialization, the command should become disabled because there's
@@ -203,7 +204,8 @@
   // automatically upon launch.
   // Wait for robustness because InProcessBrowserTest::PreRunTestOnMainThread
   // does not flush the task scheduler.
-  TabRestoreServiceLoadWaiter waiter(browser());
+  TabRestoreServiceLoadWaiter waiter(
+      TabRestoreServiceFactory::GetForProfile(browser()->profile()));
   waiter.Wait();
 
   // After initialization, the command should remain enabled because there's
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge.mm b/chrome/browser/ui/cocoa/tab_menu_bridge.mm
index 28d4609..cbc3a8e 100644
--- a/chrome/browser/ui/cocoa/tab_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/tab_menu_bridge.mm
@@ -109,6 +109,8 @@
                action:@selector(activateTab:)
         keyEquivalent:@""]);
     [item setTarget:menu_listener_.get()];
+    if (model_->active_index() == i)
+      [item setState:NSOnState];
     UpdateItemForWebContents(item, model_->GetWebContentsAt(i));
     [menu_item_.submenu addItem:item.get()];
   }
@@ -131,10 +133,6 @@
   DCHECK(tab_strip_model);
   DCHECK_EQ(tab_strip_model, model_);
 
-  // The menu doesn't represent selection in any way, so ignore it.
-  if (change.type() == TabStripModelChange::kSelectionOnly)
-    return;
-
   // If a single WebContents is being replaced, just regenerate that one menu
   // item.
   if (change.type() == TabStripModelChange::kReplaced) {
diff --git a/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
index cfeedaa..adc9b07 100644
--- a/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/tab_menu_bridge_unittest.mm
@@ -117,6 +117,12 @@
     }
   }
 
+  void ActivateModelTabNamed(const std::string& name) {
+    int index = ModelIndexForTabNamed(name);
+    DCHECK(index >= 0);
+    model()->ActivateTabAt(index);
+  }
+
   NSMenuItem* MenuItemForTabNamed(const std::string& name) {
     return [menu() itemWithTitle:base::SysUTF8ToNSString(name)];
   }
@@ -137,6 +143,19 @@
     return base::UTF16ToUTF8(model()->GetActiveWebContents()->GetTitle());
   }
 
+  void ExpectActiveMenuItemNameIs(const std::string& name) {
+    std::vector<std::string> active_items;
+    // Check the static items too, to make sure none of them are checked.
+    for (int i = 0; i < menu().numberOfItems; ++i) {
+      NSMenuItem* item = [menu() itemAtIndex:i];
+      if (item.state == NSOnState)
+        active_items.push_back(base::SysNSStringToUTF8(item.title));
+    }
+
+    ASSERT_EQ(active_items.size(), 1u);
+    EXPECT_EQ(name, active_items[0]);
+  }
+
  private:
   NSMenuItem* ItemWithTitle(NSString* title) {
     return [[NSMenuItem alloc] initWithTitle:title
@@ -243,3 +262,22 @@
   // does not correctly forget about the TabStripModel, this test will crash
   // here in ASAN builds.
 }
+
+TEST_F(TabMenuBridgeTest, ActiveItemTracksChanges) {
+  TabMenuBridge bridge(model(), menu_root());
+  bridge.BuildMenu();
+
+  AddModelTabNamed("Tab 1");
+  AddModelTabNamed("Tab 2");
+  AddModelTabNamed("Tab 3");
+  ExpectActiveMenuItemNameIs("Tab 3");
+
+  ActivateModelTabNamed("Tab 2");
+  ExpectActiveMenuItemNameIs("Tab 2");
+
+  ActivateModelTabNamed("Tab 3");
+  ExpectActiveMenuItemNameIs("Tab 3");
+
+  RemoveModelTabNamed("Tab 1");
+  ExpectActiveMenuItemNameIs("Tab 3");
+}
diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc
index a373453..06b06c4a 100644
--- a/chrome/browser/ui/search/search_tab_helper.cc
+++ b/chrome/browser/ui/search/search_tab_helper.cc
@@ -110,7 +110,7 @@
     }
     mojom_match->destination_url = match.destination_url.spec();
     mojom_match->image_dominant_color = match.image_dominant_color;
-    mojom_match->image_url = match.image_url;
+    mojom_match->image_url = match.image_url.spec();
     mojom_match->fill_into_edit = match.fill_into_edit;
     mojom_match->inline_autocompletion = match.inline_autocompletion;
     mojom_match->is_search_type = AutocompleteMatch::IsSearchType(match.type);
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 9abb332..dcdcb23df 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -260,9 +260,6 @@
   OutOfMemoryReporter::CreateForWebContents(web_contents);
   chrome::InitializePageLoadMetricsForWebContents(web_contents);
   PDFPluginPlaceholderObserver::CreateForWebContents(web_contents);
-  if (base::FeatureList::IsEnabled(kPerformanceHintsObserver)) {
-    PerformanceHintsObserver::CreateForWebContents(web_contents);
-  }
   permissions::PermissionRequestManager::CreateForWebContents(web_contents);
   // The PopupBlockerTabHelper has an implicit dependency on
   // ChromeSubresourceFilterClient being available in its constructor.
@@ -317,7 +314,11 @@
   if (OomInterventionTabHelper::IsEnabled()) {
     OomInterventionTabHelper::CreateForWebContents(web_contents);
   }
-
+  if (base::FeatureList::IsEnabled(
+          chrome::android::kContextMenuPerformanceInfo) ||
+      base::FeatureList::IsEnabled(kPerformanceHintsObserver)) {
+    PerformanceHintsObserver::CreateForWebContents(web_contents);
+  }
   SearchGeolocationDisclosureTabHelper::CreateForWebContents(web_contents);
   ViewAndroidHelper::CreateForWebContents(web_contents);
 #else
diff --git a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
index fc554d2..dd4f29c 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_match_cell_view.cc
@@ -162,7 +162,7 @@
                                          const AutocompleteMatch& match) {
   is_rich_suggestion_ = match.answer ||
                         match.type == AutocompleteMatchType::CALCULATOR ||
-                        !match.image_url.empty();
+                        !match.image_url.is_empty();
   is_search_type_ = AutocompleteMatch::IsSearchType(match.type);
 
   // Decide layout style once before Layout, while match data is available.
diff --git a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
index 76458e0..1e50da27 100644
--- a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
+++ b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
@@ -80,6 +80,10 @@
   DCHECK(controller);
   DCHECK(web_contents);
 
+  DialogDelegate::set_buttons(controller->ShouldShowCancelButton()
+                                  ? ui::DIALOG_BUTTON_OK |
+                                        ui::DIALOG_BUTTON_CANCEL
+                                  : ui::DIALOG_BUTTON_OK);
   DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
                                    controller_->GetAcceptButtonLabel());
   DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
@@ -137,14 +141,6 @@
   return gfx::Size(width, GetHeightForWidth(width));
 }
 
-int CredentialLeakDialogView::GetDialogButtons() const {
-  // |controller_| can be nullptr when the framework calls this method after a
-  // button click.
-  return controller_ && controller_->ShouldShowCancelButton()
-             ? ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL
-             : ui::DIALOG_BUTTON_OK;
-}
-
 bool CredentialLeakDialogView::ShouldShowCloseButton() const {
   return false;
 }
diff --git a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h
index 36d1cb4..aeb4aa82 100644
--- a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h
+++ b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h
@@ -30,7 +30,6 @@
   // views::DialogDelegateView:
   ui::ModalType GetModalType() const override;
   gfx::Size CalculatePreferredSize() const override;
-  int GetDialogButtons() const override;
   bool ShouldShowCloseButton() const override;
   void OnThemeChanged() override;
   base::string16 GetWindowTitle() const override;
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
index 40d4547c3..473bd73 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.cc
@@ -119,9 +119,13 @@
   CallJS("login.ResetScreen.setIsRollbackAvailable", value);
 }
 
-void ResetScreenHandler::SetIsRollbackChecked(bool value) {
-  is_rollback_checked_ = value;
-  CallJS("login.ResetScreen.setIsRollbackChecked", value);
+// Only serve the request if the confirmation dialog isn't being shown.
+void ResetScreenHandler::SetIsRollbackRequested(bool value) {
+  if (is_showing_confirmation_dialog_)
+    return;
+
+  is_rollback_requested_ = value;
+  CallJS("login.ResetScreen.setIsRollbackRequested", value);
 }
 
 void ResetScreenHandler::SetIsTpmFirmwareUpdateAvailable(bool value) {
@@ -143,12 +147,13 @@
   CallJS("login.ResetScreen.setTpmFirmwareUpdateMode", static_cast<int>(value));
 }
 
-void ResetScreenHandler::SetIsConfirmational(bool value) {
-  CallJS("login.ResetScreen.setIsConfirmational", value);
+void ResetScreenHandler::SetShouldShowConfirmationDialog(bool value) {
+  is_showing_confirmation_dialog_ = value;
+  CallJS("login.ResetScreen.setShouldShowConfirmationDialog", value);
 }
 
-void ResetScreenHandler::SetIsGoogleBrandedBuild(bool value) {
-  CallJS("login.ResetScreen.setIsGoogleBrandedBuild", value);
+void ResetScreenHandler::SetConfirmationDialogClosed() {
+  is_showing_confirmation_dialog_ = false;
 }
 
 void ResetScreenHandler::SetScreenState(State value) {
@@ -168,8 +173,8 @@
   return is_rollback_available_;
 }
 
-bool ResetScreenHandler::GetIsRollbackChecked() {
-  return is_rollback_checked_;
+bool ResetScreenHandler::GetIsRollbackRequested() {
+  return is_rollback_requested_;
 }
 
 bool ResetScreenHandler::GetIsTpmFirmwareUpdateChecked() {
diff --git a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
index 3078557..e7feeb5 100644
--- a/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/reset_screen_handler.h
@@ -36,19 +36,19 @@
   };
 
   virtual void SetIsRollbackAvailable(bool value) = 0;
-  virtual void SetIsRollbackChecked(bool value) = 0;
+  virtual void SetIsRollbackRequested(bool value) = 0;
   virtual void SetIsTpmFirmwareUpdateAvailable(bool value) = 0;
   virtual void SetIsTpmFirmwareUpdateChecked(bool value) = 0;
   virtual void SetIsTpmFirmwareUpdateEditable(bool value) = 0;
   virtual void SetTpmFirmwareUpdateMode(tpm_firmware_update::Mode value) = 0;
-  virtual void SetIsConfirmational(bool value) = 0;
-  virtual void SetIsGoogleBrandedBuild(bool value) = 0;
+  virtual void SetShouldShowConfirmationDialog(bool value) = 0;
+  virtual void SetConfirmationDialogClosed() = 0;
   virtual void SetScreenState(State value) = 0;
 
   virtual State GetScreenState() = 0;
   virtual tpm_firmware_update::Mode GetTpmFirmwareUpdateMode() = 0;
   virtual bool GetIsRollbackAvailable() = 0;
-  virtual bool GetIsRollbackChecked() = 0;
+  virtual bool GetIsRollbackRequested() = 0;
   virtual bool GetIsTpmFirmwareUpdateChecked() = 0;
 };
 
@@ -73,18 +73,18 @@
   void DeclareJSCallbacks() override;
   void Initialize() override;
   void SetIsRollbackAvailable(bool value) override;
-  void SetIsRollbackChecked(bool value) override;
+  void SetIsRollbackRequested(bool value) override;
   void SetIsTpmFirmwareUpdateAvailable(bool value) override;
   void SetIsTpmFirmwareUpdateChecked(bool value) override;
   void SetIsTpmFirmwareUpdateEditable(bool value) override;
   void SetTpmFirmwareUpdateMode(tpm_firmware_update::Mode value) override;
-  void SetIsConfirmational(bool value) override;
-  void SetIsGoogleBrandedBuild(bool value) override;
+  void SetShouldShowConfirmationDialog(bool value) override;
+  void SetConfirmationDialogClosed() override;
   void SetScreenState(State value) override;
   State GetScreenState() override;
   tpm_firmware_update::Mode GetTpmFirmwareUpdateMode() override;
   bool GetIsRollbackAvailable() override;
-  bool GetIsRollbackChecked() override;
+  bool GetIsRollbackRequested() override;
   bool GetIsTpmFirmwareUpdateChecked() override;
 
  private:
@@ -98,8 +98,9 @@
   ResetView::State state_ = ResetView::State::kRestartRequired;
   tpm_firmware_update::Mode mode_ = tpm_firmware_update::Mode::kNone;
   bool is_rollback_available_ = false;
-  bool is_rollback_checked_ = false;
+  bool is_rollback_requested_ = false;
   bool is_tpm_firmware_update_checked_ = false;
+  bool is_showing_confirmation_dialog_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(ResetScreenHandler);
 };
diff --git a/chrome/browser/ui/webui/favicon_source.cc b/chrome/browser/ui/webui/favicon_source.cc
index 111fed9..8c4283a 100644
--- a/chrome/browser/ui/webui/favicon_source.cc
+++ b/chrome/browser/ui/webui/favicon_source.cc
@@ -8,7 +8,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "base/metrics/histogram_macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/favicon/favicon_utils.h"
@@ -21,7 +21,11 @@
 #include "components/favicon/core/history_ui_favicon_request_handler.h"
 #include "components/favicon_base/favicon_url_parser.h"
 #include "components/history/core/browser/top_sites.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/manifest.h"
 #include "net/url_request/url_request.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/layout.h"
@@ -81,6 +85,7 @@
     const GURL& url,
     const content::WebContents::Getter& wc_getter,
     content::URLDataSource::GotDataCallback callback) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   const std::string path = content::URLDataSource::URLToRequestPath(url);
   favicon::FaviconService* favicon_service =
       FaviconServiceFactory::GetForProfile(profile_,
@@ -104,6 +109,18 @@
     return;
   }
 
+  if (url_format_ == chrome::FaviconUrlFormat::kFaviconLegacy) {
+    const extensions::Extension* extension =
+        extensions::ExtensionRegistry::Get(profile_)
+            ->enabled_extensions()
+            .GetExtensionOrAppByURL(GetUnsafeRequestOrigin(wc_getter));
+    if (extension) {
+      base::UmaHistogramEnumeration("Extensions.FaviconResourceRequested",
+                                    extension->GetType(),
+                                    extensions::Manifest::NUM_LOAD_TYPES);
+    }
+  }
+
   int desired_size_in_pixel =
       std::ceil(parsed.size_in_dip * parsed.device_scale_factor);
 
diff --git a/chrome/browser/ui/webui/favicon_source_unittest.cc b/chrome/browser/ui/webui/favicon_source_unittest.cc
index 78f08ca..10ee7b6 100644
--- a/chrome/browser/ui/webui/favicon_source_unittest.cc
+++ b/chrome/browser/ui/webui/favicon_source_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/strings/strcat.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/favicon/history_ui_favicon_request_handler_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -21,6 +22,9 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/web_contents_tester.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/manifest.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/native_theme/test_native_theme.h"
@@ -190,6 +194,44 @@
       test_web_contents_getter_, base::BindRepeating(&Noop));
 }
 
+TEST_F(FaviconSourceTestWithLegacyFormat,
+       ShouldRecordFaviconResourceHistogram_NonExtensionOrigin) {
+  base::HistogramTester tester;
+  source()->StartDataRequest(
+      GURL(base::StrCat({kDummyPrefix, "size/16@1x/https://www.google.com"})),
+      test_web_contents_getter_, base::DoNothing());
+  tester.ExpectBucketCount("Extensions.FaviconResourceRequested",
+                           extensions::Manifest::TYPE_EXTENSION, 0);
+}
+
+TEST_F(FaviconSourceTestWithLegacyFormat,
+       ShouldRecordFaviconResourceHistogram_ExtensionOrigin) {
+  scoped_refptr<const extensions::Extension> extension =
+      extensions::ExtensionBuilder("one").Build();
+  extensions::ExtensionRegistry::Get(&profile_)->AddEnabled(extension);
+  content::WebContentsTester::For(test_web_contents_.get())
+      ->SetLastCommittedURL(extension->url());
+  base::HistogramTester tester;
+  source()->StartDataRequest(
+      GURL(base::StrCat({kDummyPrefix, "size/16@1x/https://www.google.com"})),
+      test_web_contents_getter_, base::DoNothing());
+  tester.ExpectBucketCount("Extensions.FaviconResourceRequested",
+                           extensions::Manifest::TYPE_EXTENSION, 1);
+}
+
+TEST_F(FaviconSourceTestWithFavicon2Format,
+       ShouldNotRecordFaviconResourceHistogram) {
+  base::HistogramTester tester;
+  source()->StartDataRequest(
+      GURL(base::StrCat({kDummyPrefix, "size/16@1x/https://www.google.com"})),
+      test_web_contents_getter_, base::BindRepeating(&Noop));
+  std::unique_ptr<base::HistogramSamples> samples(
+      tester.GetHistogramSamplesSinceCreation(
+          "Extensions.FaviconResourceUsed"));
+  EXPECT_TRUE(samples);
+  EXPECT_EQ(0, samples->TotalCount());
+}
+
 TEST_F(FaviconSourceTestWithFavicon2Format, DarkDefault) {
   SetDarkMode(true);
   EXPECT_CALL(*source(), LoadIconBytes(_, IDR_DEFAULT_FAVICON_DARK));
diff --git a/chrome/browser/ui/webui/policy_ui_handler.cc b/chrome/browser/ui/webui/policy_ui_handler.cc
index 15066ca7..61f000de 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy_ui_handler.cc
@@ -51,6 +51,7 @@
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
 #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_store.h"
 #include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/policy_scheduler.h"
 #include "components/policy/core/common/policy_types.h"
@@ -103,6 +104,12 @@
 #include "extensions/common/manifest_constants.h"
 #endif
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#include "chrome/browser/google/google_update_policy_fetcher_win.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
 namespace em = enterprise_management;
 
 namespace {
@@ -808,6 +815,18 @@
 #endif  // !defined(OS_ANDROID)
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  base::PostTaskAndReplyWithResult(
+      base::CreateCOMSTATaskRunner(
+          {base::TaskPriority::USER_BLOCKING,
+           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN, base::MayBlock(),
+           base::ThreadPool()})
+          .get(),
+      FROM_HERE, base::BindOnce(&GetGoogleUpdatePolicies),
+      base::BindOnce(&PolicyUIHandler::SetUpdaterPolicies,
+                     weak_factory_.GetWeakPtr()));
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   if (!user_status_provider_.get())
     user_status_provider_ = std::make_unique<PolicyStatusProvider>();
   if (!device_status_provider_.get())
@@ -876,7 +895,7 @@
 }
 
 base::Value PolicyUIHandler::GetPolicyNames() const {
-  base::DictionaryValue names;
+  base::Value names(base::Value::Type::DICTIONARY);
   Profile* profile = Profile::FromWebUI(web_ui());
   policy::SchemaRegistry* registry = profile->GetOriginalProfile()
                                          ->GetPolicySchemaRegistryService()
@@ -884,17 +903,26 @@
   scoped_refptr<policy::SchemaMap> schema_map = registry->schema_map();
 
   // Add Chrome policy names.
-  auto chrome_policy_names = std::make_unique<base::ListValue>();
+  base::Value chrome_policy_names(base::Value::Type::LIST);
   policy::PolicyNamespace chrome_ns(policy::POLICY_DOMAIN_CHROME, "");
   const policy::Schema* chrome_schema = schema_map->GetSchema(chrome_ns);
   for (auto it = chrome_schema->GetPropertiesIterator(); !it.IsAtEnd();
        it.Advance()) {
-    chrome_policy_names->Append(base::Value(it.key()));
+    chrome_policy_names.Append(base::Value(it.key()));
   }
-  auto chrome_values = std::make_unique<base::DictionaryValue>();
-  chrome_values->SetString("name", "Chrome Policies");
-  chrome_values->SetList("policyNames", std::move(chrome_policy_names));
-  names.Set("chrome", std::move(chrome_values));
+  base::Value chrome_values(base::Value::Type::DICTIONARY);
+  chrome_values.SetStringKey("name", "Chrome Policies");
+  chrome_values.SetKey("policyNames", std::move(chrome_policy_names));
+  names.SetKey("chrome", std::move(chrome_values));
+
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  if (updater_policies_) {
+    base::Value updater_policies(base::Value::Type::DICTIONARY);
+    updater_policies.SetStringKey("name", "Updater Policies");
+    updater_policies.SetKey("policyNames", GetGoogleUpdatePolicyNames());
+    names.SetKey("updater", std::move(updater_policies));
+  }
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Add extension policy names.
@@ -906,20 +934,31 @@
 
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
-  return std::move(names);
+  return names;
 }
 
 base::Value PolicyUIHandler::GetPolicyValues() const {
   auto client = std::make_unique<policy::ChromePolicyConversionsClient>(
       web_ui()->GetWebContents()->GetBrowserContext());
+
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  if (updater_policies_) {
+    return policy::ArrayPolicyConversions(std::move(client))
+        .EnableConvertValues(true)
+        .WithUpdaterPolicies(updater_policies_->DeepCopy())
+        .ToValue();
+  }
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   return policy::ArrayPolicyConversions(std::move(client))
       .EnableConvertValues(true)
       .ToValue();
 }
 
 void PolicyUIHandler::AddExtensionPolicyNames(
-    base::DictionaryValue* names,
+    base::Value* names,
     policy::PolicyDomain policy_domain) const {
+  DCHECK(names->is_dict());
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if defined(OS_CHROMEOS)
@@ -949,21 +988,21 @@
             extensions::manifest_keys::kStorageManagedSchema)) {
       continue;
     }
-    auto extension_value = std::make_unique<base::DictionaryValue>();
-    extension_value->SetString("name", extension->name());
+    base::Value extension_value(base::Value::Type::DICTIONARY);
+    extension_value.SetStringKey("name", extension->name());
     const policy::Schema* schema = schema_map->GetSchema(
         policy::PolicyNamespace(policy_domain, extension->id()));
-    auto policy_names = std::make_unique<base::ListValue>();
+    base::Value policy_names(base::Value::Type::LIST);
     if (schema && schema->valid()) {
       // Get policy names from the extension's policy schema.
       // Store in a map, not an array, for faster lookup on JS side.
       for (auto prop = schema->GetPropertiesIterator(); !prop.IsAtEnd();
            prop.Advance()) {
-        policy_names->Append(base::Value(prop.key()));
+        policy_names.Append(base::Value(prop.key()));
       }
     }
-    extension_value->Set("policyNames", std::move(policy_names));
-    names->Set(extension->id(), std::move(extension_value));
+    extension_value.SetKey("policyNames", std::move(policy_names));
+    names->SetKey(extension->id(), std::move(extension_value));
   }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 }
@@ -1151,6 +1190,15 @@
     FireWebUIListener("policies-updated", GetPolicyNames(), GetPolicyValues());
 }
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+void PolicyUIHandler::SetUpdaterPolicies(
+    std::unique_ptr<policy::PolicyMap> updater_policies) {
+  updater_policies_ = std::move(updater_policies);
+  if (updater_policies_)
+    SendPolicies();
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
 void PolicyUIHandler::OnRefreshPoliciesDone() {
   SendPolicies();
   SendStatus();
diff --git a/chrome/browser/ui/webui/policy_ui_handler.h b/chrome/browser/ui/webui/policy_ui_handler.h
index d803ade..dcba4d6d 100644
--- a/chrome/browser/ui/webui/policy_ui_handler.h
+++ b/chrome/browser/ui/webui/policy_ui_handler.h
@@ -14,6 +14,8 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
 #include "components/policy/core/browser/policy_error_map.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
@@ -31,6 +33,10 @@
 
 class PolicyStatusProvider;
 
+namespace policy {
+class PolicyMap;
+}
+
 // The JavaScript message handler for the chrome://policy page.
 class PolicyUIHandler : public content::WebUIMessageHandler,
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -77,7 +83,7 @@
   base::Value GetPolicyNames() const;
   base::Value GetPolicyValues() const;
 
-  void AddExtensionPolicyNames(base::DictionaryValue* names,
+  void AddExtensionPolicyNames(base::Value* names,
                                policy::PolicyDomain policy_domain) const;
 
   void HandleExportPoliciesJson(const base::ListValue* args);
@@ -89,6 +95,12 @@
   // metadata is sent.
   void SendPolicies();
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // Sets |updater_policies_| in this instance and refreshes the UI via
+  // SendPolicies.
+  void SetUpdaterPolicies(std::unique_ptr<policy::PolicyMap> updater_policies);
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   // Send the status of cloud policy to the UI. For each scope that has cloud
   // policy enabled (device and/or user), a dictionary containing status
   // information is sent.
@@ -111,6 +123,10 @@
   std::unique_ptr<PolicyStatusProvider> device_status_provider_;
   std::unique_ptr<PolicyStatusProvider> machine_status_provider_;
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  std::unique_ptr<policy::PolicyMap> updater_policies_;
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   base::WeakPtrFactory<PolicyUIHandler> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(PolicyUIHandler);
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 414f5fab..e6d80c5 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc
@@ -420,17 +420,26 @@
   RefreshUI();
 }
 
-// |signin::IdentityManager::Observer| overrides. For newly added accounts,
-// |signin::IdentityManager| may take some time to fetch user's full name and
-// account image. Whenever that is completed, we may need to update the UI with
-// this new set of information. Note that we may be listening to
-// |signin::IdentityManager| but we still consider |AccountManager| to be the
-// source of truth for account list.
+// |signin::IdentityManager::Observer| overrides.
+//
+// For newly added accounts, |signin::IdentityManager| may take some time to
+// fetch user's full name and account image. Whenever that is completed, we may
+// need to update the UI with this new set of information. Note that we may be
+// listening to |signin::IdentityManager| but we still consider |AccountManager|
+// to be the source of truth for account list.
 void AccountManagerUIHandler::OnExtendedAccountInfoUpdated(
     const AccountInfo& info) {
   RefreshUI();
 }
 
+void AccountManagerUIHandler::OnErrorStateOfRefreshTokenUpdatedForAccount(
+    const CoreAccountInfo& account_info,
+    const GoogleServiceAuthError& error) {
+  if (error.state() != GoogleServiceAuthError::NONE) {
+    RefreshUI();
+  }
+}
+
 void AccountManagerUIHandler::RefreshUI() {
   FireWebUIListener("accounts-changed");
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h
index 39f24fb..21e8adf 100644
--- a/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h
@@ -44,6 +44,9 @@
 
   // |signin::IdentityManager::Observer| overrides.
   void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
+  void OnErrorStateOfRefreshTokenUpdatedForAccount(
+      const CoreAccountInfo& account_info,
+      const GoogleServiceAuthError& error) override;
 
  private:
   friend class AccountManagerUIHandlerTest;
diff --git a/chrome/browser/xsurface/BUILD.gn b/chrome/browser/xsurface/BUILD.gn
index eedc78cd..25b3341 100644
--- a/chrome/browser/xsurface/BUILD.gn
+++ b/chrome/browser/xsurface/BUILD.gn
@@ -6,7 +6,13 @@
 
 android_library("java") {
   sources = [
-    "android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapter.java",
-    "android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapterFactory.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/HybridListRenderer.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/ListContentManager.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/ListContentManagerObserver.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/SurfaceDependencyProvider.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/SurfaceRenderer.java",
   ]
+  deps =
+      [ "//third_party/android_deps:androidx_recyclerview_recyclerview_java" ]
 }
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/HybridListRenderer.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/HybridListRenderer.java
new file mode 100644
index 0000000..d18571e3
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/HybridListRenderer.java
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * A renderer that can handle mixing externally-provided views with native Android views
+ * in a RecyclerView.
+ */
+public interface HybridListRenderer {
+    /** Binds a RecyclerView and contentmanager with this renderer. */
+    void bind(RecyclerView view, ListContentManager manager);
+
+    /**
+     * Unbinds a previously attached recyclerview and contentmanager.
+     *
+     * Does nothing if nothing was previously bound.
+     */
+    void unbind();
+}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ListContentManager.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ListContentManager.java
new file mode 100644
index 0000000..ebf8cee
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ListContentManager.java
@@ -0,0 +1,49 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Map;
+
+/**
+ * Interface to provide native views to incorporate in an external surface-controlled
+ * RecyclerView.
+ *
+ * Models after a RecyclerView.Adapter.
+ */
+public interface ListContentManager {
+    /** Returns whether the item at index is a native view or not. */
+    boolean isNativeView(int index);
+
+    /** Gets the bytes needed to render an external view. */
+    byte[] getExternalViewBytes(int index);
+
+    /** Returns map of values which should go in the context of an external view. */
+    Map<String, Object> getContextValues(int index);
+
+    /**
+     * Returns the inflated native view.
+     *
+     * View should not be attached to parent. {@link bindNativeView} will
+     * be called later to attach more information to the view.
+     */
+    View getNativeView(int index, ViewGroup parent);
+
+    /**
+     * Binds the data at the specified location.
+     */
+    void bindNativeView(int index, View v);
+
+    /** Returns number of items to show. */
+    int getItemCount();
+
+    /** Adds an observer to be notified when the list content changes. */
+    void addObserver(ListContentManagerObserver o);
+
+    /** Removes the observer so it's no longer notified of content changes. */
+    void removeObserver(ListContentManagerObserver o);
+}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ListContentManagerObserver.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ListContentManagerObserver.java
new file mode 100644
index 0000000..29a50cc
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/ListContentManagerObserver.java
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+/** Interface to observe a list. */
+public interface ListContentManagerObserver {
+    /** Called when range from startIndex to startIndex+count has been inserted. */
+    default void onItemRangeInserted(int startIndex, int count) {}
+
+    /** Called when range from startIndex to startIndex+count has been removed. */
+    default void onItemRangeRemoved(int startIndex, int count) {}
+
+    /** Called when range from startIndex to startIndex+count has been changed/updated. */
+    default void onItemRangeChanged(int startIndex, int count) {}
+
+    /** Called when item at curIndex has been moved to newIndex. */
+    default void onItemMoved(int curIndex, int newIndex) {}
+}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java
new file mode 100644
index 0000000..c8e116f8
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceActionsHandler.java
@@ -0,0 +1,22 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+/**
+ * Interface to provide chromium calling points for an external surface.
+ */
+public interface SurfaceActionsHandler {
+    String KEY = "GeneralActions";
+
+    /**
+     * Navigates the current tab to a particular URL.
+     */
+    void navigateTab(String url);
+
+    /**
+     * Navigates a new tab to a particular URL.
+     */
+    void navigateNewTab(String url);
+}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapter.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapter.java
deleted file mode 100644
index a78ce1004..0000000
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapter.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.xsurface;
-
-import android.view.View;
-
-/**
- * Creates, owns, and manages an exposed view. Can be rebound to a given implementation specific
- * payload.
- */
-public interface SurfaceAdapter {
-    /**
-     * Rebinds the associated view to the given payload.
-     * @param protoPayload The payload that should describe what the view should do.
-     */
-    void bind(byte[] protoPayload);
-
-    /**
-     * Returns the single associated view.
-     */
-    View getView();
-}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapterFactory.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapterFactory.java
deleted file mode 100644
index ba294f51..0000000
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceAdapterFactory.java
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.xsurface;
-
-import android.content.Context;
-
-/**
- * Creates SurfaceAdapters on demand.
- */
-public interface SurfaceAdapterFactory {
-    /**
-     * Creates a new adapter backed by a shared set of dependencies with other adapters.
-     * @param context The context that any new Android UI objects should be created within.
-     * @return A new wrapper capable of making view objects.
-     */
-    SurfaceAdapter createSurfaceAdapter(Context context);
-}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceDependencyProvider.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceDependencyProvider.java
new file mode 100644
index 0000000..8d9abf6
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceDependencyProvider.java
@@ -0,0 +1,21 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+import android.content.Context;
+
+/**
+ * Provides logging and context for an external surface.
+ */
+public interface SurfaceDependencyProvider {
+    /** @return the context associated with the application. */
+    Context getContext();
+
+    /** @see {Log.e} */
+    void logError(String tag, String messageTemplate, Object... args);
+
+    /** @see {Log.w} */
+    void logWarning(String tag, String messageTemplate, Object... args);
+}
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceRenderer.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceRenderer.java
new file mode 100644
index 0000000..af50982
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceRenderer.java
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+import android.view.View;
+
+import java.util.Map;
+
+/**
+ * Interface to call a rendering service to render a View sent by a server.
+ */
+public interface SurfaceRenderer {
+    /** Update the card renderer with shared data bytes. */
+    void update(byte[] data);
+
+    /**
+     * Turns a stream of externally-provided bytes into an Android View.
+     *
+     * @param renderData externally-provided bytes to be rendered.
+     * @param contextValues additional context to be incorporated into the view.
+     */
+    View render(byte[] renderData, Map<String, Object> contextValues);
+}
diff --git a/chrome/common/conflicts/module_watcher_win.cc b/chrome/common/conflicts/module_watcher_win.cc
index 994b1056..730e122 100644
--- a/chrome/common/conflicts/module_watcher_win.cc
+++ b/chrome/common/conflicts/module_watcher_win.cc
@@ -12,15 +12,18 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/lazy_instance.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
+#include "base/rand_util.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
+#include "base/threading/platform_thread.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/win/scoped_handle.h"
 
@@ -111,6 +114,11 @@
 constexpr char kLdrRegisterDllNotification[] = "LdrRegisterDllNotification";
 constexpr char kLdrUnregisterDllNotification[] = "LdrUnregisterDllNotification";
 
+// It is currently estimated that around 10 DLLs are loaded in a background
+// sequence in Chrome. This number was chosen so that on average 1% of launches
+// will cause a process dump.
+constexpr int kMaxBackgroundLoadedDllCount = 1000;
+
 // Helper function for converting a UNICODE_STRING to a FilePath.
 base::FilePath ToFilePath(const UNICODE_STRING* str) {
   return base::FilePath(
@@ -131,13 +139,15 @@
 
 // static
 std::unique_ptr<ModuleWatcher> ModuleWatcher::Create(
-    OnModuleEventCallback callback) {
+    OnModuleEventCallback callback,
+    bool report_background_loaded_modules) {
   {
     base::AutoLock lock(g_module_watcher_lock.Get());
     // If a ModuleWatcher already exists then bail out.
     if (g_module_watcher_instance)
       return nullptr;
-    g_module_watcher_instance = new ModuleWatcher();
+    g_module_watcher_instance =
+        new ModuleWatcher(report_background_loaded_modules);
   }
 
   // Initialization mustn't occur while holding |g_module_watcher_lock|.
@@ -146,6 +156,8 @@
 }
 
 ModuleWatcher::~ModuleWatcher() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // Done before acquiring |g_module_watcher_lock|.
   UnregisterDllNotificationCallback();
 
@@ -156,10 +168,17 @@
   g_module_watcher_instance = nullptr;
 }
 
-ModuleWatcher::ModuleWatcher() {}
+ModuleWatcher::ModuleWatcher(bool report_background_loaded_modules)
+    : report_background_loaded_modules_(report_background_loaded_modules),
+      background_loaded_dll_count_(0),
+      num_background_loaded_dll_report_(
+          base::RandInt(0, kMaxBackgroundLoadedDllCount)),
+      weak_ptr_factory_(this) {}
 
 // Initializes the ModuleWatcher instance.
 void ModuleWatcher::Initialize(OnModuleEventCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   callback_ = std::move(callback);
   RegisterDllNotificationCallback();
 
@@ -226,13 +245,18 @@
   }
 }
 
-// static
-ModuleWatcher::OnModuleEventCallback ModuleWatcher::GetCallbackForContext(
-    void* context) {
-  base::AutoLock lock(g_module_watcher_lock.Get());
-  if (context != g_module_watcher_instance)
-    return OnModuleEventCallback();
-  return g_module_watcher_instance->callback_;
+void ModuleWatcher::DumpOnBackgroundLoadedModule() {
+  if (!report_background_loaded_modules_ ||
+      base::PlatformThread::GetCurrentThreadPriority() !=
+          base::ThreadPriority::BACKGROUND) {
+    return;
+  }
+
+  if (background_loaded_dll_count_++ == num_background_loaded_dll_report_) {
+    // DLL loaded on a thread with background priority. This can cause jank on
+    // the UI thread if it tries to acquire the loader lock.
+    base::debug::DumpWithoutCrashing();
+  }
 }
 
 // static
@@ -240,7 +264,16 @@
     unsigned long notification_reason,
     const LDR_DLL_NOTIFICATION_DATA* notification_data,
     void* context) {
-  auto callback = GetCallbackForContext(context);
+  OnModuleEventCallback callback;
+  {
+    base::AutoLock lock(g_module_watcher_lock.Get());
+    if (context == g_module_watcher_instance) {
+      callback = g_module_watcher_instance->callback_;
+
+      g_module_watcher_instance->DumpOnBackgroundLoadedModule();
+    }
+  }
+
   if (!callback)
     return;
 
@@ -262,5 +295,7 @@
 }
 
 void ModuleWatcher::RunCallback(const ModuleEvent& event) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   callback_.Run(event);
 }
diff --git a/chrome/common/conflicts/module_watcher_win.h b/chrome/common/conflicts/module_watcher_win.h
index b050fdf..c2fff5a 100644
--- a/chrome/common/conflicts/module_watcher_win.h
+++ b/chrome/common/conflicts/module_watcher_win.h
@@ -11,6 +11,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
 
 class ModuleWatcherTest;
 
@@ -89,7 +90,9 @@
   //
   // Only a single instance of a watcher may exist at any moment. This will
   // return nullptr when trying to create a second watcher.
-  static std::unique_ptr<ModuleWatcher> Create(OnModuleEventCallback callback);
+  static std::unique_ptr<ModuleWatcher> Create(
+      OnModuleEventCallback callback,
+      bool report_background_loaded_modules);
 
   // This can be called on any thread. After destruction the |callback|
   // provided to the constructor will no longer be invoked with module events.
@@ -100,7 +103,7 @@
   friend class ModuleWatcherTest;
 
   // Private to enforce Singleton semantics. See Create above.
-  ModuleWatcher();
+  explicit ModuleWatcher(bool report_background_loaded_modules);
 
   // Initializes the ModuleWatcher instance.
   void Initialize(OnModuleEventCallback callback);
@@ -119,9 +122,9 @@
       scoped_refptr<base::SequencedTaskRunner> task_runner,
       OnModuleEventCallback callback);
 
-  // Helper function for retrieving the callback associated with a given
-  // LdrNotification context.
-  static OnModuleEventCallback GetCallbackForContext(void* context);
+  // Dumps the process if executed in a background sequence and
+  // |report_background_loaded_modules_| is true.
+  void DumpOnBackgroundLoadedModule();
 
   // The loader notification callback. This is actually
   // void CALLBACK LoaderNotificationCallback(
@@ -141,6 +144,17 @@
   // Used by the DllNotification mechanism.
   void* dll_notification_cookie_ = nullptr;
 
+  // Indicates if modules loaded in a background sequence should be reported.
+  const bool report_background_loaded_modules_;
+
+  // The count of DLL that were loaded in a background sequence.
+  int background_loaded_dll_count_;
+
+  // The number of background loaded DLL that will cause a process dump.
+  const int num_background_loaded_dll_report_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
   base::WeakPtrFactory<ModuleWatcher> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ModuleWatcher);
diff --git a/chrome/common/conflicts/module_watcher_win_unittest.cc b/chrome/common/conflicts/module_watcher_win_unittest.cc
index 8de1eb13..a5f13527 100644
--- a/chrome/common/conflicts/module_watcher_win_unittest.cc
+++ b/chrome/common/conflicts/module_watcher_win_unittest.cc
@@ -59,7 +59,8 @@
 
   std::unique_ptr<ModuleWatcher> Create() {
     return ModuleWatcher::Create(
-        base::Bind(&ModuleWatcherTest::OnModuleEvent, base::Unretained(this)));
+        base::Bind(&ModuleWatcherTest::OnModuleEvent, base::Unretained(this)),
+        /* report_background_loaded_modules = */ false);
   }
 
   base::test::TaskEnvironment task_environment_;
diff --git a/chrome/common/conflicts/remote_module_watcher_win.cc b/chrome/common/conflicts/remote_module_watcher_win.cc
index 0a28e9e..0161f03 100644
--- a/chrome/common/conflicts/remote_module_watcher_win.cc
+++ b/chrome/common/conflicts/remote_module_watcher_win.cc
@@ -58,10 +58,12 @@
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
   module_event_sink_.Bind(std::move(remote_sink));
-  module_watcher_ = ModuleWatcher::Create(base::BindRepeating(
-      &OnModuleEvent, task_runner_,
-      base::BindRepeating(&RemoteModuleWatcher::HandleModuleEvent,
-                          weak_ptr_factory_.GetWeakPtr())));
+  module_watcher_ = ModuleWatcher::Create(
+      base::BindRepeating(
+          &OnModuleEvent, task_runner_,
+          base::BindRepeating(&RemoteModuleWatcher::HandleModuleEvent,
+                              weak_ptr_factory_.GetWeakPtr())),
+      /*report_background_loaded_modules=*/false);
 }
 
 void RemoteModuleWatcher::HandleModuleEvent(
diff --git a/chrome/common/mac/mock_launchd.mm b/chrome/common/mac/mock_launchd.mm
index ac3b913b..1cc1b34 100644
--- a/chrome/common/mac/mock_launchd.mm
+++ b/chrome/common/mac/mock_launchd.mm
@@ -108,8 +108,7 @@
 bool MockLaunchd::GetJobInfo(const std::string& label,
                              mac::services::JobInfo* info) {
   if (!as_service_) {
-    std::unique_ptr<MultiProcessLock> running_lock(
-        TakeNamedLock(pipe_name_, false));
+    std::unique_ptr<MultiProcessLock> running_lock = TakeNamedLock(pipe_name_);
     if (running_lock.get())
       return false;
   }
@@ -164,5 +163,5 @@
 
 void MockLaunchd::SignalReady() {
   ASSERT_TRUE(as_service_);
-  running_lock_.reset(TakeNamedLock(pipe_name_, true));
+  running_lock_ = TakeNamedLock(pipe_name_);
 }
diff --git a/chrome/common/service_process_util.h b/chrome/common/service_process_util.h
index 5f314bc..f311dc8c 100644
--- a/chrome/common/service_process_util.h
+++ b/chrome/common/service_process_util.h
@@ -21,14 +21,6 @@
 
 class MultiProcessLock;
 
-#if defined(OS_MACOSX)
-#ifdef __OBJC__
-@class NSString;
-#else
-class NSString;
-#endif
-#endif
-
 namespace base {
 class CommandLine;
 }
@@ -47,10 +39,9 @@
 #endif  // !OS_MACOSX
 
 #if defined(OS_POSIX)
-// Attempts to take a lock named |name|. If |waiting| is true then this will
-// make multiple attempts to acquire the lock.
-// Caller is responsible for ownership of the MultiProcessLock.
-MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting);
+// Attempts to take a session-wide lock named name. Returns a non-null lock if
+// successful.
+std::unique_ptr<MultiProcessLock> TakeNamedLock(const std::string& name);
 #endif
 
 // The following method is used in a process that acts as a client to the
diff --git a/chrome/common/service_process_util_linux.cc b/chrome/common/service_process_util_linux.cc
index cb75865..976813a 100644
--- a/chrome/common/service_process_util_linux.cc
+++ b/chrome/common/service_process_util_linux.cc
@@ -20,12 +20,6 @@
 
 namespace {
 
-MultiProcessLock* TakeServiceInitializingLock(bool waiting) {
-  std::string lock_name =
-      GetServiceProcessScopedName("_service_initializing");
-  return TakeNamedLock(lock_name, waiting);
-}
-
 std::string GetBaseDesktopName() {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   return "google-chrome-service.desktop";
@@ -35,10 +29,10 @@
 }
 }  // namespace
 
-MultiProcessLock* TakeServiceRunningLock(bool waiting) {
+std::unique_ptr<MultiProcessLock> TakeServiceRunningLock() {
   std::string lock_name =
       GetServiceProcessScopedName("_service_running");
-  return TakeNamedLock(lock_name, waiting);
+  return TakeNamedLock(lock_name);
 }
 
 bool ForceServiceProcessShutdown(const std::string& version,
@@ -60,12 +54,13 @@
 }
 
 bool CheckServiceProcessReady() {
-  std::unique_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false));
-  return running_lock.get() == NULL;
+  std::unique_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock());
+  return !running_lock.get();
 }
 
 bool ServiceProcessState::TakeSingletonLock() {
-  state_->initializing_lock.reset(TakeServiceInitializingLock(true));
+  state_->initializing_lock =
+      TakeNamedLock(GetServiceProcessScopedName("_service_initializing"));
   return state_->initializing_lock.get();
 }
 
diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc
index 38d3e38..848ef55 100644
--- a/chrome/common/service_process_util_posix.cc
+++ b/chrome/common/service_process_util_posix.cc
@@ -172,25 +172,13 @@
 
 #endif  // !defined(OS_MACOSX)
 
-// Attempts to take a lock named |name|. If |waiting| is true then this will
-// make multiple attempts to acquire the lock.
-// Caller is responsible for ownership of the MultiProcessLock.
-MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) {
+// Attempts to take a lock named |name|. Returns the lock if successful, or
+// nullptr if not.
+std::unique_ptr<MultiProcessLock> TakeNamedLock(const std::string& name) {
   std::unique_ptr<MultiProcessLock> lock = MultiProcessLock::Create(name);
-  if (lock == NULL) return NULL;
-  bool got_lock = false;
-  for (int i = 0; i < 10; ++i) {
-    if (lock->TryLock()) {
-      got_lock = true;
-      break;
-    }
-    if (!waiting) break;
-    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100 * i));
-  }
-  if (!got_lock) {
+  if (!lock->TryLock())
     lock.reset();
-  }
-  return lock.release();
+  return lock;
 }
 
 ServiceProcessTerminateMonitor::ServiceProcessTerminateMonitor(
@@ -324,8 +312,8 @@
   DCHECK(state_);
 
 #if !defined(OS_MACOSX)
-  state_->running_lock.reset(TakeServiceRunningLock(true));
-  if (state_->running_lock.get() == NULL) {
+  state_->running_lock = TakeServiceRunningLock();
+  if (!state_->running_lock.get()) {
     return false;
   }
 #endif
diff --git a/chrome/common/service_process_util_posix.h b/chrome/common/service_process_util_posix.h
index bf56e07..dd34e20 100644
--- a/chrome/common/service_process_util_posix.h
+++ b/chrome/common/service_process_util_posix.h
@@ -19,7 +19,7 @@
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 #include "chrome/common/multi_process_lock.h"
-MultiProcessLock* TakeServiceRunningLock(bool waiting);
+std::unique_ptr<MultiProcessLock> TakeServiceRunningLock();
 #endif
 
 #if defined(OS_MACOSX)
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index e2edf859..f561ca8 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -254,8 +254,11 @@
   // Login via existing AD account mapping when the device is domain joined if
   // the AD account mapping is available.
   if (is_ad_user) {
-    // The format for ad_upn custom attribute is domainName/userName.
-    const base::char16 kSlashDelimiter[] = STRING16_LITERAL("/");
+    // The format for ad_upn custom attribute is domainName\\userName.
+    // Note that admin configures it as "domainName\userName" but admin
+    // sdk stores it with another escape backslash character in it leading
+    // multiple backslashes.
+    const base::char16 kSlashDelimiter[] = STRING16_LITERAL("\\");
     std::vector<base::string16> tokens =
         base::SplitString(base::UTF8ToUTF16(sam_account_name), kSlashDelimiter,
                           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
index bc82039..352aebd 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -1434,7 +1434,7 @@
   // Invalid configuration in admin sdk. Don't set the username.
   std::string admin_sdk_response = base::StringPrintf(
       "{\"customSchemas\": {\"Enhanced_desktop_security\": {\"AD_accounts\":"
-      " \"%ls/\"}}}",
+      " \"%ls\\\\\"}}}",
       domain_name);
   fake_http_url_fetcher_factory()->SetFakeResponse(
       GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(),
@@ -1489,7 +1489,7 @@
   // Set valid response from admin sdk.
   std::string admin_sdk_response = base::StringPrintf(
       "{\"customSchemas\": {\"Enhanced_desktop_security\": {\"AD_accounts\":"
-      " \"%ls/%ls\"}}}",
+      " \"%ls\\\\%ls\"}}}",
       domain_name, user_name);
   fake_http_url_fetcher_factory()->SetFakeResponse(
       GURL(get_cd_user_url_.c_str()), FakeWinHttpUrlFetcher::Headers(),
diff --git a/chrome/credential_provider/gaiacp/reauth_credential.cc b/chrome/credential_provider/gaiacp/reauth_credential.cc
index f1c53cc..4823289 100644
--- a/chrome/credential_provider/gaiacp/reauth_credential.cc
+++ b/chrome/credential_provider/gaiacp/reauth_credential.cc
@@ -18,7 +18,7 @@
 
 // TODO(rakeshsoma): Change the path from "setup" to "reauth" once
 // identity team has the URL ready for consumption.
-constexpr char kGaiaReauthPath[] = "embedded/setup/windows";
+constexpr char kGaiaReauthPath[] = "embedded/reauth/windows";
 
 CReauthCredential::CReauthCredential() = default;
 
diff --git a/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc b/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
index 692815d3..5646a86 100644
--- a/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
+++ b/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
@@ -368,7 +368,7 @@
     ASSERT_TRUE(gcpw_path.empty());
   } else if (is_gem_features_enabled) {
     ASSERT_EQ(gcpw_path,
-              base::StringPrintf("embedded/setup/windows?device_id=%s",
+              base::StringPrintf("embedded/reauth/windows?device_id=%s",
                                  device_id.c_str()));
     ASSERT_TRUE(command_line.GetSwitchValueASCII(switches::kGaiaUrl).empty());
   } else {
diff --git a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
index 6e8953f9..a8ce2e02 100644
--- a/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_autofill_agent_browsertest.cc
@@ -1048,16 +1048,17 @@
   CheckTextFieldsSuggestedState(std::string(), false, std::string(), false);
 }
 
-// Tests that having a matching username does not preclude the autocomplete.
+// Tests that having a matching username precludes the autofill.
 TEST_F(PasswordAutofillAgentTest, InitialAutocompleteForMatchingFilledField) {
   username_element_.SetValue(WebString::FromUTF8(kAliceUsername));
 
   // Simulate the browser sending back the login info, it triggers the
-  // autocomplete.
+  // autofill.
   SimulateOnFillPasswordForm(fill_data_);
 
-  // The username and password should have been autocompleted.
-  CheckUsernameDOMStatePasswordSuggestedState(kAliceUsername, true,
+  // The password should have been autofilled, but the username field should
+  // have been left alone, since it contained the correct value already.
+  CheckUsernameDOMStatePasswordSuggestedState(kAliceUsername, false,
                                               kAlicePassword, true);
 
   CheckFirstFillingResult(FillingResult::kSuccess);
@@ -2680,16 +2681,15 @@
   CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
 }
 
-// Tests that both the username and the password fields are autocompleted
-// despite the empty username, when the browser sends back data more than one
-// credential.
+// Tests that the username field is not marked as autofilled when fill data has
+// the empty username.
 TEST_F(PasswordAutofillAgentTest,
        AutofillNoUsernameWhenOtherCredentialsStored) {
   fill_data_.username_field.value.clear();
   ASSERT_FALSE(fill_data_.additional_logins.empty());
   SimulateOnFillPasswordForm(fill_data_);
 
-  CheckTextFieldsSuggestedState("", true, kAlicePassword, true);
+  CheckTextFieldsSuggestedState("", false, kAlicePassword, true);
 }
 
 TEST_F(PasswordAutofillAgentTest, NoForm_PromptForAJAXSubmitWithoutNavigation) {
diff --git a/chrome/renderer/chrome_render_frame_observer_browsertest.cc b/chrome/renderer/chrome_render_frame_observer_browsertest.cc
index cef2785..de7a7e7 100644
--- a/chrome/renderer/chrome_render_frame_observer_browsertest.cc
+++ b/chrome/renderer/chrome_render_frame_observer_browsertest.cc
@@ -19,8 +19,8 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_view.h"
-#include "third_party/blink/public/web/web_widget.h"
 
 namespace {
 
diff --git a/chrome/service/service_main.cc b/chrome/service/service_main.cc
index 400adc7..cbea1fe 100644
--- a/chrome/service/service_main.cc
+++ b/chrome/service/service_main.cc
@@ -38,9 +38,21 @@
   VLOG(1) << "Service process launched: "
           << parameters.command_line.GetCommandLineString();
 
-  // If there is already a service process running, quit now.
-  std::unique_ptr<ServiceProcessState> state(new ServiceProcessState);
-  if (!state->Initialize())
+  auto initialize_service = [](ServiceProcessState* service) {
+    for (int i = 0; i < 10; ++i) {
+      if (service->Initialize())
+        return true;
+      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(i * 100));
+    }
+    return false;
+  };
+
+  // If there is already a service process running, quit now. Retry a few times
+  // in case the running service is busy exiting.
+  // TODO(ellyjones): Are these retries actually necessary / can this case
+  // happen in practice?
+  auto state = std::make_unique<ServiceProcessState>();
+  if (!initialize_service(state.get()))
     return 0;
 
   base::RunLoop run_loop;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4aafeb1..cb260b37 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1179,8 +1179,6 @@
       "../browser/sessions/session_restore_observer_browsertest.cc",
       "../browser/sessions/tab_restore_browsertest.cc",
       "../browser/sessions/tab_restore_service_browsertest.cc",
-      "../browser/sessions/tab_restore_service_load_waiter.cc",
-      "../browser/sessions/tab_restore_service_load_waiter.h",
       "../browser/signin/e2e_tests/live_sign_in_test.cc",
       "../browser/signin/e2e_tests/live_test.cc",
       "../browser/signin/e2e_tests/live_test.h",
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index 31fc6b0..9cb820c 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -79,4 +79,5 @@
   "+mojo/core/embedder",
   "+sandbox/win/tests",
   "+third_party/webrtc",
+  "+third_party/re2",
 ]
diff --git a/chrome/test/data/webui/cr_elements/cr_button_tests.js b/chrome/test/data/webui/cr_elements/cr_button_tests.js
index 7da05a6..51acf8c 100644
--- a/chrome/test/data/webui/cr_elements/cr_button_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_button_tests.js
@@ -66,9 +66,21 @@
   });
 
   test('when tabindex is -1, it stays -1', async () => {
-    document.body.innerHTML = '<cr-button tabindex="-1"></cr-button>';
+    document.body.innerHTML = '<cr-button custom-tab-index="-1"></cr-button>';
     button = document.body.querySelector('cr-button');
     assertEquals('-1', button.getAttribute('tabindex'));
+    button.disabled = true;
+    assertEquals('-1', button.getAttribute('tabindex'));
+    button.disabled = false;
+    assertEquals('-1', button.getAttribute('tabindex'));
+  });
+
+  test('tabindex update', async () => {
+    document.body.innerHTML = '<cr-button></cr-button>';
+    button = document.body.querySelector('cr-button');
+    assertEquals('0', button.getAttribute('tabindex'));
+    button.customTabIndex = 1;
+    assertEquals('1', button.getAttribute('tabindex'));
   });
 
   test('hidden', () => {
diff --git a/chrome/test/data/webui/cr_elements/cr_icon_button_tests.js b/chrome/test/data/webui/cr_elements/cr_icon_button_tests.js
index 2830b2e..d12b6cf6 100644
--- a/chrome/test/data/webui/cr_elements/cr_icon_button_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_icon_button_tests.js
@@ -79,10 +79,23 @@
   });
 
   test('when tabindex is -1, it stays -1', async () => {
-    document.body.innerHTML = '<cr-icon-button tabindex="-1"></cr-icon-button>';
+    document.body.innerHTML =
+        '<cr-icon-button custom-tab-index="-1"></cr-icon-button>';
     await test_util.flushTasks();
     button = document.body.querySelector('cr-icon-button');
     assertEquals('-1', button.getAttribute('tabindex'));
+    button.disabled = true;
+    assertEquals('-1', button.getAttribute('tabindex'));
+    button.disabled = false;
+    assertEquals('-1', button.getAttribute('tabindex'));
+  });
+
+  test('tabindex update', async () => {
+    document.body.innerHTML = '<cr-icon-button></cr-icon-button>';
+    button = document.body.querySelector('cr-icon-button');
+    assertEquals('0', button.getAttribute('tabindex'));
+    button.customTabIndex = 1;
+    assertEquals('1', button.getAttribute('tabindex'));
   });
 
   test('ripple is a circle with background icon or single iron-icon', () => {
diff --git a/chrome/test/payments/payment_request_platform_browsertest_base.cc b/chrome/test/payments/payment_request_platform_browsertest_base.cc
index 3e582eac..6c5bed7 100644
--- a/chrome/test/payments/payment_request_platform_browsertest_base.cc
+++ b/chrome/test/payments/payment_request_platform_browsertest_base.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
+#include "third_party/re2/src/re2/re2.h"
 
 namespace payments {
 
@@ -170,9 +171,32 @@
   return card;
 }
 
+autofill::CreditCard
+PaymentRequestPlatformBrowserTestBase::CreatCreditCardForProfile(
+    const autofill::AutofillProfile& profile) {
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(profile.guid());
+  return card;
+}
+
 void PaymentRequestPlatformBrowserTestBase::AddCreditCard(
     const autofill::CreditCard& card) {
   test::AddCreditCard(GetActiveWebContents()->GetBrowserContext(), card);
 }
 
+std::string PaymentRequestPlatformBrowserTestBase::ClearPortNumber(
+    const std::string& may_contain_method_url) {
+  std::string before;
+  std::string method;
+  std::string after;
+  GURL::Replacements port;
+  port.ClearPort();
+  return re2::RE2::FullMatch(
+             may_contain_method_url,
+             "(.*\"supportedMethods\":\")(https://.*)(\",\"total\".*)", &before,
+             &method, &after)
+             ? before + GURL(method).ReplaceComponents(port).spec() + after
+             : may_contain_method_url;
+}
+
 }  // namespace payments
diff --git a/chrome/test/payments/payment_request_platform_browsertest_base.h b/chrome/test/payments/payment_request_platform_browsertest_base.h
index 5f403e1..b697882 100644
--- a/chrome/test/payments/payment_request_platform_browsertest_base.h
+++ b/chrome/test/payments/payment_request_platform_browsertest_base.h
@@ -84,6 +84,11 @@
   autofill::CreditCard CreateAndAddCreditCardForProfile(
       const autofill::AutofillProfile& profile);
   void AddCreditCard(const autofill::CreditCard& card);
+  autofill::CreditCard CreatCreditCardForProfile(
+      const autofill::AutofillProfile& profile);
+
+  // Looks for the "supportedMethods" URL and removes its port number.
+  std::string ClearPortNumber(const std::string& may_contain_method_url);
 
   net::EmbeddedTestServer* https_server() { return https_server_.get(); }
   PaymentRequestTestController* test_controller() { return &test_controller_; }
diff --git a/chrome/test/payments/payment_request_test_controller.h b/chrome/test/payments/payment_request_test_controller.h
index 653e4f34..83fd735 100644
--- a/chrome/test/payments/payment_request_test_controller.h
+++ b/chrome/test/payments/payment_request_test_controller.h
@@ -13,11 +13,11 @@
 namespace sync_preferences {
 class TestingPrefServiceSyncable;
 }
-#else
+#endif
+
 namespace content {
 class WebContents;
 }
-#endif
 
 namespace payments {
 
@@ -56,13 +56,14 @@
   void SetIncognito(bool is_incognito);
   void SetValidSsl(bool valid_ssl);
   void SetCanMakePaymentEnabledPref(bool can_make_payment_enabled);
-#if defined(OS_ANDROID)
-  // Get the WebContents of the Expandable Payment Handler for testing purpose,
-  // or null if nonexistent. To guarantee a non-null return, this function
-  // should be called only if: 1) PaymentRequest UI is opening. 2)
-  // ScrollToExpandPaymentHandler feature is enabled. 3) PaymentHandler is
-  // opening.
+
+  // Get the WebContents of the Payment Handler for testing purpose, or null if
+  // nonexistent. To guarantee a non-null return, this function should be called
+  // only if: 1) PaymentRequest UI is opening. 2) ScrollToExpandPaymentHandler
+  // feature is enabled (on Android). 3) PaymentHandler is opening.
   content::WebContents* GetPaymentHandlerWebContents();
+
+#if defined(OS_ANDROID)
   // Click the security icon on the Expandable Payment Handler toolbar for
   // testing purpose. return whether it's succeeded.
   bool ClickPaymentHandlerSecurityIcon();
diff --git a/chrome/test/payments/payment_request_test_controller_android.cc b/chrome/test/payments/payment_request_test_controller_android.cc
index 083a922..2cc855a 100644
--- a/chrome/test/payments/payment_request_test_controller_android.cc
+++ b/chrome/test/payments/payment_request_test_controller_android.cc
@@ -10,7 +10,7 @@
 
 namespace payments {
 
-PaymentRequestTestController::PaymentRequestTestController() {}
+PaymentRequestTestController::PaymentRequestTestController() = default;
 
 PaymentRequestTestController::~PaymentRequestTestController() = default;
 
diff --git a/chrome/test/payments/payment_request_test_controller_desktop.cc b/chrome/test/payments/payment_request_test_controller_desktop.cc
index cb70f08..cfdf351 100644
--- a/chrome/test/payments/payment_request_test_controller_desktop.cc
+++ b/chrome/test/payments/payment_request_test_controller_desktop.cc
@@ -76,6 +76,12 @@
 
 PaymentRequestTestController::~PaymentRequestTestController() = default;
 
+content::WebContents*
+PaymentRequestTestController::GetPaymentHandlerWebContents() {
+  // Todo(1053722): return the invoked payment app's web contents for testing.
+  return nullptr;
+}
+
 void PaymentRequestTestController::SetUpOnMainThread() {
   // Register all prefs with our pref testing service, since we're not using the
   // one chrome sets up.
diff --git a/chromecast/media/cma/backend/mixer/filter_group.cc b/chromecast/media/cma/backend/mixer/filter_group.cc
index 72c18ec..78d3ad12 100644
--- a/chromecast/media/cma/backend/mixer/filter_group.cc
+++ b/chromecast/media/cma/backend/mixer/filter_group.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/memory/ptr_util.h"
+#include "base/numerics/ranges.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "chromecast/media/audio/interleaved_channel_mixer.h"
@@ -20,6 +21,32 @@
 namespace chromecast {
 namespace media {
 
+namespace {
+
+bool ParseVolumeLimit(const base::Value* dict, float* min, float* max) {
+  if (!dict->is_dict()) {
+    return false;
+  }
+  auto min_value = dict->FindDoubleKey("min");
+  auto max_value = dict->FindDoubleKey("max");
+  if (!min_value && !max_value) {
+    return false;
+  }
+  *min = 0.0f;
+  *max = 1.0f;
+  if (min_value) {
+    *min =
+        base::ClampToRange(static_cast<float>(min_value.value()), 0.0f, 1.0f);
+  }
+  if (max_value) {
+    *max =
+        base::ClampToRange(static_cast<float>(max_value.value()), *min, 1.0f);
+  }
+  return true;
+}
+
+}  // namespace
+
 FilterGroup::GroupInput::GroupInput(
     FilterGroup* group,
     std::unique_ptr<InterleavedChannelMixer> channel_mixer)
@@ -30,10 +57,13 @@
 
 FilterGroup::FilterGroup(int num_channels,
                          const std::string& name,
-                         std::unique_ptr<PostProcessingPipeline> pipeline)
+                         std::unique_ptr<PostProcessingPipeline> pipeline,
+                         const base::Value* volume_limits)
     : num_channels_(num_channels),
       name_(name),
-      post_processing_pipeline_(std::move(pipeline)) {}
+      post_processing_pipeline_(std::move(pipeline)) {
+  ParseVolumeLimits(volume_limits);
+}
 
 FilterGroup::~FilterGroup() = default;
 
@@ -84,8 +114,39 @@
       true /* is_silence */);
 }
 
+void FilterGroup::ParseVolumeLimits(const base::Value* volume_limits) {
+  if (!volume_limits) {
+    return;
+  }
+
+  DCHECK(volume_limits->is_dict());
+  // Get default limits.
+  if (ParseVolumeLimit(volume_limits, &default_volume_min_,
+                       &default_volume_max_)) {
+    LOG(INFO) << "Default volume limits for '" << name_ << "' group: ["
+              << default_volume_min_ << ", " << default_volume_max_ << "]";
+  }
+
+  float min, max;
+  for (const auto& item : volume_limits->DictItems()) {
+    if (ParseVolumeLimit(&item.second, &min, &max)) {
+      LOG(INFO) << "Volume limits for device ID '" << item.first << "' = ["
+                << min << ", " << max << "]";
+      volume_limits_.insert({item.first, {min, max}});
+    }
+  }
+}
+
 void FilterGroup::AddInput(MixerInput* input) {
   active_inputs_.insert(input);
+
+  auto it = volume_limits_.find(input->device_id());
+  if (it != volume_limits_.end()) {
+    input->SetVolumeLimits(it->second.first, it->second.second);
+    return;
+  }
+
+  input->SetVolumeLimits(default_volume_min_, default_volume_max_);
 }
 
 void FilterGroup::RemoveInput(MixerInput* input) {
diff --git a/chromecast/media/cma/backend/mixer/filter_group.h b/chromecast/media/cma/backend/mixer/filter_group.h
index 2009bd6..d6c196e 100644
--- a/chromecast/media/cma/backend/mixer/filter_group.h
+++ b/chromecast/media/cma/backend/mixer/filter_group.h
@@ -9,8 +9,10 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/values.h"
@@ -43,7 +45,8 @@
   // |pipeline| - processing pipeline.
   FilterGroup(int num_channels,
               const std::string& name,
-              std::unique_ptr<PostProcessingPipeline> pipeline);
+              std::unique_ptr<PostProcessingPipeline> pipeline,
+              const base::Value* volume_limits);
 
   ~FilterGroup();
 
@@ -118,6 +121,8 @@
   void AddStreamType(const std::string& stream_type);
 
  private:
+  using VolumeLimitsMap = base::flat_map<std::string, std::pair<float, float>>;
+
   struct GroupInput {
     GroupInput(FilterGroup* group,
                std::unique_ptr<InterleavedChannelMixer> channel_mixer);
@@ -128,10 +133,16 @@
     std::unique_ptr<InterleavedChannelMixer> channel_mixer;
   };
 
+  void ParseVolumeLimits(const base::Value* volume_limits);
   void ResizeBuffers();
 
   const int num_channels_;
   const std::string name_;
+
+  VolumeLimitsMap volume_limits_;
+  float default_volume_min_ = 0.0f;
+  float default_volume_max_ = 1.0f;
+
   std::vector<GroupInput> mixed_inputs_;
   std::vector<std::string> stream_types_;
   base::flat_set<MixerInput*> active_inputs_;
diff --git a/chromecast/media/cma/backend/mixer/filter_group_unittest.cc b/chromecast/media/cma/backend/mixer/filter_group_unittest.cc
index fb9daf4..f470171 100644
--- a/chromecast/media/cma/backend/mixer/filter_group_unittest.cc
+++ b/chromecast/media/cma/backend/mixer/filter_group_unittest.cc
@@ -172,7 +172,7 @@
     EXPECT_CALL(*post_processor_, SetContentType(kDefaultContentType));
     EXPECT_CALL(*post_processor_, UpdatePlayoutChannel(kDefaultPlayoutChannel));
     filter_group_ = std::make_unique<FilterGroup>(
-        kNumInputChannels, "test_filter", std::move(post_processor));
+        kNumInputChannels, "test_filter", std::move(post_processor), nullptr);
     input_ = std::make_unique<MixerInput>(&source_, filter_group_.get());
     AudioPostProcessor2::Config config;
     config.output_sample_rate = kInputSampleRate;
diff --git a/chromecast/media/cma/backend/mixer/mixer_input.cc b/chromecast/media/cma/backend/mixer/mixer_input.cc
index 1f1f9c5c..db742d4 100644
--- a/chromecast/media/cma/backend/mixer/mixer_input.cc
+++ b/chromecast/media/cma/backend/mixer/mixer_input.cc
@@ -47,9 +47,6 @@
       primary_(source->primary()),
       device_id_(source->device_id()),
       content_type_(source->content_type()),
-      stream_volume_multiplier_(1.0f),
-      type_volume_multiplier_(1.0f),
-      mute_volume_multiplier_(1.0f),
       slew_volume_(kDefaultSlewTimeMs),
       volume_applied_(false),
       previous_ended_in_silence_(false),
@@ -297,54 +294,94 @@
 
 void MixerInput::SetVolumeMultiplier(float multiplier) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  float old_target_volume = TargetVolume();
   stream_volume_multiplier_ = std::max(0.0f, multiplier);
   float target_volume = TargetVolume();
   LOG(INFO) << device_id_ << "(" << source_
             << "): stream volume = " << stream_volume_multiplier_
             << ", effective multiplier = " << target_volume;
-  slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
-  slew_volume_.SetVolume(target_volume);
+  if (target_volume != old_target_volume) {
+    slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
+    slew_volume_.SetVolume(target_volume);
+  }
 }
 
-void MixerInput::SetContentTypeVolume(float volume, int fade_ms) {
+void MixerInput::SetContentTypeVolume(float volume) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(content_type_ != AudioContentType::kOther);
 
-  type_volume_multiplier_ = base::ClampToRange(volume, 0.0f, 1.0f);
+  float old_target_volume = TargetVolume();
+  type_volume_multiplier_ = volume;
   float target_volume = TargetVolume();
   LOG(INFO) << device_id_ << "(" << source_
             << "): type volume = " << type_volume_multiplier_
             << ", effective multiplier = " << target_volume;
+  if (target_volume != old_target_volume) {
+    slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
+    slew_volume_.SetVolume(target_volume);
+  }
+}
+
+void MixerInput::SetVolumeLimits(float volume_min, float volume_max) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  float old_target_volume = TargetVolume();
+  volume_min_ = volume_min;
+  volume_max_ = volume_max;
+  float target_volume = TargetVolume();
+  LOG(INFO) << device_id_ << "(" << source_ << "): set volume limits to ["
+            << volume_min_ << ", " << volume_max_ << "]";
+  if (target_volume != old_target_volume) {
+    slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
+    slew_volume_.SetVolume(target_volume);
+  }
+}
+
+void MixerInput::SetOutputLimit(float limit, int fade_ms) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  float old_target_volume = TargetVolume();
+  output_volume_limit_ = limit;
+  float target_volume = TargetVolume();
+  LOG(INFO) << device_id_ << "(" << source_
+            << "): output limit = " << output_volume_limit_
+            << ", effective multiplier = " << target_volume;
   if (fade_ms < 0) {
     fade_ms = kDefaultSlewTimeMs;
   } else {
     LOG(INFO) << "Fade over " << fade_ms << " ms";
   }
-  slew_volume_.SetMaxSlewTimeMs(fade_ms);
-  slew_volume_.SetVolume(target_volume);
+  if (target_volume != old_target_volume) {
+    slew_volume_.SetMaxSlewTimeMs(fade_ms);
+    slew_volume_.SetVolume(target_volume);
+  }
 }
 
 void MixerInput::SetMuted(bool muted) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(content_type_ != AudioContentType::kOther);
 
+  float old_target_volume = TargetVolume();
   mute_volume_multiplier_ = muted ? 0.0f : 1.0f;
   float target_volume = TargetVolume();
   LOG(INFO) << device_id_ << "(" << source_
             << "): mute volume = " << mute_volume_multiplier_
             << ", effective multiplier = " << target_volume;
-  slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
-  slew_volume_.SetVolume(target_volume);
+  if (target_volume != old_target_volume) {
+    slew_volume_.SetMaxSlewTimeMs(kDefaultSlewTimeMs);
+    slew_volume_.SetVolume(target_volume);
+  }
 }
 
 float MixerInput::TargetVolume() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  float volume = stream_volume_multiplier_ * type_volume_multiplier_ *
-                 mute_volume_multiplier_;
+  float output_volume = stream_volume_multiplier_ * type_volume_multiplier_;
+  float clamped_volume =
+      base::ClampToRange(output_volume, volume_min_, volume_max_);
+  float limited_volume = std::min(clamped_volume, output_volume_limit_);
+  float muted_volume = limited_volume * mute_volume_multiplier_;
   // Volume is clamped after all gains have been multiplied, to avoid clipping.
   // TODO(kmackay): Consider removing this clamp and use a postprocessor filter
   // to avoid clipping instead.
-  return base::ClampToRange(volume, 0.0f, 1.0f);
+  return base::ClampToRange(muted_volume, 0.0f, 1.0f);
 }
 
 float MixerInput::InstantaneousVolume() {
diff --git a/chromecast/media/cma/backend/mixer/mixer_input.h b/chromecast/media/cma/backend/mixer/mixer_input.h
index 5d7657ae..ab3905dc 100644
--- a/chromecast/media/cma/backend/mixer/mixer_input.h
+++ b/chromecast/media/cma/backend/mixer/mixer_input.h
@@ -47,7 +47,6 @@
       kInternalError,
     };
 
-    // TODO(b/139311908) Track channel layout.
     virtual size_t num_channels() const = 0;
     virtual ::media::ChannelLayout channel_layout() const = 0;
     virtual int sample_rate() const = 0;
@@ -134,9 +133,19 @@
 
   // Sets the multiplier based on this stream's content type. The resulting
   // output volume should be the content type volume * the per-stream volume
-  // multiplier. If |fade_ms| is >= 0, the volume change should be faded over
-  // that many milliseconds; otherwise, the default fade time should be used.
-  void SetContentTypeVolume(float volume, int fade_ms);
+  // multiplier.
+  void SetContentTypeVolume(float volume);
+
+  // Sets min/max output volume for this stream (ie, limits the product of
+  // content type volume and per-stream volume multiplier). Note that mute
+  // and runtime output limits (for ducking) are applied after these limits.
+  void SetVolumeLimits(float volume_min, float volume_max);
+
+  // Limits the output volume for this stream to below |limit|. Used for
+  // ducking. If |fade_ms| is >= 0, the resulting volume change should be
+  // faded over that many milliseconds; otherwise, the default fade time should
+  // be used.
+  void SetOutputLimit(float limit, int fade_ms);
 
   // Sets whether or not this stream should be muted.
   void SetMuted(bool muted);
@@ -170,9 +179,12 @@
   std::unique_ptr<::media::AudioBus> fill_buffer_;
   std::unique_ptr<::media::ChannelMixer> channel_mixer_;
 
-  float stream_volume_multiplier_;
-  float type_volume_multiplier_;
-  float mute_volume_multiplier_;
+  float stream_volume_multiplier_ = 1.0f;
+  float type_volume_multiplier_ = 1.0f;
+  float volume_min_ = 0.0f;
+  float volume_max_ = 1.0f;
+  float output_volume_limit_ = 1.0f;
+  float mute_volume_multiplier_ = 1.0f;
   SlewVolume slew_volume_;
   // True if volume scale-accumulate has already been applied for at least
   // one channel of the current buffer.
diff --git a/chromecast/media/cma/backend/mixer/mixer_pipeline.cc b/chromecast/media/cma/backend/mixer/mixer_pipeline.cc
index ff3399fe..c621ef4 100644
--- a/chromecast/media/cma/backend/mixer/mixer_pipeline.cc
+++ b/chromecast/media/cma/backend/mixer/mixer_pipeline.cc
@@ -34,12 +34,13 @@
     int input_channels,
     const std::string& name,
     const base::Value* filter_list,
-    PostProcessingPipelineFactory* ppp_factory) {
+    PostProcessingPipelineFactory* ppp_factory,
+    const base::Value* volume_limits) {
   DCHECK(ppp_factory);
   auto pipeline =
       ppp_factory->CreatePipeline(name, filter_list, input_channels);
   return std::make_unique<FilterGroup>(input_channels, name,
-                                       std::move(pipeline));
+                                       std::move(pipeline), volume_limits);
 }
 
 }  // namespace
@@ -77,8 +78,9 @@
 
     DCHECK(!device_ids->GetList().empty());
     DCHECK(device_ids->GetList()[0].is_string());
-    filter_groups_.push_back(CreateFilterGroup(
-        input_channels, name, stream_pipeline.pipeline, factory));
+    filter_groups_.push_back(
+        CreateFilterGroup(input_channels, name, stream_pipeline.pipeline,
+                          factory, stream_pipeline.volume_limits));
 
     if (!SetGroupDeviceIds(device_ids, filter_groups_.back().get())) {
       return false;
@@ -99,8 +101,9 @@
     mix_group_input_channels = mix_pipeline.num_input_channels.value();
   }
   LOG(INFO) << mix_group_input_channels << " input channels to 'mix' group";
-  std::unique_ptr<FilterGroup> mix_filter = CreateFilterGroup(
-      mix_group_input_channels, "mix", mix_pipeline.pipeline, factory);
+  std::unique_ptr<FilterGroup> mix_filter =
+      CreateFilterGroup(mix_group_input_channels, "mix", mix_pipeline.pipeline,
+                        factory, mix_pipeline.volume_limits);
   for (std::unique_ptr<FilterGroup>& group : filter_groups_) {
     mix_filter->AddMixedInput(group.get());
   }
@@ -120,9 +123,9 @@
   }
   LOG(INFO) << linearize_group_input_channels
             << " input channels to 'linearize' group";
-  filter_groups_.push_back(
-      CreateFilterGroup(linearize_group_input_channels, "linearize",
-                        linearize_pipeline.pipeline, factory));
+  filter_groups_.push_back(CreateFilterGroup(
+      linearize_group_input_channels, "linearize", linearize_pipeline.pipeline,
+      factory, linearize_pipeline.volume_limits));
   output_group_ = filter_groups_.back().get();
   output_group_->AddMixedInput(loopback_output_group_);
   if (!SetGroupDeviceIds(linearize_pipeline.stream_types, output_group_)) {
diff --git a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
index b2154d2..1ef3deb 100644
--- a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
+++ b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.cc
@@ -25,16 +25,19 @@
 const char kProcessorsKey[] = "processors";
 const char kStreamsKey[] = "streams";
 const char kNumInputChannelsKey[] = "num_input_channels";
+const char kVolumeLimitsKey[] = "volume_limits";
 
 }  // namespace
 
 StreamPipelineDescriptor::StreamPipelineDescriptor(
     const base::Value* pipeline_in,
     const base::Value* stream_types_in,
-    const base::Optional<int> num_input_channels_in)
+    const base::Optional<int> num_input_channels_in,
+    const base::Value* volume_limits_in)
     : pipeline(pipeline_in),
       stream_types(stream_types_in),
-      num_input_channels(std::move(num_input_channels_in)) {}
+      num_input_channels(std::move(num_input_channels_in)),
+      volume_limits(volume_limits_in) {}
 
 StreamPipelineDescriptor::~StreamPipelineDescriptor() = default;
 
@@ -42,7 +45,8 @@
     const StreamPipelineDescriptor& other)
     : StreamPipelineDescriptor(other.pipeline,
                                other.stream_types,
-                               other.num_input_channels) {}
+                               other.num_input_channels,
+                               other.volume_limits) {}
 
 PostProcessingPipelineParser::PostProcessingPipelineParser(
     std::unique_ptr<base::DictionaryValue> config_dict)
@@ -104,8 +108,11 @@
     auto num_input_channels =
         pipeline_description_dict.FindIntKey(kNumInputChannelsKey);
 
+    const base::Value* volume_limits = pipeline_description_dict.FindKeyOfType(
+        kVolumeLimitsKey, base::Value::Type::DICTIONARY);
+
     descriptors.emplace_back(processors_list, streams_list,
-                             std::move(num_input_channels));
+                             std::move(num_input_channels), volume_limits);
   }
   return descriptors;
 }
@@ -125,7 +132,7 @@
       !postprocessor_config_->GetDictionary(key, &stream_dict)) {
     LOG(WARNING) << "No post-processor description found for \"" << key
                  << "\" in " << file_path_ << ". Using passthrough.";
-    return StreamPipelineDescriptor(nullptr, nullptr, base::nullopt);
+    return StreamPipelineDescriptor(nullptr, nullptr, base::nullopt, nullptr);
   }
   const base::Value* processors_list =
       stream_dict->FindKeyOfType(kProcessorsKey, base::Value::Type::LIST);
@@ -134,9 +141,12 @@
   const base::Value* streams_list =
       stream_dict->FindKeyOfType(kStreamsKey, base::Value::Type::LIST);
 
-  return StreamPipelineDescriptor(
-      processors_list, streams_list,
-      stream_dict->FindIntKey(kNumInputChannelsKey));
+  const base::Value* volume_limits = stream_dict->FindKeyOfType(
+      kVolumeLimitsKey, base::Value::Type::DICTIONARY);
+
+  return StreamPipelineDescriptor(processors_list, streams_list,
+                                  stream_dict->FindIntKey(kNumInputChannelsKey),
+                                  volume_limits);
 }
 
 base::FilePath PostProcessingPipelineParser::GetFilePath() const {
diff --git a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.h b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.h
index f8d5d56e..c440531 100644
--- a/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.h
+++ b/chromecast/media/cma/backend/mixer/post_processing_pipeline_parser.h
@@ -33,10 +33,12 @@
   const base::Value* pipeline;
   const base::Value* stream_types;
   const base::Optional<int> num_input_channels;
+  const base::Value* volume_limits;
 
   StreamPipelineDescriptor(const base::Value* pipeline_in,
                            const base::Value* stream_types_in,
-                           const base::Optional<int> num_input_channels_in);
+                           const base::Optional<int> num_input_channels_in,
+                           const base::Value* volume_limits_in);
   ~StreamPipelineDescriptor();
   StreamPipelineDescriptor(const StreamPipelineDescriptor& other);
   StreamPipelineDescriptor operator=(const StreamPipelineDescriptor& other) =
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer.cc b/chromecast/media/cma/backend/mixer/stream_mixer.cc
index 321fe1f..481109e 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer.cc
+++ b/chromecast/media/cma/backend/mixer/stream_mixer.cc
@@ -153,10 +153,6 @@
   StreamMixer* const mixer_;
 };
 
-float StreamMixer::VolumeInfo::GetEffectiveVolume() {
-  return std::min(volume, limit);
-}
-
 StreamMixer::StreamMixer(
     scoped_refptr<base::SequencedTaskRunner> io_task_runner)
     : StreamMixer(nullptr,
@@ -651,11 +647,9 @@
 
   auto type = input->content_type();
   if (type != AudioContentType::kOther) {
+    input->SetContentTypeVolume(volume_info_[type].volume);
     if (input->primary()) {
-      input->SetContentTypeVolume(volume_info_[type].GetEffectiveVolume(),
-                                  kUseDefaultFade);
-    } else {
-      input->SetContentTypeVolume(volume_info_[type].volume, kUseDefaultFade);
+      input->SetOutputLimit(volume_info_[type].limit, kUseDefaultFade);
     }
     input->SetMuted(volume_info_[type].muted);
   }
@@ -881,19 +875,14 @@
   DCHECK(type != AudioContentType::kOther);
 
   volume_info_[type].volume = level;
-  float effective_volume = volume_info_[type].GetEffectiveVolume();
   for (const auto& input : inputs_) {
     if (input.second->content_type() == type) {
-      if (input.second->primary()) {
-        input.second->SetContentTypeVolume(effective_volume, kUseDefaultFade);
-      } else {
-        // Volume limits don't apply to effects streams.
-        input.second->SetContentTypeVolume(level, kUseDefaultFade);
-      }
+      input.second->SetContentTypeVolume(level);
     }
   }
   if (external_audio_pipeline_supported_ && type == AudioContentType::kMedia) {
-    ExternalAudioPipelineShlib::SetExternalMediaVolume(effective_volume);
+    ExternalAudioPipelineShlib::SetExternalMediaVolume(
+        std::min(level, volume_info_[type].limit));
   }
   UpdateStreamCounts();
 }
@@ -921,7 +910,6 @@
   LOG(INFO) << "Set volume limit for " << static_cast<int>(type) << " to "
             << limit;
   volume_info_[type].limit = limit;
-  float effective_volume = volume_info_[type].GetEffectiveVolume();
   int fade_ms = kUseDefaultFade;
   if (type == AudioContentType::kMedia) {
     if (limit >= 1.0f) {  // Unducking.
@@ -933,11 +921,12 @@
   for (const auto& input : inputs_) {
     // Volume limits don't apply to effects streams.
     if (input.second->primary() && input.second->content_type() == type) {
-      input.second->SetContentTypeVolume(effective_volume, fade_ms);
+      input.second->SetOutputLimit(limit, fade_ms);
     }
   }
   if (external_audio_pipeline_supported_ && type == AudioContentType::kMedia) {
-    ExternalAudioPipelineShlib::SetExternalMediaVolume(effective_volume);
+    ExternalAudioPipelineShlib::SetExternalMediaVolume(
+        std::min(volume_info_[type].volume, limit));
   }
   UpdateStreamCounts();
 }
diff --git a/chromecast/media/cma/backend/mixer/stream_mixer.h b/chromecast/media/cma/backend/mixer/stream_mixer.h
index d590b3a..19d0fa14 100644
--- a/chromecast/media/cma/backend/mixer/stream_mixer.h
+++ b/chromecast/media/cma/backend/mixer/stream_mixer.h
@@ -146,8 +146,6 @@
 
   // Contains volume control information for an audio content type.
   struct VolumeInfo {
-    float GetEffectiveVolume();
-
     float volume = 0.0f;
     float limit = 1.0f;
     bool muted = false;
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 05f1019..0f6f5cb 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-82-4028.0-1580727110-benchmark-81.0.4044.14-r1.orderfile.xz
\ No newline at end of file
+chromeos-chrome-orderfile-field-82-4044.15-1581938780-benchmark-82.0.4065.0-r1.orderfile.xz
\ No newline at end of file
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 943400d5..ce43746 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -1526,12 +1526,13 @@
   assistant_manager_internal_->GetAlarmTimerManager()->StopRinging();
 }
 
-void AssistantManagerServiceImpl::CreateTimer(base::TimeDelta duration) {
+void AssistantManagerServiceImpl::AddTimeToTimer(const std::string& id,
+                                                 base::TimeDelta duration) {
   if (!assistant_manager_internal_)
     return;
 
-  assistant_manager_internal_->GetAlarmTimerManager()->CreateTimer(
-      duration.InSeconds(), /*label=*/std::string());
+  assistant_manager_internal_->GetAlarmTimerManager()->AddTimeToTimer(
+      id, duration.InSeconds());
 }
 
 void AssistantManagerServiceImpl::CacheAssistantStructure(
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index 63f02cf2..ad3d602 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -152,8 +152,8 @@
       mojom::AssistantFeedbackPtr assistant_feedback) override;
   void NotifyEntryIntoAssistantUi(
       mojom::AssistantEntryPoint entry_point) override;
+  void AddTimeToTimer(const std::string& id, base::TimeDelta duration) override;
   void StopAlarmTimerRinging() override;
-  void CreateTimer(base::TimeDelta duration) override;
 
   // AssistantActionObserver overrides:
   void OnScheduleWait(int id, int time_ms) override;
diff --git a/chromeos/services/assistant/fake_assistant_manager_service_impl.cc b/chromeos/services/assistant/fake_assistant_manager_service_impl.cc
index 46fa57415..489b502d4 100644
--- a/chromeos/services/assistant/fake_assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/fake_assistant_manager_service_impl.cc
@@ -116,8 +116,11 @@
 void FakeAssistantManagerServiceImpl::NotifyEntryIntoAssistantUi(
     mojom::AssistantEntryPoint entry_point) {}
 
+void FakeAssistantManagerServiceImpl::AddTimeToTimer(const std::string& id,
+                                                     base::TimeDelta duration) {
+}
+
 void FakeAssistantManagerServiceImpl::StopAlarmTimerRinging() {}
-void FakeAssistantManagerServiceImpl::CreateTimer(base::TimeDelta duration) {}
 
 void FakeAssistantManagerServiceImpl::SetStateAndInformObservers(
     State new_state) {
diff --git a/chromeos/services/assistant/fake_assistant_manager_service_impl.h b/chromeos/services/assistant/fake_assistant_manager_service_impl.h
index 9b1b8cd60..b93a7b0 100644
--- a/chromeos/services/assistant/fake_assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/fake_assistant_manager_service_impl.h
@@ -70,8 +70,8 @@
   void SendAssistantFeedback(mojom::AssistantFeedbackPtr feedback) override;
   void NotifyEntryIntoAssistantUi(
       mojom::AssistantEntryPoint entry_point) override;
+  void AddTimeToTimer(const std::string& id, base::TimeDelta duration) override;
   void StopAlarmTimerRinging() override;
-  void CreateTimer(base::TimeDelta duration) override;
 
   // Update the state to the corresponding value, and inform the
   // |AssistantStateObserver| of the change.
diff --git a/chromeos/services/assistant/public/mojom/assistant.mojom b/chromeos/services/assistant/public/mojom/assistant.mojom
index c22807de..fb68dd1 100644
--- a/chromeos/services/assistant/public/mojom/assistant.mojom
+++ b/chromeos/services/assistant/public/mojom/assistant.mojom
@@ -145,12 +145,14 @@
   // Invoked on entry to Assistant UI.
   NotifyEntryIntoAssistantUi(AssistantEntryPoint entry_point);
 
-  // ===== Alarm/Timer management methods =====
+  // Alarm/Timer methods -------------------------------------------------------
+
+  // Adds the specified |duration| to the timer identified by |id|.  Note that
+  // this method is a no-op if there is no existing timer identified by |id|.
+  AddTimeToTimer(string id, mojo_base.mojom.TimeDelta duration);
+
   // Stops timer or alarm ringing.
   StopAlarmTimerRinging();
-
-  // Create timer with |duration|.
-  CreateTimer(mojo_base.mojom.TimeDelta duration);
 };
 
 // Enumeration of Assistant entry points. These values are persisted to logs.
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 38d8479..19f96bb 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -1604,7 +1604,6 @@
                 kPrefilledPlaceholderUsernameOverridden);
       }
     }
-    username_element.SetAutofillState(WebAutofillState::kAutofilled);
     if (logger)
       logger->LogElementName(Logger::STRING_USERNAME_FILLED, username_element);
   }
@@ -1843,8 +1842,11 @@
   const uint32_t field_id = field.UniqueRendererFormControlId();
   if (field_data_manager_->DidUserType(field_id))
     return;
-  if (field.Value().Utf16() != value)
-    field.SetSuggestedValue(WebString::FromUTF16(value));
+
+  if (field.Value().Utf16() == value)
+    return;
+
+  field.SetSuggestedValue(WebString::FromUTF16(value));
   field.SetAutofillState(WebAutofillState::kAutofilled);
   // Wait to fill until a user gesture occurs. This is to make sure that we do
   // not fill in the DOM with a password until we believe the user is
diff --git a/components/autofill/core/browser/payments/strike_database.cc b/components/autofill/core/browser/payments/strike_database.cc
index 0860cd0..49f2f71 100644
--- a/components/autofill/core/browser/payments/strike_database.cc
+++ b/components/autofill/core/browser/payments/strike_database.cc
@@ -24,13 +24,16 @@
 const int kMaxInitAttempts = 3;
 }  // namespace
 
+const base::FilePath::StringPieceType kStrikeDatabaseFileName =
+    FILE_PATH_LITERAL("AutofillStrikeDatabase");
+
 StrikeDatabase::StrikeDatabase(
     leveldb_proto::ProtoDatabaseProvider* db_provider,
     base::FilePath profile_path) {
-  auto strike_database_path =
-      profile_path.Append(FILE_PATH_LITERAL("AutofillStrikeDatabase"));
+  const auto strike_database_path =
+      profile_path.Append(kStrikeDatabaseFileName);
 
-  auto database_task_runner = base::CreateSequencedTaskRunner(
+  const auto database_task_runner = base::CreateSequencedTaskRunner(
       {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT,
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
 
diff --git a/components/autofill/core/browser/payments/strike_database.h b/components/autofill/core/browser/payments/strike_database.h
index ace3b6d..0a2dd30 100644
--- a/components/autofill/core/browser/payments/strike_database.h
+++ b/components/autofill/core/browser/payments/strike_database.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/callback_forward.h"
+#include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/leveldb_proto/public/proto_database.h"
@@ -18,6 +19,8 @@
 
 namespace autofill {
 
+extern const base::FilePath::StringPieceType kStrikeDatabaseFileName;
+
 namespace {
 const char kKeyDeliminator[] = "__";
 }  // namespace
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index f7946e77..ffbcd66 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -115,6 +115,18 @@
   DISALLOW_COPY_AND_ASSIGN(EmptyUndoDelegate);
 };
 
+#if DCHECK_IS_ON()
+void AddGuidsToIndexRecursive(const BookmarkNode* node,
+                              std::set<std::string>* guid_index) {
+  bool success = guid_index->insert(node->guid()).second;
+  DCHECK(success);
+
+  // Recurse through children.
+  for (size_t i = node->children().size(); i > 0; --i)
+    AddGuidsToIndexRecursive(node->children()[i - 1].get(), guid_index);
+}
+#endif  // DCHECK_IS_ON()
+
 }  // namespace
 
 // BookmarkModel --------------------------------------------------------------
@@ -414,9 +426,11 @@
   for (BookmarkModelObserver& observer : observers_)
     observer.OnWillChangeBookmarkNode(this, node);
 
+  // The title index doesn't support changing the URL, instead we remove then
+  // add it back.
   titled_url_index_->Remove(mutable_node);
   url_index_->SetUrl(mutable_node, url);
-  AddNodeToIndexRecursive(mutable_node);
+  titled_url_index_->Add(mutable_node);
 
   if (store_)
     store_->ScheduleSave();
@@ -830,6 +844,12 @@
   if (node->is_url())
     titled_url_index_->Remove(node);
 
+  // Note that |guid_index_| is used for DCHECK-enabled builds only.
+#if DCHECK_IS_ON()
+  DCHECK(guid_index_.erase(node->guid()))
+      << "Bookmark GUID missing in index: " << node->guid();
+#endif  // DCHECK_IS_ON()
+
   CancelPendingFaviconLoadRequests(node);
 
   // Recurse through children.
@@ -863,6 +883,10 @@
   other_node_ = details->other_folder_node();
   mobile_node_ = details->mobile_folder_node();
 
+#if DCHECK_IS_ON()
+  AddGuidsToIndexRecursive(root_, &guid_index_);
+#endif  // DCHECK_IS_ON()
+
   titled_url_index_->SetNodeSorter(
       std::make_unique<TypedCountSorter>(client_.get()));
   // Sorting the permanent nodes has to happen on the main thread, so we do it
@@ -910,6 +934,14 @@
 
   if (node->is_url())
     titled_url_index_->Add(node);
+
+  // The node's GUID must be unique. Note that |guid_index_| is used for
+  // DCHECK-enabled builds only.
+#if DCHECK_IS_ON()
+  DCHECK(guid_index_.insert(node->guid()).second)
+      << "Duplicate bookmark GUID: " << node->guid();
+#endif  // DCHECK_IS_ON()
+
   for (const auto& child : node->children())
     AddNodeToIndexRecursive(child.get());
 }
diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h
index 1dbfe99..60845af 100644
--- a/components/bookmarks/browser/bookmark_model.h
+++ b/components/bookmarks/browser/bookmark_model.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -425,6 +426,11 @@
 
   std::unique_ptr<TitledUrlIndex> titled_url_index_;
 
+#if DCHECK_IS_ON()
+  // GUID index used to verify uniqueness in DCHECK-enabled builds.
+  std::set<std::string> guid_index_;
+#endif  // DCHECK_IS_ON()
+
   // Owned by |model_loader_|.
   // WARNING: in some tests this does *not* refer to
   // |ModelLoader::history_bookmark_model_|. This is because some tests
diff --git a/components/browser_ui/modaldialog/android/java/res/layout/modal_dialog_title.xml b/components/browser_ui/modaldialog/android/java/res/layout/modal_dialog_title.xml
index 0b65398..5d22b53 100644
--- a/components/browser_ui/modaldialog/android/java/res/layout/modal_dialog_title.xml
+++ b/components/browser_ui/modaldialog/android/java/res/layout/modal_dialog_title.xml
@@ -16,7 +16,7 @@
         android:layout_marginEnd="8dp"
         android:importantForAccessibility="no" />
 
-    <android.support.v7.widget.DialogTitle
+    <androidx.appcompat.widget.DialogTitle
         android:id="@+id/title"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
diff --git a/components/browser_ui/widget/android/java/res/layout/selectable_list_layout.xml b/components/browser_ui/widget/android/java/res/layout/selectable_list_layout.xml
index 42463777..7f9e76f1 100644
--- a/components/browser_ui/widget/android/java/res/layout/selectable_list_layout.xml
+++ b/components/browser_ui/widget/android/java/res/layout/selectable_list_layout.xml
@@ -27,7 +27,7 @@
         android:layout_height="match_parent"
         android:layout_marginTop="@dimen/selectable_list_toolbar_height" >
 
-        <android.support.v7.widget.RecyclerView
+        <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/recycler_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
diff --git a/components/dom_distiller/core/viewer.cc b/components/dom_distiller/core/viewer.cc
index 70ec76d..bcc0fa4 100644
--- a/components/dom_distiller/core/viewer.cc
+++ b/components/dom_distiller/core/viewer.cc
@@ -175,7 +175,13 @@
 }
 
 const std::string GetSetTitleJs(std::string title) {
+#if defined(OS_ANDROID) || defined(OS_IOS)
   base::Value value(title);
+#else  // Desktop
+  std::string suffix(
+      l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_TITLE_SUFFIX));
+  base::Value value(title + " - " + suffix);
+#endif
   std::string output;
   base::JSONWriter::Write(value, &output);
   return "setTitle(" + output + ");";
diff --git a/components/dom_distiller_strings.grdp b/components/dom_distiller_strings.grdp
index 1198c87..652b91f 100644
--- a/components/dom_distiller_strings.grdp
+++ b/components/dom_distiller_strings.grdp
@@ -31,9 +31,18 @@
   <message name="IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_CONTENT" desc="The text to show in place of reading list article content if the article is not found.">
     Could not find the requested article.
   </message>
-  <message name="IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE" desc="The text to show in place of a reading list article title while the article is loading. Translation should match TC ID 7558334641404735182, 7078777000119799848, and 8486137854427127199.">
-    Simplified view
-  </message>
+  <if expr="is_android or is_ios">
+    <then>
+      <message name="IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE" desc="The text to show in place of a reading list article title while the article is loading. Translation should match TC ID 7558334641404735182, 7078777000119799848, and 8486137854427127199.">
+        Simplified view
+      </message>
+    </then>
+    <else> <!-- Desktop -->
+      <message name="IDS_DOM_DISTILLER_VIEWER_LOADING_TITLE" desc="The text to show in place of a reading list article title while the article is loading.">
+        Loading...
+      </message>
+    </else>
+  </if>
   <message name="IDS_DOM_DISTILLER_VIEWER_NO_DATA_CONTENT" desc="The text to show in place of reading list article content if there is no data found.">
     No data found.
   </message>
@@ -49,5 +58,8 @@
   <message name="IDS_DOM_DISTILLER_WEBUI_TITLE" desc="The title to show on the DOM Distiller debug page.">
     DOM Distiller
   </message>
+  <message name="IDS_DOM_DISTILLER_VIEWER_TITLE_SUFFIX" desc="The suffix to show after the page title to indicate we are in reader mode. For example, if the page title was 'An Article', this would be appended to create the title 'An Article - Reader Mode'.">
+    Reader Mode
+  </message>
 
 </grit-part>
diff --git a/components/dom_distiller_strings_grdp/IDS_DOM_DISTILLER_VIEWER_TITLE_SUFFIX.png.sha1 b/components/dom_distiller_strings_grdp/IDS_DOM_DISTILLER_VIEWER_TITLE_SUFFIX.png.sha1
new file mode 100644
index 0000000..5608a51
--- /dev/null
+++ b/components/dom_distiller_strings_grdp/IDS_DOM_DISTILLER_VIEWER_TITLE_SUFFIX.png.sha1
@@ -0,0 +1 @@
+f049a5162a611e94ae4e6912dd941393972ced8a
\ No newline at end of file
diff --git a/components/metrics/field_trials_provider.cc b/components/metrics/field_trials_provider.cc
index bb7c97b..b13f131 100644
--- a/components/metrics/field_trials_provider.cc
+++ b/components/metrics/field_trials_provider.cc
@@ -4,6 +4,9 @@
 
 #include "components/metrics/field_trials_provider.h"
 
+#include <string>
+#include <vector>
+
 #include "base/strings/string_piece.h"
 #include "components/variations/active_field_trials.h"
 #include "components/variations/synthetic_trial_registry.h"
diff --git a/components/metrics/net/network_metrics_provider.cc b/components/metrics/net/network_metrics_provider.cc
index 279bb347..c4b88160 100644
--- a/components/metrics/net/network_metrics_provider.cc
+++ b/components/metrics/net/network_metrics_provider.cc
@@ -6,7 +6,9 @@
 
 #include <stdint.h>
 
+#include <algorithm>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -27,7 +29,6 @@
 
 #if defined(OS_ANDROID)
 #include "base/metrics/histogram_functions.h"
-#include "base/strings/string_number_conversions.h"
 #include "net/android/network_library.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
 #endif
diff --git a/components/metrics/ui/screen_info_metrics_provider.cc b/components/metrics/ui/screen_info_metrics_provider.cc
index 4465d903..87a444f8 100644
--- a/components/metrics/ui/screen_info_metrics_provider.cc
+++ b/components/metrics/ui/screen_info_metrics_provider.cc
@@ -4,6 +4,8 @@
 
 #include "components/metrics/ui/screen_info_metrics_provider.h"
 
+#include <algorithm>
+
 #include "build/build_config.h"
 #include "third_party/metrics_proto/system_profile.pb.h"
 #include "ui/display/display.h"
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index 6e12246..3c5055c8 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -870,7 +870,7 @@
 }
 
 GURL AutocompleteMatch::ImageUrl() const {
-  return answer ? answer->image_url() : GURL(image_url);
+  return answer ? answer->image_url() : image_url;
 }
 
 AutocompleteMatch AutocompleteMatch::DerivePedalSuggestion(
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index 8daee3d..518a2942 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -533,7 +533,7 @@
   // Optional image information. Used for entity suggestions. The dominant color
   // can be used to paint the image placeholder while fetching the image.
   std::string image_dominant_color;
-  std::string image_url;
+  GURL image_url;
 
   // Optional override to use for types that specify an icon sub-type.
   DocumentType document_type = DocumentType::NONE;
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index 064a49a..0c84d274e 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -1618,8 +1618,8 @@
     prefetch_limit--;
 
     const auto& image_url = suggestion.image_url();
-    if (!image_url.empty())
-      prefetch_image_urls.push_back(GURL(image_url));
+    if (!image_url.is_empty())
+      prefetch_image_urls.push_back(image_url);
 
     if (suggestion.answer())
       suggestion.answer()->AddImageURLsTo(&prefetch_image_urls);
diff --git a/components/omnibox/browser/search_suggestion_parser.cc b/components/omnibox/browser/search_suggestion_parser.cc
index e7f53a6..837c291 100644
--- a/components/omnibox/browser/search_suggestion_parser.cc
+++ b/components/omnibox/browser/search_suggestion_parser.cc
@@ -130,7 +130,7 @@
       annotation_(annotation),
       additional_query_params_(additional_query_params),
       image_dominant_color_(image_dominant_color),
-      image_url_(image_url),
+      image_url_(GURL(image_url)),
       should_prefetch_(should_prefetch) {
   match_contents_ = match_contents;
   DCHECK(!match_contents_.empty());
diff --git a/components/omnibox/browser/search_suggestion_parser.h b/components/omnibox/browser/search_suggestion_parser.h
index 7e5145c..5a631e2 100644
--- a/components/omnibox/browser/search_suggestion_parser.h
+++ b/components/omnibox/browser/search_suggestion_parser.h
@@ -166,7 +166,7 @@
     const std::string& image_dominant_color() const {
       return image_dominant_color_;
     }
-    const std::string& image_url() const { return image_url_; }
+    const GURL& image_url() const { return image_url_; }
 
     bool should_prefetch() const { return should_prefetch_; }
 
@@ -206,7 +206,7 @@
     // color can be used to paint the image placeholder while fetching the
     // image.
     std::string image_dominant_color_;
-    std::string image_url_;
+    GURL image_url_;
 
     // Should this result be prefetched?
     bool should_prefetch_;
diff --git a/components/omnibox/browser/search_suggestion_parser_unittest.cc b/components/omnibox/browser/search_suggestion_parser_unittest.cc
index 8d60005..8ca26ed 100644
--- a/components/omnibox/browser/search_suggestion_parser_unittest.cc
+++ b/components/omnibox/browser/search_suggestion_parser_unittest.cc
@@ -143,7 +143,7 @@
     ASSERT_EQ(base::ASCIIToUTF16(""), suggestion_result.annotation());
     // This entry has no image.
     ASSERT_EQ("", suggestion_result.image_dominant_color());
-    ASSERT_EQ("", suggestion_result.image_url());
+    ASSERT_EQ(GURL(), suggestion_result.image_url());
   }
   {
     const auto& suggestion_result = results.suggest_results[1];
@@ -152,7 +152,7 @@
     ASSERT_EQ(base::ASCIIToUTF16("American author"),
               suggestion_result.annotation());
     ASSERT_EQ("#424242", suggestion_result.image_dominant_color());
-    ASSERT_EQ("http://example.com/a.png", suggestion_result.image_url());
+    ASSERT_EQ(GURL("http://example.com/a.png"), suggestion_result.image_url());
   }
 }
 
@@ -233,4 +233,4 @@
   const ACMatchClassifications kNone = {
       {0, AutocompleteMatch::ACMatchClassification::NONE}};
   EXPECT_EQ(kNone, result.match_contents_class());
-}
\ No newline at end of file
+}
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.cc b/components/password_manager/core/browser/password_manager_metrics_util.cc
index cc48fe4..873bcd3 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.cc
+++ b/components/password_manager/core/browser/password_manager_metrics_util.cc
@@ -296,7 +296,8 @@
 
 void LogProtectedPasswordHashCounts(size_t gaia_hash_count,
                                     size_t enterprise_hash_count,
-                                    bool does_primary_account_exists) {
+                                    bool does_primary_account_exists,
+                                    bool is_signed_in) {
   base::UmaHistogramCounts100("PasswordManager.SavedGaiaPasswordHashCount",
                               static_cast<int>(gaia_hash_count));
   base::UmaHistogramCounts100(
@@ -306,11 +307,15 @@
   // Log parallel metrics for sync and signed-in non-sync accounts in addition
   // to above to be able to tell what fraction of signed-in non-sync users we
   // are protecting compared to syncing users.
-  base::UmaHistogramCounts100(
-      does_primary_account_exists
-          ? "PasswordManager.SavedGaiaPasswordHashCount.Sync"
-          : "PasswordManager.SavedGaiaPasswordHashCount.SignedInNonSync",
-      static_cast<int>(gaia_hash_count));
+  if (does_primary_account_exists) {
+    base::UmaHistogramCounts100(
+        "PasswordManager.SavedGaiaPasswordHashCount.Sync",
+        static_cast<int>(gaia_hash_count));
+  } else if (is_signed_in) {
+    base::UmaHistogramCounts100(
+        "PasswordManager.SavedGaiaPasswordHashCount.SignedInNonSync",
+        static_cast<int>(gaia_hash_count));
+  }
 }
 
 void LogProtectedPasswordReuse(PasswordType reused_password_type) {}
diff --git a/components/password_manager/core/browser/password_manager_metrics_util.h b/components/password_manager/core/browser/password_manager_metrics_util.h
index 06c2e0c4..2c56d69 100644
--- a/components/password_manager/core/browser/password_manager_metrics_util.h
+++ b/components/password_manager/core/browser/password_manager_metrics_util.h
@@ -593,7 +593,8 @@
 // password hashes saved. Currently only called on profile start up.
 void LogProtectedPasswordHashCounts(size_t gaia_hash_count,
                                     size_t enterprise_hash_count,
-                                    bool does_primary_account_exists);
+                                    bool does_primary_account_exists,
+                                    bool is_signed_in);
 
 #endif
 
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 09c82ba..5554a19d 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -516,9 +516,10 @@
 #endif
 
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
-void PasswordStore::PreparePasswordHashData(const std::string& sync_username) {
+void PasswordStore::PreparePasswordHashData(const std::string& sync_username,
+                                            const bool is_signed_in) {
   SchedulePasswordHashUpdate(/*should_log_metrics=*/true,
-                             !sync_username.empty());
+                             !sync_username.empty(), is_signed_in);
   ScheduleEnterprisePasswordURLUpdate();
 }
 
@@ -549,8 +550,10 @@
       metrics_util::LogGaiaPasswordHashChange(event, is_primary_account);
     }
     // This method is not being called on startup so it shouldn't log metrics.
-    SchedulePasswordHashUpdate(/*should_log_metrics=*/false,
-                               is_primary_account);
+    // |is_signed_in| is only used when |should_log_metrics| is true so
+    // it doesn't matter what the value is here.
+    SchedulePasswordHashUpdate(/*should_log_metrics=*/false, is_primary_account,
+                               /*is_signed_in=*/false);
   }
 }
 
@@ -561,7 +564,8 @@
     metrics_util::LogGaiaPasswordHashChange(event,
                                             /*is_sync_password=*/true);
     SchedulePasswordHashUpdate(/*should_log_metrics=*/false,
-                               /*does_primary_account_exists=*/false);
+                               /*does_primary_account_exists=*/false,
+                               /*is_signed_in=*/false);
   }
 }
 
@@ -604,13 +608,13 @@
   notifier_->SubscribeToSigninEvents(this);
 }
 
-void PasswordStore::SchedulePasswordHashUpdate(
-    bool should_log_metrics,
-    bool does_primary_account_exists) {
+void PasswordStore::SchedulePasswordHashUpdate(bool should_log_metrics,
+                                               bool does_primary_account_exists,
+                                               bool is_signed_in) {
   ScheduleTask(base::BindRepeating(
       &PasswordStore::SaveProtectedPasswordHashImpl, this,
       base::Passed(hash_password_manager_.RetrieveAllPasswordHashes()),
-      should_log_metrics, does_primary_account_exists));
+      should_log_metrics, does_primary_account_exists, is_signed_in));
 }
 
 void PasswordStore::ScheduleEnterprisePasswordURLUpdate() {
@@ -739,7 +743,8 @@
 void PasswordStore::SaveProtectedPasswordHashImpl(
     PasswordHashDataList protected_password_data_list,
     bool should_log_metrics,
-    bool does_primary_account_exists) {
+    bool does_primary_account_exists,
+    bool is_signed_in) {
   if (!reuse_detector_ || !protected_password_data_list.has_value())
     return;
   TRACE_EVENT0("passwords", "PasswordStore::SaveProtectedPasswordHashImpl");
@@ -756,7 +761,7 @@
   if (should_log_metrics) {
     metrics_util::LogProtectedPasswordHashCounts(
         gaia_password_hash_list.size(), enterprise_password_hash_list.size(),
-        does_primary_account_exists);
+        does_primary_account_exists, is_signed_in);
   }
   reuse_detector_->UseGaiaPasswordHash(std::move(gaia_password_hash_list));
   reuse_detector_->UseNonGaiaEnterprisePasswordHash(
diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h
index ae25f0c..6c44dd5 100644
--- a/components/password_manager/core/browser/password_store.h
+++ b/components/password_manager/core/browser/password_store.h
@@ -335,7 +335,8 @@
 #if defined(SYNC_PASSWORD_REUSE_DETECTION_ENABLED)
   // Immediately called after |Init()| to retrieve password hash data for
   // reuse detection.
-  void PreparePasswordHashData(const std::string& sync_username);
+  void PreparePasswordHashData(const std::string& sync_username,
+                               bool is_signed_in);
 
   // Checks that some suffix of |input| equals to a password saved on another
   // registry controlled domain than |domain|.
@@ -389,9 +390,11 @@
       std::unique_ptr<PasswordStoreSigninNotifier> notifier);
 
   // Schedules the update of password hashes used by reuse detector.
-  // |does_primary_account_exists| is only used if |should_log_metrics| is true.
+  // |does_primary_account_exists| and |is_signed_in| fields are only used if
+  // |should_log_metrics| is true.
   void SchedulePasswordHashUpdate(bool should_log_metrics,
-                                  bool does_primary_account_exists);
+                                  bool does_primary_account_exists,
+                                  bool is_signed_in);
 
   // Schedules the update of enterprise login and change password URLs.
   // These URLs are used in enterprise password reuse detection.
@@ -575,7 +578,8 @@
   void SaveProtectedPasswordHashImpl(
       PasswordHashDataList protected_password_data_list,
       bool should_log_metrics,
-      bool does_primary_account_exists);
+      bool does_primary_account_exists,
+      bool is_signed_in);
 
   // Propagates enterprise login urls and change password url to
   // |reuse_detector_|.
diff --git a/components/payments/content/service_worker_payment_app_finder.h b/components/payments/content/service_worker_payment_app_finder.h
index 83574a9..afc68c1 100644
--- a/components/payments/content/service_worker_payment_app_finder.h
+++ b/components/payments/content/service_worker_payment_app_finder.h
@@ -87,9 +87,7 @@
   friend struct base::DefaultSingletonTraits<ServiceWorkerPaymentAppFinder>;
   friend class PaymentRequestPaymentAppTest;
   friend class ServiceWorkerPaymentAppFinderBrowserTest;
-  friend class HybridRequestSkipUITest;
   friend class PaymentRequestPlatformBrowserTestBase;
-  friend class PaymentHandlerJustInTimeInstallationTest;
 
   ServiceWorkerPaymentAppFinder();
   ~ServiceWorkerPaymentAppFinder();
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index 3c001208..02ea48a4 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -50,6 +50,7 @@
       "//components/policy/core/common",
     ]
     public_deps = [
+      "//build:branding_buildflags",
       "//components/policy/core/browser:internal",
       "//components/policy/core/common:internal",
     ]
diff --git a/components/policy/core/browser/BUILD.gn b/components/policy/core/browser/BUILD.gn
index 00e1f04..bb408ff 100644
--- a/components/policy/core/browser/BUILD.gn
+++ b/components/policy/core/browser/BUILD.gn
@@ -49,6 +49,7 @@
   public_deps = [ "//base" ]
   deps = [
     "//base/third_party/dynamic_annotations",
+    "//build:branding_buildflags",
     "//components/google/core/common",
     "//components/keyed_service/core",
     "//components/pref_registry",
diff --git a/components/policy/core/browser/policy_conversions.cc b/components/policy/core/browser/policy_conversions.cc
index 3aeebc1..f31846d 100644
--- a/components/policy/core/browser/policy_conversions.cc
+++ b/components/policy/core/browser/policy_conversions.cc
@@ -35,6 +35,14 @@
 
 PolicyConversions::~PolicyConversions() = default;
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+PolicyConversions& PolicyConversions::WithUpdaterPolicies(
+    std::unique_ptr<PolicyMap> policies) {
+  client()->SetUpdaterPolicies(std::move(policies));
+  return *this;
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
 PolicyConversions& PolicyConversions::EnableConvertTypes(bool enabled) {
   client_->EnableConvertTypes(enabled);
   return *this;
@@ -91,6 +99,11 @@
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
   }
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  if (client()->HasUpdaterPolicies())
+    all_policies.SetKey("updaterPolicies", client()->GetUpdaterPolicies());
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS) && defined(OS_CHROMEOS)
   all_policies.SetKey("loginScreenExtensionPolicies",
                       GetExtensionPolicies(POLICY_DOMAIN_SIGNIN_EXTENSIONS));
@@ -144,6 +157,11 @@
   if (client()->HasUserPolicies()) {
     all_policies.Append(GetChromePolicies());
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+    if (client()->HasUpdaterPolicies())
+      all_policies.Append(GetUpdaterPolicies());
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     for (auto& policy :
          client()->GetExtensionPolicies(POLICY_DOMAIN_EXTENSIONS).TakeList()) {
@@ -174,8 +192,19 @@
   return all_policies;
 }
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+Value ArrayPolicyConversions::GetUpdaterPolicies() {
+  Value chrome_policies_data(Value::Type::DICTIONARY);
+  chrome_policies_data.SetKey("name", Value("Updater Policies"));
+  chrome_policies_data.SetKey("id", Value("updater"));
+  chrome_policies_data.SetKey("policies", client()->GetUpdaterPolicies());
+  return chrome_policies_data;
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
 Value ArrayPolicyConversions::GetChromePolicies() {
   Value chrome_policies_data(Value::Type::DICTIONARY);
+  chrome_policies_data.SetKey("id", Value("chrome"));
   chrome_policies_data.SetKey("name", Value("Chrome Policies"));
   chrome_policies_data.SetKey("policies", client()->GetChromePolicies());
   return chrome_policies_data;
diff --git a/components/policy/core/browser/policy_conversions.h b/components/policy/core/browser/policy_conversions.h
index fcacd51..4c38e35 100644
--- a/components/policy/core/browser/policy_conversions.h
+++ b/components/policy/core/browser/policy_conversions.h
@@ -9,6 +9,8 @@
 #include <string>
 
 #include "base/values.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_namespace.h"
 #include "components/policy/core/common/policy_types.h"
@@ -50,6 +52,11 @@
   // Enabled by default.
   PolicyConversions& EnableUserPolicies(bool enabled);
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // Sets the updater policies.
+  PolicyConversions& WithUpdaterPolicies(std::unique_ptr<PolicyMap> policies);
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   // Returns the policy data as a base::Value object.
   virtual base::Value ToValue() = 0;
 
@@ -94,6 +101,10 @@
  private:
   base::Value GetChromePolicies();
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  base::Value GetUpdaterPolicies();
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   DISALLOW_COPY_AND_ASSIGN(ArrayPolicyConversions);
 };
 
diff --git a/components/policy/core/browser/policy_conversions_client.cc b/components/policy/core/browser/policy_conversions_client.cc
index e2e1b71..3e82cda 100644
--- a/components/policy/core/browser/policy_conversions_client.cc
+++ b/components/policy/core/browser/policy_conversions_client.cc
@@ -54,6 +54,13 @@
   user_policies_enabled_ = enabled;
 }
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+void PolicyConversionsClient::SetUpdaterPolicies(
+    std::unique_ptr<PolicyMap> policies) {
+  updater_policies_ = std::move(policies);
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
 std::string PolicyConversionsClient::ConvertValueToJSON(
     const Value& value) const {
   std::string json_string;
@@ -270,4 +277,17 @@
   return user_policies_enabled_;
 }
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+Value PolicyConversionsClient::GetUpdaterPolicies() {
+  return updater_policies_.get()
+             ? GetPolicyValues(*updater_policies_, nullptr, base::nullopt)
+             : base::Value(base::Value::Type::DICTIONARY);
+}
+
+bool PolicyConversionsClient::PolicyConversionsClient::HasUpdaterPolicies()
+    const {
+  return !!updater_policies_;
+}
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
 }  // namespace policy
diff --git a/components/policy/core/browser/policy_conversions_client.h b/components/policy/core/browser/policy_conversions_client.h
index 088a953..ad69749b 100644
--- a/components/policy/core/browser/policy_conversions_client.h
+++ b/components/policy/core/browser/policy_conversions_client.h
@@ -10,6 +10,8 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/values.h"
+#include "build/branding_buildflags.h"
+#include "build/build_config.h"
 #include "components/policy/core/browser/policy_conversions.h"
 #include "components/policy/core/common/schema.h"
 #include "components/policy/policy_export.h"
@@ -58,6 +60,15 @@
   // Enabled by default.
   void EnableUserPolicies(bool enabled);
 
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // Sets the updater policies.
+  void SetUpdaterPolicies(std::unique_ptr<PolicyMap> policies);
+  // Returns true if this client is able to return information on the updater's
+  // policies.
+  bool HasUpdaterPolicies() const;
+  base::Value GetUpdaterPolicies();
+#endif  // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
   // Converts the given |value| to JSON, respecting the configuration
   // preferences that were set on this client.
   std::string ConvertValueToJSON(const base::Value& value) const;
@@ -137,6 +148,10 @@
   bool GetUserPoliciesEnabled() const;
 
  private:
+#if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  std::unique_ptr<PolicyMap> updater_policies_;
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING) && defined(OS_WIN)
+
   bool convert_types_enabled_ = true;
   bool convert_values_enabled_ = false;
   bool device_local_account_policies_enabled_ = false;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index a35cda9..92782f4 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -11570,12 +11570,11 @@
           'caption': '''Disable all variations''',
         },
       ],
-      'supported_on': ['chrome.*:81-', 'chrome_os:81-'],
+      'supported_on': ['chrome.*:82-', 'chrome_os:82-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
       },
-      'future': True,
       'example_value': 1,
       'id': 663,
       'caption': '''Determine the availability of variations''',
diff --git a/components/policy/resources/webui/policy_base.js b/components/policy/resources/webui/policy_base.js
index 4133121..e418a9042 100644
--- a/components/policy/resources/webui/policy_base.js
+++ b/components/policy/resources/webui/policy_base.js
@@ -50,6 +50,7 @@
   /**
    * @typedef {{
    *     id: ?string,
+   *     isExtension?: boolean,
    *     name: string,
    *     policies: !Array<!Policy>
    * }}
@@ -496,8 +497,7 @@
     onPoliciesReceived_(policyNames, policyValues) {
       /** @type {Array<!PolicyTableModel>} */
       const policyGroups = policyValues.map(value => {
-        const knownPolicyNames =
-            (policyNames[value.id] || policyNames.chrome).policyNames;
+        const knownPolicyNames = policyNames[value.id].policyNames;
         const knownPolicyNamesSet = new Set(knownPolicyNames);
         const receivedPolicyNames = Object.keys(value.policies);
         const allPolicyNames =
@@ -518,7 +518,7 @@
           name: value.forSigninScreen ?
               `${value.name} [${loadTimeData.getString('signinProfile')}]` :
               value.name,
-          id: value.id,
+          id: value.isExtension ? value.id : null,
           policies
         };
       });
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge.cc b/components/send_tab_to_self/send_tab_to_self_bridge.cc
index fc98d47..41c66ee 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -208,6 +208,9 @@
     if (change->type() == syncer::EntityChange::ACTION_DELETE) {
       LogApplySyncChangesStatus(UMAApplySyncChangesStatus::DELETE);
       if (entries_.find(guid) != entries_.end()) {
+        if (mru_entry_ && mru_entry_->GetGUID() == guid) {
+          mru_entry_ = nullptr;
+        }
         entries_.erase(change->storage_key());
         batch->DeleteData(guid);
         removed.push_back(change->storage_key());
diff --git a/components/sessions/core/session_constants.cc b/components/sessions/core/session_constants.cc
index f1a26c7..52eb6816 100644
--- a/components/sessions/core/session_constants.cc
+++ b/components/sessions/core/session_constants.cc
@@ -6,6 +6,16 @@
 
 namespace sessions {
 
+const base::FilePath::StringPieceType kCurrentTabSessionFileName =
+    FILE_PATH_LITERAL("Current Tabs");
+const base::FilePath::StringPieceType kLastTabSessionFileName =
+    FILE_PATH_LITERAL("Last Tabs");
+
+const base::FilePath::StringPieceType kCurrentSessionFileName =
+    FILE_PATH_LITERAL("Current Session");
+const base::FilePath::StringPieceType kLastSessionFileName =
+    FILE_PATH_LITERAL("Last Session");
+
 const int gMaxPersistNavigationCount = 6;
 
 }  // namespace sessions
diff --git a/components/sessions/core/session_constants.h b/components/sessions/core/session_constants.h
index a81c4d3..5932a12 100644
--- a/components/sessions/core/session_constants.h
+++ b/components/sessions/core/session_constants.h
@@ -5,10 +5,23 @@
 #ifndef COMPONENTS_SESSIONS_CORE_SESSION_CONSTANTS_H_
 #define COMPONENTS_SESSIONS_CORE_SESSION_CONSTANTS_H_
 
+#include "base/files/file_path.h"
 #include "components/sessions/core/sessions_export.h"
 
 namespace sessions {
 
+// File names (current and previous) for a type of TAB.
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+    kCurrentTabSessionFileName;
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+    kLastTabSessionFileName;
+
+// File names (current and previous) for a type of SESSION.
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+    kCurrentSessionFileName;
+extern const base::FilePath::StringPieceType SESSIONS_EXPORT
+    kLastSessionFileName;
+
 // The maximum number of navigation entries in each direction to persist.
 extern const int SESSIONS_EXPORT gMaxPersistNavigationCount;
 
diff --git a/components/sessions/core/snapshotting_command_storage_backend.cc b/components/sessions/core/snapshotting_command_storage_backend.cc
index 656b9c0..8fb7dbdf 100644
--- a/components/sessions/core/snapshotting_command_storage_backend.cc
+++ b/components/sessions/core/snapshotting_command_storage_backend.cc
@@ -7,26 +7,19 @@
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "components/sessions/core/session_constants.h"
 
 namespace sessions {
 namespace {
 
-// File names (current and previous) for a type of TAB.
-static const char* kCurrentTabSessionFileName = "Current Tabs";
-static const char* kLastTabSessionFileName = "Last Tabs";
-
-// File names (current and previous) for a type of SESSION.
-static const char* kCurrentSessionFileName = "Current Session";
-static const char* kLastSessionFileName = "Last Session";
-
 base::FilePath GetCurrentFilePath(
     SnapshottingCommandStorageManager::SessionType type,
     const base::FilePath& base_path) {
   base::FilePath path = base_path;
   if (type == SnapshottingCommandStorageManager::TAB_RESTORE)
-    path = path.AppendASCII(kCurrentTabSessionFileName);
+    path = path.Append(kCurrentTabSessionFileName);
   else
-    path = path.AppendASCII(kCurrentSessionFileName);
+    path = path.Append(kCurrentSessionFileName);
   return path;
 }
 
@@ -35,9 +28,9 @@
     const base::FilePath& base_path) {
   base::FilePath path = base_path;
   if (type == SnapshottingCommandStorageManager::TAB_RESTORE)
-    path = path.AppendASCII(kLastTabSessionFileName);
+    path = path.Append(kLastTabSessionFileName);
   else
-    path = path.AppendASCII(kLastSessionFileName);
+    path = path.Append(kLastSessionFileName);
   return path;
 }
 
diff --git a/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc b/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
index cc5a34e..1ba1c68 100644
--- a/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
+++ b/components/sync_bookmarks/bookmark_specifics_conversions_unittest.cc
@@ -244,7 +244,6 @@
 
 TEST(BookmarkSpecificsConversionsTest,
      ShouldCreateBookmarkNodeFromSpecificsWithIllegalTitle) {
-  const std::string kGuid = base::GenerateGUID();
   std::unique_ptr<bookmarks::BookmarkModel> model =
       bookmarks::TestBookmarkClient::CreateModel();
   testing::NiceMock<favicon::MockFaviconService> favicon_service;
@@ -256,7 +255,7 @@
     sync_pb::EntitySpecifics specifics;
     sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
     bm_specifics->set_url("http://www.url.com");
-    bm_specifics->set_guid(kGuid);
+    bm_specifics->set_guid(base::GenerateGUID());
     // Legacy clients append an extra space to illegal clients.
     bm_specifics->set_title(illegal_title + " ");
     const bookmarks::BookmarkNode* node = CreateBookmarkNodeFromSpecifics(
diff --git a/components/ukm/ukm_service.h b/components/ukm/ukm_service.h
index ff8ab5f..fb704952 100644
--- a/components/ukm/ukm_service.h
+++ b/components/ukm/ukm_service.h
@@ -25,11 +25,7 @@
 class PrefService;
 FORWARD_DECLARE_TEST(ChromeMetricsServiceClientTest, TestRegisterUKMProviders);
 FORWARD_DECLARE_TEST(IOSChromeMetricsServiceClientTest,
-                     TestRegisterUKMProvidersWhenDisabled);
-FORWARD_DECLARE_TEST(IOSChromeMetricsServiceClientTest,
-                     TestRegisterUKMProvidersWhenForceMetricsReporting);
-FORWARD_DECLARE_TEST(IOSChromeMetricsServiceClientTest,
-                     TestRegisterUKMProvidersWhenUKMFeatureEnabled);
+                     TestRegisterUkmProvidersWhenUKMFeatureEnabled);
 
 namespace metrics {
 class MetricsServiceClient;
@@ -120,11 +116,7 @@
   FRIEND_TEST_ALL_PREFIXES(::ChromeMetricsServiceClientTest,
                            TestRegisterUKMProviders);
   FRIEND_TEST_ALL_PREFIXES(::IOSChromeMetricsServiceClientTest,
-                           TestRegisterUKMProvidersWhenDisabled);
-  FRIEND_TEST_ALL_PREFIXES(::IOSChromeMetricsServiceClientTest,
-                           TestRegisterUKMProvidersWhenForceMetricsReporting);
-  FRIEND_TEST_ALL_PREFIXES(::IOSChromeMetricsServiceClientTest,
-                           TestRegisterUKMProvidersWhenUKMFeatureEnabled);
+                           TestRegisterUkmProvidersWhenUKMFeatureEnabled);
   FRIEND_TEST_ALL_PREFIXES(UkmServiceTest,
                            PurgeExtensionDataFromUnsentLogStore);
 
diff --git a/components/update_client/request_sender.cc b/components/update_client/request_sender.cc
index 0a7e820b..47cac69 100644
--- a/components/update_client/request_sender.cc
+++ b/components/update_client/request_sender.cc
@@ -24,10 +24,10 @@
 namespace {
 
 // This is an ECDSA prime256v1 named-curve key.
-constexpr int kKeyVersion = 9;
+constexpr int kKeyVersion = 10;
 const char kKeyPubBytesBase64[] =
-    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsVwVMmIJaWBjktSx9m1JrZWYBvMm"
-    "bsrGGQPhScDtao+DloD871YmEeunAaQvRMZgDh1nCaWkVG6wo75+yDbKDA==";
+    "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzOqC8cKNUYIi0UkNu0smZKDW8w5/"
+    "0EmEw1KQ6Aj/5JWBMdUVm13EIVwFwPlkO/U6vXa+iu4wyUB89GFaldJ7Bg==";
 
 }  // namespace
 
diff --git a/components/viz/test/begin_frame_source_test.cc b/components/viz/test/begin_frame_source_test.cc
index b77ebcc..e5e325b 100644
--- a/components/viz/test/begin_frame_source_test.cc
+++ b/components/viz/test/begin_frame_source_test.cc
@@ -4,6 +4,7 @@
 
 #include "components/viz/test/begin_frame_source_test.h"
 
+#include "base/location.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -33,8 +34,7 @@
 #ifdef NDEBUG
         nullptr,
 #else
-        FROM_HERE_WITH_EXPLICIT_FUNCTION(
-            "MockBeginFrameObserver::kDefaultBeginFrameArgs"),
+        FROM_HERE,
 #endif
         BeginFrameArgs::kManualSourceId,
         BeginFrameArgs::kStartingFrameNumber,
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 8f5a12ea..f4c35c7 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -1414,19 +1414,11 @@
 }
 
 const std::vector<gfx::NativeViewAccessible>
-BrowserAccessibility::GetDescendants() const {
-  std::vector<gfx::NativeViewAccessible> descendants;
-  if (!IsIgnored() && PlatformChildCount() > 0) {
-    BrowserAccessibility* next_sibling_node = PlatformGetNextSibling();
-    BrowserAccessibility* next_descendant_node =
-        BrowserAccessibilityManager::NextInTreeOrder(this);
-    while (next_descendant_node && next_descendant_node != next_sibling_node) {
-      descendants.emplace_back(next_descendant_node->GetNativeViewAccessible());
-      next_descendant_node =
-          BrowserAccessibilityManager::NextInTreeOrder(next_descendant_node);
-    }
-  }
-  return descendants;
+BrowserAccessibility::GetUIADescendants() const {
+  // This method is only called on Windows. Other platforms should not call it.
+  // The BrowserAccessibilityWin subclass overrides this method.
+  NOTREACHED();
+  return {};
 }
 
 std::string BrowserAccessibility::GetLanguage() const {
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index 93f958b93..7972319 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -483,7 +483,8 @@
       ax::mojom::MoveDirection direction,
       ax::mojom::TextAffinity affinity) const override;
 
-  const std::vector<gfx::NativeViewAccessible> GetDescendants() const override;
+  const std::vector<gfx::NativeViewAccessible> GetUIADescendants()
+      const override;
 
   std::string GetLanguage() const override;
 
diff --git a/content/browser/accessibility/browser_accessibility_mac_unittest.mm b/content/browser/accessibility/browser_accessibility_mac_unittest.mm
index 118c9f6..d9be35fd 100644
--- a/content/browser/accessibility/browser_accessibility_mac_unittest.mm
+++ b/content/browser/accessibility/browser_accessibility_mac_unittest.mm
@@ -241,9 +241,6 @@
 
 // Test Mac-specific table APIs.
 TEST_F(BrowserAccessibilityMacTest, TableAPIs) {
-  // This line is needed until we turn extra mac nodes back on by default.
-  BrowserAccessibilityManager::AllowExtraMacNodesForTesting();
-
   ui::AXTreeUpdate initial_state;
   initial_state.root_id = 1;
   initial_state.nodes.resize(7);
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index 0554096..77f9e8d 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -224,9 +224,6 @@
 bool BrowserAccessibilityManager::never_suppress_or_delay_events_for_testing_ =
     false;
 
-// A flag for use in tests to indicate that extra mac nodes are allowed.
-bool BrowserAccessibilityManager::allow_extra_mac_nodes_for_testing_ = false;
-
 // static
 base::Optional<int32_t> BrowserAccessibilityManager::last_focused_node_id_ = {};
 
@@ -694,16 +691,6 @@
   never_suppress_or_delay_events_for_testing_ = true;
 }
 
-// static
-void BrowserAccessibilityManager::AllowExtraMacNodesForTesting() {
-  allow_extra_mac_nodes_for_testing_ = true;
-}
-
-// static
-bool BrowserAccessibilityManager::GetExtraMacNodesAllowed() {
-  return allow_extra_mac_nodes_for_testing_;
-}
-
 void BrowserAccessibilityManager::Decrement(const BrowserAccessibility& node) {
   if (!delegate_)
     return;
@@ -927,6 +914,24 @@
 }
 
 // static
+// Next non-descendant object in tree using depth-first pre-order traversal.
+BrowserAccessibility* BrowserAccessibilityManager::NextNonDescendantInTreeOrder(
+    const BrowserAccessibility* object) {
+  if (!object)
+    return nullptr;
+
+  while (object) {
+    BrowserAccessibility* sibling = object->PlatformGetNextSibling();
+    if (sibling)
+      return sibling;
+
+    object = object->PlatformGetParent();
+  }
+
+  return nullptr;
+}
+
+// static
 // Previous object in tree using depth-first pre-order traversal.
 BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
     const BrowserAccessibility* object,
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index e212dd8d..63135b2 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -237,12 +237,6 @@
   // had focus.
   static void NeverSuppressOrDelayEventsForTesting();
 
-  // Extra mac nodes are temporarily disabled, except for in tests.
-  static void AllowExtraMacNodesForTesting();
-  // Are extra mac nodes allowed at all? Currently only allowed in tests.
-  // Even when returning true, platforms other than Mac OS do not enable them.
-  static bool GetExtraMacNodesAllowed();
-
   // Accessibility actions. All of these are implemented asynchronously
   // by sending a message to the renderer to perform the respective action
   // on the given node.  See the definition of |ui::AXActionData| for more
@@ -360,6 +354,8 @@
   // Walk the tree using depth-first pre-order traversal.
   static BrowserAccessibility* NextInTreeOrder(
       const BrowserAccessibility* object);
+  static BrowserAccessibility* NextNonDescendantInTreeOrder(
+      const BrowserAccessibility* object);
   static BrowserAccessibility* PreviousInTreeOrder(
       const BrowserAccessibility* object,
       bool can_wrap_to_last_element);
@@ -539,9 +535,6 @@
   // flakiness. See NeverSuppressOrDelayEventsForTesting() for details.
   static bool never_suppress_or_delay_events_for_testing_;
 
-  // Extra mac nodes are disabled even on mac currently, except for in tests.
-  static bool allow_extra_mac_nodes_for_testing_;
-
   const ui::AXEventGenerator& event_generator() const {
     return event_generator_;
   }
diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm
index 7084d84e..292d3ed8 100644
--- a/content/browser/accessibility/browser_accessibility_manager_mac.mm
+++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm
@@ -123,10 +123,7 @@
     BrowserAccessibilityFactory* factory)
     : BrowserAccessibilityManager(delegate, factory) {
   Initialize(initial_tree);
-  // Temporary fix. Disable extra mac nodes, which only affects column
-  // navigation but fixes a number of crash bugs seen only with VoiceOver.
-  // This does not affect verbalization of columns headers in cell navigation.
-  ax_tree()->SetEnableExtraMacNodes(GetExtraMacNodesAllowed());
+  ax_tree()->SetEnableExtraMacNodes(true);
 }
 
 BrowserAccessibilityManagerMac::~BrowserAccessibilityManagerMac() {}
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index 29315ee..f9aaaa5 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -1161,6 +1161,56 @@
                                                       *root_accessible));
 }
 
+TEST_F(BrowserAccessibilityManagerTest, TestNextNonDescendantInTreeOrder) {
+  ui::AXNodeData root;
+  root.id = 1;
+  root.role = ax::mojom::Role::kRootWebArea;
+
+  ui::AXNodeData node2;
+  node2.id = 2;
+  root.child_ids.push_back(2);
+
+  ui::AXNodeData node3;
+  node3.id = 3;
+  root.child_ids.push_back(3);
+
+  ui::AXNodeData node4;
+  node4.id = 4;
+  node3.child_ids.push_back(4);
+
+  ui::AXNodeData node5;
+  node5.id = 5;
+  root.child_ids.push_back(5);
+
+  std::unique_ptr<BrowserAccessibilityManager> manager(
+      BrowserAccessibilityManager::Create(
+          MakeAXTreeUpdate(root, node2, node3, node4, node5),
+          test_browser_accessibility_delegate_.get(),
+          new CountedBrowserAccessibilityFactory()));
+
+  BrowserAccessibility* root_accessible = manager->GetRoot();
+  ASSERT_NE(nullptr, root_accessible);
+  ASSERT_EQ(3U, root_accessible->PlatformChildCount());
+  BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
+  ASSERT_NE(nullptr, node2_accessible);
+  BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
+  ASSERT_NE(nullptr, node3_accessible);
+  ASSERT_EQ(1U, node3_accessible->PlatformChildCount());
+  BrowserAccessibility* node4_accessible =
+      node3_accessible->PlatformGetChild(0);
+  ASSERT_NE(nullptr, node4_accessible);
+  BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
+  ASSERT_NE(nullptr, node5_accessible);
+
+  EXPECT_EQ(nullptr, manager->NextNonDescendantInTreeOrder(nullptr));
+  EXPECT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
+  EXPECT_EQ(node3_accessible,
+            manager->NextNonDescendantInTreeOrder(node2_accessible));
+  EXPECT_EQ(node5_accessible,
+            manager->NextNonDescendantInTreeOrder(node3_accessible));
+  EXPECT_EQ(nullptr, manager->NextNonDescendantInTreeOrder(node5_accessible));
+}
+
 TEST_F(BrowserAccessibilityManagerTest, TestNextPreviousTextOnlyObject) {
   ui::AXNodeData root;
   root.id = 1;
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index 03cbecf0..a744955 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -12,24 +12,6 @@
 
 namespace content {
 
-#define EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants,                 \
-                                                expected_descendants)        \
-  {                                                                          \
-    size_t count = descendants.size();                                       \
-    EXPECT_EQ(count, expected_descendants.size());                           \
-    for (size_t i = 0; i < count; ++i) {                                     \
-      EXPECT_EQ(ui::AXPlatformNode::FromNativeViewAccessible(descendants[i]) \
-                    ->GetDelegate()                                          \
-                    ->GetData()                                              \
-                    .ToString(),                                             \
-                ui::AXPlatformNode::FromNativeViewAccessible(                \
-                    expected_descendants[i])                                 \
-                    ->GetDelegate()                                          \
-                    ->GetData()                                              \
-                    .ToString());                                            \
-    }                                                                        \
-  }
-
 class BrowserAccessibilityTest : public testing::Test {
  public:
   BrowserAccessibilityTest();
@@ -97,117 +79,6 @@
 }
 
 #if defined(OS_WIN) || BUILDFLAG(USE_ATK)
-TEST_F(BrowserAccessibilityTest, TestGetDescendants) {
-  // Set up ax tree with the following structure:
-  //
-  // root_____________________
-  // |               |       |
-  // para1____       text3   para2____ (hidden)
-  // |       |               |       |
-  // text1   text2           text4   text5 (visible)
-  ui::AXNodeData text1;
-  text1.id = 111;
-  text1.role = ax::mojom::Role::kStaticText;
-  text1.SetName("One two three.");
-
-  ui::AXNodeData text2;
-  text2.id = 112;
-  text2.role = ax::mojom::Role::kStaticText;
-  text2.SetName("Two three four.");
-
-  ui::AXNodeData text3;
-  text3.id = 113;
-  text3.role = ax::mojom::Role::kStaticText;
-  text3.SetName("Three four five.");
-
-  ui::AXNodeData text4;
-  text4.id = 114;
-  text4.role = ax::mojom::Role::kStaticText;
-  text4.SetName("four five six.");
-  text4.AddState(ax::mojom::State::kIgnored);
-
-  ui::AXNodeData text5;
-  text5.id = 115;
-  text5.role = ax::mojom::Role::kStaticText;
-  text5.SetName("five six seven.");
-
-  ui::AXNodeData para1;
-  para1.id = 11;
-  para1.role = ax::mojom::Role::kParagraph;
-  para1.child_ids.push_back(text1.id);
-  para1.child_ids.push_back(text2.id);
-
-  ui::AXNodeData para2;
-  para2.id = 12;
-  para2.role = ax::mojom::Role::kParagraph;
-  para2.child_ids.push_back(text4.id);
-  para2.child_ids.push_back(text5.id);
-  para2.AddState(ax::mojom::State::kIgnored);
-
-  ui::AXNodeData root;
-  root.id = 1;
-  root.role = ax::mojom::Role::kRootWebArea;
-  root.child_ids.push_back(para1.id);
-  root.child_ids.push_back(text3.id);
-  root.child_ids.push_back(para2.id);
-
-  std::unique_ptr<BrowserAccessibilityManager> manager(
-      BrowserAccessibilityManager::Create(
-          MakeAXTreeUpdate(root, para1, text1, text2, text3, para2, text4,
-                           text5),
-          test_browser_accessibility_delegate_.get(),
-          new BrowserAccessibilityFactory()));
-
-  BrowserAccessibility* root_obj = manager->GetRoot();
-  BrowserAccessibility* para_obj = root_obj->PlatformGetChild(0);
-  BrowserAccessibility* text1_obj = manager->GetFromID(111);
-  BrowserAccessibility* text2_obj = manager->GetFromID(112);
-  BrowserAccessibility* text3_obj = manager->GetFromID(113);
-  BrowserAccessibility* para2_obj = manager->GetFromID(12);
-  BrowserAccessibility* text4_obj = manager->GetFromID(114);
-  BrowserAccessibility* text5_obj = root_obj->PlatformGetChild(2);
-
-  // Leaf nodes should have no children.
-  std::vector<gfx::NativeViewAccessible> descendants =
-      text1_obj->GetDescendants();
-  std::vector<gfx::NativeViewAccessible> expected_descendants = {};
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  descendants = text2_obj->GetDescendants();
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  descendants = text3_obj->GetDescendants();
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  descendants = text4_obj->GetDescendants();
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  descendants = text5_obj->GetDescendants();
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  descendants = para2_obj->GetDescendants();
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  // Verify that para1 has two children (text1 and tex2).
-  descendants = para_obj->GetDescendants();
-  expected_descendants = {text1_obj->GetNativeViewAccessible(),
-                          text2_obj->GetNativeViewAccessible()};
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  // Calling GetChildNodeIds on the root should encompass the entire
-  // right and left subtrees (para1, text1, text2, and text3).
-  // para2 and its subtree should be ignored, except for text5
-  descendants = root_obj->GetDescendants();
-  expected_descendants = {para_obj->GetNativeViewAccessible(),
-                          text1_obj->GetNativeViewAccessible(),
-                          text2_obj->GetNativeViewAccessible(),
-                          text3_obj->GetNativeViewAccessible(),
-                          text5_obj->GetNativeViewAccessible()};
-  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
-
-  manager.reset();
-}
-
 TEST_F(BrowserAccessibilityTest, PlatformChildIterator) {
   // (i) => node is ignored
   // Parent Tree
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index 948c9a13..5124398 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -63,6 +63,37 @@
   return GetCOM()->AXPlatformNodeWin::GetHypertext();
 }
 
+const std::vector<gfx::NativeViewAccessible>
+BrowserAccessibilityWin::GetUIADescendants() const {
+  std::vector<gfx::NativeViewAccessible> descendants;
+  if (!IsIgnored() && !ShouldHideChildrenForUIA() && PlatformChildCount() > 0) {
+    BrowserAccessibility* next_sibling_node = PlatformGetNextSibling();
+    BrowserAccessibility* next_descendant_node =
+        BrowserAccessibilityManager::NextInTreeOrder(this);
+
+    while (next_descendant_node && next_descendant_node != next_sibling_node) {
+      // Don't add an ignored node to the returned descendants.
+      if (!next_descendant_node->IsIgnored()) {
+        descendants.emplace_back(
+            next_descendant_node->GetNativeViewAccessible());
+
+        if (!ToBrowserAccessibilityWin(next_descendant_node)
+                 ->ShouldHideChildrenForUIA()) {
+          next_descendant_node = BrowserAccessibilityManager::NextInTreeOrder(
+              next_descendant_node);
+          continue;
+        }
+      }
+      // When a node is ignored or hides its children, don't return any of its
+      // descendants.
+      next_descendant_node =
+          BrowserAccessibilityManager::NextNonDescendantInTreeOrder(
+              next_descendant_node);
+    }
+  }
+  return descendants;
+}
+
 gfx::NativeViewAccessible BrowserAccessibilityWin::GetNativeViewAccessible() {
   return GetCOM();
 }
@@ -87,4 +118,8 @@
   return GetCOM()->AXPlatformNodeWin::ComputeTextAttributes();
 }
 
+bool BrowserAccessibilityWin::ShouldHideChildrenForUIA() const {
+  return GetCOM()->AXPlatformNodeWin::ShouldHideChildrenForUIA();
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/browser_accessibility_win.h b/content/browser/accessibility/browser_accessibility_win.h
index 4065cab6..6087e0f 100644
--- a/content/browser/accessibility/browser_accessibility_win.h
+++ b/content/browser/accessibility/browser_accessibility_win.h
@@ -31,6 +31,9 @@
   base::string16 GetText() const override;
   base::string16 GetHypertext() const override;
 
+  const std::vector<gfx::NativeViewAccessible> GetUIADescendants()
+      const override;
+
   gfx::NativeViewAccessible GetNativeViewAccessible() override;
 
   class BrowserAccessibilityComWin* GetCOM() const;
@@ -38,6 +41,8 @@
  protected:
   ui::TextAttributeList ComputeTextAttributes() const override;
 
+  bool ShouldHideChildrenForUIA() const;
+
  private:
   CComObject<BrowserAccessibilityComWin>* browser_accessibility_com_;
   // Give BrowserAccessibility::Create access to our constructor.
diff --git a/content/browser/accessibility/browser_accessibility_win_unittest.cc b/content/browser/accessibility/browser_accessibility_win_unittest.cc
index 4f22194f..7aaa472 100644
--- a/content/browser/accessibility/browser_accessibility_win_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_win_unittest.cc
@@ -72,6 +72,24 @@
     EXPECT_STREQ(text, actual_text.Get());                           \
   }
 
+#define EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants,                 \
+                                                expected_descendants)        \
+  {                                                                          \
+    size_t count = descendants.size();                                       \
+    EXPECT_EQ(count, expected_descendants.size());                           \
+    for (size_t i = 0; i < count; ++i) {                                     \
+      EXPECT_EQ(ui::AXPlatformNode::FromNativeViewAccessible(descendants[i]) \
+                    ->GetDelegate()                                          \
+                    ->GetData()                                              \
+                    .ToString(),                                             \
+                ui::AXPlatformNode::FromNativeViewAccessible(                \
+                    expected_descendants[i])                                 \
+                    ->GetDelegate()                                          \
+                    ->GetData()                                              \
+                    .ToString());                                            \
+    }                                                                        \
+  }
+
 // BrowserAccessibilityWinTest ------------------------------------------------
 
 class BrowserAccessibilityWinTest : public testing::Test {
@@ -807,6 +825,141 @@
   manager.reset();
 }
 
+TEST_F(BrowserAccessibilityWinTest, TestGetUIADescendants) {
+  // Set up ax tree with the following structure:
+  //
+  // root___________________________________________________
+  // |               |       |                              |
+  // para1____       text3   para2____ (hidden)             button
+  // |       |               |       |                      |
+  // text1   text2           text4   text5 (visible)        image
+  ui::AXNodeData text1;
+  text1.id = 111;
+  text1.role = ax::mojom::Role::kStaticText;
+  text1.SetName("One two three.");
+
+  ui::AXNodeData text2;
+  text2.id = 112;
+  text2.role = ax::mojom::Role::kStaticText;
+  text2.SetName("Two three four.");
+
+  ui::AXNodeData text3;
+  text3.id = 113;
+  text3.role = ax::mojom::Role::kStaticText;
+  text3.SetName("Three four five.");
+
+  ui::AXNodeData text4;
+  text4.id = 114;
+  text4.role = ax::mojom::Role::kStaticText;
+  text4.SetName("four five six.");
+  text4.AddState(ax::mojom::State::kIgnored);
+
+  ui::AXNodeData text5;
+  text5.id = 115;
+  text5.role = ax::mojom::Role::kStaticText;
+  text5.SetName("five six seven.");
+
+  ui::AXNodeData image;
+  image.id = 116;
+  image.role = ax::mojom::Role::kImage;
+
+  ui::AXNodeData para1;
+  para1.id = 11;
+  para1.role = ax::mojom::Role::kParagraph;
+  para1.child_ids.push_back(text1.id);
+  para1.child_ids.push_back(text2.id);
+
+  ui::AXNodeData para2;
+  para2.id = 12;
+  para2.role = ax::mojom::Role::kParagraph;
+  para2.child_ids.push_back(text4.id);
+  para2.child_ids.push_back(text5.id);
+  para2.AddState(ax::mojom::State::kIgnored);
+
+  ui::AXNodeData button;
+  button.id = 13;
+  button.role = ax::mojom::Role::kButton;
+  button.child_ids.push_back(image.id);
+
+  ui::AXNodeData root;
+  root.id = 1;
+  root.role = ax::mojom::Role::kRootWebArea;
+  root.child_ids.push_back(para1.id);
+  root.child_ids.push_back(text3.id);
+  root.child_ids.push_back(para2.id);
+  root.child_ids.push_back(button.id);
+
+  std::unique_ptr<BrowserAccessibilityManager> manager(
+      BrowserAccessibilityManager::Create(
+          MakeAXTreeUpdate(root, para1, text1, text2, text3, para2, text4,
+                           text5, button, image),
+          test_browser_accessibility_delegate_.get(),
+          new BrowserAccessibilityFactory()));
+
+  BrowserAccessibility* root_obj = manager->GetRoot();
+  BrowserAccessibility* para_obj = root_obj->PlatformGetChild(0);
+  BrowserAccessibility* text1_obj = manager->GetFromID(111);
+  BrowserAccessibility* text2_obj = manager->GetFromID(112);
+  BrowserAccessibility* text3_obj = manager->GetFromID(113);
+  BrowserAccessibility* para2_obj = manager->GetFromID(12);
+  BrowserAccessibility* text4_obj = manager->GetFromID(114);
+  BrowserAccessibility* text5_obj = root_obj->PlatformGetChild(2);
+  BrowserAccessibility* button_obj = manager->GetFromID(13);
+  BrowserAccessibility* image_obj = manager->GetFromID(116);
+
+  // Leaf nodes should have no children.
+  std::vector<gfx::NativeViewAccessible> descendants =
+      text1_obj->GetUIADescendants();
+  std::vector<gfx::NativeViewAccessible> expected_descendants = {};
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = text2_obj->GetUIADescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = text3_obj->GetUIADescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = text4_obj->GetUIADescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = text5_obj->GetUIADescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = para2_obj->GetUIADescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  descendants = image_obj->GetUIADescendants();
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  // Verify that para1 has two children (text1 and tex2).
+  descendants = para_obj->GetUIADescendants();
+  expected_descendants = {text1_obj->GetNativeViewAccessible(),
+                          text2_obj->GetNativeViewAccessible()};
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  // Verify that the button hides its child.
+  descendants = button_obj->GetUIADescendants();
+  expected_descendants = {};
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  // Calling GetChildNodeIds on the root should encompass the entire
+  // right and left subtrees (para1, text1, text2, and text3).
+  // para2 and its subtree should be ignored, except for text5. The image should
+  // be ignored, but not the button.
+  LOG(INFO) << "HERE";
+
+  descendants = root_obj->GetUIADescendants();
+  expected_descendants = {para_obj->GetNativeViewAccessible(),
+                          text1_obj->GetNativeViewAccessible(),
+                          text2_obj->GetNativeViewAccessible(),
+                          text3_obj->GetNativeViewAccessible(),
+                          text5_obj->GetNativeViewAccessible(),
+                          button_obj->GetNativeViewAccessible()};
+  EXPECT_NATIVE_VIEW_ACCESSIBLE_VECTOR_EQ(descendants, expected_descendants);
+
+  manager.reset();
+}
+
 TEST_F(BrowserAccessibilityWinTest, TestCreateEmptyDocument) {
   // Try creating an empty document with busy state. Readonly is
   // set automatically.
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index 8e0cd50..f7f5d3f 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -242,10 +242,6 @@
   // flaky.
   BrowserAccessibilityManager::NeverSuppressOrDelayEventsForTesting();
 
-  // Extra mac nodes are disabled temporarily for stability purposes, but keep
-  // them on for tests.
-  BrowserAccessibilityManager::AllowExtraMacNodesForTesting();
-
   EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
 
   // Exit without running the test if we can't find an expectation file.
diff --git a/content/browser/code_cache/generated_code_cache_context.cc b/content/browser/code_cache/generated_code_cache_context.cc
index 738e8b9..1ac9d10 100644
--- a/content/browser/code_cache/generated_code_cache_context.cc
+++ b/content/browser/code_cache/generated_code_cache_context.cc
@@ -24,12 +24,9 @@
       new GeneratedCodeCache(path.AppendASCII("js"), max_bytes,
                              GeneratedCodeCache::CodeCacheType::kJavaScript));
 
-  // Only create the Wasm cache if it's enabled.
-  if (base::FeatureList::IsEnabled(blink::features::kWasmCodeCache)) {
-    generated_wasm_code_cache_.reset(new GeneratedCodeCache(
-        path.AppendASCII("wasm"), max_bytes,
-        GeneratedCodeCache::CodeCacheType::kWebAssembly));
-  }
+  generated_wasm_code_cache_.reset(
+      new GeneratedCodeCache(path.AppendASCII("wasm"), max_bytes,
+                             GeneratedCodeCache::CodeCacheType::kWebAssembly));
 }
 
 void GeneratedCodeCacheContext::Shutdown() {
diff --git a/content/browser/frame_host/render_frame_host_manager_unittest.cc b/content/browser/frame_host/render_frame_host_manager_unittest.cc
index e8f39d1..b0fd031 100644
--- a/content/browser/frame_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/with_feature_override.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/browser/child_process_security_policy_impl.h"
@@ -280,15 +281,11 @@
 
 }  // namespace
 
-class RenderFrameHostManagerTest : public RenderViewHostImplTestHarness,
-                                   public ::testing::WithParamInterface<bool> {
+class RenderFrameHostManagerTest : public base::test::WithFeatureOverride,
+                                   public RenderViewHostImplTestHarness {
  public:
-  RenderFrameHostManagerTest() {
-    if (GetParam()) {
-      feature_list_.InitAndEnableFeature(
-          features::kRenderDocumentForCrashedFrame);
-    }
-  }
+  RenderFrameHostManagerTest()
+      : WithFeatureOverride(features::kRenderDocumentForCrashedFrame) {}
 
   void SetUp() override {
     RenderViewHostImplTestHarness::SetUp();
@@ -3526,7 +3523,7 @@
   ExpectAdSubframeSignalForFrameProxy(proxy_to_main_frame, true);
 }
 
-INSTANTIATE_TEST_SUITE_P(All, RenderFrameHostManagerTest, ::testing::Bool());
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(RenderFrameHostManagerTest);
 INSTANTIATE_TEST_SUITE_P(All,
                          RenderFrameHostManagerTestWithSiteIsolation,
                          ::testing::Bool());
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 4fc132e..f8dc037 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/test/null_task_runner.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
+#include "base/test/with_feature_override.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -5182,18 +5183,11 @@
 }
 
 class TouchpadRenderWidgetHostViewAuraTest
-    : public RenderWidgetHostViewAuraTest,
-      public testing::WithParamInterface<bool> {
+    : public base::test::WithFeatureOverride,
+      public RenderWidgetHostViewAuraTest {
  public:
-  TouchpadRenderWidgetHostViewAuraTest() {
-    if (GetParam()) {
-      scoped_feature_list_.InitAndEnableFeature(
-          features::kTouchpadAsyncPinchEvents);
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          features::kTouchpadAsyncPinchEvents);
-    }
-  }
+  TouchpadRenderWidgetHostViewAuraTest()
+      : WithFeatureOverride(features::kTouchpadAsyncPinchEvents) {}
   ~TouchpadRenderWidgetHostViewAuraTest() override = default;
 
  private:
@@ -5201,9 +5195,7 @@
   DISALLOW_COPY_AND_ASSIGN(TouchpadRenderWidgetHostViewAuraTest);
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         TouchpadRenderWidgetHostViewAuraTest,
-                         testing::Bool());
+INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(TouchpadRenderWidgetHostViewAuraTest);
 
 // Test that we elide touchpad pinch gesture steams consisting of only begin
 // and end events.
diff --git a/content/browser/storage_partition_impl_unittest.cc b/content/browser/storage_partition_impl_unittest.cc
index 15b090b..4b23c04e 100644
--- a/content/browser/storage_partition_impl_unittest.cc
+++ b/content/browser/storage_partition_impl_unittest.cc
@@ -1476,10 +1476,6 @@
 TEST_F(StoragePartitionImplTest, ClearWasmCodeCache) {
   const GURL kResourceURL("http://host4/script.js");
 
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(blink::features::kWasmCodeCache);
-  ASSERT_TRUE(base::FeatureList::IsEnabled(blink::features::kWasmCodeCache));
-
   StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
       BrowserContext::GetDefaultStoragePartition(browser_context()));
   // Ensure code cache is initialized.
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 44bc490..a3dc3ee 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -722,8 +722,9 @@
     rwhv->SetSize(size);
 }
 
-void WebContentsViewAura::EndDrag(RenderWidgetHost* source_rwh,
-                                  blink::WebDragOperationsMask ops) {
+void WebContentsViewAura::EndDrag(
+    base::WeakPtr<RenderWidgetHostImpl> source_rwh_weak_ptr,
+    blink::WebDragOperationsMask ops) {
   drag_start_process_id_ = ChildProcessHost::kInvalidUniqueID;
   drag_start_view_id_ = GlobalRoutingID(ChildProcessHost::kInvalidUniqueID,
                                         MSG_ROUTING_NONE);
@@ -731,6 +732,9 @@
   if (!web_contents_)
     return;
 
+  // It is OK for source_rwh to be null.
+  RenderWidgetHost* source_rwh = source_rwh_weak_ptr.get();
+
   aura::Window* window = GetContentNativeView();
   gfx::PointF screen_loc =
       gfx::PointF(display::Screen::GetScreen()->GetCursorScreenPoint());
@@ -1110,11 +1114,11 @@
   // callback yet. So we have to make sure to delay calling EndDrag until drop
   // is done.
   if (!drag_in_progress_)
-    EndDrag(source_rwh_weak_ptr.get(), ConvertToWeb(result_op));
+    EndDrag(std::move(source_rwh_weak_ptr), ConvertToWeb(result_op));
   else
     end_drag_runner_ = base::ScopedClosureRunner(base::BindOnce(
         &WebContentsViewAura::EndDrag, weak_ptr_factory_.GetWeakPtr(),
-        source_rwh_weak_ptr.get(), ConvertToWeb(result_op)));
+        std::move(source_rwh_weak_ptr), ConvertToWeb(result_op)));
 }
 
 void WebContentsViewAura::UpdateDragCursor(blink::WebDragOperation operation) {
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index dd0c06b..c487a37 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -108,7 +108,8 @@
 
   void SizeChangedCommon(const gfx::Size& size);
 
-  void EndDrag(RenderWidgetHost* source_rwh, blink::WebDragOperationsMask ops);
+  void EndDrag(base::WeakPtr<RenderWidgetHostImpl> source_rwh_weak_ptr,
+               blink::WebDragOperationsMask ops);
 
   void InstallOverscrollControllerDelegate(RenderWidgetHostViewAura* view);
 
diff --git a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
index 8c5792c..6460653 100644
--- a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
+++ b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc
@@ -59,9 +59,6 @@
 
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kUseFakeDeviceForMediaStream);
-
-    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kEnableBlinkFeatures, "GetUserMedia");
   }
 
   void MaybeForceDisableEncodeAccelerator(bool disable) {
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 141ac8d..682a9cc 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -241,8 +241,6 @@
            kUseFeatureState},
           {wf::EnableCacheInlineScriptCode, features::kCacheInlineScriptCode,
            kUseFeatureState},
-          {wf::EnableWasmCodeCache, blink::features::kWasmCodeCache,
-           kUseFeatureState},
           {wf::EnableExperimentalProductivityFeatures,
            features::kExperimentalProductivityFeatures, kEnableOnly},
           {wf::EnableFeaturePolicyForSandbox,
diff --git a/content/public/test/nested_message_pump_android.cc b/content/public/test/nested_message_pump_android.cc
index 8e7b22e3..4c210698 100644
--- a/content/public/test/nested_message_pump_android.cc
+++ b/content/public/test/nested_message_pump_android.cc
@@ -34,9 +34,6 @@
 
   // Used to sleep until there is more work to do.
   base::WaitableEvent waitable_event;
-
-  // The time at which we should call DoDelayedWork.
-  base::TimeTicks delayed_work_time;
 };
 
 NestedMessagePumpAndroid::NestedMessagePumpAndroid()
@@ -63,23 +60,19 @@
     if (state_->should_quit)
       break;
 
-    bool did_work = state_->delegate->DoWork();
+    Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
+    bool has_more_immediate_work = next_work_info.is_immediate();
     if (state_->should_quit)
       break;
 
-    did_work |= state_->delegate->DoDelayedWork(&state_->delayed_work_time);
-    if (state_->should_quit)
-      break;
-
-    if (did_work) {
+    if (has_more_immediate_work)
       continue;
-    }
 
-    did_work = state_->delegate->DoIdleWork();
+    has_more_immediate_work = state_->delegate->DoIdleWork();
     if (state_->should_quit)
       break;
 
-    if (did_work)
+    if (has_more_immediate_work)
       continue;
 
     // No native tasks to process right now. Process tasks from the Java
@@ -88,20 +81,14 @@
     bool ret = Java_NestedSystemMessageHandler_runNestedLoopTillIdle(env);
     CHECK(ret) << "Error running java message loop, tests will likely fail.";
 
-    if (state_->delayed_work_time.is_null()) {
+    if (next_work_info.delayed_run_time.is_max()) {
       state_->waitable_event.TimedWait(max_delay);
     } else {
-      base::TimeDelta delay =
-          state_->delayed_work_time - base::TimeTicks::Now();
+      base::TimeDelta delay = next_work_info.remaining_delay();
       if (delay > max_delay)
         delay = max_delay;
-      if (delay > base::TimeDelta()) {
-        state_->waitable_event.TimedWait(delay);
-      } else {
-        // It looks like delayed_work_time indicates a time in the past, so we
-        // need to call DoDelayedWork now.
-        state_->delayed_work_time = base::TimeTicks();
-      }
+      DCHECK_GT(delay, base::TimeDelta());
+      state_->waitable_event.TimedWait(delay);
     }
   }
 
@@ -127,13 +114,11 @@
 
 void NestedMessagePumpAndroid::ScheduleDelayedWork(
     const base::TimeTicks& delayed_work_time) {
-  if (state_) {
-    // We know that we can't be blocked on Wait right now since this method can
-    // only be called on the same thread as Run, so we only need to update our
-    // record of how long to sleep when we do sleep.
-    state_->delayed_work_time = delayed_work_time;
-    return;
-  }
+  // Since this is always called from the same thread as Run(), there is nothing
+  // to do as the loop is already running. It will wait in Run() with the
+  // correct timeout when it's out of immediate tasks.
+  // TODO(gab): Consider removing ScheduleDelayedWork() when all pumps function
+  // this way (bit.ly/merge-message-pump-do-work).
 }
 
 }  // namespace content
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 052d740..5ec7180f 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/public/platform/web_url_request.h"
 #include "third_party/blink/public/web/blink.h"
 #include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_input_element.h"
 #include "third_party/blink/public/web/web_local_frame.h"
diff --git a/content/renderer/gpu_benchmarking_extension.cc b/content/renderer/gpu_benchmarking_extension.cc
index b15f110..8df7050f 100644
--- a/content/renderer/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu_benchmarking_extension.cc
@@ -50,6 +50,7 @@
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "third_party/blink/public/common/page/page_visibility_state.h"
 #include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_image_cache.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_print_params.h"
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index 4d0f0abb..58b5441 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -52,6 +52,7 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_view.h"
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 5ec5092..05c274c 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -87,6 +87,7 @@
 #include "third_party/blink/public/web/web_device_emulation_params.h"
 #include "third_party/blink/public/web/web_document_loader.h"
 #include "third_party/blink/public/web/web_frame_content_dumper.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_history_commit_type.h"
 #include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_input_method_controller.h"
diff --git a/content/shell/browser/shell.cc b/content/shell/browser/shell.cc
index da7ee8e..cb39064 100644
--- a/content/shell/browser/shell.cc
+++ b/content/shell/browser/shell.cc
@@ -192,8 +192,8 @@
 }
 
 void Shell::QuitMainMessageLoopForTesting() {
-  DCHECK(*g_quit_main_message_loop);
-  std::move(*g_quit_main_message_loop).Run();
+  if (*g_quit_main_message_loop)
+    std::move(*g_quit_main_message_loop).Run();
 }
 
 void Shell::SetShellCreatedCallback(
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index 4a14ab38..3702d09 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -118,8 +118,7 @@
   static void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure);
 
   // Used by the BlinkTestController to stop the message loop before closing all
-  // windows, for specific tests. Fails if called after the message loop has
-  // already been signalled to quit.
+  // windows, for specific tests. Has no effect if the loop is already quitting.
   static void QuitMainMessageLoopForTesting();
 
   // Used for content_browsertests. Called once.
diff --git a/content/shell/browser/web_test/blink_test_controller.cc b/content/shell/browser/web_test/blink_test_controller.cc
index e89e226..9ca2e45f 100644
--- a/content/shell/browser/web_test/blink_test_controller.cc
+++ b/content/shell/browser/web_test/blink_test_controller.cc
@@ -1429,15 +1429,13 @@
     return;
   }
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&Shell::QuitMainMessageLoopForTesting));
+  Shell::QuitMainMessageLoopForTesting();
 }
 
 void BlinkTestController::OnLeakDetectionDone(
     const LeakDetector::LeakDetectionReport& report) {
   if (!report.leaked) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&Shell::QuitMainMessageLoopForTesting));
+    Shell::QuitMainMessageLoopForTesting();
     return;
   }
 
diff --git a/device/BUILD.gn b/device/BUILD.gn
index 6c1a74a87..100f932 100644
--- a/device/BUILD.gn
+++ b/device/BUILD.gn
@@ -82,6 +82,7 @@
     "fido/cable/fido_cable_device_unittest.cc",
     "fido/cable/fido_cable_discovery_unittest.cc",
     "fido/cable/fido_cable_handshake_handler_unittest.cc",
+    "fido/cable/v2_handshake_unittest.cc",
     "fido/credential_management_handler_unittest.cc",
     "fido/ctap_request_unittest.cc",
     "fido/ctap_response_unittest.cc",
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn
index b6df0e5d1..9e9f1a6 100644
--- a/device/fido/BUILD.gn
+++ b/device/fido/BUILD.gn
@@ -59,6 +59,8 @@
     "cable/fido_cable_handshake_handler.h",
     "cable/noise.cc",
     "cable/noise.h",
+    "cable/v2_handshake.cc",
+    "cable/v2_handshake.h",
     "credential_management.cc",
     "credential_management.h",
     "credential_management_handler.cc",
diff --git a/device/fido/cable/fido_cable_device.cc b/device/fido/cable/fido_cable_device.cc
index f188bb0..86a0513 100644
--- a/device/fido/cable/fido_cable_device.cc
+++ b/device/fido/cable/fido_cable_device.cc
@@ -13,6 +13,7 @@
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/ble/fido_ble_connection.h"
 #include "device/fido/ble/fido_ble_frames.h"
+#include "device/fido/cable/v2_handshake.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 
@@ -40,23 +41,10 @@
   return constructed_nonce;
 }
 
-bool ConstructV2Nonce(base::span<uint8_t, 12> out_nonce, uint32_t counter) {
-  if (counter > kMaxCounter) {
-    return false;
-  }
-
-  // Nonce is just a little-endian counter.
-  std::array<uint8_t, sizeof(counter)> counter_bytes;
-  memcpy(counter_bytes.data(), &counter, sizeof(counter));
-  auto remaining =
-      std::copy(counter_bytes.begin(), counter_bytes.end(), out_nonce.begin());
-  std::fill(remaining, out_nonce.end(), 0);
-  return true;
-}
-
 }  // namespace
 
 FidoCableDevice::EncryptionData::EncryptionData() = default;
+FidoCableDevice::EncryptionData::~EncryptionData() = default;
 
 FidoCableDevice::FidoCableDevice(BluetoothAdapter* adapter, std::string address)
     : FidoBleDevice(adapter, std::move(address), FidoBleDevice::Type::kCaBLE) {}
@@ -69,8 +57,8 @@
 FidoDevice::CancelToken FidoCableDevice::DeviceTransact(
     std::vector<uint8_t> command,
     DeviceCallback callback) {
-  if (!encryption_data_ ||
-      !EncryptOutgoingMessage(*encryption_data_, &command)) {
+  if ((!encryption_data_ && !v2_crypter_) ||
+      !EncryptOutgoingMessage(&command)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
     state_ = State::kDeviceError;
@@ -78,8 +66,6 @@
     return 0;
   }
 
-  ++encryption_data_->write_sequence_num;
-
   FIDO_LOG(DEBUG) << "Sending encrypted message to caBLE client";
   return AddToPendingFrames(FidoBleDeviceCommand::kMsg, std::move(command),
                             std::move(callback));
@@ -92,13 +78,11 @@
   state_ = frame ? State::kReady : State::kDeviceError;
 
   if (frame && frame->command() != FidoBleDeviceCommand::kControl) {
-    if (!encryption_data_ ||
-        !DecryptIncomingMessage(*encryption_data_, &frame.value())) {
+    if ((!encryption_data_ && !v2_crypter_) ||
+        !DecryptIncomingMessage(&frame.value())) {
       state_ = State::kDeviceError;
       frame = base::nullopt;
     }
-
-    ++encryption_data_->read_sequence_num;
   }
 
   auto self = GetWeakPtr();
@@ -125,6 +109,7 @@
     base::span<const uint8_t, 8> nonce) {
   // Encryption data must be set at most once during Cable handshake protocol.
   DCHECK(!encryption_data_);
+  DCHECK(!v2_crypter_);
   encryption_data_.emplace();
   encryption_data_->read_key = fido_parsing_utils::Materialize(session_key);
   encryption_data_->write_key = fido_parsing_utils::Materialize(session_key);
@@ -132,14 +117,10 @@
 }
 
 void FidoCableDevice::SetV2EncryptionData(
-    base::span<const uint8_t, 32> read_key,
-    base::span<const uint8_t, 32> write_key) {
+    std::unique_ptr<cablev2::Crypter> crypter) {
   DCHECK(!encryption_data_);
-  encryption_data_.emplace();
-  encryption_data_->read_key = fido_parsing_utils::Materialize(read_key);
-  encryption_data_->write_key = fido_parsing_utils::Materialize(write_key);
-  memset(encryption_data_->nonce.data(), 0, encryption_data_->nonce.size());
-  encryption_data_->is_version_two = true;
+  DCHECK(!v2_crypter_);
+  v2_crypter_.emplace(std::move(crypter));
 }
 
 FidoTransportProtocol FidoCableDevice::DeviceTransport() const {
@@ -152,36 +133,42 @@
   encryption_data_->read_sequence_num = read_seq;
 }
 
-// static
 bool FidoCableDevice::EncryptOutgoingMessage(
-    const EncryptionData& encryption_data,
     std::vector<uint8_t>* message_to_encrypt) {
-  return encryption_data.is_version_two
-             ? EncryptV2OutgoingMessage(encryption_data, message_to_encrypt)
-             : EncryptV1OutgoingMessage(encryption_data, message_to_encrypt);
+  if (v2_crypter_) {
+    return v2_crypter_.value()->Encrypt(message_to_encrypt);
+  }
+
+  return EncryptV1OutgoingMessage(&encryption_data_.value(),
+                                  message_to_encrypt);
 }
 
-// static
-bool FidoCableDevice::DecryptIncomingMessage(
-    const EncryptionData& encryption_data,
-    FidoBleFrame* incoming_frame) {
-  return encryption_data.is_version_two
-             ? DecryptV2IncomingMessage(encryption_data, incoming_frame)
-             : DecryptV1IncomingMessage(encryption_data, incoming_frame);
+bool FidoCableDevice::DecryptIncomingMessage(FidoBleFrame* incoming_frame) {
+  if (v2_crypter_) {
+    std::vector<uint8_t> plaintext;
+    if (!v2_crypter_.value()->Decrypt(incoming_frame->command(),
+                                      incoming_frame->data(), &plaintext)) {
+      return false;
+    }
+    incoming_frame->data().swap(plaintext);
+    return true;
+  }
+
+  return DecryptV1IncomingMessage(&encryption_data_.value(), incoming_frame);
 }
 
 // static
 bool FidoCableDevice::EncryptV1OutgoingMessage(
-    const EncryptionData& encryption_data,
+    EncryptionData* encryption_data,
     std::vector<uint8_t>* message_to_encrypt) {
   const auto nonce =
-      ConstructV1Nonce(encryption_data.nonce, /*is_sender_client=*/true,
-                       encryption_data.write_sequence_num);
+      ConstructV1Nonce(encryption_data->nonce, /*is_sender_client=*/true,
+                       encryption_data->write_sequence_num++);
   if (!nonce)
     return false;
 
   crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
-  aes_key.Init(encryption_data.write_key);
+  aes_key.Init(encryption_data->write_key);
   DCHECK_EQ(nonce->size(), aes_key.NonceLength());
 
   const uint8_t additional_data[1] = {
@@ -193,17 +180,16 @@
 }
 
 // static
-bool FidoCableDevice::DecryptV1IncomingMessage(
-    const EncryptionData& encryption_data,
-    FidoBleFrame* incoming_frame) {
+bool FidoCableDevice::DecryptV1IncomingMessage(EncryptionData* encryption_data,
+                                               FidoBleFrame* incoming_frame) {
   const auto nonce =
-      ConstructV1Nonce(encryption_data.nonce, /*is_sender_client=*/false,
-                       encryption_data.read_sequence_num);
+      ConstructV1Nonce(encryption_data->nonce, /*is_sender_client=*/false,
+                       encryption_data->read_sequence_num);
   if (!nonce)
     return false;
 
   crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
-  aes_key.Init(encryption_data.read_key);
+  aes_key.Init(encryption_data->read_key);
   DCHECK_EQ(nonce->size(), aes_key.NonceLength());
 
   const uint8_t additional_data[1] = {
@@ -215,93 +201,7 @@
     return false;
   }
 
-  incoming_frame->data().swap(*plaintext);
-  return true;
-}
-
-// static
-bool FidoCableDevice::EncryptV2OutgoingMessage(
-    const EncryptionData& encryption_data,
-    std::vector<uint8_t>* message_to_encrypt) {
-  // Messages will be padded in order to round their length up to a multiple of
-  // kPaddingGranularity.
-  constexpr size_t kPaddingGranularity = 32;
-  static_assert(kPaddingGranularity > 0, "padding too small");
-  static_assert(kPaddingGranularity < 256, "padding too large");
-  static_assert((kPaddingGranularity & (kPaddingGranularity - 1)) == 0,
-                "padding must be a power of two");
-
-  // Padding consists of a some number of zero bytes appended to the message and
-  // the final byte in the message is the number of zeros.
-  base::CheckedNumeric<size_t> padded_size_checked = message_to_encrypt->size();
-  padded_size_checked += 1;  // padding-length byte.
-  padded_size_checked = (padded_size_checked + kPaddingGranularity - 1) &
-                        ~(kPaddingGranularity - 1);
-  if (!padded_size_checked.IsValid()) {
-    return false;
-  }
-
-  const size_t padded_size = padded_size_checked.ValueOrDie();
-  DCHECK_GT(padded_size, message_to_encrypt->size());
-  const size_t num_zeros = padded_size - message_to_encrypt->size() - 1;
-
-  std::vector<uint8_t> padded_message(padded_size, 0);
-  memcpy(padded_message.data(), message_to_encrypt->data(),
-         message_to_encrypt->size());
-  // The number of added zeros has to fit in a single byte so it has to be less
-  // than 256.
-  DCHECK_LT(num_zeros, 256u);
-  padded_message[padded_message.size() - 1] = static_cast<uint8_t>(num_zeros);
-
-  std::array<uint8_t, 12> nonce;
-  if (!ConstructV2Nonce(nonce, encryption_data.write_sequence_num)) {
-    return false;
-  }
-
-  crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
-  aes_key.Init(encryption_data.write_key);
-  DCHECK_EQ(nonce.size(), aes_key.NonceLength());
-
-  const uint8_t additional_data[2] = {
-      base::strict_cast<uint8_t>(FidoBleDeviceCommand::kMsg), /*version=*/2};
-  std::vector<uint8_t> ciphertext =
-      aes_key.Seal(padded_message, nonce, additional_data);
-  message_to_encrypt->swap(ciphertext);
-  return true;
-}
-
-// static
-bool FidoCableDevice::DecryptV2IncomingMessage(
-    const EncryptionData& encryption_data,
-    FidoBleFrame* incoming_frame) {
-  std::array<uint8_t, 12> nonce;
-  if (!ConstructV2Nonce(nonce, encryption_data.read_sequence_num)) {
-    return false;
-  }
-
-  crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
-  aes_key.Init(encryption_data.read_key);
-  DCHECK_EQ(nonce.size(), aes_key.NonceLength());
-
-  const uint8_t additional_data[2] = {
-      base::strict_cast<uint8_t>(incoming_frame->command()), /*version=*/2};
-  base::Optional<std::vector<uint8_t>> plaintext =
-      aes_key.Open(incoming_frame->data(), nonce, additional_data);
-  if (!plaintext) {
-    FIDO_LOG(ERROR) << "Failed to decrypt caBLE message.";
-    return false;
-  }
-
-  if (plaintext->empty()) {
-    return false;
-  }
-
-  const size_t padding_length = (*plaintext)[plaintext->size() - 1];
-  if (padding_length + 1 > plaintext->size()) {
-    return false;
-  }
-  plaintext->resize(plaintext->size() - padding_length - 1);
-
+  encryption_data->read_sequence_num++;
   incoming_frame->data().swap(*plaintext);
   return true;
 }
diff --git a/device/fido/cable/fido_cable_device.h b/device/fido/cable/fido_cable_device.h
index 4e6d362..20201efc 100644
--- a/device/fido/cable/fido_cable_device.h
+++ b/device/fido/cable/fido_cable_device.h
@@ -20,6 +20,10 @@
 
 namespace device {
 
+namespace cablev2 {
+class Crypter;
+}
+
 class BluetoothAdapter;
 class FidoBleConnection;
 class FidoBleFrame;
@@ -47,8 +51,7 @@
   void SetV1EncryptionData(base::span<const uint8_t, 32> session_key,
                            base::span<const uint8_t, 8> nonce);
   // Configure caBLE v2 keys.
-  void SetV2EncryptionData(base::span<const uint8_t, 32> read_key,
-                           base::span<const uint8_t, 32> write_key);
+  void SetV2EncryptionData(std::unique_ptr<cablev2::Crypter> crypter);
   FidoTransportProtocol DeviceTransport() const override;
 
   // SetCountersForTesting allows tests to set the message counters. Non-test
@@ -61,33 +64,26 @@
   // data within FidoBleFrame.
   struct EncryptionData {
     EncryptionData();
+    ~EncryptionData();
 
     std::array<uint8_t, 32> read_key;
     std::array<uint8_t, 32> write_key;
     std::array<uint8_t, 8> nonce;
     uint32_t write_sequence_num = 0;
     uint32_t read_sequence_num = 0;
-    bool is_version_two = false;
   };
 
-  static bool EncryptOutgoingMessage(const EncryptionData& encryption_data,
-                                     std::vector<uint8_t>* message_to_encrypt);
-  static bool DecryptIncomingMessage(const EncryptionData& encryption_data,
-                                     FidoBleFrame* incoming_frame);
+  bool EncryptOutgoingMessage(std::vector<uint8_t>* message_to_encrypt);
+  bool DecryptIncomingMessage(FidoBleFrame* incoming_frame);
 
   static bool EncryptV1OutgoingMessage(
-      const EncryptionData& encryption_data,
+      EncryptionData* encryption_data,
       std::vector<uint8_t>* message_to_encrypt);
-  static bool DecryptV1IncomingMessage(const EncryptionData& encryption_data,
-                                       FidoBleFrame* incoming_frame);
-
-  static bool EncryptV2OutgoingMessage(
-      const EncryptionData& encryption_data,
-      std::vector<uint8_t>* message_to_encrypt);
-  static bool DecryptV2IncomingMessage(const EncryptionData& encryption_data,
+  static bool DecryptV1IncomingMessage(EncryptionData* encryption_data,
                                        FidoBleFrame* incoming_frame);
 
   base::Optional<EncryptionData> encryption_data_;
+  base::Optional<std::unique_ptr<cablev2::Crypter>> v2_crypter_;
   base::WeakPtrFactory<FidoCableDevice> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(FidoCableDevice);
diff --git a/device/fido/cable/fido_cable_handshake_handler.cc b/device/fido/cable/fido_cable_handshake_handler.cc
index 9251fcfe..a4a6a4d0 100644
--- a/device/fido/cable/fido_cable_handshake_handler.cc
+++ b/device/fido/cable/fido_cable_handshake_handler.cc
@@ -23,6 +23,7 @@
 #include "crypto/sha2.h"
 #include "device/fido/cable/fido_cable_device.h"
 #include "device/fido/cable/noise.h"
+#include "device/fido/cable/v2_handshake.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
 #include "third_party/boringssl/src/include/openssl/digest.h"
@@ -189,170 +190,31 @@
     base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>
         pairing_callback)
     : cable_device_(cable_device),
-      eid_(fido_parsing_utils::Materialize(eid)),
-      pairing_callback_(std::move(pairing_callback)) {
-  HKDF(psk_.data(), psk_.size(), EVP_sha256(), psk_gen_key.data(),
-       psk_gen_key.size(), /*salt=*/nonce.data(), nonce.size(),
-       /*info=*/nullptr, 0);
-  if (peer_identity) {
-    peer_identity_ = fido_parsing_utils::Materialize(*peer_identity);
-  }
-}
+      pairing_callback_(std::move(pairing_callback)),
+      handshake_(psk_gen_key, nonce, eid, peer_identity) {}
 
-FidoCableV2HandshakeHandler::~FidoCableV2HandshakeHandler() {}
-
-namespace {
-
-template <size_t N>
-bool CopyBytestring(std::array<uint8_t, N>* out,
-                    const cbor::Value::MapValue& map,
-                    int key) {
-  const auto it = map.find(cbor::Value(key));
-  if (it == map.end() || !it->second.is_bytestring()) {
-    return false;
-  }
-  const std::vector<uint8_t> bytestring = it->second.GetBytestring();
-  return fido_parsing_utils::ExtractArray(bytestring, /*pos=*/0, out);
-}
-
-}  // namespace
+FidoCableV2HandshakeHandler::~FidoCableV2HandshakeHandler() = default;
 
 void FidoCableV2HandshakeHandler::InitiateCableHandshake(
     FidoDevice::DeviceCallback callback) {
-  noise_.Init(peer_identity_ ? Noise::HandshakeType::kNKpsk0
-                             : Noise::HandshakeType::kNNpsk0);
-
-  if (peer_identity_) {
-    static const uint8_t kPrologue[] = "caBLE handshake";
-    noise_.MixHash(kPrologue);
-  } else {
-    static const uint8_t kPrologue[] = "caBLE QR code handshake";
-    noise_.MixHash(kPrologue);
-  }
-
-  noise_.MixKeyAndHash(psk_);
-  ephemeral_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
-  const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key_.get());
-  CHECK(EC_KEY_generate_key(ephemeral_key_.get()));
-  uint8_t ephemeral_key_public_bytes[kP256PointSize];
-  CHECK_EQ(sizeof(ephemeral_key_public_bytes),
-           EC_POINT_point2oct(
-               group, EC_KEY_get0_public_key(ephemeral_key_.get()),
-               POINT_CONVERSION_UNCOMPRESSED, ephemeral_key_public_bytes,
-               sizeof(ephemeral_key_public_bytes), /*ctx=*/nullptr));
-  noise_.MixHash(ephemeral_key_public_bytes);
-  noise_.MixKey(ephemeral_key_public_bytes);
-
-  if (peer_identity_) {
-    // If we know the identity of the peer from a previous interaction, NKpsk0
-    // is performed to ensure that other browsers, which may also know the PSK,
-    // cannot impersonate the authenticator.
-    bssl::UniquePtr<EC_POINT> peer_identity_point(EC_POINT_new(group));
-    uint8_t es_key[32];
-    if (!EC_POINT_oct2point(group, peer_identity_point.get(),
-                            peer_identity_->data(), peer_identity_->size(),
-                            /*ctx=*/nullptr) ||
-        !ECDH_compute_key(es_key, sizeof(es_key), peer_identity_point.get(),
-                          ephemeral_key_.get(), /*kdf=*/nullptr)) {
-      FIDO_LOG(DEBUG) << "Dropping handshake because peer identity is invalid";
-      return;
-    }
-    noise_.MixKey(es_key);
-  }
-
-  std::vector<uint8_t> ciphertext =
-      noise_.EncryptAndHash(base::span<const uint8_t>());
-
-  std::vector<uint8_t> handshake_message;
-  handshake_message.reserve(eid_.size() + sizeof(ephemeral_key_public_bytes) +
-                            ciphertext.size());
-  handshake_message.insert(handshake_message.end(), eid_.begin(), eid_.end());
-  handshake_message.insert(
-      handshake_message.end(), ephemeral_key_public_bytes,
-      ephemeral_key_public_bytes + sizeof(ephemeral_key_public_bytes));
-  handshake_message.insert(handshake_message.end(), ciphertext.begin(),
-                           ciphertext.end());
-
-  cable_device_->SendHandshakeMessage(std::move(handshake_message),
-                                      std::move(callback));
+  std::vector<uint8_t> message = handshake_.BuildInitialMessage();
+  cable_device_->SendHandshakeMessage(std::move(message), std::move(callback));
 }
 
 bool FidoCableV2HandshakeHandler::ValidateAuthenticatorHandshakeMessage(
     base::span<const uint8_t> response) {
-  if (response.size() < kP256PointSize) {
-    return false;
-  }
-  auto peer_point_bytes = response.subspan(0, kP256PointSize);
-  auto ciphertext = response.subspan(kP256PointSize);
-
-  bssl::UniquePtr<EC_POINT> peer_point(
-      EC_POINT_new(EC_KEY_get0_group(ephemeral_key_.get())));
-  uint8_t shared_key[32];
-  const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key_.get());
-  if (!EC_POINT_oct2point(group, peer_point.get(), peer_point_bytes.data(),
-                          peer_point_bytes.size(), /*ctx=*/nullptr) ||
-      !ECDH_compute_key(shared_key, sizeof(shared_key), peer_point.get(),
-                        ephemeral_key_.get(), /*kdf=*/nullptr)) {
+  base::Optional<std::pair<std::unique_ptr<cablev2::Crypter>,
+                           base::Optional<std::unique_ptr<CableDiscoveryData>>>>
+      result = handshake_.ProcessResponse(response);
+  if (!result) {
     return false;
   }
 
-  noise_.MixHash(peer_point_bytes);
-  noise_.MixKey(peer_point_bytes);
-  noise_.MixKey(shared_key);
-
-  auto plaintext = noise_.DecryptAndHash(ciphertext);
-  if (!plaintext || plaintext->empty() != peer_identity_.has_value()) {
-    FIDO_LOG(DEBUG) << "Invalid caBLE handshake message";
-    return false;
+  if (result->second.has_value()) {
+    pairing_callback_.Run(std::move(result->second.value()));
   }
 
-  if (!peer_identity_) {
-    // Handshakes without a peer identity (i.e. NNpsk0 handshakes setup from a
-    // QR code) send a padded message in the reply. This message can,
-    // optionally, contain CBOR-encoded, long-term pairing information.
-    const size_t padding_length = (*plaintext)[plaintext->size() - 1];
-    if (padding_length + 1 > plaintext->size()) {
-      FIDO_LOG(DEBUG) << "Invalid padding in caBLE handshake message";
-      return false;
-    }
-    plaintext->resize(plaintext->size() - padding_length - 1);
-
-    if (!plaintext->empty()) {
-      base::Optional<cbor::Value> pairing = cbor::Reader::Read(*plaintext);
-      if (!pairing || !pairing->is_map()) {
-        FIDO_LOG(DEBUG) << "CBOR parse failure in caBLE handshake message";
-        return false;
-      }
-
-      auto future_discovery = std::make_unique<CableDiscoveryData>();
-      future_discovery->version = CableDiscoveryData::Version::V2;
-      future_discovery->v2.emplace();
-      future_discovery->v2->peer_identity.emplace();
-
-      const cbor::Value::MapValue& pairing_map(pairing->GetMap());
-      const auto name_it = pairing_map.find(cbor::Value(4));
-      if (!CopyBytestring(&future_discovery->v2->eid_gen_key, pairing_map, 1) ||
-          !CopyBytestring(&future_discovery->v2->psk_gen_key, pairing_map, 2) ||
-          !CopyBytestring(&future_discovery->v2->peer_identity.value(),
-                          pairing_map, 3) ||
-          name_it == pairing_map.end() || !name_it->second.is_string() ||
-          !EC_POINT_oct2point(group, peer_point.get(),
-                              future_discovery->v2->peer_identity->data(),
-                              future_discovery->v2->peer_identity->size(),
-                              /*ctx=*/nullptr)) {
-        FIDO_LOG(DEBUG) << "CBOR structure error in caBLE handshake message";
-        return false;
-      }
-
-      future_discovery->v2->peer_name = name_it->second.GetString();
-      pairing_callback_.Run(std::move(future_discovery));
-    }
-  }
-
-  std::array<uint8_t, 32> read_key, write_key;
-  std::tie(write_key, read_key) = noise_.traffic_keys();
-  cable_device_->SetV2EncryptionData(read_key, write_key);
-
+  cable_device_->SetV2EncryptionData(std::move(result->first));
   return true;
 }
 
diff --git a/device/fido/cable/fido_cable_handshake_handler.h b/device/fido/cable/fido_cable_handshake_handler.h
index ac8d1e04..ce0d80d1 100644
--- a/device/fido/cable/fido_cable_handshake_handler.h
+++ b/device/fido/cable/fido_cable_handshake_handler.h
@@ -19,6 +19,7 @@
 #include "base/optional.h"
 #include "device/fido/cable/cable_discovery_data.h"
 #include "device/fido/cable/noise.h"
+#include "device/fido/cable/v2_handshake.h"
 #include "device/fido/fido_device.h"
 #include "third_party/boringssl/src/include/openssl/base.h"
 
@@ -97,14 +98,9 @@
 
  private:
   FidoCableDevice* const cable_device_;
-  Noise noise_;
-  std::array<uint8_t, 16> eid_;
-  std::array<uint8_t, 32> psk_;
-
-  base::Optional<std::array<uint8_t, 65>> peer_identity_;
-  bssl::UniquePtr<EC_KEY> ephemeral_key_;
   base::RepeatingCallback<void(std::unique_ptr<CableDiscoveryData>)>
       pairing_callback_;
+  cablev2::HandshakeInitiator handshake_;
 };
 
 }  // namespace device
diff --git a/device/fido/cable/v2_handshake.cc b/device/fido/cable/v2_handshake.cc
new file mode 100644
index 0000000..d0a904b6
--- /dev/null
+++ b/device/fido/cable/v2_handshake.cc
@@ -0,0 +1,456 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/cable/v2_handshake.h"
+
+#include "base/numerics/safe_math.h"
+#include "base/strings/string_number_conversions.h"  // TODO REMOVE
+#include "components/cbor/reader.h"
+#include "components/cbor/writer.h"
+#include "components/device_event_log/device_event_log.h"
+#include "crypto/aead.h"
+#include "device/fido/fido_constants.h"
+#include "device/fido/fido_parsing_utils.h"
+#include "third_party/boringssl/src/include/openssl/bytestring.h"
+#include "third_party/boringssl/src/include/openssl/digest.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ec_key.h"
+#include "third_party/boringssl/src/include/openssl/ecdh.h"
+#include "third_party/boringssl/src/include/openssl/hkdf.h"
+#include "third_party/boringssl/src/include/openssl/obj.h"
+#include "third_party/boringssl/src/include/openssl/sha.h"
+
+namespace {
+
+// Maximum value of a sequence number. Exceeding this causes all operations to
+// return an error. This is assumed to be vastly larger than any caBLE exchange
+// will ever reach.
+constexpr uint32_t kMaxSequence = (1 << 24) - 1;
+
+bool ConstructNonce(uint32_t counter, base::span<uint8_t, 12> out_nonce) {
+  if (counter > kMaxSequence) {
+    return false;
+  }
+
+  // Nonce is just a little-endian counter.
+  std::array<uint8_t, sizeof(counter)> counter_bytes;
+  memcpy(counter_bytes.data(), &counter, sizeof(counter));
+  auto remaining =
+      std::copy(counter_bytes.begin(), counter_bytes.end(), out_nonce.begin());
+  std::fill(remaining, out_nonce.end(), 0);
+  return true;
+}
+
+template <size_t N>
+bool CopyBytestring(std::array<uint8_t, N>* out,
+                    const cbor::Value::MapValue& map,
+                    int key) {
+  const auto it = map.find(cbor::Value(key));
+  if (it == map.end() || !it->second.is_bytestring()) {
+    return false;
+  }
+  const std::vector<uint8_t> bytestring = it->second.GetBytestring();
+  return device::fido_parsing_utils::ExtractArray(bytestring, /*pos=*/0, out);
+}
+
+CBS CBSFromSpan(base::span<const uint8_t> in) {
+  CBS cbs;
+  CBS_init(&cbs, in.data(), in.size());
+  return cbs;
+}
+
+bool CBS_get_span(CBS* cbs, base::span<const uint8_t>* out_span, size_t N) {
+  CBS contents;
+  if (!CBS_get_bytes(cbs, &contents, N)) {
+    return false;
+  }
+  *out_span =
+      base::span<const uint8_t>(CBS_data(&contents), CBS_len(&contents));
+  return true;
+}
+
+constexpr uint8_t kPairedPrologue[] = "caBLE handshake";
+constexpr uint8_t kQRPrologue[] = "caBLE QR code handshake";
+
+}  // namespace
+
+namespace device {
+namespace cablev2 {
+
+Crypter::Crypter(base::span<const uint8_t, 32> read_key,
+                 base::span<const uint8_t, 32> write_key)
+    : read_key_(fido_parsing_utils::Materialize(read_key)),
+      write_key_(fido_parsing_utils::Materialize(write_key)) {}
+
+Crypter::~Crypter() = default;
+
+bool Crypter::Encrypt(std::vector<uint8_t>* message_to_encrypt) {
+  // Messages will be padded in order to round their length up to a multiple
+  // of kPaddingGranularity.
+  constexpr size_t kPaddingGranularity = 32;
+  static_assert(kPaddingGranularity > 0, "padding too small");
+  static_assert(kPaddingGranularity < 256, "padding too large");
+  static_assert((kPaddingGranularity & (kPaddingGranularity - 1)) == 0,
+                "padding must be a power of two");
+
+  // Padding consists of a some number of zero bytes appended to the message
+  // and the final byte in the message is the number of zeros.
+  base::CheckedNumeric<size_t> padded_size_checked = message_to_encrypt->size();
+  padded_size_checked += 1;  // padding-length byte.
+  padded_size_checked = (padded_size_checked + kPaddingGranularity - 1) &
+                        ~(kPaddingGranularity - 1);
+  if (!padded_size_checked.IsValid()) {
+    NOTREACHED();
+    return false;
+  }
+
+  const size_t padded_size = padded_size_checked.ValueOrDie();
+  CHECK_GT(padded_size, message_to_encrypt->size());
+  const size_t num_zeros = padded_size - message_to_encrypt->size() - 1;
+
+  std::vector<uint8_t> padded_message(padded_size, 0);
+  memcpy(padded_message.data(), message_to_encrypt->data(),
+         message_to_encrypt->size());
+  // The number of added zeros has to fit in a single byte so it has to be
+  // less than 256.
+  DCHECK_LT(num_zeros, 256u);
+  padded_message[padded_message.size() - 1] = static_cast<uint8_t>(num_zeros);
+
+  std::array<uint8_t, 12> nonce;
+  if (!ConstructNonce(write_sequence_num_++, nonce)) {
+    return false;
+  }
+
+  crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
+  aes_key.Init(write_key_);
+  DCHECK_EQ(nonce.size(), aes_key.NonceLength());
+
+  const uint8_t additional_data[2] = {
+      base::strict_cast<uint8_t>(device::FidoBleDeviceCommand::kMsg),
+      /*version=*/2};
+  std::vector<uint8_t> ciphertext =
+      aes_key.Seal(padded_message, nonce, additional_data);
+  message_to_encrypt->swap(ciphertext);
+  return true;
+}
+
+bool Crypter::Decrypt(FidoBleDeviceCommand command,
+                      base::span<const uint8_t> ciphertext,
+                      std::vector<uint8_t>* out_plaintext) {
+  std::array<uint8_t, 12> nonce;
+  if (!ConstructNonce(read_sequence_num_, nonce)) {
+    return false;
+  }
+
+  crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
+  aes_key.Init(read_key_);
+  DCHECK_EQ(nonce.size(), aes_key.NonceLength());
+
+  const uint8_t additional_data[2] = {base::strict_cast<uint8_t>(command),
+                                      /*version=*/2};
+  base::Optional<std::vector<uint8_t>> plaintext =
+      aes_key.Open(ciphertext, nonce, additional_data);
+
+  if (!plaintext) {
+    return false;
+  }
+  read_sequence_num_++;
+
+  if (plaintext->empty()) {
+    FIDO_LOG(ERROR) << "Invalid caBLE message.";
+    return false;
+  }
+
+  const size_t padding_length = (*plaintext)[plaintext->size() - 1];
+  if (padding_length + 1 > plaintext->size()) {
+    FIDO_LOG(ERROR) << "Invalid caBLE message.";
+    return false;
+  }
+  plaintext->resize(plaintext->size() - padding_length - 1);
+
+  out_plaintext->swap(*plaintext);
+  return true;
+}
+
+bool Crypter::IsCounterpartyOfForTesting(const Crypter& other) const {
+  return read_key_ == other.write_key_ && write_key_ == other.read_key_;
+}
+
+HandshakeInitiator::HandshakeInitiator(
+    base::span<const uint8_t, 32> psk_gen_key,
+    base::span<const uint8_t, 8> nonce,
+    base::span<const uint8_t, kCableEphemeralIdSize> eid,
+    base::Optional<base::span<const uint8_t, kP256PointSize>> peer_identity)
+    : eid_(fido_parsing_utils::Materialize(eid)) {
+  HKDF(psk_.data(), psk_.size(), EVP_sha256(), psk_gen_key.data(),
+       psk_gen_key.size(), /*salt=*/nonce.data(), nonce.size(),
+       /*info=*/nullptr, 0);
+  if (peer_identity) {
+    peer_identity_ = fido_parsing_utils::Materialize(*peer_identity);
+  }
+}
+
+HandshakeInitiator::~HandshakeInitiator() = default;
+
+std::vector<uint8_t> HandshakeInitiator::BuildInitialMessage() {
+  if (peer_identity_) {
+    noise_.Init(Noise::HandshakeType::kNKpsk0);
+    noise_.MixHash(kPairedPrologue);
+  } else {
+    noise_.Init(Noise::HandshakeType::kNNpsk0);
+    noise_.MixHash(kQRPrologue);
+  }
+
+  noise_.MixKeyAndHash(psk_);
+  ephemeral_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key_.get());
+  CHECK(EC_KEY_generate_key(ephemeral_key_.get()));
+  uint8_t ephemeral_key_public_bytes[kP256PointSize];
+  CHECK_EQ(sizeof(ephemeral_key_public_bytes),
+           EC_POINT_point2oct(
+               group, EC_KEY_get0_public_key(ephemeral_key_.get()),
+               POINT_CONVERSION_UNCOMPRESSED, ephemeral_key_public_bytes,
+               sizeof(ephemeral_key_public_bytes), /*ctx=*/nullptr));
+  noise_.MixHash(ephemeral_key_public_bytes);
+  noise_.MixKey(ephemeral_key_public_bytes);
+
+  if (peer_identity_) {
+    // If we know the identity of the peer from a previous interaction, NKpsk0
+    // is performed to ensure that other browsers, which may also know the PSK,
+    // cannot impersonate the authenticator.
+    bssl::UniquePtr<EC_POINT> peer_identity_point(EC_POINT_new(group));
+    uint8_t es_key[32];
+    CHECK(EC_POINT_oct2point(group, peer_identity_point.get(),
+                             peer_identity_->data(), peer_identity_->size(),
+                             /*ctx=*/nullptr) &&
+          ECDH_compute_key(es_key, sizeof(es_key), peer_identity_point.get(),
+                           ephemeral_key_.get(), /*kdf=*/nullptr));
+    noise_.MixKey(es_key);
+  }
+
+  std::vector<uint8_t> ciphertext =
+      noise_.EncryptAndHash(base::span<const uint8_t>());
+
+  std::vector<uint8_t> handshake_message;
+  handshake_message.reserve(eid_.size() + sizeof(ephemeral_key_public_bytes) +
+                            ciphertext.size());
+  handshake_message.insert(handshake_message.end(), eid_.begin(), eid_.end());
+  handshake_message.insert(
+      handshake_message.end(), ephemeral_key_public_bytes,
+      ephemeral_key_public_bytes + sizeof(ephemeral_key_public_bytes));
+  handshake_message.insert(handshake_message.end(), ciphertext.begin(),
+                           ciphertext.end());
+
+  return handshake_message;
+}
+
+base::Optional<std::pair<std::unique_ptr<Crypter>,
+                         base::Optional<std::unique_ptr<CableDiscoveryData>>>>
+HandshakeInitiator::ProcessResponse(base::span<const uint8_t> response) {
+  if (response.size() < kP256PointSize) {
+    return base::nullopt;
+  }
+  auto peer_point_bytes = response.subspan(0, kP256PointSize);
+  auto ciphertext = response.subspan(kP256PointSize);
+
+  bssl::UniquePtr<EC_POINT> peer_point(
+      EC_POINT_new(EC_KEY_get0_group(ephemeral_key_.get())));
+  uint8_t shared_key[32];
+  const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key_.get());
+  if (!EC_POINT_oct2point(group, peer_point.get(), peer_point_bytes.data(),
+                          peer_point_bytes.size(), /*ctx=*/nullptr) ||
+      !ECDH_compute_key(shared_key, sizeof(shared_key), peer_point.get(),
+                        ephemeral_key_.get(), /*kdf=*/nullptr)) {
+    FIDO_LOG(DEBUG) << "Peer's P-256 point not on curve.";
+    return base::nullopt;
+  }
+
+  noise_.MixHash(peer_point_bytes);
+  noise_.MixKey(peer_point_bytes);
+  noise_.MixKey(shared_key);
+
+  auto plaintext = noise_.DecryptAndHash(ciphertext);
+  if (!plaintext || plaintext->empty() != peer_identity_.has_value()) {
+    FIDO_LOG(DEBUG) << "Invalid caBLE handshake message";
+    return base::nullopt;
+  }
+
+  base::Optional<std::unique_ptr<CableDiscoveryData>> discovery_data;
+  if (!peer_identity_) {
+    // Handshakes without a peer identity (i.e. NNpsk0 handshakes setup from a
+    // QR code) send a padded message in the reply. This message can,
+    // optionally, contain CBOR-encoded, long-term pairing information.
+    const size_t padding_length = (*plaintext)[plaintext->size() - 1];
+    if (padding_length + 1 > plaintext->size()) {
+      FIDO_LOG(DEBUG) << "Invalid padding in caBLE handshake message";
+      return base::nullopt;
+    }
+    plaintext->resize(plaintext->size() - padding_length - 1);
+
+    if (!plaintext->empty()) {
+      base::Optional<cbor::Value> pairing = cbor::Reader::Read(*plaintext);
+      if (!pairing || !pairing->is_map()) {
+        FIDO_LOG(DEBUG) << "CBOR parse failure in caBLE handshake message";
+        return base::nullopt;
+      }
+
+      auto future_discovery = std::make_unique<CableDiscoveryData>();
+      future_discovery->version = CableDiscoveryData::Version::V2;
+      future_discovery->v2.emplace();
+      future_discovery->v2->peer_identity.emplace();
+
+      const cbor::Value::MapValue& pairing_map(pairing->GetMap());
+      const auto name_it = pairing_map.find(cbor::Value(4));
+      if (!CopyBytestring(&future_discovery->v2->eid_gen_key, pairing_map, 1) ||
+          !CopyBytestring(&future_discovery->v2->psk_gen_key, pairing_map, 2) ||
+          !CopyBytestring(&future_discovery->v2->peer_identity.value(),
+                          pairing_map, 3) ||
+          name_it == pairing_map.end() || !name_it->second.is_string() ||
+          !EC_POINT_oct2point(group, peer_point.get(),
+                              future_discovery->v2->peer_identity->data(),
+                              future_discovery->v2->peer_identity->size(),
+                              /*ctx=*/nullptr)) {
+        FIDO_LOG(DEBUG) << "CBOR structure error in caBLE handshake message";
+        return base::nullopt;
+      }
+
+      future_discovery->v2->peer_name = name_it->second.GetString();
+      discovery_data.emplace(std::move(future_discovery));
+    }
+  }
+
+  std::array<uint8_t, 32> read_key, write_key;
+  std::tie(write_key, read_key) = noise_.traffic_keys();
+  return std::make_pair(std::make_unique<cablev2::Crypter>(read_key, write_key),
+                        std::move(discovery_data));
+}
+
+base::Optional<std::unique_ptr<Crypter>> RespondToHandshake(
+    base::span<const uint8_t, 32> psk_gen_key,
+    base::span<const uint8_t, 8> nonce,
+    base::span<const uint8_t, kCableEphemeralIdSize> expected_eid,
+    const EC_KEY* identity,
+    const CableDiscoveryData* pairing_data,
+    base::span<const uint8_t> in,
+    std::vector<uint8_t>* out_response) {
+  DCHECK(identity == nullptr || pairing_data == nullptr);
+
+  CBS cbs = CBSFromSpan(in);
+
+  base::span<const uint8_t> eid;
+  base::span<const uint8_t> peer_point_bytes;
+  base::span<const uint8_t> ciphertext;
+  if (!CBS_get_span(&cbs, &eid, device::kCableEphemeralIdSize) ||
+      !CBS_get_span(&cbs, &peer_point_bytes, kP256PointSize) ||
+      !CBS_get_span(&cbs, &ciphertext, 16) || CBS_len(&cbs) != 0) {
+    return base::nullopt;
+  }
+
+  if (eid.size() != expected_eid.size() ||
+      memcmp(eid.data(), expected_eid.data(), eid.size()) != 0) {
+    return base::nullopt;
+  }
+
+  Noise noise;
+  if (identity) {
+    noise.Init(device::Noise::HandshakeType::kNKpsk0);
+    noise.MixHash(kPairedPrologue);
+  } else {
+    noise.Init(device::Noise::HandshakeType::kNNpsk0);
+    noise.MixHash(kQRPrologue);
+  }
+
+  std::array<uint8_t, 32> psk;
+  HKDF(psk.data(), psk.size(), EVP_sha256(), psk_gen_key.data(),
+       psk_gen_key.size(),
+       /*salt=*/nonce.data(), nonce.size(),
+       /*info=*/nullptr, 0);
+
+  noise.MixKeyAndHash(psk);
+  noise.MixHash(peer_point_bytes);
+  noise.MixKey(peer_point_bytes);
+
+  bssl::UniquePtr<EC_KEY> ephemeral_key(
+      EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  const EC_GROUP* group = EC_KEY_get0_group(ephemeral_key.get());
+  CHECK(EC_KEY_generate_key(ephemeral_key.get()));
+  bssl::UniquePtr<EC_POINT> peer_point(EC_POINT_new(group));
+  if (!EC_POINT_oct2point(group, peer_point.get(), peer_point_bytes.data(),
+                          peer_point_bytes.size(),
+                          /*ctx=*/nullptr)) {
+    FIDO_LOG(DEBUG) << "Peer's P-256 point not on curve.";
+    return base::nullopt;
+  }
+
+  if (identity) {
+    uint8_t es_key[32];
+    if (!ECDH_compute_key(es_key, sizeof(es_key), peer_point.get(), identity,
+                          /*kdf=*/nullptr)) {
+      return base::nullopt;
+    }
+    noise.MixKey(es_key);
+  }
+
+  auto plaintext = noise.DecryptAndHash(ciphertext);
+  if (!plaintext || !plaintext->empty()) {
+    FIDO_LOG(DEBUG) << "Failed to decrypt handshake ciphertext.";
+    return base::nullopt;
+  }
+
+  uint8_t ephemeral_key_public_bytes[kP256PointSize];
+  CHECK_EQ(sizeof(ephemeral_key_public_bytes),
+           EC_POINT_point2oct(
+               group, EC_KEY_get0_public_key(ephemeral_key.get()),
+               POINT_CONVERSION_UNCOMPRESSED, ephemeral_key_public_bytes,
+               sizeof(ephemeral_key_public_bytes), /*ctx=*/nullptr));
+  noise.MixHash(ephemeral_key_public_bytes);
+  noise.MixKey(ephemeral_key_public_bytes);
+
+  uint8_t shared_key[32];
+  if (!ECDH_compute_key(shared_key, sizeof(shared_key), peer_point.get(),
+                        ephemeral_key.get(), /*kdf=*/nullptr)) {
+    return base::nullopt;
+  }
+  noise.MixKey(shared_key);
+
+  std::vector<uint8_t> my_ciphertext;
+  if (!identity) {
+    uint8_t my_plaintext[256];
+    memset(my_plaintext, 0, sizeof(my_plaintext));
+
+    if (pairing_data) {
+      cbor::Value::MapValue pairing;
+      pairing.emplace(1, pairing_data->v2->eid_gen_key);
+      pairing.emplace(2, pairing_data->v2->psk_gen_key);
+      pairing.emplace(3, pairing_data->v2->peer_identity.value());
+      pairing.emplace(4, pairing_data->v2->peer_name.value());
+      base::Optional<std::vector<uint8_t>> cbor_bytes =
+          cbor::Writer::Write(cbor::Value(std::move(pairing)));
+      if (!cbor_bytes || cbor_bytes->size() > sizeof(my_plaintext) - 1) {
+        FIDO_LOG(DEBUG) << "Pairing encoding failed or result too large.";
+        return base::nullopt;
+      }
+      memcpy(my_plaintext, cbor_bytes->data(), cbor_bytes->size());
+      my_plaintext[255] = sizeof(my_plaintext) - 1 - cbor_bytes->size();
+    } else {
+      my_plaintext[255] = 255;
+    }
+    my_ciphertext = noise.EncryptAndHash(my_plaintext);
+  } else {
+    my_ciphertext = noise.EncryptAndHash(base::span<const uint8_t>());
+  }
+
+  out_response->insert(
+      out_response->end(), ephemeral_key_public_bytes,
+      ephemeral_key_public_bytes + sizeof(ephemeral_key_public_bytes));
+  out_response->insert(out_response->end(), my_ciphertext.begin(),
+                       my_ciphertext.end());
+
+  std::array<uint8_t, 32> read_key, write_key;
+  std::tie(read_key, write_key) = noise.traffic_keys();
+  return std::make_unique<Crypter>(read_key, write_key);
+}
+
+}  // namespace cablev2
+}  // namespace device
diff --git a/device/fido/cable/v2_handshake.h b/device/fido/cable/v2_handshake.h
new file mode 100644
index 0000000..6a34bc9
--- /dev/null
+++ b/device/fido/cable/v2_handshake.h
@@ -0,0 +1,121 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DEVICE_FIDO_CABLE_V2_HANDSHAKE_H_
+#define DEVICE_FIDO_CABLE_V2_HANDSHAKE_H_
+
+#include <stdint.h>
+
+#include <array>
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/containers/span.h"
+#include "base/optional.h"
+#include "device/fido/cable/cable_discovery_data.h"
+#include "device/fido/cable/noise.h"
+#include "device/fido/fido_constants.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
+
+namespace device {
+namespace cablev2 {
+
+// kP256PointSize is the number of bytes in an X9.62 encoding of a P-256 point.
+constexpr size_t kP256PointSize = 65;
+
+// Crypter handles the post-handshake encryption of CTAP2 messages.
+class COMPONENT_EXPORT(DEVICE_FIDO) Crypter {
+ public:
+  Crypter(base::span<const uint8_t, 32> read_key,
+          base::span<const uint8_t, 32> write_key);
+  ~Crypter();
+
+  // Encrypt encrypts |message_to_encrypt| and overrides it with the
+  // ciphertext. It returns true on success and false on error.
+  bool Encrypt(std::vector<uint8_t>* message_to_encrypt);
+
+  // Decrypt decrypts |ciphertext|, which was received as the payload of a
+  // message with the given command, and writes the plaintext to
+  // |out_plaintext|. It returns true on success and false on error.
+  //
+  // (In practice, command must always be |kMsg|. But passing it here makes it
+  // less likely that other code will forget to check that.)
+  bool Decrypt(FidoBleDeviceCommand command,
+               base::span<const uint8_t> ciphertext,
+               std::vector<uint8_t>* out_plaintext);
+
+  // IsCounterpartyOfForTesting returns true if |other| is the mirror-image of
+  // this object. (I.e. read/write keys are equal but swapped.)
+  bool IsCounterpartyOfForTesting(const Crypter& other) const;
+
+ private:
+  const std::array<uint8_t, 32> read_key_, write_key_;
+  uint32_t read_sequence_num_ = 0;
+  uint32_t write_sequence_num_ = 0;
+};
+
+// HandshakeInitiator starts a caBLE v2 handshake and processes the single
+// response message from the other party.
+class COMPONENT_EXPORT(DEVICE_FIDO) HandshakeInitiator {
+ public:
+  HandshakeInitiator(
+      // psk_gen_key is either derived from QR-code secrets or comes from
+      // pairing data.
+      base::span<const uint8_t, 32> psk_gen_key,
+      // nonce is randomly generated per advertisement and ensures that BLE
+      // adverts are non-deterministic.
+      base::span<const uint8_t, 8> nonce,
+      // eid is the EID that was advertised for this handshake. This is checked
+      // as part of the handshake.
+      base::span<const uint8_t, kCableEphemeralIdSize> eid,
+      // peer_identity, if given, specifies that this is a paired handshake
+      // and then contains an X9.62, P-256 public key for the peer. Otherwise
+      // this is a QR-code handshake.
+      base::Optional<base::span<const uint8_t, kP256PointSize>> peer_identity);
+
+  ~HandshakeInitiator();
+
+  // BuildInitialMessage returns the handshake message to send to the peer to
+  // start a handshake.
+  std::vector<uint8_t> BuildInitialMessage();
+
+  // ProcessResponse processes the handshake response from the peer. If
+  // successful it returns a |Crypter| for protecting future messages on the
+  // connection. If the peer choose to send long-term pairing data, that is also
+  // returned.
+  base::Optional<std::pair<std::unique_ptr<Crypter>,
+                           base::Optional<std::unique_ptr<CableDiscoveryData>>>>
+  ProcessResponse(base::span<const uint8_t> response);
+
+ private:
+  Noise noise_;
+  std::array<uint8_t, 16> eid_;
+  std::array<uint8_t, 32> psk_;
+
+  base::Optional<std::array<uint8_t, kP256PointSize>> peer_identity_;
+  bssl::UniquePtr<EC_KEY> ephemeral_key_;
+};
+
+// RespondToHandshake responds to a caBLE v2 handshake started by a peer.
+COMPONENT_EXPORT(DEVICE_FIDO)
+base::Optional<std::unique_ptr<Crypter>> RespondToHandshake(
+    // See |HandshakeInitiator| comments about these first three arguments.
+    base::span<const uint8_t, 32> psk_gen_key,
+    base::span<const uint8_t, 8> nonce,
+    base::span<const uint8_t, kCableEphemeralIdSize> expected_eid,
+    // identity, if not nullptr, specifies that this is a paired handshake and
+    // contains the long-term identity key for this authenticator.
+    const EC_KEY* identity,
+    // pairing_data, if not nullptr, contains long-term pairing data that will
+    // be shared with the peer. This is mutually exclusive with |identity|.
+    const CableDiscoveryData* pairing_data,
+    // in contains the initial handshake message from the peer.
+    base::span<const uint8_t> in,
+    // out_response is set to the response handshake message, if successful.
+    std::vector<uint8_t>* out_response);
+
+}  // namespace cablev2
+}  // namespace device
+
+#endif  // DEVICE_FIDO_CABLE_V2_HANDSHAKE_H_
diff --git a/device/fido/cable/v2_handshake_unittest.cc b/device/fido/cable/v2_handshake_unittest.cc
new file mode 100644
index 0000000..6df4c0d
--- /dev/null
+++ b/device/fido/cable/v2_handshake_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/fido/cable/v2_handshake.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/boringssl/src/include/openssl/ec.h"
+#include "third_party/boringssl/src/include/openssl/ec_key.h"
+#include "third_party/boringssl/src/include/openssl/obj.h"
+
+namespace device {
+namespace cablev2 {
+
+namespace {
+
+class CableV2HandshakeTest : public ::testing::Test {
+ public:
+  CableV2HandshakeTest() {
+    std::fill(psk_gen_key_.begin(), psk_gen_key_.end(), 0);
+    std::fill(nonce_.begin(), nonce_.end(), 1);
+    std::fill(eid_.begin(), eid_.end(), 2);
+
+    p256_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+    const EC_GROUP* group = EC_KEY_get0_group(p256_key_.get());
+    CHECK(EC_KEY_generate_key(p256_key_.get()));
+    CHECK_EQ(p256_public_key_.size(),
+             EC_POINT_point2oct(group, EC_KEY_get0_public_key(p256_key_.get()),
+                                POINT_CONVERSION_UNCOMPRESSED,
+                                p256_public_key_.data(),
+                                p256_public_key_.size(), /*ctx=*/nullptr));
+  }
+
+ protected:
+  std::array<uint8_t, 32> psk_gen_key_;
+  std::array<uint8_t, 8> nonce_;
+  std::array<uint8_t, kCableEphemeralIdSize> eid_;
+  bssl::UniquePtr<EC_KEY> p256_key_;
+  std::array<uint8_t, kP256PointSize> p256_public_key_;
+};
+
+TEST_F(CableV2HandshakeTest, MessageEncrytion) {
+  std::array<uint8_t, 32> key1, key2;
+  std::fill(key1.begin(), key1.end(), 1);
+  std::fill(key2.begin(), key2.end(), 2);
+
+  Crypter a(key1, key2);
+  Crypter b(key2, key1);
+
+  static constexpr FidoBleDeviceCommand command = FidoBleDeviceCommand::kMsg;
+  static constexpr size_t kMaxSize = 530;
+  std::vector<uint8_t> message, ciphertext, plaintext;
+  message.reserve(kMaxSize);
+  ciphertext.reserve(kMaxSize);
+  plaintext.reserve(kMaxSize);
+
+  for (size_t i = 0; i < kMaxSize; i++) {
+    ciphertext = message;
+    ASSERT_TRUE(a.Encrypt(&ciphertext));
+    ASSERT_TRUE(b.Decrypt(command, ciphertext, &plaintext));
+    ASSERT_TRUE(plaintext == message);
+
+    ciphertext[(13 * i) % ciphertext.size()] ^= 1;
+    ASSERT_FALSE(b.Decrypt(command, ciphertext, &plaintext));
+
+    message.push_back(i & 0xff);
+  }
+}
+
+TEST_F(CableV2HandshakeTest, OneTimeQRHandshake) {
+  std::array<uint8_t, 32> wrong_psk_gen_key = psk_gen_key_;
+  wrong_psk_gen_key[0] ^= 1;
+
+  for (const bool use_correct_key : {false, true}) {
+    HandshakeInitiator initiator(
+        use_correct_key ? psk_gen_key_ : wrong_psk_gen_key, nonce_, eid_,
+        /*peer_identity=*/base::nullopt);
+    std::vector<uint8_t> message = initiator.BuildInitialMessage();
+    std::vector<uint8_t> response;
+    base::Optional<std::unique_ptr<Crypter>> response_crypter(
+        RespondToHandshake(psk_gen_key_, nonce_, eid_, /*identity=*/nullptr,
+                           /*pairing_data=*/nullptr, message, &response));
+    ASSERT_EQ(response_crypter.has_value(), use_correct_key);
+    if (!use_correct_key) {
+      continue;
+    }
+
+    base::Optional<
+        std::pair<std::unique_ptr<Crypter>,
+                  base::Optional<std::unique_ptr<CableDiscoveryData>>>>
+        initiator_result(initiator.ProcessResponse(response));
+    ASSERT_TRUE(initiator_result.has_value());
+    EXPECT_FALSE(initiator_result->second.has_value());
+    EXPECT_TRUE(response_crypter.value()->IsCounterpartyOfForTesting(
+        *initiator_result->first));
+  }
+}
+
+TEST_F(CableV2HandshakeTest, PairingQRHandshake) {
+  CableDiscoveryData pairing;
+  pairing.v2.emplace();
+  std::fill(pairing.v2->eid_gen_key.begin(), pairing.v2->eid_gen_key.end(), 1);
+  std::fill(pairing.v2->psk_gen_key.begin(), pairing.v2->psk_gen_key.end(), 2);
+  pairing.v2->peer_identity = p256_public_key_;
+  pairing.v2->peer_name = "Unittest";
+
+  HandshakeInitiator initiator(psk_gen_key_, nonce_, eid_,
+                               /*peer_identity=*/base::nullopt);
+  std::vector<uint8_t> message = initiator.BuildInitialMessage();
+  std::vector<uint8_t> response;
+  base::Optional<std::unique_ptr<Crypter>> response_crypter(
+      RespondToHandshake(psk_gen_key_, nonce_, eid_, /*identity=*/nullptr,
+                         &pairing, message, &response));
+  ASSERT_TRUE(response_crypter.has_value());
+  base::Optional<std::pair<std::unique_ptr<Crypter>,
+                           base::Optional<std::unique_ptr<CableDiscoveryData>>>>
+      initiator_result(initiator.ProcessResponse(response));
+  ASSERT_TRUE(initiator_result.has_value());
+  EXPECT_TRUE(initiator_result->second.has_value());
+  EXPECT_EQ(initiator_result->second.value()->v2->eid_gen_key,
+            pairing.v2->eid_gen_key);
+  EXPECT_EQ(initiator_result->second.value()->v2->psk_gen_key,
+            pairing.v2->psk_gen_key);
+  EXPECT_EQ(initiator_result->second.value()->v2->peer_identity,
+            pairing.v2->peer_identity);
+  EXPECT_EQ(initiator_result->second.value()->v2->peer_name,
+            pairing.v2->peer_name);
+  EXPECT_TRUE(response_crypter.value()->IsCounterpartyOfForTesting(
+      *initiator_result->first));
+}
+
+TEST_F(CableV2HandshakeTest, PairedHandshake) {
+  bssl::UniquePtr<EC_KEY> wrong_key(
+      EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+  CHECK(EC_KEY_generate_key(wrong_key.get()));
+
+  for (const bool use_correct_key : {false, true}) {
+    SCOPED_TRACE(use_correct_key);
+
+    HandshakeInitiator initiator(psk_gen_key_, nonce_, eid_, p256_public_key_);
+    std::vector<uint8_t> message = initiator.BuildInitialMessage();
+    std::vector<uint8_t> response;
+    base::Optional<std::unique_ptr<Crypter>> response_crypter(
+        RespondToHandshake(psk_gen_key_, nonce_, eid_,
+                           use_correct_key ? p256_key_.get() : wrong_key.get(),
+                           /*pairing=*/nullptr, message, &response));
+    ASSERT_EQ(response_crypter.has_value(), use_correct_key);
+
+    if (!use_correct_key) {
+      continue;
+    }
+
+    base::Optional<
+        std::pair<std::unique_ptr<Crypter>,
+                  base::Optional<std::unique_ptr<CableDiscoveryData>>>>
+        initiator_result(initiator.ProcessResponse(response));
+    ASSERT_TRUE(initiator_result.has_value());
+    EXPECT_FALSE(initiator_result->second.has_value());
+    EXPECT_TRUE(response_crypter.value()->IsCounterpartyOfForTesting(
+        *initiator_result->first));
+  }
+}
+
+}  // namespace
+}  // namespace cablev2
+}  // namespace device
diff --git a/extensions/browser/api/declarative_net_request/ruleset_source.cc b/extensions/browser/api/declarative_net_request/ruleset_source.cc
index 9a6f3d0..8c3d2d7 100644
--- a/extensions/browser/api/declarative_net_request/ruleset_source.cc
+++ b/extensions/browser/api/declarative_net_request/ruleset_source.cc
@@ -232,9 +232,9 @@
 RulesetSource RulesetSource::CreateStatic(const Extension& extension) {
   return RulesetSource(
       declarative_net_request::DNRManifestData::GetRulesetPath(extension),
-      file_util::GetIndexedRulesetPath(extension.path()), kStaticRulesetID,
-      dnr_api::SOURCE_TYPE_MANIFEST, dnr_api::MAX_NUMBER_OF_RULES,
-      extension.id());
+      extension.path().Append(file_util::GetIndexedRulesetRelativePath()),
+      kStaticRulesetID, dnr_api::SOURCE_TYPE_MANIFEST,
+      dnr_api::MAX_NUMBER_OF_RULES, extension.id());
 }
 
 // static
diff --git a/extensions/browser/computed_hashes.cc b/extensions/browser/computed_hashes.cc
index 4139bb15..9dfe709f 100644
--- a/extensions/browser/computed_hashes.cc
+++ b/extensions/browser/computed_hashes.cc
@@ -20,7 +20,6 @@
 #include "build/build_config.h"
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
-#include "extensions/browser/content_verifier/content_verifier_utils.h"
 #include "extensions/browser/content_verifier/scoped_uma_recorder.h"
 
 namespace extensions {
@@ -66,30 +65,29 @@
 
 const ComputedHashes::Data::HashInfo* ComputedHashes::Data::GetItem(
     const base::FilePath& relative_path) const {
-  base::FilePath::StringType canonicalized_path =
+  CanonicalRelativePath canonical_path =
       content_verifier_utils::CanonicalizeRelativePath(relative_path);
-  auto iter = items_.find(canonicalized_path);
+  auto iter = items_.find(canonical_path);
   return iter == items_.end() ? nullptr : &iter->second;
 }
 
 void ComputedHashes::Data::Add(const base::FilePath& relative_path,
                                int block_size,
                                std::vector<std::string> hashes) {
-  base::FilePath::StringType canonicalized_path =
+  CanonicalRelativePath canonical_path =
       content_verifier_utils::CanonicalizeRelativePath(relative_path);
-  items_.insert(
-      std::make_pair(canonicalized_path,
-                     HashInfo(block_size, std::move(hashes),
-                              relative_path.NormalizePathSeparatorsTo('/'))));
+  items_.insert(std::make_pair(
+      canonical_path, HashInfo(block_size, std::move(hashes),
+                               relative_path.NormalizePathSeparatorsTo('/'))));
 }
 
 void ComputedHashes::Data::Remove(const base::FilePath& relative_path) {
-  base::FilePath::StringType canonicalized_path =
+  CanonicalRelativePath canonical_path =
       content_verifier_utils::CanonicalizeRelativePath(relative_path);
-  items_.erase(canonicalized_path);
+  items_.erase(canonical_path);
 }
 
-const std::map<base::FilePath::StringType, ComputedHashes::Data::HashInfo>&
+const std::map<CanonicalRelativePath, ComputedHashes::Data::HashInfo>&
 ComputedHashes::Data::items() const {
   return items_;
 }
diff --git a/extensions/browser/computed_hashes.h b/extensions/browser/computed_hashes.h
index 7e4e7ca..261ddedf 100644
--- a/extensions/browser/computed_hashes.h
+++ b/extensions/browser/computed_hashes.h
@@ -15,12 +15,14 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/optional.h"
+#include "extensions/browser/content_verifier/content_verifier_utils.h"
 
 namespace extensions {
 
 using IsCancelledCallback = base::RepeatingCallback<bool(void)>;
 using ShouldComputeHashesCallback =
     base::RepeatingCallback<bool(const base::FilePath& relative_path)>;
+using CanonicalRelativePath = content_verifier_utils::CanonicalRelativePath;
 
 // A class for storage and serialization of a set of SHA256 block hashes
 // computed over the files inside an extension.
@@ -51,7 +53,7 @@
       HashInfo(HashInfo&&);
       HashInfo& operator=(HashInfo&&);
     };
-    using Items = std::map<base::FilePath::StringType, HashInfo>;
+    using Items = std::map<CanonicalRelativePath, HashInfo>;
 
     Data();
     ~Data();
diff --git a/extensions/browser/content_verifier.cc b/extensions/browser/content_verifier.cc
index 52a4fc6..7db50ce 100644
--- a/extensions/browser/content_verifier.cc
+++ b/extensions/browser/content_verifier.cc
@@ -716,8 +716,10 @@
     base::FilePath relative_path = relative_unix_path.NormalizePathSeparators();
     base::FilePath full_path = extension_root.Append(relative_path);
 
-    if (full_path == file_util::GetIndexedRulesetPath(extension_root))
+    if (full_path ==
+        extension_root.Append(file_util::GetIndexedRulesetRelativePath())) {
       continue;
+    }
 
     if (locales_relative_dir.IsParent(relative_path)) {
       if (!all_locales) {
diff --git a/extensions/browser/content_verifier.h b/extensions/browser/content_verifier.h
index f39849f5..0edb714 100644
--- a/extensions/browser/content_verifier.h
+++ b/extensions/browser/content_verifier.h
@@ -49,9 +49,10 @@
 //   2. Relative unix path: Some underlying parts of content-verification
 //      require uniform separator, we use '/' as separator so it is effectively
 //      unix style. Note that this is a reversible transformation.
-//   3. Canonicalized relative_path: Canonicalized paths are used as keys of
-//      maps within VerifiedContents and ComputedHashes. This takes care of OS
-//      specific file access issues:
+//   3. content_verifier_utils::CanonicalRelativePath:
+//      Canonicalized relative paths are used as keys of maps within
+//      VerifiedContents and ComputedHashes. This takes care of OS specific file
+//      access issues:
 //      - windows/mac is case insensitive while accessing files.
 //      - windows ignores (.| )+ suffixes in filename while accessing a file.
 //      Canonicalization consists of normalizing the separators, lower casing
diff --git a/extensions/browser/content_verifier/content_verifier_utils.cc b/extensions/browser/content_verifier/content_verifier_utils.cc
index cd1b780a..55fee81 100644
--- a/extensions/browser/content_verifier/content_verifier_utils.cc
+++ b/extensions/browser/content_verifier/content_verifier_utils.cc
@@ -22,15 +22,15 @@
   return true;
 }
 
-base::FilePath::StringType CanonicalizeRelativePath(
+CanonicalRelativePath CanonicalizeRelativePath(
     const base::FilePath& relative_path) {
-  base::FilePath::StringType canonicalized_path =
+  base::FilePath::StringType canonical_path =
       relative_path.NormalizePathSeparatorsTo('/').value();
   if (!IsFileAccessCaseSensitive())
-    canonicalized_path = base::ToLowerASCII(canonicalized_path);
+    canonical_path = base::ToLowerASCII(canonical_path);
   if (IsDotSpaceFilenameSuffixIgnored())
-    TrimDotSpaceSuffix(canonicalized_path, &canonicalized_path);
-  return canonicalized_path;
+    TrimDotSpaceSuffix(canonical_path, &canonical_path);
+  return CanonicalRelativePath(std::move(canonical_path));
 }
 
 }  // namespace content_verifier_utils
diff --git a/extensions/browser/content_verifier/content_verifier_utils.h b/extensions/browser/content_verifier/content_verifier_utils.h
index 1114fbc5..a30116e 100644
--- a/extensions/browser/content_verifier/content_verifier_utils.h
+++ b/extensions/browser/content_verifier/content_verifier_utils.h
@@ -5,11 +5,24 @@
 #define EXTENSIONS_BROWSER_CONTENT_VERIFIER_CONTENT_VERIFIER_UTILS_H_
 
 #include "base/files/file_path.h"
+#include "base/util/type_safety/strong_alias.h"
 #include "build/build_config.h"
 
 namespace extensions {
 namespace content_verifier_utils {
 
+// Extension relative FilePath's canonical version for content verification
+// system. Canonicalization consists of:
+//   - Normalizing path separators to '/'.
+//     This is done because GURLs generally use '/' separators (that is passed
+//     to content verifier via extension_protocols) and manifest.json paths
+//     also specify '/' separators.
+//   - In case-insensitive OS, lower casing path.
+//   - In Windows, trimming "dot-space" suffix in path.
+using CanonicalRelativePath =
+    ::util::StrongAlias<class CanonicalRelativePathTag,
+                        base::FilePath::StringType>;
+
 // Returns true if |path| ends with (.| )+.
 // |out_path| will contain "." and/or " " suffix removed from |path|.
 bool TrimDotSpaceSuffix(const base::FilePath::StringType& path,
@@ -39,7 +52,7 @@
 
 // Returns platform specific canonicalized version of |relative_path| for
 // content verification system.
-base::FilePath::StringType CanonicalizeRelativePath(
+CanonicalRelativePath CanonicalizeRelativePath(
     const base::FilePath& relative_path);
 
 }  // namespace content_verifier_utils
diff --git a/extensions/browser/verified_contents.cc b/extensions/browser/verified_contents.cc
index 73ccd07..82d71f1 100644
--- a/extensions/browser/verified_contents.cc
+++ b/extensions/browser/verified_contents.cc
@@ -171,11 +171,11 @@
         return nullptr;
       }
 
-      base::FilePath::StringType canonicalized_path =
+      content_verifier_utils::CanonicalRelativePath canonical_path =
           content_verifier_utils::CanonicalizeRelativePath(
               base::FilePath::FromUTF8Unsafe(*file_path_string));
       auto i = verified_contents->root_hashes_.insert(
-          std::make_pair(canonicalized_path, std::string()));
+          std::make_pair(canonical_path, std::string()));
       i->second.swap(root_hash);
     }
 
@@ -334,10 +334,11 @@
 }
 
 bool VerifiedContents::TreeHashRootEqualsImpl(
-    const base::FilePath::StringType& normalized_relative_path,
+    const content_verifier_utils::CanonicalRelativePath&
+        canonical_relative_path,
     const std::string& expected) const {
   std::pair<RootHashes::const_iterator, RootHashes::const_iterator> hashes =
-      root_hashes_.equal_range(normalized_relative_path);
+      root_hashes_.equal_range(canonical_relative_path);
   for (auto iter = hashes.first; iter != hashes.second; ++iter) {
     if (expected == iter->second)
       return true;
diff --git a/extensions/browser/verified_contents.h b/extensions/browser/verified_contents.h
index d08feeef..1f807b13 100644
--- a/extensions/browser/verified_contents.h
+++ b/extensions/browser/verified_contents.h
@@ -16,9 +16,12 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/version.h"
+#include "extensions/browser/content_verifier/content_verifier_utils.h"
 
 namespace extensions {
 
+using CanonicalRelativePath = content_verifier_utils::CanonicalRelativePath;
+
 // This class encapsulates the data in a "verified_contents.json" file
 // generated by the webstore for a .crx file. That data includes a set of
 // signed expected hashes of file content which can be used to check for
@@ -66,7 +69,7 @@
                        const std::string& signature_bytes);
 
   bool TreeHashRootEqualsImpl(
-      const base::FilePath::StringType& normalized_relative_path,
+      const CanonicalRelativePath& canonical_relative_path,
       const std::string& expected) const;
 
   // The public key we should use for signature verification.
@@ -95,7 +98,7 @@
   // TODO(crbug.com/29941) - we should give developers client-side warnings in
   // each of those cases, and have the webstore reject the cases they can
   // statically detect.
-  typedef std::multimap<base::FilePath::StringType, std::string> RootHashes;
+  typedef std::multimap<CanonicalRelativePath, std::string> RootHashes;
   RootHashes root_hashes_;
 
   DISALLOW_COPY_AND_ASSIGN(VerifiedContents);
diff --git a/extensions/common/file_util.cc b/extensions/common/file_util.cc
index d40f987..3f2f184b 100644
--- a/extensions/common/file_util.cc
+++ b/extensions/common/file_util.cc
@@ -585,17 +585,15 @@
 base::FilePath GetComputedHashesPath(const base::FilePath& extension_path) {
   return extension_path.Append(kMetadataFolder).Append(kComputedHashesFilename);
 }
-base::FilePath GetIndexedRulesetPath(const base::FilePath& extension_path) {
-  return extension_path.Append(kMetadataFolder).Append(kIndexedRulesetFilename);
+base::FilePath GetIndexedRulesetRelativePath() {
+  return base::FilePath(kMetadataFolder).Append(kIndexedRulesetFilename);
 }
 
 std::vector<base::FilePath> GetReservedMetadataFilePaths(
     const base::FilePath& extension_path) {
-  return {
-      GetVerifiedContentsPath(extension_path),
-      GetComputedHashesPath(extension_path),
-      GetIndexedRulesetPath(extension_path),
-  };
+  return {GetVerifiedContentsPath(extension_path),
+          GetComputedHashesPath(extension_path),
+          extension_path.Append(GetIndexedRulesetRelativePath())};
 }
 
 }  // namespace file_util
diff --git a/extensions/common/file_util.h b/extensions/common/file_util.h
index d799743..f6eea0e 100644
--- a/extensions/common/file_util.h
+++ b/extensions/common/file_util.h
@@ -172,7 +172,7 @@
 
 // Helper function to get path used for the indexed ruleset by the Declarative
 // Net Request API.
-base::FilePath GetIndexedRulesetPath(const base::FilePath& extension_path);
+base::FilePath GetIndexedRulesetRelativePath();
 
 // Returns the list of file-paths reserved for use by the Extension system in
 // the kMetadataFolder.
diff --git a/ios/build/bots/scripts/test_apps.py b/ios/build/bots/scripts/test_apps.py
index 0d03030..4f6d140 100644
--- a/ios/build/bots/scripts/test_apps.py
+++ b/ios/build/bots/scripts/test_apps.py
@@ -5,6 +5,7 @@
 
 import os
 import plistlib
+import subprocess
 import time
 
 import test_runner
@@ -51,6 +52,20 @@
   return test_filter
 
 
+def get_bundle_id(app_path):
+  """Get bundle identifier for app.
+
+  Args:
+    app_path: (str) A path to app.
+  """
+  return subprocess.check_output([
+      '/usr/libexec/PlistBuddy',
+      '-c',
+      'Print:CFBundleIdentifier',
+      os.path.join(app_path, 'Info.plist'),
+  ]).rstrip()
+
+
 class GTestsApp(object):
   """Gtests app to run.
 
@@ -140,6 +155,7 @@
     module_data = {
         'TestBundlePath': self.test_app_path,
         'TestHostPath': self.test_app_path,
+        'TestHostBundleIdentifier': get_bundle_id(self.test_app_path),
         'TestingEnvironmentVariables': {
             'DYLD_LIBRARY_PATH':
                 '%s:__PLATFORMS__/iPhoneSimulator.platform/Developer/Library' %
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index cdb53b0..73ffe56 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -439,11 +439,7 @@
 
     self.app_name = os.path.splitext(os.path.split(app_path)[-1])[0]
     self.app_path = app_path
-    self.cfbundleid = subprocess.check_output([
-        '/usr/libexec/PlistBuddy',
-        '-c', 'Print:CFBundleIdentifier',
-        os.path.join(app_path, 'Info.plist'),
-    ]).rstrip()
+    self.cfbundleid = test_apps.get_bundle_id(app_path)
     self.env_vars = env_vars or []
     self.logs = collections.OrderedDict()
     self.out_dir = out_dir
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h
index 87ef3a8..2f62ddca 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h
@@ -104,6 +104,14 @@
   // Completes the two-phase initialization of IOSChromeMetricsServiceClient.
   void Initialize();
 
+  // Registers providers to the MetricsService. These provide data from
+  // alternate sources.
+  void RegisterMetricsServiceProviders();
+
+  // Registers providers to the UkmService. These provide data from alternate
+  // sources.
+  void RegisterUKMProviders();
+
   // Callbacks for various stages of final log info collection. Do not call
   // these directly.
   void CollectFinalHistograms();
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
index a9406206..ed8024fb 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
@@ -28,8 +28,10 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/metrics/call_stack_profile_metrics_provider.h"
+#include "components/metrics/cpu_metrics_provider.h"
 #include "components/metrics/demographic_metrics_provider.h"
 #include "components/metrics/drive_metrics_provider.h"
+#include "components/metrics/field_trials_provider.h"
 #include "components/metrics/metrics_log_uploader.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/metrics/metrics_reporting_default_state.h"
@@ -124,6 +126,9 @@
 
 }  // namespace
 
+// UKM suffix for field trial recording.
+const char kUKMFieldTrialSuffix[] = "UKM";
+
 IOSChromeMetricsServiceClient::IOSChromeMetricsServiceClient(
     metrics::MetricsStateManager* state_manager)
     : metrics_state_manager_(state_manager),
@@ -236,6 +241,7 @@
   PrefService* local_state = GetApplicationContext()->GetLocalState();
   metrics_service_ = std::make_unique<metrics::MetricsService>(
       metrics_state_manager_, this, local_state);
+  RegisterMetricsServiceProviders();
 
   if (IsMetricsReportingForceEnabled() ||
       base::FeatureList::IsEnabled(ukm::kUkmFeature)) {
@@ -246,9 +252,11 @@
         std::make_unique<metrics::DemographicMetricsProvider>(
             std::make_unique<metrics::ChromeBrowserStateClient>(),
             metrics::MetricsLogUploader::MetricServiceType::UKM));
+    RegisterUKMProviders();
   }
+}
 
-  // Register metrics providers.
+void IOSChromeMetricsServiceClient::RegisterMetricsServiceProviders() {
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<metrics::NetworkMetricsProvider>(
           base::BindRepeating(&GetNetworkConnectionTrackerAsync)));
@@ -256,14 +264,12 @@
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<OmniboxMetricsProvider>());
 
-  {
-    auto stability_metrics_provider =
-        std::make_unique<IOSChromeStabilityMetricsProvider>(
-            GetApplicationContext()->GetLocalState());
-    stability_metrics_provider_ = stability_metrics_provider.get();
-    metrics_service_->RegisterMetricsProvider(
-        std::move(stability_metrics_provider));
-  }
+  auto stability_metrics_provider =
+      std::make_unique<IOSChromeStabilityMetricsProvider>(
+          GetApplicationContext()->GetLocalState());
+  stability_metrics_provider_ = stability_metrics_provider.get();
+  metrics_service_->RegisterMetricsProvider(
+      std::move(stability_metrics_provider));
 
   // NOTE: metrics_state_manager_->IsMetricsReportingEnabled() returns false
   // during local testing. To test locally, modify
@@ -301,6 +307,19 @@
           metrics::MetricsLogUploader::MetricServiceType::UMA));
 }
 
+void IOSChromeMetricsServiceClient::RegisterUKMProviders() {
+  ukm_service_->RegisterMetricsProvider(
+      std::make_unique<metrics::CPUMetricsProvider>());
+
+  ukm_service_->RegisterMetricsProvider(
+      std::make_unique<metrics::ScreenInfoMetricsProvider>());
+
+  // TODO(crbug.com/754877): Support synthetic trials for UKM.
+  ukm_service_->RegisterMetricsProvider(
+      std::make_unique<variations::FieldTrialsProvider>(nullptr,
+                                                        kUKMFieldTrialSuffix));
+}
+
 void IOSChromeMetricsServiceClient::CollectFinalHistograms() {
   DCHECK(thread_checker_.CalledOnValidThread());
 
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client_unittest.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client_unittest.mm
index 2f25942..9c0854ae 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client_unittest.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client_unittest.mm
@@ -113,8 +113,19 @@
 
   std::unique_ptr<IOSChromeMetricsServiceClient> chrome_metrics_service_client =
       IOSChromeMetricsServiceClient::Create(metrics_state_manager_.get());
+
+  ukm::UkmService* ukmService =
+      chrome_metrics_service_client->GetUkmService();
   // Verify that the UKM service is instantiated when enabled.
-  EXPECT_TRUE(chrome_metrics_service_client->GetUkmService());
+  EXPECT_TRUE(ukmService);
+
+  // Number of providers registered by
+  // IOSChromeMetricsServiceClient::RegisterMetricsServiceProviders(), namely
+  // CPUMetricsProvider, ScreenInfoMetricsProvider, FieldTrialsProvider.
+  const size_t expected_providers = 3;
+
+  EXPECT_EQ(expected_providers,
+            ukmService->metrics_providers_.GetProviders().size());
 }
 
 TEST_F(IOSChromeMetricsServiceClientTest,
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
index 2e1cdc7..c17821b 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
@@ -157,8 +157,11 @@
         performAction:grey_tapAtPoint(CGPointMake(0, 0))];
 
     // Verify the table view is not visible.
-    [[EarlGrey selectElementWithMatcher:grey_kindOfClass([UITableView class])]
-        assertWithMatcher:grey_notVisible()];
+    [[EarlGrey
+        selectElementWithMatcher:grey_allOf(
+                                     grey_kindOfClass([UITableView class]),
+                                     grey_not(grey_notVisible()), nil)]
+        assertWithMatcher:grey_nil()];
   }
   [super tearDown];
 }
@@ -292,8 +295,11 @@
         performAction:grey_tapAtPoint(CGPointMake(0, 0))];
 
     // Verify the table view is not visible.
-    [[EarlGrey selectElementWithMatcher:grey_kindOfClass([UITableView class])]
-        assertWithMatcher:grey_notVisible()];
+    [[EarlGrey
+        selectElementWithMatcher:grey_allOf(
+                                     grey_kindOfClass([UITableView class]),
+                                     grey_not(grey_notVisible()), nil)]
+        assertWithMatcher:grey_nil()];
   }
 
   // Bring up the regular keyboard again.
@@ -368,8 +374,10 @@
       performAction:grey_tapAtPoint(CGPointMake(0, 0))];
 
   // Verify the table view is not visible.
-  [[EarlGrey selectElementWithMatcher:grey_kindOfClass([UITableView class])]
-      assertWithMatcher:grey_notVisible()];
+  [[EarlGrey
+      selectElementWithMatcher:grey_allOf(grey_kindOfClass([UITableView class]),
+                                          grey_not(grey_notVisible()), nil)]
+      assertWithMatcher:grey_nil()];
 
   // Bring up the regular keyboard again.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm
index b0abd51..b3e8656 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm
@@ -100,7 +100,9 @@
 
   // Tap the dismiss button.
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSigninPromoCloseButtonId)]
+      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
+                                              kSigninPromoCloseButtonId),
+                                          grey_sufficientlyVisible(), nil)]
       performAction:grey_tap()];
 
   // Wait until promo is gone.
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 72736f7..938488f 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -502,7 +502,7 @@
 @property(nonatomic, assign, getter=isBroadcasting) BOOL broadcasting;
 // Whether the controller is currently dismissing a presented view controller.
 @property(nonatomic, assign, getter=isDismissingModal) BOOL dismissingModal;
-// Whether web usage is enabled for the WebStates in |self.tabModel|.
+// Whether web usage is enabled for the WebStates in |self.browser|.
 @property(nonatomic, assign, getter=isWebUsageEnabled) BOOL webUsageEnabled;
 // Whether a new tab animation is occurring.
 @property(nonatomic, assign, getter=isInNewTabAnimation) BOOL inNewTabAnimation;
@@ -604,7 +604,7 @@
 // In most cases, they will not, to improve startup performance.
 // In order to handle this, initialization of various aspects of BVC have been
 // broken out into the following functions, which have expectations (enforced
-// with DCHECKs) regarding |self.browserState|, |self.tabModel|, and [self
+// with DCHECKs) regarding |self.browserState|, |self.browser|, and [self
 // isViewLoaded].
 
 // Updates non-view-related functionality with the given browser and tab
@@ -621,8 +621,8 @@
 - (void)addConstraintsToToolbar;
 // Updates view-related functionality with the given tab model and browser
 // state. The view must have been loaded.  Uses |self.browserState| and
-// |self.tabModel|.
-- (void)addUIFunctionalityForModelAndBrowserState;
+// |self.browser|.
+- (void)addUIFunctionalityForBrowserAndBrowserState;
 // Sets the correct frame and hierarchy for subviews and helper views.  Only
 // insert views on |initialLayout|.
 - (void)setUpViewLayout:(BOOL)initialLayout;
@@ -941,7 +941,7 @@
     _toolbarUIUpdater = [[LegacyToolbarUIUpdater alloc]
         initWithToolbarUI:[[ToolbarUIState alloc] init]
              toolbarOwner:self
-             webStateList:self.tabModel.webStateList];
+             webStateList:self.browser->GetWebStateList()];
     [_toolbarUIUpdater startUpdating];
     StartBroadcastingToolbarUI(_toolbarUIUpdater.toolbarUI, broadcaster);
 
@@ -949,10 +949,10 @@
         initWithState:[[MainContentUIState alloc] init]];
     _webMainContentUIForwarder = [[WebScrollViewMainContentUIForwarder alloc]
         initWithUpdater:_mainContentUIUpdater
-           webStateList:self.tabModel.webStateList];
+           webStateList:self.browser->GetWebStateList()];
     StartBroadcastingMainContentUI(self, broadcaster);
 
-    fullscreenController->SetWebStateList(self.tabModel.webStateList);
+    fullscreenController->SetWebStateList(self.browser->GetWebStateList());
 
     _fullscreenUIUpdater =
         std::make_unique<FullscreenUIUpdater>(fullscreenController, self);
@@ -1060,9 +1060,8 @@
 }
 
 - (web::WebState*)currentWebState {
-  return self.tabModel.webStateList
-             ? self.tabModel.webStateList->GetActiveWebState()
-             : nullptr;
+  return self.browser ? self.browser->GetWebStateList()->GetActiveWebState()
+                      : nullptr;
 }
 
 - (BubblePresenter*)bubblePresenter {
@@ -1376,7 +1375,7 @@
 
   // Uninstall delegates so that any delegate callbacks triggered by subsequent
   // WebStateDestroyed() signals are not handled.
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   for (int index = 0; index < webStateList->count(); ++index)
     [self uninstallDelegatesForWebState:webStateList->GetWebStateAt(index)];
 
@@ -1387,7 +1386,7 @@
   self.tabStripCoordinator = nil;
   self.tabStripView = nil;
 
-  self.tabModel.webStateList->RemoveObserver(_webStateListObserver.get());
+  self.browser->GetWebStateList()->RemoveObserver(_webStateListObserver.get());
   self.browser = nullptr;
   self.bubblePresenter = nil;
 
@@ -1405,8 +1404,6 @@
   self.toolbarInterface = nil;
   [self.infobarContainerCoordinator stop];
   self.infobarContainerCoordinator = nil;
-  // SideSwipeController is a tab model observer, so it needs to stop observing
-  // before self.tabModel is released.
   _sideSwipeController = nil;
   _webStateListObserver.reset();
   _allWebStateObservationForwarder = nullptr;
@@ -1496,8 +1493,8 @@
   [self addConstraintsToToolbar];
 
   // If the tab model and browser state are valid, finish initialization.
-  if (self.tabModel && self.browserState)
-    [self addUIFunctionalityForModelAndBrowserState];
+  if (self.browser && self.browserState)
+    [self addUIFunctionalityForBrowserAndBrowserState];
 
   // Add a tap gesture recognizer to save the last tap location for the source
   // location of the new tab animation.
@@ -1588,7 +1585,8 @@
   [self updateDialogPresenterActiveState];
   [self updateBroadcastState];
   web::WebState* activeWebState =
-      self.tabModel ? self.tabModel.webStateList->GetActiveWebState() : nullptr;
+      self.browser ? self.browser->GetWebStateList()->GetActiveWebState()
+                   : nullptr;
   if (activeWebState) {
     activeWebState->WasHidden();
     if (!self.presentedViewController)
@@ -1875,21 +1873,21 @@
   _isOffTheRecord = self.browserState->IsOffTheRecord();
   WebStateListWebUsageEnablerFactory::GetInstance()
       ->GetForBrowserState(self.browserState)
-      ->SetWebStateList(self.tabModel.webStateList);
+      ->SetWebStateList(self.browser->GetWebStateList());
 
   _webStateObserverBridge = std::make_unique<web::WebStateObserverBridge>(self);
   _allWebStateObservationForwarder =
       std::make_unique<AllWebStateObservationForwarder>(
-          self.tabModel.webStateList, _webStateObserverBridge.get());
+          self.browser->GetWebStateList(), _webStateObserverBridge.get());
 
   _webStateListObserver = std::make_unique<WebStateListObserverBridge>(self);
-  self.tabModel.webStateList->AddObserver(_webStateListObserver.get());
+  self.browser->GetWebStateList()->AddObserver(_webStateListObserver.get());
   _URLLoadingObserverBridge = std::make_unique<UrlLoadingObserverBridge>(self);
   UrlLoadingNotifier* urlLoadingNotifier =
       UrlLoadingNotifierFactory::GetForBrowserState(self.browserState);
   urlLoadingNotifier->AddObserver(_URLLoadingObserverBridge.get());
 
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   for (int index = 0; index < webStateList->count(); ++index)
     [self installDelegatesForWebState:webStateList->GetWebStateAt(index)];
 
@@ -1899,7 +1897,7 @@
   // Set the TTS playback controller's WebStateList.
   TextToSpeechPlaybackControllerFactory::GetInstance()
       ->GetForBrowserState(self.browserState)
-      ->SetWebStateList(self.tabModel.webStateList);
+      ->SetWebStateList(self.browser->GetWebStateList());
 
   // When starting the browser with an open tab, it is necessary to reset the
   // clipsToBounds property of the WKWebView so the page can bleed behind the
@@ -1938,7 +1936,7 @@
 
   // Create the location bar model and controller.
   _locationBarModelDelegate.reset(
-      new LocationBarModelDelegateIOS(self.tabModel.webStateList));
+      new LocationBarModelDelegateIOS(self.browser->GetWebStateList()));
   _locationBarModel = std::make_unique<LocationBarModelImpl>(
       _locationBarModelDelegate.get(), kMaxURLDisplayChars);
   self.helper = [_dependencyFactory newBrowserViewControllerHelper];
@@ -2003,7 +2001,7 @@
     self.infobarContainerCoordinator = [[InfobarContainerCoordinator alloc]
         initWithBaseViewController:self
                       browserState:self.browserState
-                      webStateList:self.tabModel.webStateList];
+                      webStateList:self.browser->GetWebStateList()];
     self.infobarContainerCoordinator.commandDispatcher = self.dispatcher;
     self.infobarContainerCoordinator.positioner = self;
     self.infobarContainerCoordinator.syncPresenter = self;
@@ -2166,11 +2164,11 @@
 }
 
 // Enable functionality that only makes sense if the views are loaded and
-// both browser state and tab model are valid.
-- (void)addUIFunctionalityForModelAndBrowserState {
+// both browser state and browser are valid.
+- (void)addUIFunctionalityForBrowserAndBrowserState {
   DCHECK(self.browserState);
   DCHECK(_locationBarModel);
-  DCHECK(self.tabModel);
+  DCHECK(self.browser);
   DCHECK([self isViewLoaded]);
 
   [self.sideSwipeController addHorizontalGesturesToView:self.view];
@@ -2188,7 +2186,7 @@
   if (!IsDownloadInfobarMessagesUIEnabled()) {
     // DownloadManagerCoordinator is already created.
     DCHECK(_downloadManagerCoordinator);
-    _downloadManagerCoordinator.webStateList = self.tabModel.webStateList;
+    _downloadManagerCoordinator.webStateList = self.browser->GetWebStateList();
     _downloadManagerCoordinator.bottomMarginHeightAnchor =
         [NamedGuide guideWithName:kSecondaryToolbarGuide view:self.contentArea]
             .heightAnchor;
@@ -2212,10 +2210,11 @@
   _sadTabCoordinator.dispatcher = self.dispatcher;
   _sadTabCoordinator.overscrollDelegate = self;
 
-  // If there are any existing SadTabHelpers in |self.tabModel.webStateList|,
-  // update the helpers delegate with the new |_sadTabCoordinator|.
+  // If there are any existing SadTabHelpers in
+  // |self.browser->GetWebStateList()|, update the helpers delegate with the new
+  // |_sadTabCoordinator|.
   DCHECK(_sadTabCoordinator);
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   for (int i = 0; i < webStateList->count(); i++) {
     SadTabTabHelper* sadTabHelper =
         SadTabTabHelper::FromWebState(webStateList->GetWebStateAt(i));
@@ -2423,7 +2422,7 @@
       initWithBrowserState:self.browserState
           parentController:self
                 dispatcher:self.dispatcher
-              webStateList:self.tabModel.webStateList];
+              webStateList:self.browser->GetWebStateList()];
 }
 
 - (void)updateOverlayContainerOrder {
@@ -2562,7 +2561,7 @@
   if (NTPHelper && NTPHelper->IsActive()) {
     return _ntpCoordinatorsForWebStates[webState].viewController.view;
   }
-  DCHECK(self.tabModel.webStateList->GetIndexOfWebState(webState) !=
+  DCHECK(self.browser->GetWebStateList()->GetIndexOfWebState(webState) !=
          WebStateList::kInvalidIndex);
   // TODO(crbug.com/904588): Move |RecordPageLoadStart| to TabUsageRecorder.
   if (webState->IsEvicted() && [self.tabModel tabUsageRecorder]) {
@@ -2731,7 +2730,7 @@
     // tabs (since doing so is a no-op for the tabs that don't have it set).
     _expectingForegroundTab = NO;
 
-    WebStateList* webStateList = self.tabModel.webStateList;
+    WebStateList* webStateList = self.browser->GetWebStateList();
     for (int index = 0; index < webStateList->count(); ++index) {
       web::WebState* webState = webStateList->GetWebStateAt(index);
       PagePlaceholderTabHelper::FromWebState(webState)
@@ -2865,7 +2864,7 @@
 - (NSArray<UIView*>*)snapshotGenerator:(SnapshotGenerator*)snapshotGenerator
            snapshotOverlaysForWebState:(web::WebState*)webState {
   DCHECK(webState);
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   DCHECK_NE(webStateList->GetIndexOfWebState(webState),
             WebStateList::kInvalidIndex);
   if (!self.webUsageEnabled || webState != webStateList->GetActiveWebState())
@@ -2979,7 +2978,8 @@
   // (typically deleting a WebState and then activating another as a side
   // effect). See crbug.com/988504 for details. In this case, the request to
   // create a new WebState is silently dropped.
-  if (self.tabModel.webStateList && self.tabModel.webStateList->IsMutating())
+  if (self.browser->GetWebStateList() &&
+      self.browser->GetWebStateList()->IsMutating())
     return nil;
 
   // Check if requested web state is a popup and block it if necessary.
@@ -3012,9 +3012,9 @@
   // are no navigation items.
   DCHECK(webState->HasOpener() ||
          !webState->GetNavigationManager()->GetItemCount());
-  if (![self tabModel])
+  if (!self.browser)
     return;
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   int index = webStateList->GetIndexOfWebState(webState);
   if (index != WebStateList::kInvalidIndex)
     webStateList->CloseWebStateAt(index, WebStateList::CLOSE_USER_ACTION);
@@ -3353,7 +3353,7 @@
         transitionType:(ui::PageTransition)transitionType {
   [_bookmarkInteractionController dismissBookmarkModalControllerAnimated:YES];
 
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   web::WebState* current_web_state = webStateList->GetActiveWebState();
   if (current_web_state &&
       (transitionType & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR)) {
@@ -3393,7 +3393,7 @@
   if ([self canShowTabStrip])
     return;
 
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   web::WebState* webStateBeingActivated =
       webStateList->GetWebStateAt(newWebStateIndex);
 
@@ -3565,7 +3565,7 @@
 
 - (void)dialogPresenter:(DialogPresenter*)presenter
     willShowDialogForWebState:(web::WebState*)webState {
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   int indexOfWebState = webStateList->GetIndexOfWebState(webState);
   if (indexOfWebState != WebStateList::kInvalidIndex) {
     webStateList->ActivateWebStateAt(indexOfWebState);
@@ -3817,7 +3817,9 @@
 }
 
 - (NSUInteger)tabsCount {
-  return self.tabModel.count;
+  if (_isShutdown)
+    return 0;
+  return self.browser->GetWebStateList()->count();
 }
 
 - (BOOL)canGoBack {
@@ -3831,13 +3833,14 @@
 }
 
 - (void)focusTabAtIndex:(NSUInteger)index {
-  if (self.tabModel.count > index) {
-    self.tabModel.webStateList->ActivateWebStateAt(static_cast<int>(index));
+  WebStateList* webStateList = self.browser->GetWebStateList();
+  if (webStateList->ContainsIndex(index)) {
+    webStateList->ActivateWebStateAt(static_cast<int>(index));
   }
 }
 
 - (void)focusNextTab {
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   if (!webStateList)
     return;
 
@@ -3856,7 +3859,7 @@
 }
 
 - (void)focusPreviousTab {
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   if (!webStateList)
     return;
 
@@ -4098,7 +4101,7 @@
 }
 
 - (void)closeCurrentTab {
-  WebStateList* webStateList = self.tabModel.webStateList;
+  WebStateList* webStateList = self.browser->GetWebStateList();
   if (!webStateList)
     return;
 
@@ -4255,7 +4258,7 @@
   DCHECK(webState);
   [self installDelegatesForWebState:webState];
 
-  DCHECK_EQ(self.tabModel.webStateList, webStateList);
+  DCHECK_EQ(self.browser->GetWebStateList(), webStateList);
 
   // Don't initiate Tab animation while session restoration is in progress
   // (see crbug.com/763964).
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
index c0862e3..339006d 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
@@ -51,6 +51,8 @@
     InfobarModalTransitionDriver* modalTransitionDriver;
 // Readwrite redefinition.
 @property(nonatomic, assign, readwrite) BOOL bannerWasPresented;
+// YES if the banner is currently being dismissed.
+@property(nonatomic, assign) BOOL bannerIsBeingDismissed;
 // Completion block used to dismiss the banner after a set period of time. This
 // needs to be created by dispatch_block_create() since it may get cancelled.
 @property(nonatomic, copy) dispatch_block_t dismissBannerBlock;
@@ -442,12 +444,24 @@
   // Make sure the banner is completely presented before trying to dismiss it.
   [self.bannerTransitionDriver completePresentationTransitionIfRunning];
 
-  if (self.baseViewController.presentedViewController &&
+  // The banner dismiss can be triggered concurrently due to different events
+  // like swiping it up, entering the TabSwitcher, presenting another VC or the
+  // InfobarDelelgate being destroyed. Trying to dismiss it twice might cause a
+  // UIKit crash on iOS12.
+  if (!self.bannerIsBeingDismissed &&
+      self.baseViewController.presentedViewController &&
       self.baseViewController.presentedViewController ==
           self.bannerViewController) {
+    self.bannerIsBeingDismissed = YES;
     [self infobarBannerWillBeDismissed:userInitiated];
-    [self.baseViewController dismissViewControllerAnimated:animated
-                                                completion:completion];
+    __weak __typeof(self) weakSelf = self;
+    [self.baseViewController
+        dismissViewControllerAnimated:YES
+                           completion:^{
+                             weakSelf.bannerIsBeingDismissed = NO;
+                             if (completion)
+                               completion();
+                           }];
   } else if (completion) {
     completion();
   }
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
index cedfb12..26929f9 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_password_coordinator.mm
@@ -177,7 +177,10 @@
   if (presentedFromBanner)
     return;
 
-  self.passwordInfoBarDelegate->InfobarPresenting(NO /*automatic*/);
+  // There's a chance the Delegate was destroyed while the presentation was
+  // taking place. Check if the delegate still exists.
+  if (self.passwordInfoBarDelegate)
+    self.passwordInfoBarDelegate->InfobarPresenting(NO /*automatic*/);
 }
 
 - (void)dismissBannerIfReady {
diff --git a/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm b/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm
index ae11b9d..857a338 100644
--- a/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/autocomplete_match_formatter.mm
@@ -82,7 +82,7 @@
 - (BOOL)hasImage {
   BOOL hasAnswerImage =
       self.hasAnswer && _match.answer->second_line().image_url().is_valid();
-  BOOL hasRichEntityImage = !_match.image_url.empty();
+  BOOL hasRichEntityImage = !_match.image_url.is_empty();
   return hasAnswerImage || hasRichEntityImage;
 }
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm
index ddd72cd..e2691006 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_icon_formatter.mm
@@ -97,7 +97,7 @@
   if (isAnswer && match.answer->second_line().image_url().is_valid()) {
     iconType = OmniboxIconTypeImage;
     imageURL = match.answer->second_line().image_url();
-  } else if (!match.image_url.empty()) {
+  } else if (!match.image_url.is_empty()) {
     iconType = OmniboxIconTypeImage;
     imageURL = GURL(match.image_url);
   } else if (!AutocompleteMatch::IsSearchType(match.type) &&
diff --git a/ios/chrome/browser/ui/overlays/BUILD.gn b/ios/chrome/browser/ui/overlays/BUILD.gn
index 942e124..042f71d 100644
--- a/ios/chrome/browser/ui/overlays/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/BUILD.gn
@@ -90,7 +90,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "overlay_container_coordinator_unittest.mm",
     "overlay_presentation_context_fullscreen_disabler_unittest.mm",
     "overlay_request_mediator_unittest.mm",
     "overlay_request_mediator_util_unittest.mm",
diff --git a/ios/chrome/browser/ui/overlays/overlay_container_coordinator_unittest.mm b/ios/chrome/browser/ui/overlays/overlay_container_coordinator_unittest.mm
deleted file mode 100644
index 805dcd3..0000000
--- a/ios/chrome/browser/ui/overlays/overlay_container_coordinator_unittest.mm
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/overlays/overlay_container_coordinator.h"
-
-#import "ios/chrome/browser/main/test_browser.h"
-#include "ios/chrome/browser/overlays/public/overlay_request.h"
-#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_overlay.h"
-#import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
-#import "ios/chrome/browser/web_state_list/web_state_list.h"
-#import "ios/chrome/browser/web_state_list/web_state_opener.h"
-#import "ios/chrome/test/scoped_key_window.h"
-#import "ios/web/public/test/fakes/test_web_state.h"
-#include "ios/web/public/test/web_task_environment.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-// The modality to use for tests.
-const OverlayModality kModality = OverlayModality::kWebContentArea;
-// Adds a supported request to |web_state|'s queue at kModality.  A JavaScript
-// alert request is added since the test modality is kWebContentArea.
-void AddRequest(web::WebState* web_state) {
-  OverlayRequestQueue* queue =
-      OverlayRequestQueue::FromWebState(web_state, kModality);
-  const GURL kUrl("http://chromium.test");
-  JavaScriptDialogSource source(web_state, kUrl, true);
-  const std::string kMessage("message");
-  queue->AddRequest(
-      OverlayRequest::CreateWithConfig<JavaScriptAlertOverlayRequestConfig>(
-          source, kMessage));
-}
-}  // namespace
-
-// Test fixture for OverlayContainerCoordinator.
-class OverlayContainerCoordinatorTest : public PlatformTest {
- protected:
-  OverlayContainerCoordinatorTest()
-      : base_view_controller_([[UIViewController alloc] init]),
-        coordinator_([[OverlayContainerCoordinator alloc]
-            initWithBaseViewController:base_view_controller_
-                               browser:&browser_
-                              modality:kModality]) {
-    // Add a WebState to the Browser.
-    WebStateList* web_state_list = browser_.GetWebStateList();
-    web_state_list->InsertWebState(0, std::make_unique<web::TestWebState>(),
-                                   WebStateList::INSERT_ACTIVATE,
-                                   WebStateOpener());
-    web_state_ = web_state_list->GetActiveWebState();
-
-    // Set up the view hierarchy so that overlay presentation can occur.
-    scoped_window_.Get().rootViewController = base_view_controller_;
-    [coordinator_ start];
-  }
-  ~OverlayContainerCoordinatorTest() override { [coordinator_ stop]; }
-
-  ScopedKeyWindow scoped_window_;
-  web::WebTaskEnvironment task_environment_;
-  TestBrowser browser_;
-  web::WebState* web_state_ = nullptr;
-  UIViewController* base_view_controller_ = nil;
-  OverlayContainerCoordinator* coordinator_ = nil;
-};
-
-// Tests that the FullscreenController is disabled when an overlay presented.
-TEST_F(OverlayContainerCoordinatorTest, DisableFullscreen) {
-  FullscreenController* controller =
-      FullscreenController::FromBrowserState(browser_.GetBrowserState());
-  ASSERT_TRUE(controller->IsEnabled());
-  AddRequest(web_state_);
-  ASSERT_FALSE(controller->IsEnabled());
-}
diff --git a/ios/chrome/browser/ui/passwords/password_breach_app_interface.mm b/ios/chrome/browser/ui/passwords/password_breach_app_interface.mm
index 2dc2095b..2b30c80e 100644
--- a/ios/chrome/browser/ui/passwords/password_breach_app_interface.mm
+++ b/ios/chrome/browser/ui/passwords/password_breach_app_interface.mm
@@ -14,12 +14,11 @@
 @implementation PasswordBreachAppInterface
 
 + (void)showPasswordBreach {
-  auto dispatcher =
-      chrome_test_util::DispatcherForActiveBrowserViewController();
+  auto handler = chrome_test_util::HandlerForActiveBrowser();
   auto leakType = password_manager::CreateLeakType(
       password_manager::IsSaved(true), password_manager::IsReused(false),
       password_manager::IsSyncing(true));
-  [(id<PasswordBreachCommands>)dispatcher
+  [(id<PasswordBreachCommands>)handler
       showPasswordBreachForLeakType:leakType
                                 URL:GURL("example.com")];
 }
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm
index 07c0310..43f3c95 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_egtest.mm
@@ -77,7 +77,9 @@
       performAction:grey_longPress()];
 
   // Check that the first four entries are shown the back tab history menu.
-  [[EarlGrey selectElementWithMatcher:grey_text(entry0)]
+  [[EarlGrey
+      selectElementWithMatcher:grey_allOf(grey_text(entry0),
+                                          grey_sufficientlyVisible(), nil)]
       assertWithMatcher:grey_notNil()];
   [[EarlGrey selectElementWithMatcher:grey_text(entry1)]
       assertWithMatcher:grey_notNil()];
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
index bc6fa92..b153af0 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -130,8 +130,9 @@
 
   // Tap "Show Full History"
   id<GREYMatcher> showHistoryMatcher =
-      chrome_test_util::StaticTextWithAccessibilityLabelId(
-          IDS_HISTORY_SHOWFULLHISTORY_LINK);
+      grey_allOf(chrome_test_util::StaticTextWithAccessibilityLabelId(
+                     IDS_HISTORY_SHOWFULLHISTORY_LINK),
+                 grey_sufficientlyVisible(), nil);
   [[EarlGrey selectElementWithMatcher:showHistoryMatcher]
       performAction:grey_tap()];
 
@@ -179,8 +180,9 @@
   // Tap on "Other Devices", to hide the sign-in promo.
   NSString* otherDevicesLabel =
       l10n_util::GetNSString(IDS_IOS_RECENT_TABS_OTHER_DEVICES);
-  id<GREYMatcher> otherDevicesMatcher =
-      chrome_test_util::ButtonWithAccessibilityLabel(otherDevicesLabel);
+  id<GREYMatcher> otherDevicesMatcher = grey_allOf(
+      chrome_test_util::ButtonWithAccessibilityLabel(otherDevicesLabel),
+      grey_sufficientlyVisible(), nil);
   [[EarlGrey selectElementWithMatcher:otherDevicesMatcher]
       performAction:grey_tap()];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
@@ -208,22 +210,21 @@
   }
   OpenRecentTabsPanel();
 
+  id<GREYMatcher> recentTabsViewController =
+      grey_allOf(grey_accessibilityID(
+                     kRecentTabsTableViewControllerAccessibilityIdentifier),
+                 grey_sufficientlyVisible(), nil);
+
   // Check that the TableView is presented.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     kRecentTabsTableViewControllerAccessibilityIdentifier)]
+  [[EarlGrey selectElementWithMatcher:recentTabsViewController]
       assertWithMatcher:grey_notNil()];
 
   // Swipe TableView down.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     kRecentTabsTableViewControllerAccessibilityIdentifier)]
+  [[EarlGrey selectElementWithMatcher:recentTabsViewController]
       performAction:grey_swipeFastInDirection(kGREYDirectionDown)];
 
   // Check that the TableView has been dismissed.
-  [[EarlGrey selectElementWithMatcher:
-                 grey_accessibilityID(
-                     kRecentTabsTableViewControllerAccessibilityIdentifier)]
+  [[EarlGrey selectElementWithMatcher:recentTabsViewController]
       assertWithMatcher:grey_nil()];
 
   [ChromeEarlGrey closeCurrentTab];
diff --git a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
index ca98f8a..e9ccf89 100644
--- a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
@@ -124,7 +124,9 @@
       checkSigninPromoVisibleWithMode:SigninPromoViewModeColdState];
   // Tap on dismiss button.
   [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityID(kSigninPromoCloseButtonId)]
+      selectElementWithMatcher:grey_allOf(grey_accessibilityID(
+                                              kSigninPromoCloseButtonId),
+                                          grey_sufficientlyVisible(), nil)]
       performAction:grey_tap()];
   // Check that the sign-in promo is not visible anymore.
   [SigninEarlGreyUI checkSigninPromoNotVisible];
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
index 51ca862..6c7cde584 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.mm
@@ -293,20 +293,24 @@
   // necessary animations.
   if (self.bvcContainer) {
     if (base::FeatureList::IsEnabled(kContainedBVC)) {
-      [self.baseViewController contentWillAppearAnimated:animated];
-      self.baseViewController.childViewControllerForStatusBarStyle = nil;
+      // This is done with a dispatch to make sure that the view isn't added to
+      // the view hierarchy right away, as it is not the expectations of the
+      // API.
+      dispatch_async(dispatch_get_main_queue(), ^{
+        [self.baseViewController contentWillAppearAnimated:animated];
+        self.baseViewController.childViewControllerForStatusBarStyle = nil;
 
-      self.transitionHandler = [[TabGridTransitionHandler alloc]
-          initWithLayoutProvider:self.baseViewController];
-      self.transitionHandler.animationDisabled = !animated;
-      [self.transitionHandler
-          transitionFromBrowser:self.bvcContainer
-                      toTabGrid:self.baseViewController
-                 withCompletion:^{
-                   self.bvcContainer = nil;
-                   [self.baseViewController contentDidAppear];
-                 }];
-
+        self.transitionHandler = [[TabGridTransitionHandler alloc]
+            initWithLayoutProvider:self.baseViewController];
+        self.transitionHandler.animationDisabled = !animated;
+        [self.transitionHandler
+            transitionFromBrowser:self.bvcContainer
+                        toTabGrid:self.baseViewController
+                   withCompletion:^{
+                     self.bvcContainer = nil;
+                     [self.baseViewController contentDidAppear];
+                   }];
+      });
     } else {
       self.bvcContainer.transitioningDelegate = self.legacyTransitionHandler;
       self.bvcContainer = nil;
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm
index be1d8e2..3bba378 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm
@@ -107,8 +107,12 @@
 
   // Now setting a TabSwitcher will make the switcher active.
   [coordinator_ showTabSwitcher:coordinator_.tabSwitcher];
-  EXPECT_EQ([coordinator_.tabSwitcher viewController],
-            coordinator_.activeViewController);
+  bool tab_switcher_active = base::test::ios::WaitUntilConditionOrTimeout(
+      base::test::ios::kWaitForUIElementTimeout, ^bool {
+        return [coordinator_.tabSwitcher viewController] ==
+               coordinator_.activeViewController;
+      });
+  EXPECT_TRUE(tab_switcher_active);
 }
 
 // Tests that it is possible to set a TabViewController after setting a
@@ -124,8 +128,12 @@
 
   // Showing the TabSwitcher again will make it active.
   [coordinator_ showTabSwitcher:coordinator_.tabSwitcher];
-  EXPECT_EQ([coordinator_.tabSwitcher viewController],
-            coordinator_.activeViewController);
+  bool tab_switcher_active = base::test::ios::WaitUntilConditionOrTimeout(
+      base::test::ios::kWaitForUIElementTimeout, ^bool {
+        return [coordinator_.tabSwitcher viewController] ==
+               coordinator_.activeViewController;
+      });
+  EXPECT_TRUE(tab_switcher_active);
 }
 
 // Tests swapping between two TabViewControllers.
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm b/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm
index 3d4ad9f..5a21eba0 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm
+++ b/ios/chrome/browser/ui/tabs/tab_strip_egtest.mm
@@ -42,7 +42,9 @@
                                                  : @"No more tabs.");
     NSString* nextTabTitle = [ChromeEarlGrey nextTabTitle];
 
-    [[EarlGrey selectElementWithMatcher:grey_text(nextTabTitle)]
+    [[EarlGrey
+        selectElementWithMatcher:grey_allOf(grey_text(nextTabTitle),
+                                            grey_sufficientlyVisible(), nil)]
         performAction:grey_tap()];
 
     GREYAssertEqualObjects([ChromeEarlGrey currentTabTitle], nextTabTitle,
diff --git a/ios/chrome/test/app/chrome_test_util.h b/ios/chrome/test/app/chrome_test_util.h
index 2187b40..6b5d09c 100644
--- a/ios/chrome/test/app/chrome_test_util.h
+++ b/ios/chrome/test/app/chrome_test_util.h
@@ -32,7 +32,7 @@
 ChromeBrowserState* GetCurrentIncognitoBrowserState();
 
 // Returns the dispatcher for the main BVC.
-// TODO(crbug.com/738881): Use DispatcherForActiveBrowserViewController()
+// TODO(crbug.com/738881): Use HandlerForActiveBrowser()
 // instead.
 id<BrowserCommands> BrowserCommandDispatcherForMainBVC();
 
@@ -41,10 +41,8 @@
 // possible.
 UIViewController* GetActiveViewController();
 
-// Returns the dispatcher for the active BrowserViewController. If the
-// BrowserViewController isn't presented, returns nil.
-id<ApplicationCommands, BrowserCommands>
-DispatcherForActiveBrowserViewController();
+// Returns the dispatcher for the active Browser.
+id<ApplicationCommands, BrowserCommands> HandlerForActiveBrowser();
 
 // Removes all presented infobars.
 void RemoveAllInfoBars();
diff --git a/ios/chrome/test/app/chrome_test_util.mm b/ios/chrome/test/app/chrome_test_util.mm
index 2b67474..de1528d1f 100644
--- a/ios/chrome/test/app/chrome_test_util.mm
+++ b/ios/chrome/test/app/chrome_test_util.mm
@@ -19,6 +19,7 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#import "ios/chrome/browser/main/browser.h"
 #import "ios/chrome/browser/metrics/previous_session_info.h"
 #import "ios/chrome/browser/metrics/previous_session_info_private.h"
 #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h"
@@ -125,11 +126,10 @@
   return active_view_controller;
 }
 
-id<ApplicationCommands, BrowserCommands>
-DispatcherForActiveBrowserViewController() {
-  UIViewController* vc = GetActiveViewController();
-  BrowserViewController* bvc = base::mac::ObjCCast<BrowserViewController>(vc);
-  return bvc.dispatcher;
+id<ApplicationCommands, BrowserCommands> HandlerForActiveBrowser() {
+  return static_cast<id<ApplicationCommands, BrowserCommands>>(
+      GetMainController()
+          .interfaceProvider.currentInterface.browser->GetCommandDispatcher());
 }
 
 void RemoveAllInfoBars() {
diff --git a/ios/chrome/test/app/tab_test_util.mm b/ios/chrome/test/app/tab_test_util.mm
index 6322111..304e6b6 100644
--- a/ios/chrome/test/app/tab_test_util.mm
+++ b/ios/chrome/test/app/tab_test_util.mm
@@ -53,20 +53,20 @@
 void OpenNewTab() {
   @autoreleasepool {  // Make sure that all internals are deallocated.
     OpenNewTabCommand* command = [OpenNewTabCommand command];
-    id<ApplicationCommands, BrowserCommands> BVCDispatcher =
-        chrome_test_util::DispatcherForActiveBrowserViewController();
-    if (BVCDispatcher) {
-      [BVCDispatcher openURLInNewTab:command];
+    if (GetMainController().tabSwitcherActive) {
+      // The TabGrid is currently presented.
+      Browser* browser =
+          GetMainController().interfaceProvider.mainInterface.browser;
+      UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
+      [GetMainController().tabSwitcher
+          dismissWithNewTabAnimationToBrowser:browser
+                            withUrlLoadParams:params
+                                      atIndex:INT_MAX];
       return;
     }
-      // The TabGrid is currently presented.
-    Browser* browser =
-        GetMainController().interfaceProvider.mainInterface.browser;
-    UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
-    [GetMainController().tabSwitcher
-        dismissWithNewTabAnimationToBrowser:browser
-                          withUrlLoadParams:params
-                                    atIndex:INT_MAX];
+    id<ApplicationCommands, BrowserCommands> handler =
+        chrome_test_util::HandlerForActiveBrowser();
+    [handler openURLInNewTab:command];
   }
 }
 
@@ -80,20 +80,20 @@
 void OpenNewIncognitoTab() {
   @autoreleasepool {  // Make sure that all internals are deallocated.
     OpenNewTabCommand* command = [OpenNewTabCommand incognitoTabCommand];
-    id<ApplicationCommands, BrowserCommands> BVCDispatcher =
-        chrome_test_util::DispatcherForActiveBrowserViewController();
-    if (BVCDispatcher) {
-      [BVCDispatcher openURLInNewTab:command];
+    if (GetMainController().tabSwitcherActive) {
+      // The TabGrid is currently presented.
+      Browser* browser =
+          GetMainController().interfaceProvider.incognitoInterface.browser;
+      UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
+      [GetMainController().tabSwitcher
+          dismissWithNewTabAnimationToBrowser:browser
+                            withUrlLoadParams:params
+                                      atIndex:INT_MAX];
       return;
     }
-      // The TabGrid is currently presented.
-    Browser* browser =
-        GetMainController().interfaceProvider.incognitoInterface.browser;
-    UrlLoadParams params = UrlLoadParams::InNewTab(GURL(kChromeUINewTabURL));
-    [GetMainController().tabSwitcher
-        dismissWithNewTabAnimationToBrowser:browser
-                          withUrlLoadParams:params
-                                    atIndex:INT_MAX];
+    id<ApplicationCommands, BrowserCommands> handler =
+        chrome_test_util::HandlerForActiveBrowser();
+    [handler openURLInNewTab:command];
   }
 }
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index dff42f87..f474888 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -120,8 +120,7 @@
 }
 
 + (void)dismissSettings {
-  [chrome_test_util::DispatcherForActiveBrowserViewController()
-      closeSettingsUI];
+  [chrome_test_util::HandlerForActiveBrowser() closeSettingsUI];
 }
 
 #pragma mark - Tab Utilities (EG2)
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 20738e0..ec78272 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -388,7 +388,7 @@
   return grey_allOf(
       [ChromeMatchersAppInterface
           buttonWithAccessibilityLabelID:(IDS_IOS_NAVIGATION_BAR_DONE_BUTTON)],
-      grey_userInteractionEnabled(), nil);
+      grey_userInteractionEnabled(), grey_sufficientlyVisible(), nil);
 }
 
 + (id<GREYMatcher>)bookmarksNavigationBarDoneButton {
@@ -495,11 +495,13 @@
 }
 
 + (id<GREYMatcher>)primarySignInButton {
-  return grey_accessibilityID(kSigninPromoPrimaryButtonId);
+  return grey_allOf(grey_accessibilityID(kSigninPromoPrimaryButtonId),
+                    grey_sufficientlyVisible(), nil);
 }
 
 + (id<GREYMatcher>)secondarySignInButton {
-  return grey_accessibilityID(kSigninPromoSecondaryButtonId);
+  return grey_allOf(grey_accessibilityID(kSigninPromoSecondaryButtonId),
+                    grey_sufficientlyVisible(), nil);
 }
 
 + (id<GREYMatcher>)settingsAccountButton {
diff --git a/media/mojo/clients/mojo_renderer_factory.h b/media/mojo/clients/mojo_renderer_factory.h
index 7b8e582..4211975 100644
--- a/media/mojo/clients/mojo_renderer_factory.h
+++ b/media/mojo/clients/mojo_renderer_factory.h
@@ -32,8 +32,6 @@
 // MediaPlayerRendererClientFactory for examples of small wrappers around MRF.
 class MojoRendererFactory : public RendererFactory {
  public:
-  using GetTypeSpecificIdCB = base::Callback<std::string()>;
-
   explicit MojoRendererFactory(
       media::mojom::InterfaceFactory* interface_factory);
   ~MojoRendererFactory() final;
diff --git a/net/http/structured_headers.cc b/net/http/structured_headers.cc
index 3084943..9058ad0 100644
--- a/net/http/structured_headers.cc
+++ b/net/http/structured_headers.cc
@@ -22,20 +22,22 @@
 #define DIGIT "0123456789"
 #define LCALPHA "abcdefghijklmnopqrstuvwxyz"
 #define UCALPHA "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define TCHAR DIGIT LCALPHA UCALPHA "!#$%&'*+-.^_`|~"
 // https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09#section-3.9
-// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-13#section-3.7
-constexpr char kTokenChars[] = DIGIT UCALPHA LCALPHA "_-.:%*/";
+constexpr char kTokenChars09[] = DIGIT UCALPHA LCALPHA "_-.:%*/";
+// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15#section-3.3.4
+constexpr char kTokenChars15[] = TCHAR ":/";
 // https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09#section-3.1
 constexpr char kKeyChars09[] = DIGIT LCALPHA "_-";
-// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-13#section-3.1
-constexpr char kKeyChars13[] = DIGIT LCALPHA "_-*";
+// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15#section-3.1.2
+constexpr char kKeyChars15[] = DIGIT LCALPHA "_-.*";
 #undef DIGIT
 #undef LCALPHA
 #undef UCALPHA
 
-// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-13#section-3.4
-constexpr int64_t kMaxInteger = 999999999999999L;
-constexpr int64_t kMinInteger = -999999999999999L;
+// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15#section-3.3.1
+constexpr int64_t kMaxInteger = 999'999'999'999'999L;
+constexpr int64_t kMinInteger = -999'999'999'999'999L;
 
 // Smallest value which is too large for an sh-float. This is the smallest
 // double which will round up to 1e14 when serialized, which exceeds the range
@@ -43,27 +45,21 @@
 // verified by unit tests.
 constexpr double kTooLargeFloat = 1e14 - 0.05;
 
-// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09#section-3.8
-// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-13#section-3.6
-bool IsPrintableASCII(char c) {
-  return ' ' <= c && c <= '~';  // 0x20 (' ') to 0x7E ('~')
-}
-
 // Parser for (a subset of) Structured Headers for HTTP defined in [SH09] and
-// [SH13]. [SH09] compatibility is retained for use by Web Packaging, and can be
+// [SH15]. [SH09] compatibility is retained for use by Web Packaging, and can be
 // removed once that spec is updated, and users have migrated to new headers.
 // [SH09] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09
-// [SH13] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-13
+// [SH15] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15
 class StructuredHeaderParser {
  public:
   enum DraftVersion {
     kDraft09,
-    kDraft13,
+    kDraft15,
   };
   explicit StructuredHeaderParser(base::StringPiece str, DraftVersion version)
       : input_(str), version_(version) {
     // [SH09] 4.2 Step 1.
-    // [SH13] 4.2 Step 2.
+    // [SH15] 4.2 Step 2.
     // Discard any leading OWS from input_string.
     SkipWhitespaces();
   }
@@ -71,10 +67,10 @@
   // Callers should call this after ReadSomething(), to check if parser has
   // consumed all the input successfully.
   bool FinishParsing() {
-    // [SH09] 4.2 Step 7. [SH13] 4.2 Step 6.
+    // [SH09] 4.2 Step 7. [SH15] 4.2 Step 6.
     // Discard any leading OWS from input_string.
     SkipWhitespaces();
-    // [SH09] 4.2 Step 8. [SH13] 4.2 Step 7.
+    // [SH09] 4.2 Step 8. [SH15] 4.2 Step 7.
     // If input_string is not empty, fail parsing.
     return input_.empty();
   }
@@ -86,7 +82,7 @@
     while (true) {
       std::vector<Item> inner_list;
       while (true) {
-        base::Optional<Item> item(ReadItem());
+        base::Optional<Item> item(ReadBareItem());
         if (!item)
           return base::nullopt;
         inner_list.push_back(std::move(*item));
@@ -104,18 +100,20 @@
     return result;
   }
 
-  // Parses a List ([SH13] 4.2.1).
+  // Parses a List ([SH15] 4.2.1).
   base::Optional<List> ReadList() {
-    DCHECK_EQ(version_, kDraft13);
+    DCHECK_EQ(version_, kDraft15);
     List members;
     while (!input_.empty()) {
-      base::Optional<ParameterizedMember> member(ReadParameterizedMember());
+      base::Optional<ParameterizedMember> member(ReadItemOrInnerList());
       if (!member)
         return base::nullopt;
       members.push_back(std::move(*member));
       SkipWhitespaces();
-      if (!ConsumeChar(','))
+      if (input_.empty())
         break;
+      if (!ConsumeChar(','))
+        return base::nullopt;
       SkipWhitespaces();
       if (input_.empty())
         return base::nullopt;
@@ -123,10 +121,22 @@
     return members;
   }
 
-  // Parses an Item ([SH09] 4.2.7, [SH13] 4.2.3).
-  base::Optional<Item> ReadItem() {
+  // Parses an Item ([SH15] 4.2.3).
+  base::Optional<ParameterizedItem> ReadItem() {
+    base::Optional<Item> item = ReadBareItem();
+    if (!item)
+      return base::nullopt;
+    base::Optional<Parameters> parameters = ReadParameters();
+    if (!parameters)
+      return base::nullopt;
+    return ParameterizedItem(std::move(*item), std::move(*parameters));
+  }
+
+  // Parses a bare Item ([SH15] 4.2.3.1, though this is also the algorithm for
+  // parsing an Item from [SH09] 4.2.7).
+  base::Optional<Item> ReadBareItem() {
     if (input_.empty()) {
-      DVLOG(1) << "ReadItem: unexpected EOF";
+      DVLOG(1) << "ReadBareItem: unexpected EOF";
       return base::nullopt;
     }
     switch (input_.front()) {
@@ -181,7 +191,7 @@
 
       Item value;
       if (ConsumeChar('=')) {
-        auto item = ReadItem();
+        auto item = ReadBareItem();
         if (!item)
           return base::nullopt;
         value = std::move(*item);
@@ -197,27 +207,27 @@
                                    std::move(parameters));
   }
 
-  // Parses a Parameterized Member ([SH13] 4.2.1.1).
-  base::Optional<ParameterizedMember> ReadParameterizedMember() {
-    DCHECK_EQ(version_, kDraft13);
+  // Parses an Item or Inner List ([SH15] 4.2.1.1).
+  base::Optional<ParameterizedMember> ReadItemOrInnerList() {
+    DCHECK_EQ(version_, kDraft15);
     std::vector<Item> member;
-    bool member_is_inner_list = ConsumeChar('(');
+    bool member_is_inner_list = (!input_.empty() && input_.front() == '(');
     if (member_is_inner_list) {
-      base::Optional<std::vector<Item>> inner_list = ReadInnerList();
-      if (!inner_list)
-        return base::nullopt;
-      member = std::move(*inner_list);
+      return ReadInnerList();
     } else {
-      base::Optional<Item> item = ReadItem();
+      auto item = ReadItem();
       if (!item)
         return base::nullopt;
-      member.push_back(std::move(*item));
+      return ParameterizedMember(std::move(item->item),
+                                 std::move(item->params));
     }
+  }
 
-    ParameterizedMember::Parameters parameters;
+  // Parses Parameters ([SH15] 4.2.3.2)
+  base::Optional<Parameters> ReadParameters() {
+    Parameters parameters;
     base::flat_set<std::string> keys;
 
-    SkipWhitespaces();
     while (ConsumeChar(';')) {
       SkipWhitespaces();
 
@@ -232,7 +242,7 @@
 
       Item value;
       if (ConsumeChar('=')) {
-        auto item = ReadItem();
+        auto item = ReadBareItem();
         if (!item)
           return base::nullopt;
         value = std::move(*item);
@@ -240,20 +250,24 @@
       parameters.emplace_back(std::move(*name), std::move(value));
       SkipWhitespaces();
     }
-    return ParameterizedMember(std::move(member), member_is_inner_list,
-                               std::move(parameters));
+    return parameters;
   }
 
-  // Parses an Inner List ([SH13] 4.2.1.2).
-  // Note that the initial '(' character should already have been consumed by
-  // the caller to determine that this is in fact an inner list.
-  base::Optional<std::vector<Item>> ReadInnerList() {
-    DCHECK_EQ(version_, kDraft13);
-    std::vector<Item> inner_list;
+  // Parses an Inner List ([SH15] 4.2.1.2).
+  base::Optional<ParameterizedMember> ReadInnerList() {
+    DCHECK_EQ(version_, kDraft15);
+    if (!ConsumeChar('('))
+      return base::nullopt;
+    std::vector<ParameterizedItem> inner_list;
     while (true) {
       SkipWhitespaces();
       if (ConsumeChar(')')) {
-        return inner_list;
+        base::Optional<Parameters> parameters;
+        parameters = ReadParameters();
+        if (!parameters)
+          return base::nullopt;
+        return ParameterizedMember(std::move(inner_list), true,
+                                   std::move(*parameters));
       }
       auto item = ReadItem();
       if (!item)
@@ -266,14 +280,14 @@
     return base::nullopt;
   }
 
-  // Parses a Key ([SH09] 4.2.2, [SH13] 4.2.1.3).
+  // Parses a Key ([SH09] 4.2.2, [SH15] 4.2.3.3).
   base::Optional<std::string> ReadKey() {
     if (input_.empty() || !base::IsAsciiLower(input_.front())) {
       LogParseError("ReadKey", "lcalpha");
       return base::nullopt;
     }
     const char* allowed_chars =
-        (version_ == kDraft09 ? kKeyChars09 : kKeyChars13);
+        (version_ == kDraft09 ? kKeyChars09 : kKeyChars15);
     size_t len = input_.find_first_not_of(allowed_chars);
     if (len == base::StringPiece::npos)
       len = input_.size();
@@ -282,13 +296,14 @@
     return key;
   }
 
-  // Parses a Token ([SH09] 4.2.10, [SH13] 4.2.6).
+  // Parses a Token ([SH09] 4.2.10, [SH15] 4.2.6).
   base::Optional<Item> ReadToken() {
     if (input_.empty() || !base::IsAsciiAlpha(input_.front())) {
       LogParseError("ReadToken", "ALPHA");
       return base::nullopt;
     }
-    size_t len = input_.find_first_not_of(kTokenChars);
+    size_t len = input_.find_first_not_of(version_ == kDraft09 ? kTokenChars09
+                                                               : kTokenChars15);
     if (len == base::StringPiece::npos)
       len = input_.size();
     std::string token(input_.substr(0, len));
@@ -296,7 +311,7 @@
     return Item(std::move(token), Item::kTokenType);
   }
 
-  // Parses a Number ([SH09] 4.2.8, [SH13] 4.2.4).
+  // Parses a Number ([SH09] 4.2.8, [SH15] 4.2.4).
   base::Optional<Item> ReadNumber() {
     bool is_negative = ConsumeChar('-');
     bool is_float = false;
@@ -316,8 +331,8 @@
       return base::nullopt;
     }
     if (!is_float) {
-      // [SH13] restricts the range of integers further.
-      if (version_ == kDraft13 && i > 15) {
+      // [SH15] restricts the range of integers further.
+      if (version_ == kDraft15 && i > 15) {
         LogParseError("ReadNumber", "integer too long");
         return base::nullopt;
       }
@@ -351,12 +366,12 @@
       int64_t n;
       if (!base::StringToInt64(output_number_string, &n))
         return base::nullopt;
-      DCHECK(version_ != kDraft13 || (n <= kMaxInteger && n >= kMinInteger));
+      DCHECK(version_ != kDraft15 || (n <= kMaxInteger && n >= kMinInteger));
       return Item(is_negative ? -n : n);
     }
   }
 
-  // Parses a String ([SH09] 4.2.9, [SH13] 4.2.5).
+  // Parses a String ([SH09] 4.2.9, [SH15] 4.2.5).
   base::Optional<Item> ReadString() {
     std::string s;
     if (!ConsumeChar('"')) {
@@ -366,7 +381,7 @@
     while (!ConsumeChar('"')) {
       size_t i = 0;
       for (; i < input_.size(); ++i) {
-        if (!IsPrintableASCII(input_[i])) {
+        if (!base::IsAsciiPrintable(input_[i])) {
           DVLOG(1) << "ReadString: non printable-ASCII character";
           return base::nullopt;
         }
@@ -395,7 +410,7 @@
     return s;
   }
 
-  // Parses a Byte Sequence ([SH09] 4.2.11, [SH13] 4.2.7).
+  // Parses a Byte Sequence ([SH09] 4.2.11, [SH15] 4.2.7).
   base::Optional<Item> ReadByteSequence() {
     if (!ConsumeChar('*')) {
       LogParseError("ReadByteSequence", "'*'");
@@ -420,7 +435,7 @@
     return Item(std::move(binary), Item::kByteSequenceType);
   }
 
-  // Parses a Boolean ([SH13] 4.2.8).
+  // Parses a Boolean ([SH15] 4.2.8).
   // Note that this only parses ?0 and ?1 forms from SH version 10+, not the
   // previous ?F and ?T, which were not needed by any consumers of SH version 9.
   base::Optional<Item> ReadBoolean() {
@@ -461,8 +476,8 @@
   DISALLOW_COPY_AND_ASSIGN(StructuredHeaderParser);
 };
 
-// Serializer for (a subset of) Structured Headers for HTTP defined in [SH13].
-// [SH13] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-13
+// Serializer for (a subset of) Structured Headers for HTTP defined in [SH15].
+// [SH15] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15
 class StructuredHeaderSerializer {
  public:
   StructuredHeaderSerializer() = default;
@@ -473,8 +488,8 @@
 
   std::string Output() { return output_.str(); }
 
+  // Serializes a List ([SH15] 4.1.1).
   bool WriteList(const List& value) {
-    // Serializes a List ([SH13] 4.1.1).
     bool first = true;
     for (const auto& member : value) {
       if (!first)
@@ -486,12 +501,20 @@
     return true;
   }
 
-  bool WriteItem(const Item& value) {
-    // Serializes an Item ([SH13] 4.1.6).
+  // Serializes an Item ([SH15] 4.1.3).
+  bool WriteItem(const ParameterizedItem& value) {
+    if (!WriteBareItem(value.item))
+      return false;
+    return WriteParameters(value.params);
+  }
+
+  // Serializes an Item ([SH15] 4.1.3).
+  bool WriteBareItem(const Item& value) {
     if (value.is_string()) {
+      // Serializes a String ([SH15] 4.1.6).
       output_ << "\"";
       for (const char& c : value.GetString()) {
-        if (!IsPrintableASCII(c))
+        if (!base::IsAsciiPrintable(c))
           return false;
         if (c == '\\' || c == '\"')
           output_ << "\\";
@@ -501,15 +524,18 @@
       return true;
     }
     if (value.is_token()) {
+      // Serializes a Token ([SH15] 4.1.7).
       if (!value.GetString().size() ||
           !base::IsAsciiAlpha(value.GetString().front()))
         return false;
-      if (value.GetString().find_first_not_of(kTokenChars) != std::string::npos)
+      if (value.GetString().find_first_not_of(kTokenChars15) !=
+          std::string::npos)
         return false;
       output_ << value.GetString();
       return true;
     }
     if (value.is_byte_sequence()) {
+      // Serializes a Byte Sequence ([SH15] 4.1.8).
       output_ << "*";
       output_ << base::Base64Encode(
           base::as_bytes(base::make_span(value.GetString())));
@@ -517,12 +543,14 @@
       return true;
     }
     if (value.is_integer()) {
+      // Serializes an Integer ([SH15] 4.1.4).
       if (value.GetInteger() > kMaxInteger || value.GetInteger() < kMinInteger)
         return false;
       output_ << value.GetInteger();
       return true;
     }
     if (value.is_float()) {
+      // Serializes a Float ([SH15] 4.1.5).
       double float_value = value.GetFloat();
       if (!std::isfinite(float_value) || fabs(float_value) >= kTooLargeFloat)
         return false;
@@ -570,6 +598,7 @@
       return true;
     }
     if (value.is_boolean()) {
+      // Serializes a Boolean ([SH15] 4.1.9).
       output_ << (value.GetBoolean() ? "?1" : "?0");
       return true;
     }
@@ -578,7 +607,7 @@
 
  private:
   bool WriteParameterizedMember(const ParameterizedMember& value) {
-    // Serializes a parameterized member ([SH13] 4.1.1).
+    // Serializes a parameterized member ([SH15] 4.1.1).
     if (value.member_is_inner_list) {
       if (!WriteInnerList(value.member))
         return false;
@@ -590,11 +619,11 @@
     return WriteParameters(value.params);
   }
 
-  bool WriteInnerList(const std::vector<Item>& value) {
-    // Serializes an inner list ([SH13] 4.1.1.1).
+  bool WriteInnerList(const std::vector<ParameterizedItem>& value) {
+    // Serializes an inner list ([SH15] 4.1.1.1).
     output_ << "(";
     bool first = true;
-    for (const Item& member : value) {
+    for (const ParameterizedItem& member : value) {
       if (!first)
         output_ << " ";
       if (!WriteItem(member))
@@ -605,8 +634,8 @@
     return true;
   }
 
-  bool WriteParameters(const ParameterizedMember::Parameters& value) {
-    // Serializes a parameter list ([SH13] 4.1.1.2).
+  bool WriteParameters(const Parameters& value) {
+    // Serializes a parameter list ([SH15] 4.1.1.2).
     for (const auto& param_name_and_value : value) {
       const std::string& param_name = param_name_and_value.first;
       const Item& param_value = param_name_and_value.second;
@@ -615,7 +644,7 @@
         return false;
       if (!param_value.is_null()) {
         output_ << "=";
-        if (!WriteItem(param_value))
+        if (!WriteBareItem(param_value))
           return false;
       }
     }
@@ -623,10 +652,12 @@
   }
 
   bool WriteKey(const std::string& value) {
-    // Serializes a Key ([SH13] 4.1.1.3).
+    // Serializes a Key ([SH15] 4.1.1.3).
     if (!value.size())
       return false;
-    if (value.find_first_not_of(kKeyChars13) != std::string::npos)
+    if (value.find_first_not_of(kKeyChars15) != std::string::npos)
+      return false;
+    if (!base::IsAsciiLower(value[0]))
       return false;
     output_ << value;
     return true;
@@ -672,20 +703,27 @@
   return false;
 }
 
+ParameterizedItem::ParameterizedItem(const ParameterizedItem&) = default;
+ParameterizedItem& ParameterizedItem::operator=(const ParameterizedItem&) =
+    default;
+ParameterizedItem::ParameterizedItem(Item id, const Parameters& ps)
+    : item(std::move(id)), params(ps) {}
+ParameterizedItem::~ParameterizedItem() = default;
+
 ParameterizedMember::ParameterizedMember(const ParameterizedMember&) = default;
 ParameterizedMember& ParameterizedMember::operator=(
     const ParameterizedMember&) = default;
-ParameterizedMember::ParameterizedMember(std::vector<Item> id,
+ParameterizedMember::ParameterizedMember(std::vector<ParameterizedItem> id,
                                          bool member_is_inner_list,
                                          const Parameters& ps)
     : member(std::move(id)),
       member_is_inner_list(member_is_inner_list),
       params(ps) {}
-ParameterizedMember::ParameterizedMember(std::vector<Item> id,
+ParameterizedMember::ParameterizedMember(std::vector<ParameterizedItem> id,
                                          const Parameters& ps)
     : member(std::move(id)), member_is_inner_list(true), params(ps) {}
 ParameterizedMember::ParameterizedMember(Item id, const Parameters& ps)
-    : member({std::move(id)}), member_is_inner_list(false), params(ps) {}
+    : member({{std::move(id), {}}}), member_is_inner_list(false), params(ps) {}
 ParameterizedMember::~ParameterizedMember() = default;
 
 ParameterisedIdentifier::ParameterisedIdentifier(
@@ -696,9 +734,17 @@
     : identifier(std::move(id)), params(ps) {}
 ParameterisedIdentifier::~ParameterisedIdentifier() = default;
 
-base::Optional<Item> ParseItem(base::StringPiece str) {
-  StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft13);
-  base::Optional<Item> item = parser.ReadItem();
+base::Optional<ParameterizedItem> ParseItem(base::StringPiece str) {
+  StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15);
+  base::Optional<ParameterizedItem> item = parser.ReadItem();
+  if (item && parser.FinishParsing())
+    return item;
+  return base::nullopt;
+}
+
+base::Optional<Item> ParseBareItem(base::StringPiece str) {
+  StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15);
+  base::Optional<Item> item = parser.ReadBareItem();
   if (item && parser.FinishParsing())
     return item;
   return base::nullopt;
@@ -722,7 +768,7 @@
 }
 
 base::Optional<List> ParseList(base::StringPiece str) {
-  StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft13);
+  StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15);
   base::Optional<List> list = parser.ReadList();
   if (list && parser.FinishParsing())
     return list;
@@ -731,6 +777,13 @@
 
 base::Optional<std::string> SerializeItem(const Item& value) {
   StructuredHeaderSerializer s;
+  if (s.WriteItem(ParameterizedItem(value, {})))
+    return s.Output();
+  return base::nullopt;
+}
+
+base::Optional<std::string> SerializeItem(const ParameterizedItem& value) {
+  StructuredHeaderSerializer s;
   if (s.WriteItem(value))
     return s.Output();
   return base::nullopt;
diff --git a/net/http/structured_headers.h b/net/http/structured_headers.h
index 44109bf..0f1f235 100644
--- a/net/http/structured_headers.h
+++ b/net/http/structured_headers.h
@@ -21,9 +21,9 @@
 // This file implements parsing of HTTP structured headers, as defined in
 // https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html.
 //
-// Both drafts 9 and 13 are currently supported. The major difference
+// Both drafts 9 and 15 are currently supported. The major difference
 // between the two drafts is in the various list formats: Draft 9 describes
-// Parameterised lists and lists-of-lists, while draft 13 uses a single List
+// Parameterised lists and lists-of-lists, while draft 15 uses a single List
 // syntax, whose members may be inner lists. There should be no ambiguity,
 // however, as the code which calls this parser should be expecting only a
 // single type for a given header.
@@ -135,12 +135,33 @@
          std::tie(rhs.identifier, rhs.params);
 }
 
-// Holds a ParameterizedMember, which may be either an Inner List, or a single
-// Item, with any number of parameters. Parameter ordering is significant.
-struct NET_EXPORT ParameterizedMember {
-  using Parameters = std::vector<std::pair<std::string, Item>>;
+using Parameters = std::vector<std::pair<std::string, Item>>;
 
-  std::vector<Item> member;
+struct NET_EXPORT ParameterizedItem {
+  Item item;
+  Parameters params;
+
+  ParameterizedItem(const ParameterizedItem&);
+  ParameterizedItem& operator=(const ParameterizedItem&);
+  ParameterizedItem(Item, const Parameters&);
+  ~ParameterizedItem();
+};
+
+inline bool operator==(const ParameterizedItem& lhs,
+                       const ParameterizedItem& rhs) {
+  return std::tie(lhs.item, lhs.params) == std::tie(rhs.item, rhs.params);
+}
+
+inline bool operator!=(const ParameterizedItem& lhs,
+                       const ParameterizedItem& rhs) {
+  return !(lhs == rhs);
+}
+
+// Holds a ParameterizedMember, which may be either an single Item, or an Inner
+// List of ParameterizedItems, along with any number of parameters. Parameter
+// ordering is significant.
+struct NET_EXPORT ParameterizedMember {
+  std::vector<ParameterizedItem> member;
   // If false, then |member| should only hold one Item.
   bool member_is_inner_list;
 
@@ -148,9 +169,11 @@
 
   ParameterizedMember(const ParameterizedMember&);
   ParameterizedMember& operator=(const ParameterizedMember&);
-  ParameterizedMember(std::vector<Item>, bool, const Parameters&);
+  ParameterizedMember(std::vector<ParameterizedItem>,
+                      bool member_is_inner_list,
+                      const Parameters&);
   // Shorthand constructor for a member which is an inner list.
-  ParameterizedMember(std::vector<Item>, const Parameters&);
+  ParameterizedMember(std::vector<ParameterizedItem>, const Parameters&);
   // Shorthand constructor for a member which is a single Item.
   ParameterizedMember(Item, const Parameters&);
   ~ParameterizedMember();
@@ -166,13 +189,18 @@
 using ParameterisedList = std::vector<ParameterisedIdentifier>;
 // Structured Headers Draft 09 List of Lists.
 using ListOfLists = std::vector<std::vector<Item>>;
-// Structured Headers Draft 13 List.
+// Structured Headers Draft 15 List.
 using List = std::vector<ParameterizedMember>;
 
 // Returns the result of parsing the header value as an Item, if it can be
-// parsed as one, or nullopt if it cannot. Note that this uses the Draft 13
+// parsed as one, or nullopt if it cannot. Note that this uses the Draft 15
 // parsing rules, and so applies tighter range limits to integers.
-NET_EXPORT base::Optional<Item> ParseItem(base::StringPiece str);
+NET_EXPORT base::Optional<ParameterizedItem> ParseItem(base::StringPiece str);
+
+// Returns the result of parsing the header value as an Item with no parameters,
+// or nullopt if it cannot. Note that this uses the Draft 15 parsing rules, and
+// so applies tighter range limits to integers.
+NET_EXPORT base::Optional<Item> ParseBareItem(base::StringPiece str);
 
 // Returns the result of parsing the header value as a Parameterised List, if it
 // can be parsed as one, or nullopt if it cannot. Note that parameter keys will
@@ -192,11 +220,13 @@
 
 // Returns the result of parsing the header value as a general List, if it can
 // be parsed as one, or nullopt if it cannot.
-// Structured-Headers Draft 13 only.
+// Structured-Headers Draft 15 only.
 NET_EXPORT base::Optional<List> ParseList(base::StringPiece str);
 
-// Serialization is implemented for Structured-Headers Draft 13 only.
+// Serialization is implemented for Structured-Headers Draft 15 only.
 NET_EXPORT base::Optional<std::string> SerializeItem(const Item& value);
+NET_EXPORT base::Optional<std::string> SerializeItem(
+    const ParameterizedItem& value);
 NET_EXPORT base::Optional<std::string> SerializeList(const List& value);
 
 }  // namespace structured_headers
diff --git a/net/http/structured_headers_unittest.cc b/net/http/structured_headers_unittest.cc
index dc0b54f7..90621c5 100644
--- a/net/http/structured_headers_unittest.cc
+++ b/net/http/structured_headers_unittest.cc
@@ -29,6 +29,10 @@
   return std::make_pair(key, Item());
 }
 
+std::pair<std::string, Item> DoubleParam(std::string key, double value) {
+  return std::make_pair(key, Item(value));
+}
+
 std::pair<std::string, Item> Param(std::string key, int64_t value) {
   return std::make_pair(key, Item(value));
 }
@@ -37,6 +41,10 @@
   return std::make_pair(key, Item(value));
 }
 
+std::pair<std::string, Item> TokenParam(std::string key, std::string value) {
+  return std::make_pair(key, Token(value));
+}
+
 // Most test cases are taken from
 // https://github.com/httpwg/structured-header-tests.
 const struct ItemTestCase {
@@ -50,7 +58,7 @@
     {"basic token - item", "a_b-c.d3:f%00/*", Token("a_b-c.d3:f%00/*")},
     {"token with capitals - item", "fooBar", Token("fooBar")},
     {"token starting with capitals - item", "FooBar", Token("FooBar")},
-    {"bad token - item", "abc$%!", base::nullopt},
+    {"bad token - item", "abc$@%!", base::nullopt},
     {"leading whitespace", " foo", Token("foo"), "foo"},
     {"trailing whitespace", "foo ", Token("foo"), "foo"},
     // Number
@@ -638,7 +646,42 @@
     {"c-style unicode escape in string", "\"\\u0061\"", base::nullopt},
 };
 
-// For Structured Headers Draft 13
+// For Structured Headers Draft 15
+const struct ParameterizedItemTestCase {
+  const char* name;
+  const char* raw;
+  const base::Optional<ParameterizedItem>
+      expected;           // nullopt if parse error is expected.
+  const char* canonical;  // nullptr if parse error is expected, or if canonical
+                          // format is identical to raw.
+} parameterized_item_test_cases[] = {
+    {"single parameter item",
+     "text/html;q=1.0",
+     {{Token("text/html"), {DoubleParam("q", 1)}}}},
+    {"missing parameter value item",
+     "text/html;a;q=1.0",
+     {{Token("text/html"), {Param("a"), DoubleParam("q", 1)}}}},
+    {"missing terminal parameter value item",
+     "text/html;q=1.0;a",
+     {{Token("text/html"), {DoubleParam("q", 1), Param("a")}}}},
+    {"whitespace before = parameterised item", "text/html, text/plain;q =0.5",
+     base::nullopt},
+    {"whitespace after = parameterised item", "text/html, text/plain;q= 0.5",
+     base::nullopt},
+    {"whitespace before ; parameterised item", "text/html, text/plain ;q=0.5",
+     base::nullopt},
+    {"whitespace after ; parameterised item",
+     "text/plain; q=0.5",
+     {{Token("text/plain"), {DoubleParam("q", 0.5)}}},
+     "text/plain;q=0.5"},
+    {"extra whitespace parameterised item",
+     "text/plain;  q=0.5;  charset=utf-8",
+     {{Token("text/plain"),
+       {DoubleParam("q", 0.5), TokenParam("charset", "utf-8")}}},
+     "text/plain;q=0.5;charset=utf-8"},
+};
+
+// For Structured Headers Draft 15
 const struct ListTestCase {
   const char* name;
   const char* raw;
@@ -659,55 +702,85 @@
     // Lists of lists
     {"basic list of lists",
      "(1 2), (42 43)",
-     {{{{Integer(1L), Integer(2L)}, {}}, {{Integer(42L), Integer(43L)}, {}}}}},
+     {{{{{Integer(1L), {}}, {Integer(2L), {}}}, {}},
+       {{{Integer(42L), {}}, {Integer(43L), {}}}, {}}}}},
     {"single item list of lists",
      "(42)",
-     {{{std::vector<Item>{Integer(42L)}, {}}}}},
-    {"empty item list of lists", "()", {{{std::vector<Item>(), {}}}}},
+     {{{std::vector<ParameterizedItem>{{Integer(42L), {}}}, {}}}}},
+    {"empty item list of lists",
+     "()",
+     {{{std::vector<ParameterizedItem>(), {}}}}},
     {"empty middle item list of lists",
      "(1),(),(42)",
-     {{{std::vector<Item>{Integer(1L)}, {}},
-       {std::vector<Item>(), {}},
-       {std::vector<Item>{Integer(42L)}, {}}}},
+     {{{std::vector<ParameterizedItem>{{Integer(1L), {}}}, {}},
+       {std::vector<ParameterizedItem>(), {}},
+       {std::vector<ParameterizedItem>{{Integer(42L), {}}}, {}}}},
      "(1), (), (42)"},
     {"extra whitespace list of lists",
      "(1  42)",
-     {{{{Integer(1L), Integer(42L)}, {}}}},
+     {{{{{Integer(1L), {}}, {Integer(42L), {}}}, {}}}},
      "(1 42)"},
     {"no trailing parenthesis list of lists", "(1 42", base::nullopt},
     {"no trailing parenthesis middle list of lists", "(1 2, (42 43)",
      base::nullopt},
     // Parameterized Lists
     {"basic parameterised list",
-     "abc_123;a=1;b=2; cdef_456, ghi;q=\"9\";r=\"w\"",
+     "abc_123;a=1;b=2; cdef_456, ghi;q=\"9\";r=\"+w\"",
      {{{Token("abc_123"), {Param("a", 1), Param("b", 2), Param("cdef_456")}},
-       {Token("ghi"), {Param("q", "9"), Param("r", "w")}}}},
-     "abc_123;a=1;b=2;cdef_456, ghi;q=\"9\";r=\"w\""},
+       {Token("ghi"), {Param("q", "9"), Param("r", "+w")}}}},
+     "abc_123;a=1;b=2;cdef_456, ghi;q=\"9\";r=\"+w\""},
     {"single item parameterised list",
-     "text/html;q=1",
-     {{{Token("text/html"), {Param("q", 1)}}}}},
+     "text/html;q=1.0",
+     {{{Token("text/html"), {DoubleParam("q", 1)}}}}},
+    {"missing parameter value parameterised list",
+     "text/html;a;q=1.0",
+     {{{Token("text/html"), {Param("a"), DoubleParam("q", 1)}}}}},
+    {"missing terminal parameter value parameterised list",
+     "text/html;q=1.0;a",
+     {{{Token("text/html"), {DoubleParam("q", 1), Param("a")}}}}},
     {"no whitespace parameterised list",
-     "text/html,text/plain;q=1",
-     {{{Token("text/html"), {}}, {Token("text/plain"), {Param("q", 1)}}}},
-     "text/html, text/plain;q=1"},
-    {"whitespace before = parameterised list", "text/html, text/plain;q =1",
+     "text/html,text/plain;q=0.5",
+     {{{Token("text/html"), {}},
+       {Token("text/plain"), {DoubleParam("q", 0.5)}}}},
+     "text/html, text/plain;q=0.5"},
+    {"whitespace before = parameterised list", "text/html, text/plain;q =0.5",
      base::nullopt},
-    {"whitespace after = parameterised list", "text/html, text/plain;q= 1",
+    {"whitespace after = parameterised list", "text/html, text/plain;q= 0.5",
      base::nullopt},
-    {"extra whitespace param-list",
-     "text/html  ,  text/plain ;  q=1",
-     {{{Token("text/html"), {}}, {Token("text/plain"), {Param("q", 1)}}}},
-     "text/html, text/plain;q=1"},
-    {"empty item parameterised list", "text/html,,text/plain;q=1",
+    {"whitespace before ; parameterised list", "text/html, text/plain ;q=0.5",
+     base::nullopt},
+    {"whitespace after ; parameterised list",
+     "text/html, text/plain; q=0.5",
+     {{{Token("text/html"), {}},
+       {Token("text/plain"), {DoubleParam("q", 0.5)}}}},
+     "text/html, text/plain;q=0.5"},
+    {"extra whitespace parameterised list",
+     "text/html  ,  text/plain;  q=0.5;  charset=utf-8",
+     {{{Token("text/html"), {}},
+       {Token("text/plain"),
+        {DoubleParam("q", 0.5), TokenParam("charset", "utf-8")}}}},
+     "text/html, text/plain;q=0.5;charset=utf-8"},
+    {"trailing comma parameterised list", "text/html,text/plain;q=0.5,",
+     base::nullopt},
+    {"empty item parameterised list", "text/html,,text/plain;q=0.5",
      base::nullopt},
 };
 
 }  // namespace
 
-TEST(StructuredHeaderTest, ParseItem) {
+TEST(StructuredHeaderTest, ParseBareItem) {
   for (const auto& c : item_test_cases) {
     SCOPED_TRACE(c.name);
-    base::Optional<Item> result = ParseItem(c.raw);
+    base::Optional<Item> result = ParseBareItem(c.raw);
+    EXPECT_EQ(result, c.expected);
+  }
+}
+
+// For Structured Headers Draft 15, these tests include parameters on Items.
+TEST(StructuredHeaderTest, ParseItem) {
+  for (const auto& c : parameterized_item_test_cases) {
+    SCOPED_TRACE(c.name);
+    base::Optional<ParameterizedItem> result = ParseItem(c.raw);
     EXPECT_EQ(result, c.expected);
   }
 }
@@ -826,7 +899,7 @@
   }
 }
 
-// For Structured Headers Draft 13
+// For Structured Headers Draft 15
 TEST(StructuredHeaderTest, ParseList) {
   for (const auto& c : list_test_cases) {
     SCOPED_TRACE(c.name);
@@ -835,7 +908,7 @@
   }
 }
 
-// Serializer tests are all exclusively for Structured Headers Draft 13
+// Serializer tests are all exclusively for Structured Headers Draft 15
 
 TEST(StructuredHeaderTest, SerializeItem) {
   for (const auto& c : item_test_cases) {
@@ -848,6 +921,17 @@
   }
 }
 
+TEST(StructuredHeaderTest, SerializeParameterizedItem) {
+  for (const auto& c : parameterized_item_test_cases) {
+    SCOPED_TRACE(c.name);
+    if (c.expected) {
+      base::Optional<std::string> result = SerializeItem(*c.expected);
+      EXPECT_TRUE(result.has_value());
+      EXPECT_EQ(result.value(), std::string(c.canonical ? c.canonical : c.raw));
+    }
+  }
+}
+
 TEST(StructuredHeaderTest, UnserializableItems) {
   // Test that items with unknown type are not serialized.
   EXPECT_FALSE(SerializeItem(Item()).has_value());
@@ -885,6 +969,39 @@
   }
 }
 
+TEST(StructuredHeaderTest, UnserializableKeys) {
+  static const struct UnserializableString {
+    const char* name;
+    const char* value;
+  } bad_keys[] = {
+      {"empty key", ""},
+      {"contains high ascii", "a\xff"},
+      {"contains nonprintable character", "a\x7f"},
+      {"contains C0", "a\x01"},
+      {"UTF-8 encoded", "a\xc3\xa9"},
+      {"contains TAB", "a\t"},
+      {"contains LF", "a\n"},
+      {"contains CR", "a\r"},
+      {"contains SP", "a "},
+      {"begins with uppercase", "Atoken"},
+      {"begins with digit", "9token"},
+      {"begins with hyphen", "-token"},
+      {"begins with LF", "\ntoken"},
+      {"begins with SP", " token"},
+      {"begins with colon", ":token"},
+      {"begins with percent", "%token"},
+      {"begins with period", ".token"},
+      {"begins with asterisk", "*token"},
+      {"begins with slash", "/token"},
+  };
+  for (const auto& bad_key : bad_keys) {
+    SCOPED_TRACE(bad_key.name);
+    base::Optional<std::string> serialization =
+        SerializeItem(ParameterizedItem("a", {{bad_key.value, "a"}}));
+    EXPECT_FALSE(serialization.has_value()) << *serialization;
+  }
+}
+
 TEST(StructuredHeaderTest, UnserializableStrings) {
   static const struct UnserializableString {
     const char* name;
@@ -989,7 +1106,7 @@
        {{Token("abc"), {Param("a:", 1)}}}},
       {"Param value is unserializable", {{Token("abc"), {{"a", Token("\n")}}}}},
       {"Inner list contains unserializable item",
-       {{std::vector<Item>{Token("\n")}, {}}}},
+       {{std::vector<ParameterizedItem>{{Token("\n"), {}}}, {}}}},
   };
   for (const auto& bad_list : bad_lists) {
     SCOPED_TRACE(bad_list.name);
diff --git a/remoting/android/java/res/layout/credits.xml b/remoting/android/java/res/layout/credits.xml
index ba8b497..6af4cc2 100644
--- a/remoting/android/java/res/layout/credits.xml
+++ b/remoting/android/java/res/layout/credits.xml
@@ -9,7 +9,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
-    <android.support.v7.widget.Toolbar
+    <androidx.appcompat.widget.Toolbar
         android:id="@+id/toolbar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/remoting/android/java/res/layout/desktop.xml b/remoting/android/java/res/layout/desktop.xml
index 0850c7c..7f4a83e 100644
--- a/remoting/android/java/res/layout/desktop.xml
+++ b/remoting/android/java/res/layout/desktop.xml
@@ -16,7 +16,7 @@
         android:layout_height="match_parent"
         android:fitsSystemWindows="true"
         android:elevation="4dp">
-        <android.support.v7.widget.Toolbar
+        <androidx.appcompat.widget.Toolbar
             android:id="@+id/toolbar"
             android:alpha="0.85"
             android:layout_width="match_parent"
diff --git a/remoting/android/java/res/layout/help.xml b/remoting/android/java/res/layout/help.xml
index da9936c..f905a72f 100644
--- a/remoting/android/java/res/layout/help.xml
+++ b/remoting/android/java/res/layout/help.xml
@@ -9,7 +9,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
-    <android.support.v7.widget.Toolbar
+    <androidx.appcompat.widget.Toolbar
         android:id="@+id/toolbar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/remoting/android/java/res/layout/main.xml b/remoting/android/java/res/layout/main.xml
index 51aea0e..2f7f6bcf 100644
--- a/remoting/android/java/res/layout/main.xml
+++ b/remoting/android/java/res/layout/main.xml
@@ -5,7 +5,7 @@
      found in the LICENSE file.
 -->
 
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -15,7 +15,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical">
-        <android.support.v7.widget.Toolbar
+        <androidx.appcompat.widget.Toolbar
             android:id="@+id/toolbar"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -81,4 +81,4 @@
         android:background="@android:color/background_light"
         android:fitsSystemWindows="true">
     </LinearLayout>
-</android.support.v4.widget.DrawerLayout>
+</androidx.drawerlayout.widget.DrawerLayout>
diff --git a/testing/buildbot/chromium.goma.json b/testing/buildbot/chromium.goma.json
index c3e445c..93a7067 100644
--- a/testing/buildbot/chromium.goma.json
+++ b/testing/buildbot/chromium.goma.json
@@ -1,6 +1,16 @@
 {
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
+  "Chromium Android ARM 32-bit Goma RBE ToT": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  },
+  "Chromium Android ARM 32-bit Goma RBE ToT (ATS)": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  },
   "Chromium Linux Goma RBE Staging": {
     "additional_compile_targets": [
       "all"
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index be56a13..2f96f1b7 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2071,6 +2071,16 @@
   {
     'name': 'chromium.goma',
     'machines': {
+      'Chromium Android ARM 32-bit Goma RBE ToT': {
+        'additional_compile_targets': [
+          'all',
+        ],
+      },
+      'Chromium Android ARM 32-bit Goma RBE ToT (ATS)': {
+        'additional_compile_targets': [
+          'all',
+        ],
+      },
       'Chromium Linux Goma RBE Staging': {
         'mixins': [
           'linux-xenial',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5664554..4a62d77 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2513,20 +2513,6 @@
                     "disable_features": [
                         "HappinessTrackingSurveysForDesktopSettingsPrivacy"
                     ]
-                },
-                {
-                    "name": "Settings_Privacy",
-                    "params": {
-                        "en_site_id": "gjhdonsbcw64p5i2sfhrdetucq",
-                        "probability": "0.5",
-                        "survey": "settings-privacy"
-                    },
-                    "enable_features": [
-                        "HappinessTrackingSurveysForDesktopSettingsPrivacy"
-                    ],
-                    "disable_features": [
-                        "HappinessTrackingSurveysForDesktopSettings"
-                    ]
                 }
             ]
         }
@@ -6002,25 +5988,6 @@
             ]
         }
     ],
-    "V8WasmCodeCache": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "WasmCodeCache"
-                    ]
-                }
-            ]
-        }
-    ],
     "V8WasmLiftoffMobile": [
         {
             "platforms": [
@@ -6542,6 +6509,23 @@
             ]
         }
     ],
+    "WebUITabStrip": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "WebUITabStrip"
+                    ]
+                }
+            ]
+        }
+    ],
     "WebViewWakeMetricsService": [
         {
             "platforms": [
diff --git a/third_party/blink/common/feature_policy/document_policy.cc b/third_party/blink/common/feature_policy/document_policy.cc
index 5c1a4eff..a472449 100644
--- a/third_party/blink/common/feature_policy/document_policy.cc
+++ b/third_party/blink/common/feature_policy/document_policy.cc
@@ -64,7 +64,7 @@
               net::structured_headers::Item::ItemType::kTokenType),
           {}));
     } else {
-      net::structured_headers::ParameterizedMember::Parameters params;
+      net::structured_headers::Parameters params;
       params.push_back(std::pair<std::string, net::structured_headers::Item>{
           info.feature_param_name, PolicyValueToItem(value)});
       root.push_back(net::structured_headers::ParameterizedMember(
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 00034bce..afc23a8 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -214,12 +214,6 @@
 const base::Feature kTextFragmentAnchor{"TextFragmentAnchor",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enables the site isolated Wasm code cache that is keyed on the resource URL
-// and the origin lock of the renderer that is requesting the resource. When
-// this flag is enabled, content/GeneratedCodeCache handles code cache requests.
-const base::Feature kWasmCodeCache = {"WasmCodeCache",
-                                      base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Writable files and native file system access. https://crbug.com/853326
 const base::Feature kNativeFileSystemAPI{"NativeFileSystemAPI",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 2927f78..1cd7f39 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -62,7 +62,6 @@
 BLINK_COMMON_EXPORT extern const base::Feature kStopNonTimersInBackground;
 BLINK_COMMON_EXPORT extern const base::Feature kStorageAccessAPI;
 BLINK_COMMON_EXPORT extern const base::Feature kTextFragmentAnchor;
-BLINK_COMMON_EXPORT extern const base::Feature kWasmCodeCache;
 BLINK_COMMON_EXPORT extern const base::Feature kFontAccess;
 BLINK_COMMON_EXPORT extern const base::Feature kNativeFileSystemAPI;
 BLINK_COMMON_EXPORT extern const base::Feature kFileHandlingAPI;
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 513b133..3ef2b0d 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -94,7 +94,6 @@
   BLINK_PLATFORM_EXPORT static void EnableCookieDeprecationMessages(bool);
   BLINK_PLATFORM_EXPORT static void EnableCookiesWithoutSameSiteMustBeSecure(
       bool);
-  BLINK_PLATFORM_EXPORT static void EnableWasmCodeCache(bool);
   BLINK_PLATFORM_EXPORT static void EnableCanvas2dImageChromium(bool);
   BLINK_PLATFORM_EXPORT static void EnableCooperativeScheduling(bool);
   BLINK_PLATFORM_EXPORT static void EnableCSSHexAlphaColor(bool);
diff --git a/third_party/blink/public/web/web_view.h b/third_party/blink/public/web/web_view.h
index 70b5a99..6e5438e 100644
--- a/third_party/blink/public/web/web_view.h
+++ b/third_party/blink/public/web/web_view.h
@@ -54,6 +54,7 @@
 namespace blink {
 class PageScheduler;
 class WebFrame;
+class WebFrameWidget;
 class WebHitTestResult;
 class WebLocalFrame;
 class WebPageImportanceSignals;
@@ -484,7 +485,7 @@
 
   // TODO(lfg): Remove this once the refactor of WebView/WebWidget is
   // completed.
-  virtual WebWidget* MainFrameWidget() = 0;
+  virtual WebFrameWidget* MainFrameWidget() = 0;
 
   // Portals --------------------------------------------------------------
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc b/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc
index c0b5126d..6665790 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_wasm_response_extensions.cc
@@ -211,8 +211,6 @@
 
 RawResource* GetRawResource(ScriptState* script_state,
                             const String& url_string) {
-  if (!RuntimeEnabledFeatures::WasmCodeCacheEnabled())
-    return nullptr;
   ExecutionContext* execution_context = ExecutionContext::From(script_state);
   if (!execution_context)
     return nullptr;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 1e3b142..4b310d0 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -113,7 +113,9 @@
     : ExecutionContextLifecycleObserver(context),
       element_(element),
       document_(&element_->GetDocument()),
-      state_(this) {}
+      state_(this) {
+  document_->AddDisplayLockContext(this);
+}
 
 DisplayLockContext::~DisplayLockContext() {
   DCHECK_EQ(state_, kUnlocked);
@@ -333,6 +335,8 @@
 bool DisplayLockContext::ShouldStyle(DisplayLockLifecycleTarget target) const {
   return target == DisplayLockLifecycleTarget::kSelf || update_forced_ ||
          state_ > kUpdating ||
+         (document_->ActivatableDisplayLocksForced() &&
+          IsActivatable(DisplayLockActivationReason::kAny)) ||
          ShouldPerformUpdatePhase(DisplayLockBudget::Phase::kStyle);
 }
 
@@ -359,7 +363,10 @@
     return;
   }
 
-  if (state_ != kCommitting && state_ != kUpdating && !update_forced_)
+  bool update_forced =
+      update_forced_ || (document_->ActivatableDisplayLocksForced() &&
+                         IsActivatable(DisplayLockActivationReason::kAny));
+  if (state_ != kCommitting && state_ != kUpdating && !update_forced)
     return;
 
   if (element_->ChildNeedsReattachLayoutTree())
@@ -376,6 +383,8 @@
 bool DisplayLockContext::ShouldLayout(DisplayLockLifecycleTarget target) const {
   return target == DisplayLockLifecycleTarget::kSelf || update_forced_ ||
          state_ > kUpdating ||
+         (document_->ActivatableDisplayLocksForced() &&
+          IsActivatable(DisplayLockActivationReason::kAny)) ||
          ShouldPerformUpdatePhase(DisplayLockBudget::Phase::kLayout);
 }
 
@@ -786,6 +795,9 @@
   DCHECK(element_);
   document_ = &element_->GetDocument();
 
+  old_document.RemoveDisplayLockContext(this);
+  document_->AddDisplayLockContext(this);
+
   if (is_observed_) {
     old_document.UnregisterDisplayLockActivationObservation(element_);
     document_->RegisterDisplayLockActivationObservation(element_);
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index 673a337..a2e2cc9 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -274,6 +274,13 @@
   StyleRecalcChange AdjustStyleRecalcChangeForChildren(
       StyleRecalcChange change);
 
+  void DidForceActivatableDisplayLocks() {
+    if (IsLocked() && IsActivatable(DisplayLockActivationReason::kAny)) {
+      MarkForStyleRecalcIfNeeded();
+      MarkForLayoutIfNeeded();
+    }
+  }
+
  private:
   friend class DisplayLockContextTest;
   friend class DisplayLockBudgetTest;
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 202b2bc..e7c45d50 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -97,6 +97,7 @@
 #include "third_party/blink/renderer/core/css/style_engine.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
 #include "third_party/blink/renderer/core/css/style_sheet_list.h"
+#include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/dom/attr.h"
 #include "third_party/blink/renderer/core/dom/beforeunload_event_listener.h"
@@ -8198,6 +8199,7 @@
   visitor->Trace(policy_);
   visitor->Trace(slot_assignment_engine_);
   visitor->Trace(viewport_data_);
+  visitor->Trace(display_lock_contexts_);
   visitor->Trace(navigation_initiator_);
   visitor->Trace(lazy_load_image_observer_);
   visitor->Trace(isolated_world_csp_map_);
@@ -8415,6 +8417,51 @@
   return locked_display_lock_count_;
 }
 
+void Document::AddDisplayLockContext(DisplayLockContext* context) {
+  display_lock_contexts_.insert(context);
+}
+
+void Document::RemoveDisplayLockContext(DisplayLockContext* context) {
+  display_lock_contexts_.erase(context);
+}
+
+Document::ScopedForceActivatableDisplayLocks
+Document::GetScopedForceActivatableLocks() {
+  return ScopedForceActivatableDisplayLocks(this);
+}
+
+Document::ScopedForceActivatableDisplayLocks::
+    ScopedForceActivatableDisplayLocks(Document* document)
+    : document_(document) {
+  if (++document_->activatable_display_locks_forced_ == 1) {
+    for (auto context : document_->display_lock_contexts_)
+      context->DidForceActivatableDisplayLocks();
+  }
+}
+
+Document::ScopedForceActivatableDisplayLocks::
+    ScopedForceActivatableDisplayLocks(
+        ScopedForceActivatableDisplayLocks&& other)
+    : document_(other.document_) {
+  other.document_ = nullptr;
+}
+
+Document::ScopedForceActivatableDisplayLocks&
+Document::ScopedForceActivatableDisplayLocks::operator=(
+    ScopedForceActivatableDisplayLocks&& other) {
+  document_ = other.document_;
+  other.document_ = nullptr;
+  return *this;
+}
+
+Document::ScopedForceActivatableDisplayLocks::
+    ~ScopedForceActivatableDisplayLocks() {
+  if (!document_)
+    return;
+  DCHECK(document_->activatable_display_locks_forced_);
+  --document_->activatable_display_locks_forced_;
+}
+
 void Document::RegisterDisplayLockActivationObservation(Element* element) {
   EnsureDisplayLockActivationObserver().observe(element);
 }
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 15e03c6f..00edda8 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -100,6 +100,7 @@
 class ChromeClient;
 class Comment;
 class ComputedAccessibleNode;
+class DisplayLockContext;
 class ElementIntersectionObserverData;
 class WindowAgent;
 class WindowAgentFactory;
@@ -1641,10 +1642,35 @@
   void RemoveLockedDisplayLock();
   int LockedDisplayLockCount() const;
 
+  void AddDisplayLockContext(DisplayLockContext*);
+  void RemoveDisplayLockContext(DisplayLockContext*);
+
   // Manage the element's observation for display lock activation.
   void RegisterDisplayLockActivationObservation(Element*);
   void UnregisterDisplayLockActivationObservation(Element*);
 
+  class ScopedForceActivatableDisplayLocks {
+    STACK_ALLOCATED();
+
+   public:
+    ScopedForceActivatableDisplayLocks(ScopedForceActivatableDisplayLocks&&);
+    ~ScopedForceActivatableDisplayLocks();
+
+    ScopedForceActivatableDisplayLocks& operator=(
+        ScopedForceActivatableDisplayLocks&&);
+
+   private:
+    friend Document;
+    ScopedForceActivatableDisplayLocks(Document*);
+
+    Document* document_;
+  };
+
+  ScopedForceActivatableDisplayLocks GetScopedForceActivatableLocks();
+  bool ActivatableDisplayLocksForced() const {
+    return activatable_display_locks_forced_ > 0;
+  }
+
   // Deferred compositor commits are disallowed by default, and are only allowed
   // for same-origin navigations to an html document fetched with http.
   bool DeferredCompositorCommitIsAllowed() {
@@ -2232,6 +2258,10 @@
   int display_lock_blocking_all_activation_count_ = 0;
   // Number of locked display locks in the document.
   int locked_display_lock_count_ = 0;
+  // All of this document's display lock contexts.
+  HeapHashSet<WeakMember<DisplayLockContext>> display_lock_contexts_;
+  // If non-zero, then the activatable locks have been globally forced.
+  int activatable_display_locks_forced_ = 0;
 
   bool deferred_compositor_commit_is_allowed_ = false;
 
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.cc b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
index 4036724..1367abe 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -203,47 +203,6 @@
   return Results(*this, &text_searcher_, buffer_, search_text_16_bit, options);
 }
 
-bool FindBuffer::PushScopedForcedUpdateIfNeeded(const Element& element) {
-  if (auto* context = element.GetDisplayLockContext()) {
-    DCHECK(!context->IsLocked() ||
-           context->IsActivatable(DisplayLockActivationReason::kFindInPage));
-    scoped_forced_update_list_.push_back(context->GetScopedForcedUpdate());
-    return true;
-  }
-  return false;
-}
-
-void FindBuffer::CollectScopedForcedUpdates(Node& start_node,
-                                            const Node* search_range_end_node,
-                                            const Node* node_after_block) {
-  if (!RuntimeEnabledFeatures::CSSRenderSubtreeEnabled())
-    return;
-  if (start_node.GetDocument().LockedDisplayLockCount() ==
-      start_node.GetDocument().DisplayLockBlockingAllActivationCount())
-    return;
-
-  Node* node = &start_node;
-  // We assume |start_node| is always visible/activatable if locked, so we don't
-  // need to check activatability of ancestors here.
-  for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(*node)) {
-    auto* ancestor_element = DynamicTo<Element>(ancestor);
-    if (!ancestor_element)
-      continue;
-    PushScopedForcedUpdateIfNeeded(*ancestor_element);
-  }
-
-  while (node && node != node_after_block && node != search_range_end_node) {
-    if (ShouldIgnoreContents(*node)) {
-      // Will skip display:none/non-activatable locked subtrees/etc.
-      node = FlatTreeTraversal::NextSkippingChildren(*node);
-      continue;
-    }
-    if (auto* element = DynamicTo<Element>(node))
-      PushScopedForcedUpdateIfNeeded(*element);
-    node = FlatTreeTraversal::Next(*node);
-  }
-}
-
 void FindBuffer::CollectTextUntilBlockBoundary(
     const EphemeralRangeInFlatTree& range) {
   // Collects text until block boundary located at or after |start_node|
@@ -276,14 +235,6 @@
   // We will also stop if we encountered/passed |end_node|.
   Node* end_node = range.EndPosition().NodeAsRangeLastNode();
 
-  if (node) {
-    CollectScopedForcedUpdates(*node, end_node, just_after_block);
-    if (!scoped_forced_update_list_.IsEmpty()) {
-      node->GetDocument().UpdateStyleAndLayout(
-          DocumentUpdateReason::kFindInPage);
-    }
-  }
-
   while (node && node != just_after_block) {
     if (ShouldIgnoreContents(*node)) {
       if (end_node && (end_node == node ||
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.h b/third_party/blink/renderer/core/editing/finder/find_buffer.h
index b8626bfc..97cb1ce 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.h
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.h
@@ -133,19 +133,6 @@
   // another LayoutBlockFlow or after |end_position|) to |node_after_block_|.
   void CollectTextUntilBlockBoundary(const EphemeralRangeInFlatTree& range);
 
-  // Adds the ScopedForcedUpdate of |element|'s DisplayLockContext (if it's
-  // there) to |scoped_forced_update_list_|. Returns true if we added a
-  // ScopedForceUpdate.
-  bool PushScopedForcedUpdateIfNeeded(const Element& element);
-
-  // Collects all ScopedForceUpdates of any activatable-locked element
-  // within the range of [start_node, search_range_end_node] or
-  // [start_node, node_after_block) whichever is smaller, to
-  // |scoped_forced_update_list_|.
-  void CollectScopedForcedUpdates(Node& start_node,
-                                  const Node* search_range_end_node,
-                                  const Node* node_after_block);
-
   // Mapping for position in buffer -> actual node where the text came from,
   // along with the offset in the NGOffsetMapping of this find_buffer.
   // This is needed because when we find a match in the buffer, we want to know
@@ -190,7 +177,6 @@
   Node* node_after_block_ = nullptr;
   Vector<UChar> buffer_;
   Vector<BufferNodeMapping> buffer_node_mappings_;
-  Vector<DisplayLockContext::ScopedForcedUpdate> scoped_forced_update_list_;
   TextSearcherICU text_searcher_;
 
   const NGOffsetMapping* offset_mapping_ = nullptr;
diff --git a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
index e3da155..d2eb93d 100644
--- a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
@@ -52,6 +52,7 @@
   void Dispose() {
     DCHECK_GT(callback_handle_, 0);
     document_->CancelIdleCallback(callback_handle_);
+    forced_activatable_display_locks_.reset();
   }
 
   void ForceInvocationForTesting() {
@@ -72,6 +73,12 @@
       controller_->DidFinishTask(identifier_, search_text_, *options_,
                                  true /* finished_whole_request */,
                                  PositionInFlatTree(), 0 /* match_count */);
+      forced_activatable_display_locks_.reset();
+    } else if (!forced_activatable_display_locks_) {
+      forced_activatable_display_locks_.emplace(
+          controller_->GetLocalFrame()
+              ->GetDocument()
+              ->GetScopedForceActivatableLocks());
     }
 
     Document& document = *controller_->GetLocalFrame()->GetDocument();
@@ -96,8 +103,7 @@
         return;
     }
 
-    // TODO(editing-dev): Use of UpdateStyleAndLayout
-    // needs to be audited.  see http://crbug.com/590369 for more details.
+    // This is required if we forced any of the display-locks.
     search_start.GetDocument()->UpdateStyleAndLayout(
         DocumentUpdateReason::kFindInPage);
 
@@ -156,6 +162,8 @@
   const int identifier_;
   const WebString search_text_;
   mojom::blink::FindOptionsPtr options_;
+  base::Optional<Document::ScopedForceActivatableDisplayLocks>
+      forced_activatable_display_locks_;
 };
 
 FindTaskController::FindTaskController(WebLocalFrameImpl& owner_frame,
diff --git a/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc b/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc
index 3b15d70..f71a66f0 100644
--- a/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_content_dumper.cc
@@ -6,6 +6,7 @@
 
 #include "base/stl_util.h"
 #include "third_party/blink/public/web/web_document.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 #include "third_party/blink/public/web/web_view.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 02737ef..c8f281a 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1457,7 +1457,7 @@
   fullscreen_controller_->DidExitFullscreen();
 }
 
-void WebViewImpl::SetWebWidget(WebWidget* widget) {
+void WebViewImpl::SetWebFrameWidget(WebFrameWidget* widget) {
   web_widget_ = widget;
 }
 
@@ -3408,7 +3408,7 @@
   SetVisibilityState(PageVisibilityState::kVisible, /*is_initial_state=*/false);
 }
 
-WebWidget* WebViewImpl::MainFrameWidget() {
+WebFrameWidget* WebViewImpl::MainFrameWidget() {
   return web_widget_;
 }
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.h b/third_party/blink/renderer/core/exported/web_view_impl.h
index cde5826..c928c208 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -46,9 +46,9 @@
 #include "third_party/blink/public/platform/web_size.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/public/web/web_frame_widget.h"
 #include "third_party/blink/public/web/web_navigation_policy.h"
 #include "third_party/blink/public/web/web_view.h"
-#include "third_party/blink/public/web/web_widget.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/exported/web_page_popup_impl.h"
 #include "third_party/blink/renderer/core/frame/resize_viewport_anchor.h"
@@ -202,7 +202,7 @@
   void PutPageIntoBackForwardCache() override;
   void RestorePageFromBackForwardCache(
       base::TimeTicks navigation_start) override;
-  WebWidget* MainFrameWidget() override;
+  WebFrameWidget* MainFrameWidget() override;
   void SetBaseBackgroundColor(SkColor) override;
   void SetBackgroundColorOverride(SkColor) override;
   void ClearBackgroundColorOverride() override;
@@ -410,7 +410,7 @@
   void DidEnterFullscreen();
   void DidExitFullscreen();
 
-  void SetWebWidget(WebWidget* widget);
+  void SetWebFrameWidget(WebFrameWidget* widget);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(WebFrameTest, DivScrollIntoEditableTest);
@@ -687,7 +687,7 @@
 
   // The WebWidget for the main frame. This is expected to be unset when the
   // WebWidget destroys itself.
-  WebWidget* web_widget_ = nullptr;
+  WebFrameWidget* web_widget_ = nullptr;
 
   // We defer commits when transitioning to a new page. ChromeClientImpl calls
   // StopDeferringCommits() to release this when a new page is loaded.
diff --git a/third_party/blink/renderer/core/feature_policy/document_policy_parser.cc b/third_party/blink/renderer/core/feature_policy/document_policy_parser.cc
index 80e8c42..cc39320 100644
--- a/third_party/blink/renderer/core/feature_policy/document_policy_parser.cc
+++ b/third_party/blink/renderer/core/feature_policy/document_policy_parser.cc
@@ -43,12 +43,13 @@
   DocumentPolicy::FeatureState policy;
   for (const net::structured_headers::ParameterizedMember& directive :
        root.value()) {
-    // Each directive is allowed exactly 1 member.
-    if (directive.member.size() != 1)
+    // Directives must not be inner lists.
+    if (directive.member_is_inner_list)
       return base::nullopt;
 
     const net::structured_headers::Item& feature_token =
-        directive.member.front();
+        directive.member.front().item;
+
     // The item in directive should be token type.
     if (!feature_token.is_token())
       return base::nullopt;
diff --git a/third_party/blink/renderer/core/frame/find_in_page.cc b/third_party/blink/renderer/core/frame/find_in_page.cc
index 8a5a348..af7ec74 100644
--- a/third_party/blink/renderer/core/frame/find_in_page.cc
+++ b/third_party/blink/renderer/core/frame/find_in_page.cc
@@ -170,6 +170,9 @@
   // Unlikely, but just in case we try to find-in-page on a detached frame.
   DCHECK(frame_->GetFrame()->GetPage());
 
+  auto forced_activatable_locks =
+      frame_->GetFrame()->GetDocument()->GetScopedForceActivatableLocks();
+
   // Up-to-date, clean tree is required for finding text in page, since it
   // relies on TextIterator to look over the text.
   frame_->GetFrame()->GetDocument()->UpdateStyleAndLayout(
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index 68f2386..c6e0e0d 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -15,7 +15,7 @@
     : WebFrameWidgetBase(client),
       web_view_(&web_view),
       self_keep_alive_(PERSISTENT_FROM_HERE, this) {
-  web_view_->SetWebWidget(this);
+  web_view_->SetWebFrameWidget(this);
 }
 
 WebViewFrameWidget::~WebViewFrameWidget() = default;
@@ -24,7 +24,7 @@
   GetPage()->WillCloseAnimationHost(nullptr);
   // Closing the WebViewFrameWidget happens in response to the local main frame
   // being detached from the Page/WebViewImpl.
-  web_view_->SetWebWidget(nullptr);
+  web_view_->SetWebFrameWidget(nullptr);
   web_view_ = nullptr;
   WebFrameWidgetBase::Close();
   self_keep_alive_.Clear();
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h
index 0c86465..85851eb1c 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.h
+++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -626,13 +626,6 @@
 
   DisplayMode display_mode_;
 
-  // If any portion of an attached HTMLMediaElement (HTMLME) and the MediaSource
-  // Extensions (MSE) API is alive (having pending activity or traceable from a
-  // GC root), the whole group is not GC'ed. Here, using Member, because
-  // |media_source_|'s wrapper needs to remain alive at least to successfully
-  // dispatch any events enqueued by behavior of the HTMLME+MSE API. It makes
-  // |media_source_|'s wrapper remain alive as long as this HTMLMediaElement's
-  // wrapper is alive.
   Member<MediaSource> media_source_;
 
   // Stores "official playback position", updated periodically from "current
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.cc b/third_party/blink/renderer/core/html/portal/html_portal_element.cc
index 070fb49..6d3bc25 100644
--- a/third_party/blink/renderer/core/html/portal/html_portal_element.cc
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.cc
@@ -310,13 +310,16 @@
   return portal_->GetToken();
 }
 
-HTMLPortalElement::InsertionNotificationRequest HTMLPortalElement::InsertedInto(
+Node::InsertionNotificationRequest HTMLPortalElement::InsertedInto(
     ContainerNode& node) {
   auto result = HTMLFrameOwnerElement::InsertedInto(node);
 
   if (!CheckPortalsEnabledOrWarn())
     return result;
 
+  if (!SubframeLoadingDisabler::CanLoadFrame(*this))
+    return result;
+
   switch (GetGuestContentsEligibility()) {
     case GuestContentsEligibility::kIneligible:
       return result;
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index 2718b43..d0e8ac1 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -685,7 +685,7 @@
       &status, kPreferAcceleration, FloatSize());
   if (status != kNormalSourceImageStatus)
     return;
-  DCHECK(image_input->IsStaticBitmapImage());
+  DCHECK(IsA<StaticBitmapImage>(image_input.get()));
   scoped_refptr<StaticBitmapImage> input =
       static_cast<StaticBitmapImage*>(image_input.get());
 
@@ -709,7 +709,7 @@
   SourceImageStatus status;
   scoped_refptr<Image> raw_input = offscreen_canvas->GetSourceImageForCanvas(
       &status, kPreferNoAcceleration, FloatSize(offscreen_canvas->Size()));
-  DCHECK(raw_input->IsStaticBitmapImage());
+  DCHECK(IsA<StaticBitmapImage>(raw_input.get()));
   scoped_refptr<StaticBitmapImage> input =
       static_cast<StaticBitmapImage*>(raw_input.get());
   raw_input = nullptr;
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 400716d..2b95873 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -666,6 +666,8 @@
   // css-flexbox section 4.5
   const Length& min = IsHorizontalFlow() ? child.StyleRef().MinWidth()
                                          : child.StyleRef().MinHeight();
+  // TODO(dgrogan): min.IsIntrinsic should also get past this check when in the
+  // item's block direction.
   if (!min.IsAuto())
     return false;
 
@@ -673,6 +675,8 @@
   if (StyleRef().IsDeprecatedWebkitBox())
     return false;
 
+  // TODO(dgrogan): MainAxisOverflowForChild == kClip also qualifies, not just
+  // kVisible.
   return !child.ShouldApplySizeContainment() &&
          MainAxisOverflowForChild(child) == EOverflow::kVisible;
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
index 31f4606..2ca8f6d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_absolute_utils.cc
@@ -473,10 +473,10 @@
 
   LayoutUnit min_block_size = ResolveMinBlockLength(
       space, style, border_padding, style.LogicalMinHeight(),
-      child_block_size_or_indefinite, LengthResolvePhase::kLayout);
+      LengthResolvePhase::kLayout);
   LayoutUnit max_block_size = ResolveMaxBlockLength(
       space, style, border_padding, style.LogicalMaxHeight(),
-      child_block_size_or_indefinite, LengthResolvePhase::kLayout);
+      LengthResolvePhase::kLayout);
 
   bool is_start_dominant;
   if (style.GetWritingMode() == WritingMode::kHorizontalTb) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
index fa2c62be..7bc8dbaf 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_column_layout_algorithm.cc
@@ -879,7 +879,7 @@
 
   const ComputedStyle& style = Style();
   LayoutUnit max = ResolveMaxBlockLength(
-      ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(), size,
+      ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(),
       LengthResolvePhase::kLayout);
   LayoutUnit extent = ResolveMainBlockLength(
       ConstraintSpace(), style, border_padding_, style.LogicalHeight(), size,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
index c237a2c..df3b257dd 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -426,6 +426,11 @@
         // We want the child's min/max size in its writing mode, not ours.
         // We'll only ever use it if the child's inline axis is our main axis.
         NGConstraintSpace child_space = BuildSpaceForIntrinsicBlockSize(child);
+        if (child.Style().OverflowBlockDirection() == EOverflow::kAuto) {
+          // Ensure this child has been laid out so its auto scrollbars are
+          // included in its intrinsic sizes.
+          IntrinsicBlockSizeFunc();
+        }
         min_max_size = child.ComputeMinMaxSize(
             child_style.GetWritingMode(),
             MinMaxSizeInput(content_box_size_.block_size), &child_space);
@@ -453,17 +458,14 @@
           LengthResolvePhase::kLayout);
       min_max_sizes_in_cross_axis_direction.max_size = ResolveMaxBlockLength(
           flex_basis_space, child_style, border_padding_in_child_writing_mode,
-          max_property_in_cross_axis, IntrinsicBlockSizeFunc,
-          LengthResolvePhase::kLayout);
+          max_property_in_cross_axis, LengthResolvePhase::kLayout);
       min_max_sizes_in_cross_axis_direction.min_size = ResolveMinBlockLength(
           flex_basis_space, child_style, border_padding_in_child_writing_mode,
-          min_property_in_cross_axis, IntrinsicBlockSizeFunc,
-          LengthResolvePhase::kLayout);
+          min_property_in_cross_axis, LengthResolvePhase::kLayout);
     } else {
       min_max_sizes_in_main_axis_direction.max_size = ResolveMaxBlockLength(
           flex_basis_space, child_style, border_padding_in_child_writing_mode,
-          max_property_in_main_axis, IntrinsicBlockSizeFunc,
-          LengthResolvePhase::kLayout);
+          max_property_in_main_axis, LengthResolvePhase::kLayout);
       min_max_sizes_in_cross_axis_direction.max_size = ResolveMaxInlineLength(
           flex_basis_space, child_style, border_padding_in_child_writing_mode,
           MinMaxSizeFunc, max_property_in_cross_axis,
@@ -582,6 +584,8 @@
 
     const Length& min = is_horizontal_flow_ ? child.Style().MinWidth()
                                             : child.Style().MinHeight();
+    // TODO(dgrogan): min.IsIntrinsic should enter this block when it's in the
+    // item's block direction.
     if (min.IsAuto()) {
       if (algorithm_->ShouldApplyMinSizeAutoForChild(*child.GetLayoutBox())) {
         // TODO(dgrogan): This should probably apply to column flexboxes also,
@@ -678,7 +682,7 @@
     } else {
       min_max_sizes_in_main_axis_direction.min_size = ResolveMinBlockLength(
           flex_basis_space, child_style, border_padding_in_child_writing_mode,
-          min, IntrinsicBlockSizeFunc, LengthResolvePhase::kLayout);
+          min, LengthResolvePhase::kLayout);
     }
     min_max_sizes_in_main_axis_direction -= main_axis_border_padding;
     DCHECK_GE(min_max_sizes_in_main_axis_direction.min_size, 0);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
index 3572c61a..39d388c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.cc
@@ -286,7 +286,7 @@
                                  max_length, LengthResolvePhase::kIntrinsic);
   } else {
     max = ResolveMaxBlockLength(space, style, border_padding, max_length,
-                                content_size, LengthResolvePhase::kIntrinsic);
+                                LengthResolvePhase::kIntrinsic);
   }
   computed_sizes.Constrain(max);
 
@@ -299,7 +299,7 @@
                                  min_length, LengthResolvePhase::kIntrinsic);
   } else {
     min = ResolveMinBlockLength(space, style, border_padding, min_length,
-                                content_size, LengthResolvePhase::kIntrinsic);
+                                LengthResolvePhase::kIntrinsic);
   }
   computed_sizes.Encompass(min);
 
@@ -486,7 +486,7 @@
         nullptr) {
   LayoutUnit min = ResolveMinBlockLength(
       constraint_space, style, border_padding, style.LogicalMinHeight(),
-      content_size, LengthResolvePhase::kLayout,
+      LengthResolvePhase::kLayout,
       opt_percentage_resolution_block_size_for_min_max);
   const Length& logical_height = style.LogicalHeight();
   // Scrollable percentage-sized children of table cells, in the table
@@ -518,7 +518,7 @@
 
   LayoutUnit max = ResolveMaxBlockLength(
       constraint_space, style, border_padding, style.LogicalMaxHeight(),
-      content_size, LengthResolvePhase::kLayout,
+      LengthResolvePhase::kLayout,
       opt_percentage_resolution_block_size_for_min_max);
 
   return ConstrainByMinMax(extent, min, max);
@@ -565,12 +565,12 @@
   LayoutUnit inline_max = ResolveMaxInlineLength(
       space, style, border_padding, child_min_max_size, style.LogicalMaxWidth(),
       LengthResolvePhase::kLayout);
-  LayoutUnit block_min = ResolveMinBlockLength(
-      space, style, border_padding, style.LogicalMinHeight(),
-      border_padding.BlockSum(), LengthResolvePhase::kLayout);
-  LayoutUnit block_max = ResolveMaxBlockLength(
-      space, style, border_padding, style.LogicalMaxHeight(), LayoutUnit::Max(),
-      LengthResolvePhase::kLayout);
+  LayoutUnit block_min = ResolveMinBlockLength(space, style, border_padding,
+                                               style.LogicalMinHeight(),
+                                               LengthResolvePhase::kLayout);
+  LayoutUnit block_max = ResolveMaxBlockLength(space, style, border_padding,
+                                               style.LogicalMaxHeight(),
+                                               LengthResolvePhase::kLayout);
 
   const Length& inline_length = style.LogicalWidth();
   const Length& block_length = style.LogicalHeight();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
index f97ad17..3f5414ac 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_length_utils.h
@@ -199,7 +199,6 @@
     const ComputedStyle& style,
     const NGBoxStrut& border_padding,
     const Length& length,
-    LayoutUnit content_size,
     LengthResolvePhase phase,
     const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
         nullptr) {
@@ -209,41 +208,16 @@
     return border_padding.BlockSum();
 
   return ResolveBlockLengthInternal(
-      constraint_space, style, border_padding, length, content_size, phase,
+      constraint_space, style, border_padding, length, kIndefiniteSize, phase,
       opt_percentage_resolution_block_size_for_min_max);
 }
 
-template <typename IntrinsicBlockSizeFunc>
-inline LayoutUnit ResolveMinBlockLength(
-    const NGConstraintSpace& constraint_space,
-    const ComputedStyle& style,
-    const NGBoxStrut& border_padding,
-    const Length& length,
-    const IntrinsicBlockSizeFunc& intrinsic_block_size_func,
-    LengthResolvePhase phase,
-    const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
-        nullptr) {
-  if (LIKELY(BlockLengthUnresolvable(
-          constraint_space, length, phase,
-          opt_percentage_resolution_block_size_for_min_max)))
-    return border_padding.BlockSum();
-
-  LayoutUnit intrinsic_block_size = kIndefiniteSize;
-  if (length.IsIntrinsicOrAuto())
-    intrinsic_block_size = intrinsic_block_size_func();
-
-  return ResolveBlockLengthInternal(
-      constraint_space, style, border_padding, length, intrinsic_block_size,
-      phase, opt_percentage_resolution_block_size_for_min_max);
-}
-
 // Used for resolving max block lengths, (|ComputedStyle::MaxLogicalHeight|).
 inline LayoutUnit ResolveMaxBlockLength(
     const NGConstraintSpace& constraint_space,
     const ComputedStyle& style,
     const NGBoxStrut& border_padding,
     const Length& length,
-    LayoutUnit content_size,
     LengthResolvePhase phase,
     const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
         nullptr) {
@@ -253,34 +227,10 @@
     return LayoutUnit::Max();
 
   return ResolveBlockLengthInternal(
-      constraint_space, style, border_padding, length, content_size, phase,
+      constraint_space, style, border_padding, length, kIndefiniteSize, phase,
       opt_percentage_resolution_block_size_for_min_max);
 }
 
-template <typename IntrinsicBlockSizeFunc>
-inline LayoutUnit ResolveMaxBlockLength(
-    const NGConstraintSpace& constraint_space,
-    const ComputedStyle& style,
-    const NGBoxStrut& border_padding,
-    const Length& length,
-    const IntrinsicBlockSizeFunc& intrinsic_block_size_func,
-    LengthResolvePhase phase,
-    const LayoutUnit* opt_percentage_resolution_block_size_for_min_max =
-        nullptr) {
-  if (LIKELY(BlockLengthUnresolvable(
-          constraint_space, length, phase,
-          opt_percentage_resolution_block_size_for_min_max)))
-    return LayoutUnit::Max();
-
-  LayoutUnit intrinsic_block_size = kIndefiniteSize;
-  if (length.IsIntrinsicOrAuto())
-    intrinsic_block_size = intrinsic_block_size_func();
-
-  return ResolveBlockLengthInternal(
-      constraint_space, style, border_padding, length, intrinsic_block_size,
-      phase, opt_percentage_resolution_block_size_for_min_max);
-}
-
 // Used for resolving main block lengths, (|ComputedStyle::LogicalHeight|).
 inline LayoutUnit ResolveMainBlockLength(
     const NGConstraintSpace& constraint_space,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
index 7a77aa98..4c821c2 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -110,16 +110,13 @@
 
 LayoutSVGResourceClipper::~LayoutSVGResourceClipper() = default;
 
-void LayoutSVGResourceClipper::RemoveAllClientsFromCache(
-    bool mark_for_invalidation) {
+void LayoutSVGResourceClipper::RemoveAllClientsFromCache() {
   clip_content_path_validity_ = kClipContentPathUnknown;
   clip_content_path_.Clear();
   cached_paint_record_.reset();
   local_clip_bounds_ = FloatRect();
-  MarkAllClientsForInvalidation(
-      mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation |
-                                  SVGResourceClient::kBoundariesInvalidation
-                            : SVGResourceClient::kParentOnlyInvalidation);
+  MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation |
+                                SVGResourceClient::kBoundariesInvalidation);
 }
 
 base::Optional<Path> LayoutSVGResourceClipper::AsPath() {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
index b8f71fb..1a47dce 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
@@ -35,7 +35,7 @@
 
   const char* GetName() const override { return "LayoutSVGResourceClipper"; }
 
-  void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override;
+  void RemoveAllClientsFromCache() override;
 
   FloatRect ResourceBoundingBox(const FloatRect& reference_box);
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
index be7143b..da7fd93 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
@@ -143,7 +143,7 @@
   if (is_invalidating_)
     return;
   LocalSVGResource* resource = ResourceForContainer(*this);
-  if (!resource || !resource->HasClients())
+  if (!resource)
     return;
   // Remove modes for which invalidations have already been
   // performed. If no modes remain we are done.
@@ -155,8 +155,7 @@
   is_invalidating_ = true;
 
   // Invalidate clients registered via an SVGResource.
-  if (resource)
-    resource->NotifyContentChanged(invalidation_mask);
+  resource->NotifyContentChanged(invalidation_mask);
 
   is_invalidating_ = false;
 }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
index 0efc2261..2ff5039 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
@@ -42,7 +42,7 @@
   explicit LayoutSVGResourceContainer(SVGElement*);
   ~LayoutSVGResourceContainer() override;
 
-  virtual void RemoveAllClientsFromCache(bool mark_for_invalidation = true) = 0;
+  virtual void RemoveAllClientsFromCache() = 0;
 
   // Remove any cached data for the |client|, and return true if so.
   virtual bool RemoveClientFromCache(SVGResourceClient&) { return false; }
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc
index 2b7b164..5c7ee29 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc
@@ -65,16 +65,13 @@
   return child->IsSVGResourceFilterPrimitive();
 }
 
-void LayoutSVGResourceFilter::RemoveAllClientsFromCache(
-    bool mark_for_invalidation) {
+void LayoutSVGResourceFilter::RemoveAllClientsFromCache() {
   // LayoutSVGResourceFilter::removeClientFromCache will be called for
   // all clients through markAllClientsForInvalidation so no explicit
   // display item invalidation is needed here.
   DisposeFilterMap();
-  MarkAllClientsForInvalidation(
-      mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation |
-                                  SVGResourceClient::kBoundariesInvalidation
-                            : SVGResourceClient::kParentOnlyInvalidation);
+  MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation |
+                                SVGResourceClient::kBoundariesInvalidation);
 }
 
 bool LayoutSVGResourceFilter::RemoveClientFromCache(SVGResourceClient& client) {
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h
index 17433d8..7d75dda 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h
@@ -76,7 +76,7 @@
            LayoutSVGResourceContainer::IsOfType(type);
   }
 
-  void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override;
+  void RemoveAllClientsFromCache() override;
   bool RemoveClientFromCache(SVGResourceClient&) override;
 
   FloatRect ResourceBoundingBox(const FloatRect& reference_box) const;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc
index ab3d552..24613470 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.cc
@@ -41,14 +41,11 @@
       should_collect_gradient_attributes_(true),
       gradient_map_(MakeGarbageCollected<GradientMap>()) {}
 
-void LayoutSVGResourceGradient::RemoveAllClientsFromCache(
-    bool mark_for_invalidation) {
+void LayoutSVGResourceGradient::RemoveAllClientsFromCache() {
   gradient_map_->clear();
   should_collect_gradient_attributes_ = true;
   To<SVGGradientElement>(*GetElement()).InvalidateDependentGradients();
-  MarkAllClientsForInvalidation(
-      mark_for_invalidation ? SVGResourceClient::kPaintInvalidation
-                            : SVGResourceClient::kParentOnlyInvalidation);
+  MarkAllClientsForInvalidation(SVGResourceClient::kPaintInvalidation);
 }
 
 bool LayoutSVGResourceGradient::RemoveClientFromCache(
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h
index 7998f7f..530933fd 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_gradient.h
@@ -36,7 +36,7 @@
  public:
   explicit LayoutSVGResourceGradient(SVGGradientElement*);
 
-  void RemoveAllClientsFromCache(bool mark_for_invalidation = true) final;
+  void RemoveAllClientsFromCache() final;
   bool RemoveClientFromCache(SVGResourceClient&) final;
 
   SVGPaintServer PreparePaintServer(const SVGResourceClient&,
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc
index ba43918..491b18a 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.cc
@@ -47,12 +47,9 @@
   ClearInvalidationMask();
 }
 
-void LayoutSVGResourceMarker::RemoveAllClientsFromCache(
-    bool mark_for_invalidation) {
-  MarkAllClientsForInvalidation(
-      mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation |
-                                  SVGResourceClient::kBoundariesInvalidation
-                            : SVGResourceClient::kParentOnlyInvalidation);
+void LayoutSVGResourceMarker::RemoveAllClientsFromCache() {
+  MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation |
+                                SVGResourceClient::kBoundariesInvalidation);
 }
 
 FloatRect LayoutSVGResourceMarker::MarkerBoundaries(
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h
index d8596cd..58a23afc 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_marker.h
@@ -35,7 +35,7 @@
 
   const char* GetName() const override { return "LayoutSVGResourceMarker"; }
 
-  void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override;
+  void RemoveAllClientsFromCache() override;
 
   // Calculates marker boundaries, mapped to the target element's coordinate
   // space.
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc
index df8d481..b1e201d 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.cc
@@ -36,14 +36,11 @@
 
 LayoutSVGResourceMasker::~LayoutSVGResourceMasker() = default;
 
-void LayoutSVGResourceMasker::RemoveAllClientsFromCache(
-    bool mark_for_invalidation) {
+void LayoutSVGResourceMasker::RemoveAllClientsFromCache() {
   cached_paint_record_.reset();
   mask_content_boundaries_ = FloatRect();
-  MarkAllClientsForInvalidation(
-      mark_for_invalidation ? SVGResourceClient::kLayoutInvalidation |
-                                  SVGResourceClient::kBoundariesInvalidation
-                            : SVGResourceClient::kParentOnlyInvalidation);
+  MarkAllClientsForInvalidation(SVGResourceClient::kLayoutInvalidation |
+                                SVGResourceClient::kBoundariesInvalidation);
 }
 
 sk_sp<const PaintRecord> LayoutSVGResourceMasker::CreatePaintRecord(
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h
index 70442a2..6bb3600 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h
@@ -38,7 +38,7 @@
 
   const char* GetName() const override { return "LayoutSVGResourceMasker"; }
 
-  void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override;
+  void RemoveAllClientsFromCache() override;
 
   FloatRect ResourceBoundingBox(const FloatRect& reference_box);
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc
index 306d7a8..6944d12 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.cc
@@ -52,13 +52,10 @@
       attributes_wrapper_(MakeGarbageCollected<PatternAttributesWrapper>()),
       pattern_map_(MakeGarbageCollected<PatternMap>()) {}
 
-void LayoutSVGResourcePattern::RemoveAllClientsFromCache(
-    bool mark_for_invalidation) {
+void LayoutSVGResourcePattern::RemoveAllClientsFromCache() {
   pattern_map_->clear();
   should_collect_pattern_attributes_ = true;
-  MarkAllClientsForInvalidation(
-      mark_for_invalidation ? SVGResourceClient::kPaintInvalidation
-                            : SVGResourceClient::kParentOnlyInvalidation);
+  MarkAllClientsForInvalidation(SVGResourceClient::kPaintInvalidation);
 }
 
 bool LayoutSVGResourcePattern::RemoveClientFromCache(
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h
index 11889ed..4630fee 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_pattern.h
@@ -41,7 +41,7 @@
 
   const char* GetName() const override { return "LayoutSVGResourcePattern"; }
 
-  void RemoveAllClientsFromCache(bool mark_for_invalidation = true) override;
+  void RemoveAllClientsFromCache() override;
   bool RemoveClientFromCache(SVGResourceClient&) override;
 
   SVGPaintServer PreparePaintServer(
diff --git a/third_party/blink/renderer/core/layout/svg/svg_resources.cc b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
index b4fb61b1..7fde67c 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_resources.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_resources.cc
@@ -700,18 +700,13 @@
   LayoutObject* layout_object = element_->GetLayoutObject();
   if (!layout_object)
     return;
-  bool mark_for_invalidation =
-      invalidation_mask & ~SVGResourceClient::kParentOnlyInvalidation;
   if (layout_object->IsSVGResourceContainer()) {
-    ToLayoutSVGResourceContainer(layout_object)
-        ->RemoveAllClientsFromCache(mark_for_invalidation);
+    ToLayoutSVGResourceContainer(layout_object)->RemoveAllClientsFromCache();
     return;
   }
 
-  if (mark_for_invalidation) {
-    LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object,
-                                                          invalidation_mask);
-  }
+  LayoutSVGResourceContainer::MarkClientForInvalidation(*layout_object,
+                                                        invalidation_mask);
 
   // Special case for filter invalidation.
   if (invalidation_mask & SVGResourceClient::kSkipAncestorInvalidation)
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
index 3d484e467..e36530ca 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_test.cc
@@ -123,7 +123,7 @@
       image->SetData(SharedBuffer::Create(
                          kJpegImage, kJpegImageSubrangeWithDimensionsLength),
                      true));
-  EXPECT_TRUE(image->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image.get()));
   EXPECT_EQ(1, image->width());
   EXPECT_EQ(1, image->height());
 }
@@ -310,7 +310,7 @@
 
   // A placeholder image.
   EXPECT_TRUE(image_resource->ShouldShowPlaceholder());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_FALSE(IsA<SVGImage>(image_resource->GetContent()->GetImage()));
 }
 
@@ -352,7 +352,7 @@
 
   // A non-placeholder bitmap image.
   EXPECT_FALSE(image_resource->ShouldShowPlaceholder());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_FALSE(IsA<SVGImage>(image_resource->GetContent()->GetImage()));
 }
 
@@ -481,7 +481,7 @@
   image_resource->AppendData(kBoundary, strlen(kBoundary));
   image_resource->Loader()->DidFinishLoading(base::TimeTicks(), 0, 0, 0, false);
   EXPECT_TRUE(image_resource->GetContent()->HasImage());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_TRUE(image_resource->GetContent()
                   ->GetImage()
                   ->PaintImageForCurrentFrame()
@@ -636,7 +636,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(2, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
 }
 
 TEST(ImageResourceReloadTest, ReloadIfLoFiOrPlaceholderForPlaceholder) {
@@ -677,7 +677,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(1, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
 }
 
 TEST(ImageResourceTest, SVGImageWithSubresource) {
@@ -692,7 +692,7 @@
   EXPECT_FALSE(image_resource->ErrorOccurred());
   ASSERT_TRUE(image_resource->GetContent()->HasImage());
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
 
   // At this point, image is (mostly) available but the loading is not yet
   // finished because of SVG's subresources, and thus ImageChanged() or
@@ -740,7 +740,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(2, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(kJpegImageHeight,
             image_resource->GetContent()->GetImage()->height());
@@ -756,7 +756,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(2, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(kJpegImageHeight,
             image_resource->GetContent()->GetImage()->height());
@@ -776,7 +776,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(1, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
 
@@ -790,7 +790,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(1, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
 }
@@ -810,7 +810,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(2, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(kJpegImageHeight,
             image_resource->GetContent()->GetImage()->height());
@@ -825,7 +825,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(4, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(50, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(50, image_resource->GetContent()->GetImage()->height());
 }
@@ -845,7 +845,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(2, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(kJpegImageHeight,
             image_resource->GetContent()->GetImage()->height());
@@ -859,7 +859,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(3, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
 }
@@ -878,7 +878,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(1, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
 
@@ -892,7 +892,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(3, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(kJpegImageWidth, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(kJpegImageHeight,
             image_resource->GetContent()->GetImage()->height());
@@ -912,7 +912,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(1, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(200, image_resource->GetContent()->GetImage()->height());
 
@@ -925,7 +925,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(2, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_FALSE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(300, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(300, image_resource->GetContent()->GetImage()->height());
 }
@@ -1294,7 +1294,7 @@
 
   // |imageResource2| is still a non-placeholder image.
   EXPECT_FALSE(image_resource2->ShouldShowPlaceholder());
-  EXPECT_TRUE(image_resource2->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource2->GetContent()->GetImage()));
 }
 
 TEST(ImageResourceTest,
@@ -1389,7 +1389,7 @@
               image_resource->GetContent()->GetImage()->width());
     EXPECT_EQ(kJpegImageHeight,
               image_resource->GetContent()->GetImage()->height());
-    EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+    EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   }
 }
 
@@ -1584,7 +1584,7 @@
   EXPECT_FALSE(image_resource->GetContent()->GetImage()->IsNull());
   EXPECT_EQ(5, observer->ImageChangedCount());
   EXPECT_TRUE(observer->ImageNotifyFinishedCalled());
-  EXPECT_TRUE(image_resource->GetContent()->GetImage()->IsBitmapImage());
+  EXPECT_TRUE(IsA<BitmapImage>(image_resource->GetContent()->GetImage()));
   EXPECT_EQ(50, image_resource->GetContent()->GetImage()->width());
   EXPECT_EQ(50, image_resource->GetContent()->GetImage()->height());
 }
diff --git a/third_party/blink/renderer/core/page/drag_image.cc b/third_party/blink/renderer/core/page/drag_image.cc
index cc1acf3b..a3f539e 100644
--- a/third_party/blink/renderer/core/page/drag_image.cc
+++ b/third_party/blink/renderer/core/page/drag_image.cc
@@ -109,9 +109,10 @@
     return nullptr;
 
   ImageOrientation orientation;
+  auto* bitmap_image = DynamicTo<BitmapImage>(image);
   if (should_respect_image_orientation == kRespectImageOrientation &&
-      image->IsBitmapImage())
-    orientation = ToBitmapImage(image)->CurrentFrameOrientation();
+      bitmap_image)
+    orientation = bitmap_image->CurrentFrameOrientation();
 
   SkBitmap bm;
   paint_image = Image::ResizeAndOrientImage(
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
index ac1b453..589f375 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
@@ -182,6 +182,9 @@
   PositionInFlatTree search_start =
       PositionInFlatTree::FirstPositionInNode(document);
 
+  auto forced_lock_scope = document.GetScopedForceActivatableLocks();
+  document.UpdateStyleAndLayout(DocumentUpdateReason::kFindInPage);
+
   EphemeralRangeInFlatTree match =
       FindMatchFromPosition(document, search_start);
 
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 3f962604..5f4aa703f 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -1578,8 +1578,7 @@
     if (!cached_image->HasImage())
       return false;
 
-    Image* image = cached_image->GetImage();
-    if (!image->IsBitmapImage())
+    if (!IsA<BitmapImage>(cached_image->GetImage()))
       return false;
 
     UseCounter::Count(GetLayoutObject().GetDocument(),
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 0051a55..6e898c26 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -3291,17 +3291,14 @@
 }
 
 void PaintPropertyTreeBuilder::UpdatePaintingLayer() {
-  bool changed_painting_layer = false;
   if (object_.HasLayer() &&
       ToLayoutBoxModelObject(object_).HasSelfPaintingLayer()) {
     context_.painting_layer = ToLayoutBoxModelObject(object_).Layer();
-    changed_painting_layer = true;
   } else if (object_.IsColumnSpanAll() ||
              object_.IsFloatingWithNonContainingBlockParent()) {
     // See LayoutObject::paintingLayer() for the special-cases of floating under
     // inline and multicolumn.
     context_.painting_layer = object_.PaintingLayer();
-    changed_painting_layer = true;
   }
   DCHECK(context_.painting_layer == object_.PaintingLayer());
 }
diff --git a/third_party/blink/renderer/core/paint/paint_timing_detector.cc b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
index 3ca4453..f3cf59d 100644
--- a/third_party/blink/renderer/core/paint/paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing_detector.cc
@@ -25,11 +25,13 @@
 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
 #include "third_party/blink/renderer/platform/graphics/paint/float_clip_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
 #include "third_party/blink/renderer/platform/graphics/paint/scoped_paint_chunk_properties.h"
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
@@ -52,7 +54,7 @@
   }
   // Generated images are excluded here, as they are likely to serve for
   // background purpose.
-  if (!image.IsBitmapImage() && !image.IsStaticBitmapImage() &&
+  if (!IsA<BitmapImage>(image) && !IsA<StaticBitmapImage>(image) &&
       !IsA<SVGImage>(image) && !image.IsPlaceholderImage())
     return false;
   return true;
diff --git a/third_party/blink/renderer/core/svg/svg_resource.h b/third_party/blink/renderer/core/svg/svg_resource.h
index 21c8ca6..f71d94d 100644
--- a/third_party/blink/renderer/core/svg/svg_resource.h
+++ b/third_party/blink/renderer/core/svg/svg_resource.h
@@ -71,8 +71,6 @@
   void AddClient(SVGResourceClient&);
   void RemoveClient(SVGResourceClient&);
 
-  bool HasClients() const { return !clients_.IsEmpty(); }
-
   virtual void Trace(Visitor*);
 
  protected:
diff --git a/third_party/blink/renderer/core/svg/svg_resource_client.h b/third_party/blink/renderer/core/svg/svg_resource_client.h
index d63b3b53..60ac325 100644
--- a/third_party/blink/renderer/core/svg/svg_resource_client.h
+++ b/third_party/blink/renderer/core/svg/svg_resource_client.h
@@ -24,8 +24,7 @@
     kLayoutInvalidation = 1 << 0,
     kBoundariesInvalidation = 1 << 1,
     kPaintInvalidation = 1 << 2,
-    kParentOnlyInvalidation = 1 << 3,
-    kSkipAncestorInvalidation = 1 << 4,
+    kSkipAncestorInvalidation = 1 << 3,
   };
   virtual void ResourceContentChanged(InvalidationModeMask) = 0;
   virtual void ResourceElementChanged() = 0;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index b5ddc44b..f553bd4 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -459,8 +459,7 @@
 
   canvas()->GetDocument().GetCanvasFontCache()->WillUseCurrentFont();
   StringBuilder serialized_font;
-  const FontDescription& font_description =
-      GetState().GetFont().GetFontDescription();
+  const FontDescription& font_description = GetState().GetFontDescription();
 
   if (font_description.Style() == ItalicSlopeValue())
     serialized_font.Append("italic ");
@@ -933,7 +932,7 @@
   if (!GetState().HasRealizedFont())
     setFont(GetState().UnparsedFont());
   canvas()->GetDocument().GetCanvasFontCache()->WillUseCurrentFont();
-  return GetState().GetFont();
+  return ModifiableState().GetFont();
 }
 
 void CanvasRenderingContext2D::SetIsInHiddenPage(bool hidden) {
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
index dfefdf4..38056df 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
@@ -262,11 +262,19 @@
     selector->RegisterForInvalidationCallbacks(this);
 }
 
-const Font& CanvasRenderingContext2DState::GetFont() const {
+const Font& CanvasRenderingContext2DState::GetFont() {
   DCHECK(realized_font_);
+  if (!font_.IsFallbackValid())
+    FontsNeedUpdate(font_.GetFontSelector());
   return font_;
 }
 
+const FontDescription& CanvasRenderingContext2DState::GetFontDescription()
+    const {
+  DCHECK(realized_font_);
+  return font_.GetFontDescription();
+}
+
 void CanvasRenderingContext2DState::SetTransform(
     const AffineTransform& transform) {
   is_transform_invertible_ = transform.IsInvertible();
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
index 8309a1ab..4ec9894a4 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
@@ -79,7 +79,8 @@
   }
 
   void SetFont(const Font&, FontSelector*);
-  const Font& GetFont() const;
+  const Font& GetFont();
+  const FontDescription& GetFontDescription() const;
   bool HasRealizedFont() const { return realized_font_; }
   void SetUnparsedFont(const String& font) { unparsed_font_ = font; }
   const String& UnparsedFont() const { return unparsed_font_; }
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index f697e02..db7ab4b 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -355,8 +355,7 @@
     return kDefaultFont;
 
   StringBuilder serialized_font;
-  const FontDescription& font_description =
-      GetState().GetFont().GetFontDescription();
+  const FontDescription& font_description = GetState().GetFontDescription();
 
   if (font_description.Style() == ItalicSlopeValue())
     serialized_font.Append("italic ");
@@ -606,7 +605,7 @@
 const Font& OffscreenCanvasRenderingContext2D::AccessFont() {
   if (!GetState().HasRealizedFont())
     setFont(GetState().UnparsedFont());
-  return GetState().GetFont();
+  return ModifiableState().GetFont();
 }
 
 bool OffscreenCanvasRenderingContext2D::IsCanvas2DBufferValid() const {
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 625cc1a3..1e68f50 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -617,7 +617,7 @@
     GetMediaDevicesDispatcher()->GetAudioInputCapabilities(
         WTF::Bind(&UserMediaProcessor::SelectAudioDeviceSettings,
                   WrapWeakPersistent(this),
-                  WrapWeakPersistent(current_request_info_->web_request())));
+                  WrapPersistent(current_request_info_->web_request())));
   } else {
     if (!blink::IsAudioInputMediaType(audio_controls.stream_type)) {
       String failed_constraint_name =
@@ -774,7 +774,7 @@
     GetMediaDevicesDispatcher()->GetVideoInputCapabilities(
         WTF::Bind(&UserMediaProcessor::SelectVideoDeviceSettings,
                   WrapWeakPersistent(this),
-                  WrapWeakPersistent(current_request_info_->web_request())));
+                  WrapPersistent(current_request_info_->web_request())));
   } else {
     if (!blink::IsVideoInputMediaType(video_controls.stream_type)) {
       String failed_constraint_name =
@@ -986,8 +986,8 @@
         video_device_id,
         WTF::Bind(&UserMediaProcessor::GotAllVideoInputFormatsForDevice,
                   WrapWeakPersistent(this),
-                  WrapWeakPersistent(current_request_info_->web_request()),
-                  label, video_device_id));
+                  WrapPersistent(current_request_info_->web_request()), label,
+                  video_device_id));
   }
 }
 
@@ -1481,7 +1481,7 @@
       FROM_HERE,
       WTF::Bind(&UserMediaProcessor::DelayedGetUserMediaRequestSucceeded,
                 WrapWeakPersistent(this), current_request_info_->request_id(),
-                stream, WrapWeakPersistent(web_request)));
+                stream, WrapPersistent(web_request)));
 }
 
 void UserMediaProcessor::DelayedGetUserMediaRequestSucceeded(
@@ -1515,8 +1515,8 @@
       FROM_HERE,
       WTF::Bind(&UserMediaProcessor::DelayedGetUserMediaRequestFailed,
                 WrapWeakPersistent(this), current_request_info_->request_id(),
-                WrapWeakPersistent(current_request_info_->web_request()),
-                result, constraint_name));
+                WrapPersistent(current_request_info_->web_request()), result,
+                constraint_name));
 }
 
 void UserMediaProcessor::DelayedGetUserMediaRequestFailed(
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 755a8bb..65d8fc0 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -5457,14 +5457,15 @@
   // Still not clear whether we will take the accelerated upload path
   // at this point; it depends on what came back from
   // CanUseTexImageViaGPU, for example.
+  auto* static_bitmap_image = DynamicTo<StaticBitmapImage>(image.get());
   upload_via_gpu &= source_canvas_webgl_context ||
-                    (image->IsStaticBitmapImage() && image->IsTextureBacked());
+                    (static_bitmap_image && image->IsTextureBacked());
 
   if (upload_via_gpu) {
     AcceleratedStaticBitmapImage* accel_image = nullptr;
     if (image) {
-      accel_image = static_cast<AcceleratedStaticBitmapImage*>(
-          ToStaticBitmapImage(image.get()));
+      accel_image =
+          static_cast<AcceleratedStaticBitmapImage*>(static_bitmap_image);
     }
 
     // The GPU-GPU copy path uses the Y-up coordinate system.
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index 78fa75b..0f8cea15 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -599,6 +599,53 @@
   EventListener::Trace(visitor);
 }
 
+XR::OverlayFullscreenExitObserver::OverlayFullscreenExitObserver(XR* xr)
+    : xr_(xr) {
+  DVLOG(2) << __func__;
+}
+
+XR::OverlayFullscreenExitObserver::~OverlayFullscreenExitObserver() = default;
+
+void XR::OverlayFullscreenExitObserver::Invoke(
+    ExecutionContext* execution_context,
+    Event* event) {
+  DVLOG(2) << __func__ << ": event type=" << event->type();
+
+  element_->GetDocument().removeEventListener(
+      event_type_names::kFullscreenchange, this, true);
+
+  if (event->type() == event_type_names::kFullscreenchange) {
+    // Succeeded, proceed with session shutdown.
+    xr_->ExitPresent(std::move(on_exited_));
+  }
+}
+
+void XR::OverlayFullscreenExitObserver::ExitFullscreen(
+    Element* element,
+    base::OnceClosure on_exited) {
+  DVLOG(2) << __func__;
+  element_ = element;
+  on_exited_ = std::move(on_exited);
+
+  element->GetDocument().addEventListener(event_type_names::kFullscreenchange,
+                                          this, true);
+  // "ua_originated" means that the browser process already exited
+  // fullscreen. Set it to false because we need the browser process
+  // to get notified that it needs to exit fullscreen. Use
+  // FullyExitFullscreen to ensure that we return to non-fullscreen mode.
+  // ExitFullscreen only unfullscreens a single element, potentially
+  // leaving others in fullscreen mode.
+  constexpr bool kUaOriginated = false;
+
+  Fullscreen::FullyExitFullscreen(element_->GetDocument(), kUaOriginated);
+}
+
+void XR::OverlayFullscreenExitObserver::Trace(Visitor* visitor) {
+  visitor->Trace(xr_);
+  visitor->Trace(element_);
+  EventListener::Trace(visitor);
+}
+
 device::mojom::blink::XRSessionOptionsPtr XR::XRSessionOptionsFromQuery(
     const PendingRequestSessionQuery& query) {
   device::mojom::blink::XRSessionOptionsPtr session_options =
@@ -672,12 +719,6 @@
 
 void XR::ExitPresent(base::OnceClosure on_exited) {
   DVLOG(1) << __func__;
-  if (service_) {
-    service_->ExitPresent(std::move(on_exited));
-  } else {
-    // The service was already shut down, run the callback immediately.
-    std::move(on_exited).Run();
-  }
 
   // If the document was potentially being shown in a DOM overlay via
   // fullscreened elements, make sure to clear any fullscreen states on exiting
@@ -689,36 +730,43 @@
   // - renderer processes XR session shutdown (this method)
   // - browser re-enters fullscreen unexpectedly
   LocalFrame* frame = GetFrame();
-  if (!frame)
-    return;
+  if (frame) {
+    Document* doc = frame->GetDocument();
+    DCHECK(doc);
+    DVLOG(3) << __func__ << ": doc->IsXrOverlay()=" << doc->IsXrOverlay();
+    if (doc->IsXrOverlay()) {
+      Element* fullscreen_element = Fullscreen::FullscreenElementFrom(*doc);
+      DVLOG(3) << __func__ << ": fullscreen_element=" << fullscreen_element;
+      doc->SetIsXrOverlay(false, fullscreen_element);
 
-  Document* doc = frame->GetDocument();
-  DCHECK(doc);
-  if (doc->IsXrOverlay()) {
-    Element* fullscreen_element = Fullscreen::FullscreenElementFrom(*doc);
-    doc->SetIsXrOverlay(false, fullscreen_element);
-    if (fullscreen_element) {
-      // "ua_originated" means that the browser process already exited
-      // fullscreen. Set it to false because we need the browser process
-      // to get notified that it needs to exit fullscreen. Use
-      // FullyExitFullscreen to ensure that we return to non-fullscreen mode.
-      // ExitFullscreen only unfullscreens a single element, potentially
-      // leaving others in fullscreen mode.
-      constexpr bool kUaOriginated = false;
-      Fullscreen::FullyExitFullscreen(*doc, kUaOriginated);
+      // Restore the FrameView background color that was changed in
+      // OnRequestSessionReturned. The layout view can be null on navigation.
+      auto* layout_view = doc->GetLayoutView();
+      if (layout_view) {
+        auto* frame_view = layout_view->GetFrameView();
+        // SetBaseBackgroundColor updates composited layer mappings.
+        // That DCHECKs IsAllowedToQueryCompositingState which requires
+        // DocumentLifecycle >= kInCompositingUpdate.
+        frame_view->UpdateLifecycleToCompositingInputsClean(
+            DocumentUpdateReason::kBaseColor);
+        frame_view->SetBaseBackgroundColor(original_base_background_color_);
+      }
+
+      if (fullscreen_element) {
+        fullscreen_exit_observer_ =
+            MakeGarbageCollected<OverlayFullscreenExitObserver>(this);
+        fullscreen_exit_observer_->ExitFullscreen(fullscreen_element,
+                                                  std::move(on_exited));
+        return;
+      }
     }
-    // Restore the FrameView background color that was changed in
-    // OnRequestSessionReturned. The layout view can be null on navigation.
-    auto* layout_view = doc->GetLayoutView();
-    if (layout_view) {
-      auto* frame_view = layout_view->GetFrameView();
-      // SetBaseBackgroundColor updates composited layer mappings.
-      // That DCHECKs IsAllowedToQueryCompositingState which requires
-      // DocumentLifecycle >= kInCompositingUpdate.
-      frame_view->UpdateLifecycleToCompositingInputsClean(
-          DocumentUpdateReason::kBaseColor);
-      frame_view->SetBaseBackgroundColor(original_base_background_color_);
-    }
+  }
+
+  if (service_) {
+    service_->ExitPresent(std::move(on_exited));
+  } else {
+    // The service was already shut down, run the callback immediately.
+    std::move(on_exited).Run();
   }
 }
 
@@ -1347,6 +1395,7 @@
   visitor->Trace(outstanding_support_queries_);
   visitor->Trace(outstanding_request_queries_);
   visitor->Trace(fullscreen_event_manager_);
+  visitor->Trace(fullscreen_exit_observer_);
   ExecutionContextLifecycleObserver::Trace(visitor);
   EventTargetWithInlineData::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index de2e7d4..cbf8abd 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -300,6 +300,7 @@
     void Invoke(ExecutionContext*, Event*) override;
 
     void RequestFullscreen();
+    void OnSessionStarting();
 
     void Trace(Visitor*) override;
 
@@ -310,6 +311,27 @@
     DISALLOW_COPY_AND_ASSIGN(OverlayFullscreenEventManager);
   };
 
+  // Native event listener used when waiting for fullscreen mode to fully exit
+  // when ending an XR session.
+  class OverlayFullscreenExitObserver : public NativeEventListener {
+   public:
+    OverlayFullscreenExitObserver(XR* xr);
+    ~OverlayFullscreenExitObserver() override;
+
+    // NativeEventListener
+    void Invoke(ExecutionContext*, Event*) override;
+
+    void ExitFullscreen(Element* element, base::OnceClosure on_exited);
+
+    void Trace(Visitor*) override;
+
+   private:
+    Member<XR> xr_;
+    Member<Element> element_;
+    base::OnceClosure on_exited_;
+    DISALLOW_COPY_AND_ASSIGN(OverlayFullscreenExitObserver);
+  };
+
   ScriptPromise InternalIsSessionSupported(ScriptState*,
                                            const String&,
                                            ExceptionState& exception_state,
@@ -409,6 +431,10 @@
   // transition to fullscreen mode completes or fails, and reject/resolve
   // the pending request session promise accordingly.
   Member<OverlayFullscreenEventManager> fullscreen_event_manager_;
+  // DOM overlay mode uses a separate temporary fullscreen event listener
+  // if it needs to wait for fullscreen mode to fully exit when ending
+  // the session.
+  Member<OverlayFullscreenExitObserver> fullscreen_exit_observer_;
 
   // In DOM overlay mode, save and restore the FrameView background color.
   Color original_base_background_color_;
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index 1eaf4ca..bb8e507 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -151,10 +151,6 @@
   RuntimeEnabledFeatures::SetCookiesWithoutSameSiteMustBeSecureEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableWasmCodeCache(bool enable) {
-  RuntimeEnabledFeatures::SetWasmCodeCacheEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableCanvas2dImageChromium(bool enable) {
   RuntimeEnabledFeatures::SetCanvas2dImageChromiumEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.h b/third_party/blink/renderer/platform/graphics/bitmap_image.h
index 220413c..b36599ef 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.h
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/platform/graphics/image_animation_policy.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_animation.h"
 #include "third_party/blink/renderer/platform/timer.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
 #include "third_party/skia/include/core/SkRefCnt.h"
@@ -184,7 +185,10 @@
   PaintImage::AnimationSequenceId reset_animation_sequence_id_ = 0;
 };
 
-DEFINE_IMAGE_TYPE_CASTS(BitmapImage);
+template <>
+struct DowncastTraits<BitmapImage> {
+  static bool AllowFrom(const Image& image) { return image.IsBitmapImage(); }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 9b05e3f..27a3327 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -648,9 +648,10 @@
 
   ImageOrientation image_orientation = kOriginTopLeft;
   SkMatrix matrix;
-  if (paint_image && image->IsBitmapImage() &&
+  auto* bitmap_image = DynamicTo<BitmapImage>(image);
+  if (paint_image && bitmap_image &&
       respect_image_orientation == kRespectImageOrientation) {
-    image_orientation = ToBitmapImage(image)->CurrentFrameOrientation();
+    image_orientation = bitmap_image->CurrentFrameOrientation();
     image_size_ = IntSize(paint_image.width(), paint_image.height());
     if (image_orientation.UsesWidthAsHeight())
       image_size_ = image_size_.TransposedSize();
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index 5b17e10..6a10c32 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -338,10 +338,9 @@
   if (!paint_image)
     return {};
 
-  if (respect_image_orientation == kRespectImageOrientation &&
-      IsBitmapImage()) {
-    ImageOrientation orientation =
-        ToBitmapImage(this)->CurrentFrameOrientation();
+  auto* bitmap_image = DynamicTo<BitmapImage>(this);
+  if (respect_image_orientation == kRespectImageOrientation && bitmap_image) {
+    ImageOrientation orientation = bitmap_image->CurrentFrameOrientation();
     paint_image = ResizeAndOrientImage(paint_image, orientation);
     if (!paint_image)
       return {};
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index b52860f..942fb8e 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -322,10 +322,6 @@
   DISALLOW_COPY_AND_ASSIGN(Image);
 };
 
-#define DEFINE_IMAGE_TYPE_CASTS(typeName)                          \
-  DEFINE_TYPE_CASTS(typeName, Image, image, image->Is##typeName(), \
-                    image.Is##typeName())
-
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
index 3a640b7..f14b5b8 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 
@@ -130,7 +131,12 @@
   bool is_origin_clean_ = true;
 };
 
-DEFINE_IMAGE_TYPE_CASTS(StaticBitmapImage);
+template <>
+struct DowncastTraits<StaticBitmapImage> {
+  static bool AllowFrom(const Image& image) {
+    return image.IsStaticBitmapImage();
+  }
+};
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
index 93140b3..4edab9bad 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -411,9 +411,6 @@
 bool ResourceLoader::ShouldFetchCodeCache() {
   if (!RuntimeEnabledFeatures::IsolatedCodeCacheEnabled())
     return false;
-  if (resource_->GetType() == ResourceType::kRaw &&
-      !RuntimeEnabledFeatures::WasmCodeCacheEnabled())
-    return false;
 
   const ResourceRequest& request = resource_->GetResourceRequest();
   if (!request.Url().ProtocolIsInHTTPFamily())
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a53fd1c..43f6bd18 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1767,10 +1767,6 @@
       status: "experimental",
     },
     {
-      name: "WasmCodeCache",
-      status: "experimental",
-    },
-    {
       name: "WebAnimationsAPI",
       status: "experimental",
       implied_by: ['AnimationWorklet']
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c878d03..d19dd47a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -618,6 +618,9 @@
 crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-097a-manual.html [ Skip ]
 crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-040-manual.html [ Skip ]
 
+# `text-decoration-skip-ink: all` not implemented yet.
+crbug.com/1054656 external/wpt/css/css-text-decor/text-decoration-skip-ink-005.html [ Skip ]
+
 crbug.com/722825 media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Timeout Pass ]
 crbug.com/722825 virtual/audio-service/media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Timeout Pass ]
 
@@ -1489,7 +1492,6 @@
 # Flexbox in NG
 #
 # Fail in NG flex, pass in legacy flex.
-crbug.com/845235 virtual/layout_ng_flex_box/css3/flexbox/overflow-auto-resizes-correctly.html [ Failure ]
 crbug.com/845235 virtual/layout_ng_flex_box/css3/flexbox/relpos-with-percentage-top.html [ Failure ]
 crbug.com/845235 virtual/layout_ng_flex_box/external/wpt/css/css-flexbox/hittest-overlapping-margin.html [ Failure ]
 crbug.com/845235 virtual/layout_ng_flex_box/external/wpt/css/css-flexbox/hittest-overlapping-order.html [ Failure ]
@@ -3827,9 +3829,6 @@
 crbug.com/626703 [ Linux ] virtual/web-components-v0-disabled/external/wpt/html/semantics/forms/the-input-element/maxlength-number.html [ Timeout ]
 crbug.com/626703 [ Mac ] virtual/web-components-v0-disabled/external/wpt/html/semantics/forms/the-input-element/maxlength-number.html [ Timeout ]
 crbug.com/626703 [ Win ] virtual/web-components-v0-disabled/external/wpt/html/semantics/forms/the-input-element/maxlength-number.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-text-decor/text-decoration-skip-ink-005.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-text-decor/text-decoration-skip-ink-005.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-text-decor/text-decoration-skip-ink-005.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html [ Timeout ]
@@ -3840,21 +3839,6 @@
 crbug.com/626703 [ Linux ] external/wpt/workers/abrupt-completion.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/workers/abrupt-completion.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/workers/abrupt-completion.html [ Timeout ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/grid-model/grid-overflow-padding-001.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/grid-model/grid-overflow-padding-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/grid-model/grid-overflow-padding-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-003.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-003.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-003.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/grid-model/grid-overflow-padding-002.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/grid-model/grid-overflow-padding-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/grid-model/grid-overflow-padding-002.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-002.html [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-002.html [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-002.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-ifame-002.sub.html [ Timeout ]
 crbug.com/626703 [ Mac ] external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-ifame-002.sub.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-in-cross-origin-ifame-002.sub.html [ Timeout ]
@@ -5115,6 +5099,11 @@
 crbug.com/707359 [ Mac ] fast/css-grid-layout/grid-self-baseline-03.html [ Failure ]
 crbug.com/707359 [ Mac ] fast/css-grid-layout/grid-self-baseline-04.html [ Failure ]
 crbug.com/707359 [ Mac ] fast/css-grid-layout/grid-self-baseline-horiz-04.html [ Failure ]
+crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-overflow-padding-001.html [ Failure ]
+crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-overflow-padding-002.html [ Failure ]
+crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html [ Failure ]
+crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-002.html [ Failure ]
+crbug.com/1053825 external/wpt/css/css-grid/grid-model/grid-areas-overflowing-grid-container-003.html [ Failure ]
 
 # The 'last baseline' keyword is not implemented yet
 crbug.com/885175 external/wpt/css/css-grid/alignment/grid-item-self-baseline-001.html [ Failure ]
diff --git a/third_party/blink/web_tests/css3/flexbox/overflow-auto-resizes-correctly.html b/third_party/blink/web_tests/css3/flexbox/overflow-auto-resizes-correctly.html
index a836543d..865341fe 100644
--- a/third_party/blink/web_tests/css3/flexbox/overflow-auto-resizes-correctly.html
+++ b/third_party/blink/web_tests/css3/flexbox/overflow-auto-resizes-correctly.html
@@ -23,11 +23,6 @@
   overflow-y: auto;
   max-height: 200px;
 }
-.intrinsic-height-box {
-  min-height: -webkit-min-content;
-  overflow: auto;
-  flex-basis: 0;
-}
 .rect {
   min-height: 100px;
   min-width: 100px;
@@ -43,12 +38,6 @@
   </div>
 </div>
 
-<div class="vflex">
-  <div class="intrinsic-height-box">
-    <div class="rect" style="min-width: 300px"></div>
-  </div>
-</div>
-
 <div class="hflex">
   <div class="vbox">
     <div class="rect" style="min-height: 300px;"></div>
@@ -73,11 +62,6 @@
     assert_equals(hbox.clientHeight, hbox.scrollHeight);
   }, 'hbox dimensions');
 
-  var intrinsicHeightBox = document.querySelector('.intrinsic-height-box');
-  test(function() {
-    assert_equals(intrinsicHeightBox.clientHeight, intrinsicHeightBox.scrollHeight);
-  }, 'intrinsicHeightBox dimensions');
-
   var measure = document.getElementById('measure');
   var scrollbarSize = measure.offsetWidth - measure.clientWidth;
   test(function() {
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index 4d96559..1e8e982 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -190768,6 +190768,9 @@
    "webmessaging/README.md": [
     []
    ],
+   "webmessaging/broadcastchannel/basics-expected.txt": [
+    []
+   ],
    "webmessaging/broadcastchannel/resources/origin.html": [
     []
    ],
@@ -193129,9 +193132,6 @@
    "workers/interfaces/WorkerGlobalScope/onerror/propagate-to-window-onerror.js": [
     []
    ],
-   "workers/interfaces/WorkerGlobalScope/self.js": [
-    []
-   ],
    "workers/interfaces/WorkerUtils/WindowTimers/001.js": [
     []
    ],
@@ -370481,10 +370481,39 @@
      {}
     ]
    ],
-   "workers/interfaces/WorkerGlobalScope/self.html": [
+   "workers/interfaces/WorkerGlobalScope/self.any.js": [
     [
-     "workers/interfaces/WorkerGlobalScope/self.html",
-     {}
+     "workers/interfaces/WorkerGlobalScope/self.any.serviceworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "!default,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "workers/interfaces/WorkerGlobalScope/self.any.sharedworker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "!default,worker"
+       ]
+      ]
+     }
+    ],
+    [
+     "workers/interfaces/WorkerGlobalScope/self.any.worker.html",
+     {
+      "script_metadata": [
+       [
+        "global",
+        "!default,worker"
+       ]
+      ]
+     }
     ]
    ],
    "workers/interfaces/WorkerUtils/WindowTimers/001.html": [
@@ -370702,7 +370731,9 @@
    "workers/modules/dedicated-worker-import-referrer.html": [
     [
      "workers/modules/dedicated-worker-import-referrer.html",
-     {}
+     {
+      "timeout": "long"
+     }
     ]
    ],
    "workers/modules/dedicated-worker-import.any.js": [
@@ -579082,11 +579113,11 @@
    "testharness"
   ],
   "service-workers/service-worker/unregister-immediately-before-installed.https.html": [
-   "fab5a3b40a3f9c8dae1effa7ddc59709a5688b8f",
+   "79cdaf062dc729c8029c553b485e59d510b87982",
    "testharness"
   ],
   "service-workers/service-worker/unregister-immediately-during-extendable-events.https.html": [
-   "e39db84990d3ec798584af0f958d7a79daf7f65e",
+   "6ba87a7ce80e198132b38b3878d069efe423b071",
    "testharness"
   ],
   "service-workers/service-worker/unregister-immediately.https.html": [
@@ -589338,7 +589369,7 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/executors/executorservo.py": [
-   "8f8e120e66da6403f256f99f279f1f5dc8008272",
+   "4816c3df06e87f8e85301918c341bbff97817777",
    "support"
   ],
   "tools/wptrunner/wptrunner/executors/executorservodriver.py": [
@@ -598413,8 +598444,12 @@
    "55c3dbdba18a686011f893be82f5c220b1e7ce74",
    "testharness"
   ],
+  "webmessaging/broadcastchannel/basics-expected.txt": [
+   "683366c11d5db703e1db43deb37260487474c5a5",
+   "support"
+  ],
   "webmessaging/broadcastchannel/basics.html": [
-   "ed16e32f5437dc430b5cc11b967e6538ef6cf393",
+   "3d8ba76fb1ba35658c44702059925fe53e3fb6f6",
    "testharness"
   ],
   "webmessaging/broadcastchannel/blobs.html": [
@@ -605877,14 +605912,10 @@
    "0daf488d6f522b80c3884fb75d21df996294a18a",
    "support"
   ],
-  "workers/interfaces/WorkerGlobalScope/self.html": [
-   "41522dfdd53e0a48308ddf5ab4a10581627bb20d",
+  "workers/interfaces/WorkerGlobalScope/self.any.js": [
+   "a1aacc08808bcb15473977b933d29944b02def2f",
    "testharness"
   ],
-  "workers/interfaces/WorkerGlobalScope/self.js": [
-   "5d3c560d49b2f010c423c4428dcea9e7d2b810d7",
-   "support"
-  ],
   "workers/interfaces/WorkerUtils/WindowTimers/001.html": [
    "9fe5e2b1bc7b5e174083d569080a1c382b389410",
    "testharness"
@@ -606122,7 +606153,7 @@
    "testharness"
   ],
   "workers/modules/dedicated-worker-import-referrer.html": [
-   "32d3b3f53de0e22ac07feb67e57121d5d6b0354b",
+   "855df62194369614edfe7f5172e5bfe0e8ac966b",
    "testharness"
   ],
   "workers/modules/dedicated-worker-import.any.js": [
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-before-installed.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-before-installed.https.html
index fab5a3b..79cdaf0 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-before-installed.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-before-installed.https.html
@@ -25,7 +25,7 @@
   await service_worker_unregister(test, /*scope=*/script_url);
 
   // Clear-Site-Data must cause register() to fail.
-  const register_promise = promise_rejects(test, 'AbortError',
+  const register_promise = promise_rejects_dom(test, 'AbortError',
     navigator.serviceWorker.register(script_url, { scope: scope_url}));;
 
   await Promise.all([clear_site_data(), register_promise]);
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-during-extendable-events.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-during-extendable-events.https.html
index e39db84..6ba87a7 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-during-extendable-events.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/unregister-immediately-during-extendable-events.https.html
@@ -30,8 +30,8 @@
   const frame = await add_controlled_iframe(test, scope_url);
 
   // Clear-Site-Data must cause the pending fetch promise to reject.
-  const fetch_promise = promise_rejects(
-    test, new TypeError(), frame.contentWindow.fetch('waituntil-forever'));
+  const fetch_promise = promise_rejects_js(
+    test, TypeError, frame.contentWindow.fetch('waituntil-forever'));
 
   const event_watcher = new EventWatcher(
     test, frame.contentWindow.navigator.serviceWorker, 'controllerchange');
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/executorservo.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/executorservo.py
index 8f8e120e..4816c3df 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/executorservo.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/executors/executorservo.py
@@ -332,6 +332,8 @@
         self.test = test
         success, data = ServoTimedRunner(self.logger, self.do_crashtest, self.protocol,
                                          test_url, timeout, self.extra_timeout).run()
+        # Ensure that no processes hang around if they timeout.
+        self.proc.kill()
 
         if success:
             return self.convert_result(test, data)
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics-expected.txt b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics-expected.txt
new file mode 100644
index 0000000..683366c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS postMessage results in correct event
+PASS messages are delivered in port creation order
+PASS messages aren't delivered to a closed port
+PASS messages aren't delivered to a port closed after calling postMessage.
+PASS closing and creating channels during message delivery works correctly
+FAIL Closing a channel in onmessage prevents already queued tasks from firing onmessage events assert_array_equals: lengths differ, expected array ["c2: first", "c3: first", "c3: done"] length 3, got ["c2: first", "c3: first", "c2: done", "c3: done"] length 4
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics.html b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics.html
index ed16e32..3d8ba76 100644
--- a/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics.html
+++ b/third_party/blink/web_tests/external/wpt/webmessaging/broadcastchannel/basics.html
@@ -69,6 +69,17 @@
     c1.postMessage('test');
   }, 'messages aren\'t delivered to a closed port');
 
+ async_test(t => {
+    let c1 = new BroadcastChannel('closed');
+    let c2 = new BroadcastChannel('closed');
+    let c3 = new BroadcastChannel('closed');
+
+    c2.onmessage = t.unreached_func();
+    c3.onmessage = t.step_func(() => t.done());
+    c1.postMessage('test');
+    c2.close();
+}, 'messages aren\'t delivered to a port closed after calling postMessage.');
+
 async_test(t => {
     let c1 = new BroadcastChannel('create-in-onmessage');
     let c2 = new BroadcastChannel('create-in-onmessage');
@@ -87,8 +98,6 @@
     c2.postMessage('second');
   }, 'closing and creating channels during message delivery works correctly');
 
-// TODO(mek): Depending on https://github.com/whatwg/html/issues/1371 adjust
-// this test to match the correct behavior.
 async_test(t => {
     let c1 = new BroadcastChannel('close-in-onmessage');
     let c2 = new BroadcastChannel('close-in-onmessage');
@@ -108,13 +117,12 @@
           assert_array_equals(events, [
               'c2: first',
               'c3: first',
-              'c2: done',
               'c3: done']);
           t.done();
         }
       }));
     c1.postMessage('first');
     c1.postMessage('done');
-  }, 'Closing a channel in onmessage doesn\'t cancel already queued events');
+  }, 'Closing a channel in onmessage prevents already queued tasks from firing onmessage events');
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.any.js b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.any.js
new file mode 100644
index 0000000..a1aacc08
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.any.js
@@ -0,0 +1,19 @@
+// META: global=!default,worker
+
+test(function() {
+  assert_equals(self, self);
+}, 'self === self');
+
+test(function() {
+  assert_true(self instanceof WorkerGlobalScope);
+}, 'self instanceof WorkerGlobalScope');
+
+test(function() {
+  assert_true('self' in self);
+}, '\'self\' in self');
+
+test(function() {
+  var x = self;
+  self = 1;
+  assert_equals(self, x);
+}, 'self = 1');
diff --git a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.html b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.html
deleted file mode 100644
index 41522dfd..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!doctype html>
-<title>self</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<script>
-async_test(function() {
-  var worker = new Worker('self.js');
-  worker.onmessage = this.step_func(function(e) {
-    for (var i = 0; i < e.data.length; ++i) {
-      assert_true(e.data[i][0], e.data[i][1]);
-    }
-    this.done();
-  });
-});
-</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.js b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.js
deleted file mode 100644
index 5d3c560..0000000
--- a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/self.js
+++ /dev/null
@@ -1,17 +0,0 @@
-var results = [];
-function check(func, msg) {
-  try {
-    results.push([func(), msg]);
-  } catch(ex) {
-    results.push([String(ex), msg]);
-  }
-}
-check(function() { return self === self; }, 'self === self');
-check(function() { return self instanceof WorkerGlobalScope; }, 'self instanceof WorkerGlobalScope');
-check(function() { return 'self' in self; }, '\'self\' in self');
-check(function() {
-  var x = self;
-  self = 1;
-  return x === self;
-}, 'self = 1');
-postMessage(results);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/fast/canvas/canvas-fillText-font-change-crash.html b/third_party/blink/web_tests/fast/canvas/canvas-fillText-font-change-crash.html
new file mode 100644
index 0000000..7ed0e31f4
--- /dev/null
+++ b/third_party/blink/web_tests/fast/canvas/canvas-fillText-font-change-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<canvas id="target"></canvas>
+<script>
+// https://crbug.com/1051715
+test(() => {
+  const ctx = document.getElementById('target').getContext('2d');
+  ctx.font = '25px custom-font';
+
+  const style = document.createElement('style');
+  style.textContent = '@font-face { font-family: custom-font; src: url(fake-font.woff); }';
+  document.body.appendChild(style);
+
+  ctx.fillText('foo', 0, 0);
+
+  // This is a crash test. Nothing to assert.
+}, '@font-face rule changes should not lead to canvas crash');
+</script>
diff --git a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt b/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt
index 4ebf844c..e0d2159 100644
--- a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt
+++ b/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/README.txt
@@ -1,4 +1,4 @@
 # This suite runs the tests in http/tests/devtools/wasm-isolated-code-cache with
-# --enable-features=IsolatedCodeCache,WasmCodeCache --site-per-process.
+# --enable-features=IsolatedCodeCache --site-per-process.
 # This feature is required for security to enforce site isolation on V8
 # code caches. Tracking bug: crbug.com/812168
diff --git a/third_party/blink/web_tests/wpt_internal/portals/portals-crbug-1041406.html b/third_party/blink/web_tests/wpt_internal/portals/portals-crbug-1041406.html
new file mode 100644
index 0000000..0a5e0fb
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/portals/portals-crbug-1041406.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>Regression test for https://crbug.com/1041406</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+   setup({single_test: true});
+
+   // Callback ordering in test:
+   // 1) iframe loads and runs cb1()
+   // 2) during appendChild, iframe gets disconnected, during which body load is
+   //    marked as complete and cb2() runs
+
+  function cb1() {
+    var div_b = document.getElementById('b');
+    var div_a = document.getElementById('a');
+    div_b.appendChild(div_a);
+  }
+
+  function cb2() {
+    var portal = document.querySelector('portal');
+    var iframe = document.querySelector('iframe');
+    iframe.after(portal);
+    done();
+  }
+</script>
+<body onload="cb2()">
+  <div id="a">
+    <iframe src="resources/simple-portal.html" onload="cb1()"></iframe>
+  </div>
+  <div id="b"></div>
+  <portal src="resources/simple-portal.html"></portal>
+</body>
diff --git a/third_party/fuchsia-sdk/BUILD.gn b/third_party/fuchsia-sdk/BUILD.gn
index cb7cfe9c..880542a 100644
--- a/third_party/fuchsia-sdk/BUILD.gn
+++ b/third_party/fuchsia-sdk/BUILD.gn
@@ -7,58 +7,20 @@
 import("//build/toolchain/toolchain.gni")
 import("//third_party/fuchsia-sdk/sdk/build/fuchsia_sdk_pkg.gni")
 
-copy("vulkan_base_configs") {
-  sources = [ "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_image_pipe_swapchain.json" ]
-
-  outputs =
-      [ "${root_gen_dir}/data/vulkan/explicit_layer.d/{{source_file_part}}" ]
-}
-
-copy("vulkan_image_pipe") {
-  sources = [ "sdk/arch/${target_cpu}/dist/libVkLayer_image_pipe_swapchain.so" ]
-
-  outputs = [ "${root_out_dir}${shlib_subdir}/{{source_file_part}}" ]
-}
-
 group("vulkan_base") {
   data_deps = [
-    ":vulkan_base_configs",
-    ":vulkan_image_pipe",
-    "sdk/pkg/trace-engine",
     "sdk/pkg/vulkan",
+    "sdk/pkg/vulkan_layers:VkLayer_image_pipe_swapchain",
   ]
 }
 
-copy("vulkan_validation_configs") {
-  sources = [
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_core_validation.json",
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_khronos_validation.json",
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_object_lifetimes.json",
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_stateless_validation.json",
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_thread_safety.json",
-    "sdk/pkg/vulkan_layers/data/vulkan/explicit_layer.d/VkLayer_unique_objects.json",
-  ]
-
-  outputs =
-      [ "${root_gen_dir}/data/vulkan/explicit_layer.d/{{source_file_part}}" ]
-}
-
-copy("vulkan_validation_libs") {
-  sources = [
-    "sdk/arch/${target_cpu}/dist/VkLayer_core_validation.so",
-    "sdk/arch/${target_cpu}/dist/VkLayer_khronos_validation.so",
-    "sdk/arch/${target_cpu}/dist/VkLayer_object_lifetimes.so",
-    "sdk/arch/${target_cpu}/dist/VkLayer_stateless_validation.so",
-    "sdk/arch/${target_cpu}/dist/VkLayer_thread_safety.so",
-    "sdk/arch/${target_cpu}/dist/VkLayer_unique_objects.so",
-  ]
-
-  outputs = [ "${root_out_dir}${shlib_subdir}/{{source_file_part}}" ]
-}
-
 group("vulkan_validation") {
   data_deps = [
-    ":vulkan_validation_configs",
-    ":vulkan_validation_libs",
+    "sdk/pkg/vulkan_layers:VkLayer_core_validation",
+    "sdk/pkg/vulkan_layers:VkLayer_khronos_validation",
+    "sdk/pkg/vulkan_layers:VkLayer_object_lifetimes",
+    "sdk/pkg/vulkan_layers:VkLayer_stateless_validation",
+    "sdk/pkg/vulkan_layers:VkLayer_thread_safety",
+    "sdk/pkg/vulkan_layers:VkLayer_unique_objects",
   ]
 }
diff --git a/third_party/robolectric/BUILD.gn b/third_party/robolectric/BUILD.gn
index 46d22d7a..734f0c2 100644
--- a/third_party/robolectric/BUILD.gn
+++ b/third_party/robolectric/BUILD.gn
@@ -202,13 +202,18 @@
   deps = [
     ":robolectric_annotations_java",
     ":sdk_list_txt",
-    "//build/android:sun_tools_java",
     "//third_party/android_deps:com_google_code_gson_gson_java",
     "//third_party/android_deps:com_google_guava_guava_java",
     "//third_party/android_deps:org_ow2_asm_asm_commons_java",
     "//third_party/android_deps:org_ow2_asm_asm_java",
     "//third_party/android_deps:org_ow2_asm_asm_tree_java",
   ]
+  javac_args = [
+    "--add-exports",
+    "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    "--add-exports",
+    "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+  ]
   provider_configurations = [ "local/processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor" ]
   main_class = "org.robolectric.annotation.processing.RobolectricProcessor"
   sources = [
@@ -398,12 +403,12 @@
     "//third_party/junit:junit",
   ]
   sources = [
+    "local/robolectric/src/main/java/org/robolectric/util/reflector/UnsafeAccess.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/Accessor.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/ForType.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/Reflector.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/ReflectorClassWriter.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/Static.java",
-    "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/UnsafeAccess.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/WeakerHashMap.java",
     "robolectric/utils/reflector/src/main/java/org/robolectric/util/reflector/WithType.java",
     "robolectric/utils/src/main/java/org/robolectric/AndroidMetadata.java",
@@ -1092,12 +1097,18 @@
     ":robolectric_shadowapi_java",
     ":robolectric_utils_java",
     ":shadows_core_java",
-    "//build/android:sun_tools_java",
     "//third_party/android_deps:com_android_support_multidex_java_orig",
     "//third_party/android_deps:com_google_code_gson_gson_java",
     "//third_party/android_deps:com_google_guava_guava_java",
   ]
 
+  javac_args = [
+    "--add-exports",
+    "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    "--add-exports",
+    "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+  ]
+
   # Work-around for gradle generator not yet supporting annotation processors.
   gradle_treat_as_prebuilt = true
 }
diff --git a/third_party/robolectric/README.chromium b/third_party/robolectric/README.chromium
index 7848442..1c9e637 100644
--- a/third_party/robolectric/README.chromium
+++ b/third_party/robolectric/README.chromium
@@ -16,6 +16,7 @@
 - Added custom_asynctask folder to have workable copies of shadows required
   for our own implementation of AsyncTask
 - Added DefaultSdkProvider as a local file with Jellybean references removed.
+- Removed non-compiling JDK9 class from UnsafeAccess.java.
 How To Update:
 - Visit the migration guide to see which APIs changed and need updating.
   http://robolectric.org/migrating/
diff --git a/third_party/robolectric/local/robolectric/src/main/java/org/robolectric/util/reflector/UnsafeAccess.java b/third_party/robolectric/local/robolectric/src/main/java/org/robolectric/util/reflector/UnsafeAccess.java
new file mode 100644
index 0000000..a835746
--- /dev/null
+++ b/third_party/robolectric/local/robolectric/src/main/java/org/robolectric/util/reflector/UnsafeAccess.java
@@ -0,0 +1,79 @@
+package org.robolectric.util.reflector;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import sun.misc.Unsafe;
+
+/** Access to sun.misc.Unsafe and the various scary things within. */
+@SuppressWarnings("NewApi")
+public class UnsafeAccess {
+    private static final Danger DANGER = new Danger11Plus();
+
+    interface Danger {
+        <T> Class<?> defineClass(Class<T> iClass, String reflectorClassName, byte[] bytecode);
+    }
+
+    static <T> Class<?> defineClass(Class<T> iClass, String reflectorClassName, byte[] bytecode) {
+        return DANGER.defineClass(iClass, reflectorClassName, bytecode);
+    }
+
+    private static class Danger11Plus implements Danger {
+        private final Method privateLookupInMethod;
+        private final Method defineClassMethod;
+
+        {
+            try {
+                privateLookupInMethod = MethodHandles.class.getMethod(
+                        "privateLookupIn", Class.class, MethodHandles.Lookup.class);
+                defineClassMethod =
+                        MethodHandles.Lookup.class.getMethod("defineClass", byte[].class);
+            } catch (NoSuchMethodException e) {
+                throw new AssertionError(e);
+            }
+        }
+
+        @Override
+        public <T> Class<?> defineClass(
+                Class<T> iClass, String reflectorClassName, byte[] bytecode) {
+            MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+            try {
+                // MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(iClass,
+                // lookup);
+                MethodHandles.Lookup privateLookup =
+                        (Lookup) privateLookupInMethod.invoke(lookup, iClass, lookup);
+
+                // return privateLookup.defineClass(bytecode);
+                return (Class<?>) defineClassMethod.invoke(privateLookup, bytecode);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw new AssertionError(e);
+            }
+        }
+    }
+
+    /**
+     * Returns the Java version as an int value.
+     *
+     * @return the Java version as an int value (8, 9, etc.)
+     */
+    private static int getJavaVersion() {
+        String version = System.getProperty("java.version");
+        assert version != null;
+        if (version.startsWith("1.")) {
+            version = version.substring(2);
+        }
+        // Allow these formats:
+        // 1.8.0_72-ea
+        // 9-ea
+        // 9
+        // 9.0.1
+        int dotPos = version.indexOf('.');
+        int dashPos = version.indexOf('-');
+        return Integer.parseInt(
+                version.substring(0, dotPos > -1 ? dotPos : dashPos > -1 ? dashPos : 1));
+    }
+}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c7df1f0..957f710b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7871,6 +7871,13 @@
   <int value="27" label="Too many quads."/>
 </enum>
 
+<enum name="CancelableTaskStatus">
+  <int value="0" label="Live, on same sequence"/>
+  <int value="1" label="Live, on other sequence"/>
+  <int value="2" label="Canceled, on same sequence"/>
+  <int value="3" label="Canceled, on other sequence"/>
+</enum>
+
 <enum name="CanMakePaymentUsage">
   <int value="0" label="Used"/>
   <int value="1" label="Not Used"/>
@@ -38017,6 +38024,7 @@
   <int value="-868138290" label="CrostiniPortForwarding:disabled"/>
   <int value="-867087281" label="enable-virtual-keyboard"/>
   <int value="-866993841" label="OfflinePagesCTV2:disabled"/>
+  <int value="-865414778" label="ContextMenuPerformanceInfo:enabled"/>
   <int value="-864266073" label="cros-regions-mode"/>
   <int value="-864234985" label="UseDdljsonApi:enabled"/>
   <int value="-864232986" label="StartSurfaceAndroid:disabled"/>
@@ -40180,6 +40188,7 @@
   <int value="1810258949" label="DisplayLocking:enabled"/>
   <int value="1810311887" label="WebAssemblyThreads:enabled"/>
   <int value="1812368073" label="enable-new-app-list-mixer"/>
+  <int value="1812978232" label="ContextMenuPerformanceInfo:disabled"/>
   <int value="1814671708" label="disable-password-manager-reauthentication"/>
   <int value="1816174635" label="RequestUnbufferedDispatch:enabled"/>
   <int value="1816843861" label="ServiceWorkerServicification:enabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0ec6b9e..87931db0 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -51340,6 +51340,16 @@
   </summary>
 </histogram>
 
+<histogram name="Extensions.FaviconResourceRequested" enum="ExtensionType"
+    expires_after="2020-12-01">
+  <owner>archanasimha@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
+  <summary>
+    Records when an extension with the chrome://favicon host permission makes a
+    network level request for a favicon resource.
+  </summary>
+</histogram>
+
 <histogram name="Extensions.FeatureProviderStaticInitTime" units="ms"
     expires_after="M90">
   <owner>rdevlin.cronin@chromium.org</owner>
@@ -79182,8 +79192,9 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Dialog.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="M85">
+    enum="DownloadLocationDirectoryType" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>
     Records the directory type when the user selects the download directory
     through download location dialog. May be recorded even when the user didn't
@@ -79192,8 +79203,9 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Dialog.Result"
-    enum="DownloadLocationDialogResult" expires_after="M82">
+    enum="DownloadLocationDialogResult" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>
     Records whether the user accepted or dismissed the dialog to select a
     download location.
@@ -79201,8 +79213,9 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Dialog.Type"
-    enum="DownloadLocationDialogType" expires_after="M82">
+    enum="DownloadLocationDialogType" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>
     Records the download location dialog type when the dialog is shown to the
     user.
@@ -79210,10 +79223,10 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="M82">
+    enum="DownloadLocationDirectoryType" expires_after="2020-03-01">
+  <owner>xingliu@chromium.org</owner>
   <owner>dtrainor@chromium.org</owner>
   <owner>qinmin@chromium.org</owner>
-  <owner>xingliu@chromium.org</owner>
   <summary>
     Records the directory type shown when a user opens download location dialog
     or download location setting. This is recorded when the directory provider
@@ -79222,14 +79235,16 @@
 </histogram>
 
 <histogram name="MobileDownload.Location.Download.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="M82">
+    enum="DownloadLocationDirectoryType" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>Records the directory type when download is completed.</summary>
 </histogram>
 
 <histogram name="MobileDownload.Location.Setting.DirectoryType"
-    enum="DownloadLocationDirectoryType" expires_after="M82">
+    enum="DownloadLocationDirectoryType" expires_after="2021-03-01">
   <owner>xingliu@chromium.org</owner>
+  <owner>clank-downloads@google.com</owner>
   <summary>
     Records the directory type when the user selects the download directory
     through download preference. May be recorded even when the user didn't
@@ -115779,8 +115794,9 @@
 </histogram>
 
 <histogram name="PDF.LoadStatus" enum="ChromePDFViewerLoadStatus"
-    expires_after="2020-03-01">
-  <owner>tommycli@chromium.org</owner>
+    expires_after="2020-09-01">
+  <owner>kmoon@chromium.org</owner>
+  <owner>thestig@chromium.org</owner>
   <summary>
     Tracks what happens when Chrome tries to load a PDF. This metric tracks all
     cases, but is focused on measuring failed PDF embeds, which occur if the
@@ -125723,12 +125739,14 @@
 </histogram>
 
 <histogram name="Profile.GetProfileInfoPath.OutsideUserDir" enum="BooleanHit"
-    expires_after="M77">
-  <owner>anthonyvd@chromium.org</owner>
+    expires_after="M84">
+  <owner>msarda@chromium.org</owner>
+  <owner>alexilin@chromium.org</owner>
   <summary>
-    Whether Profile::GetProfileInfoPath is called with a profile outside the
-    user data directory. We expect this to never happen but need to verify in
-    stable. This metric may be removed after M54.
+    Whether Profile::GetProfileInfoPath is called with a profile that does not
+    have the user data directory as a parent directory. This is expected to
+    never happen, but there are a few events recorded on stable as of M80. This
+    metric should be removed once http://crbug.com/981374 is fixed.
   </summary>
 </histogram>
 
@@ -125749,14 +125767,14 @@
 </histogram>
 
 <histogram name="Profile.InitProfileUserPrefs.OutsideUserDir" enum="BooleanHit"
-    expires_after="M82">
+    expires_after="M84">
   <owner>msarda@chromium.org</owner>
   <owner>alexilin@chromium.org</owner>
   <summary>
-    Whether Profile::InitProfileUserPrefs is called with a profile outside the
-    user data directory. This is expected to never happen, but there are a few
-    events recorded on stable as of M75. This metric should be removed once
-    http://crbug.com/981374 is fixed.
+    Whether Profile::InitProfileUserPrefs is called with a profile that does not
+    have the user data directory as a parent directory. This is expected to
+    never happen, but there are a few events recorded on stable as of M80. This
+    metric should be removed once http://crbug.com/981374 is fixed.
   </summary>
 </histogram>
 
@@ -138029,6 +138047,10 @@
 
 <histogram name="Scheduler.CancelableTaskTracker.TaskCanceled"
     enum="BooleanCanceled" expires_after="2020-05-01">
+  <obsolete>
+    2020-02-25: Replaced by Scheduler.CancelableTaskTracker.TaskState, which
+    allows correlation of live/canceled with same/off-sequence states.
+  </obsolete>
   <owner>wez@chromium.org</owner>
   <owner>scheduler-dev@chromium.org</owner>
   <summary>
@@ -138040,6 +138062,11 @@
 
 <histogram name="Scheduler.CancelableTaskTracker.TaskDuration" units="ms"
     expires_after="2020-05-01">
+  <obsolete>
+    2020-02-25: Replaced by Scheduler.CancelableTaskTracker.TaskDuration2_*
+    which correllate duration with liveness, off-sequence and task priority
+    states.
+  </obsolete>
   <owner>wez@chromium.org</owner>
   <owner>scheduler-dev@chromium.org</owner>
   <summary>
@@ -138048,6 +138075,30 @@
   </summary>
 </histogram>
 
+<histogram name="Scheduler.CancelableTaskTracker.TaskDuration2" units="ms"
+    expires_after="2020-05-01">
+<!-- Name completed by histogram_suffixes name="CancelableTaskTrackerDurationTypes" -->
+
+  <owner>wez@chromium.org</owner>
+  <owner>scheduler-dev@chromium.org</owner>
+  <summary>
+    Time taken for a task posted to a CancelableTaskTracker to run. Durations
+    are not recorded for tasks skipped due to having been canceled.
+  </summary>
+</histogram>
+
+<histogram name="Scheduler.CancelableTaskTracker.TaskStatus"
+    enum="CancelableTaskStatus" expires_after="2020-05-01">
+  <owner>wez@chromium.org</owner>
+  <owner>scheduler-dev@chromium.org</owner>
+  <summary>
+    Records whether a task posted to CancelableTaskTracker was canceled before
+    it got the chance to run, and whether it was same-sequence or off-sequence.
+    Recorded for every task posted via CancelableTaskTracker, immediately before
+    it would be run.
+  </summary>
+</histogram>
+
 <histogram name="Scheduler.Experimental.CPUTimePerThread"
     enum="SchedulerThreadType" expires_after="M81">
   <owner>altimin@chromium.org</owner>
@@ -181500,6 +181551,26 @@
   <affected-histogram name="PLT.BeginToFinish_Reload"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="CancelableTaskTrackerDurationTypes" separator="_">
+  <suffix name="CanceledBackgroundOffSequence"
+      label="Background off-sequence task was canceled."/>
+  <suffix name="CanceledBackgroundSameSequence"
+      label="Background same-sequence task was canceled."/>
+  <suffix name="CanceledForegroundOffSequence"
+      label="Foreground off-sequence task was canceled."/>
+  <suffix name="CanceledForegroundSameSequence"
+      label="Foreground same-sequence task was canceled."/>
+  <suffix name="LiveBackgroundOffSequence"
+      label="Background off-sequence task was not canceled."/>
+  <suffix name="LiveBackgroundSameSequence"
+      label="Background same-sequence task was not canceled."/>
+  <suffix name="LiveForegroundOffSequence"
+      label="Foreground off-sequence task was not canceled."/>
+  <suffix name="LiveForegroundSameSequence"
+      label="Foreground same-sequence task was not canceled."/>
+  <affected-histogram name="Scheduler.CancelableTaskTracker.TaskDuration2"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="CanvasRequestedImageMimeTypeFunctions" separator="_">
   <suffix name="convertToBlobPromise"
       label="Image formats passed to OffscreenCanvas.convertToBlob (promise)"/>
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 83a183e..849c3b3 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -390,7 +390,6 @@
 crbug.com/1014655 [ android-webview ] v8.browsing_mobile/browse:social:instagram:2019 [ Skip ]
 crbug.com/1036141 [ android-webview ] v8.browsing_mobile/browse:shopping:lazada:2019 [ Skip ]
 crbug.com/1036143 [ android-pixel-2 ] v8.browsing_mobile/browse:chrome:omnibox:2019 [ Skip ]
-crbug.com/1037905 [ android-nexus-6 android-webview ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
 crbug.com/1039801 [ android-webview ] v8.browsing_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
 
 
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index 89e246d..70046c5 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -696,4 +696,27 @@
   return false;
 }
 
+bool HasPresentationalChildren(const ax::mojom::Role role) {
+  // See http://www.w3.org/TR/core-aam-1.1/#exclude_elements2.
+  if (IsImage(role))
+    return true;
+
+  switch (role) {
+    case ax::mojom::Role::kButton:
+    case ax::mojom::Role::kCheckBox:
+    case ax::mojom::Role::kMath:
+    case ax::mojom::Role::kMenuItemCheckBox:
+    case ax::mojom::Role::kMenuItemRadio:
+    case ax::mojom::Role::kMenuListOption:
+    case ax::mojom::Role::kProgressIndicator:
+    case ax::mojom::Role::kScrollBar:
+    case ax::mojom::Role::kSlider:
+    case ax::mojom::Role::kSwitch:
+    case ax::mojom::Role::kTab:
+      return true;
+    default:
+      return false;
+  }
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/ax_role_properties.h b/ui/accessibility/ax_role_properties.h
index 90975c4..a918dff 100644
--- a/ui/accessibility/ax_role_properties.h
+++ b/ui/accessibility/ax_role_properties.h
@@ -145,6 +145,10 @@
 // Returns true if the node should be read only by default
 AX_EXPORT bool ShouldHaveReadonlyStateByDefault(const ax::mojom::Role role);
 
+// Returns true for objects which have the characteristic "Children
+// Presentational: True".
+AX_EXPORT bool HasPresentationalChildren(const ax::mojom::Role role);
+
 }  // namespace ui
 
 #endif  // UI_ACCESSIBILITY_AX_ROLE_PROPERTIES_H_
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index e8fbcff..e571168 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -353,12 +353,21 @@
   // Resize.
   extra_mac_nodes.resize(extra_node_count);
 
+  std::vector<AXTreeObserver::Change> changes;
+  changes.reserve(extra_node_count +
+                  1);  // Room for extra nodes + table itself.
+
   // Create column nodes.
-  for (size_t i = 0; i < col_count; i++)
+  for (size_t i = 0; i < col_count; i++) {
     extra_mac_nodes[i] = CreateExtraMacColumnNode(i);
+    changes.push_back(AXTreeObserver::Change(
+        extra_mac_nodes[i], AXTreeObserver::ChangeType::NODE_CREATED));
+  }
 
   // Create table header container node.
   extra_mac_nodes[col_count] = CreateExtraMacTableHeaderNode();
+  changes.push_back(AXTreeObserver::Change(
+      extra_mac_nodes[col_count], AXTreeObserver::ChangeType::NODE_CREATED));
 
   // Update the columns to reflect current state of the table.
   for (size_t i = 0; i < col_count; i++)
@@ -370,6 +379,12 @@
   data.AddIntListAttribute(ax::mojom::IntListAttribute::kIndirectChildIds,
                            all_headers);
   extra_mac_nodes[col_count]->SetData(data);
+
+  changes.push_back(AXTreeObserver::Change(
+      table_node_, AXTreeObserver::ChangeType::NODE_CHANGED));
+  for (AXTreeObserver& observer : tree_->observers()) {
+    observer.OnAtomicUpdateFinished(tree_, false, changes);
+  }
 }
 
 AXNode* AXTableInfo::CreateExtraMacColumnNode(size_t col_index) {
@@ -383,13 +398,8 @@
   data.id = id;
   data.role = ax::mojom::Role::kColumn;
   node->SetData(data);
-  for (AXTreeObserver& observer : tree_->observers()) {
+  for (AXTreeObserver& observer : tree_->observers())
     observer.OnNodeCreated(tree_, node);
-    observer.OnAtomicUpdateFinished(
-        tree_, false,
-        {AXTreeObserver::Change(node,
-                                AXTreeObserver::ChangeType::NODE_CREATED)});
-  }
   return node;
 }
 
@@ -405,13 +415,8 @@
   data.role = ax::mojom::Role::kTableHeaderContainer;
   node->SetData(data);
 
-  for (AXTreeObserver& observer : tree_->observers()) {
+  for (AXTreeObserver& observer : tree_->observers())
     observer.OnNodeCreated(tree_, node);
-    observer.OnAtomicUpdateFinished(
-        tree_, false,
-        {AXTreeObserver::Change(node,
-                                AXTreeObserver::ChangeType::NODE_CREATED)});
-  }
 
   return node;
 }
@@ -445,18 +450,29 @@
 }
 
 void AXTableInfo::ClearExtraMacNodes() {
-  for (size_t i = 0; i < extra_mac_nodes.size(); i++) {
+  for (AXNode* extra_mac_node : extra_mac_nodes) {
     for (AXTreeObserver& observer : tree_->observers())
-      observer.OnNodeWillBeDeleted(tree_, extra_mac_nodes[i]);
-    delete extra_mac_nodes[i];
+      observer.OnNodeWillBeDeleted(tree_, extra_mac_node);
+    AXNode::AXID deleted_id = extra_mac_node->id();
+    delete extra_mac_node;
+    for (AXTreeObserver& observer : tree_->observers())
+      observer.OnNodeDeleted(tree_, deleted_id);
   }
+  extra_mac_nodes.clear();
 }
 
 AXTableInfo::AXTableInfo(AXTree* tree, AXNode* table_node)
     : tree_(tree), table_node_(table_node) {}
 
 AXTableInfo::~AXTableInfo() {
-  ClearExtraMacNodes();
+  if (!extra_mac_nodes.empty()) {
+    ClearExtraMacNodes();
+    for (AXTreeObserver& observer : tree_->observers()) {
+      observer.OnAtomicUpdateFinished(
+          tree_, false,
+          {{table_node_, AXTreeObserver::ChangeType::NODE_CHANGED}});
+    }
+  }
 }
 
 }  // namespace ui
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index 794f9e52..e7e7c6f 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -614,8 +614,6 @@
 }
 
 void AXTree::Destroy() {
-  for (auto& entry : table_info_map_)
-    delete entry.second;
   table_info_map_.clear();
   if (root_) {
     RecursivelyNotifyNodeDeletedForTreeTeardown(root_);
@@ -1102,19 +1100,14 @@
   if (cached != table_info_map_.end()) {
     // Get existing table info, and update if invalid because the
     // tree has changed since the last time we accessed it.
-    AXTableInfo* table_info = cached->second;
+    AXTableInfo* table_info = cached->second.get();
     if (!table_info->valid()) {
-      bool success = table_info->Update();
-      if (!success) {
+      if (!table_info->Update()) {
         // If Update() returned false, this is no longer a valid table.
         // Remove it from the map.
-        delete table_info;
-        table_info = nullptr;
         table_info_map_.erase(table_node->id());
+        return nullptr;
       }
-      // See note about const_cast, above.
-      for (AXTreeObserver& observer : observers_)
-        observer.OnNodeChanged(tree, table_node);
     }
     return table_info;
   }
@@ -1123,10 +1116,7 @@
   if (!table_info)
     return nullptr;
 
-  table_info_map_[table_node->id()] = table_info;
-  for (AXTreeObserver& observer : observers_)
-    observer.OnNodeChanged(tree, table_node);
-
+  table_info_map_[table_node->id()] = base::WrapUnique<AXTableInfo>(table_info);
   return table_info;
 }
 
@@ -1434,9 +1424,13 @@
     AXNode* node,
     const AXTreeUpdateState* update_state) {
   DCHECK(!GetTreeUpdateInProgressState());
-  if (node->id() == AXNode::kInvalidAXID)
+
+  AXNode::AXID id = node->id();
+  if (id == AXNode::kInvalidAXID)
     return;
 
+  table_info_map_.erase(id);
+
   for (AXTreeObserver& observer : observers_) {
     if (update_state->IsReparentedNode(node)) {
       observer.OnNodeWillBeReparented(this, node);
@@ -1444,6 +1438,9 @@
       observer.OnNodeWillBeDeleted(this, node);
     }
   }
+
+  DCHECK(table_info_map_.find(id) == table_info_map_.end())
+      << "Table info should never be recreated during node deletion";
 }
 
 void AXTree::RecursivelyNotifyNodeDeletedForTreeTeardown(AXNode* node) {
@@ -1745,13 +1742,6 @@
   empty_data.id = node->id();
   UpdateReverseRelations(node, empty_data);
 
-  // Remove any table infos.
-  const auto& table_info_entry = table_info_map_.find(node->id());
-  if (table_info_entry != table_info_map_.end()) {
-    delete table_info_entry->second;
-    table_info_map_.erase(node->id());
-  }
-
   id_map_.erase(node->id());
   for (auto* child : node->children())
     DestroyNodeAndSubtree(child, update_state);
diff --git a/ui/accessibility/ax_tree.h b/ui/accessibility/ax_tree.h
index 1ba24868..3544570 100644
--- a/ui/accessibility/ax_tree.h
+++ b/ui/accessibility/ax_tree.h
@@ -310,7 +310,8 @@
 
   // Map from node ID to cached table info, if the given node is a table.
   // Invalidated every time the tree is updated.
-  mutable std::unordered_map<int32_t, AXTableInfo*> table_info_map_;
+  mutable std::unordered_map<int32_t, std::unique_ptr<AXTableInfo>>
+      table_info_map_;
 
   // The next negative node ID to use for internal nodes.
   int32_t next_negative_internal_node_id_ = -1;
diff --git a/ui/accessibility/platform/atk_util_auralinux.cc b/ui/accessibility/platform/atk_util_auralinux.cc
index 5eec1b51..7b167da 100644
--- a/ui/accessibility/platform/atk_util_auralinux.cc
+++ b/ui/accessibility/platform/atk_util_auralinux.cc
@@ -145,11 +145,4 @@
   return discard ? DiscardAtkKeyEvent::Discard : DiscardAtkKeyEvent::Retain;
 }
 
-#if !defined(USE_X11)
-DiscardAtkKeyEvent AtkUtilAuraLinux::HandleKeyEvent(
-    const ui::KeyEvent& ui_key_event) {
-  NOTREACHED();
-}
-#endif
-
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index 4838ab9..dab9bce 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -292,8 +292,9 @@
       ax::mojom::MoveDirection direction,
       ax::mojom::TextAffinity affinity) const = 0;
 
-  // Return a vector of all the descendants of this delegate's node.
-  virtual const std::vector<gfx::NativeViewAccessible> GetDescendants()
+  // Return a vector of all the descendants of this delegate's node. This method
+  // is only meaningful for Windows UIA.
+  virtual const std::vector<gfx::NativeViewAccessible> GetUIADescendants()
       const = 0;
 
   // Return a string representing the language code.
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
index 4037636..dbf7127 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.cc
@@ -551,7 +551,7 @@
 }
 
 const std::vector<gfx::NativeViewAccessible>
-AXPlatformNodeDelegateBase::GetDescendants() const {
+AXPlatformNodeDelegateBase::GetUIADescendants() const {
   return {};
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_delegate_base.h b/ui/accessibility/platform/ax_platform_node_delegate_base.h
index e9abafb..715cff3 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate_base.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate_base.h
@@ -204,7 +204,8 @@
       ax::mojom::MoveDirection direction,
       ax::mojom::TextAffinity affinity) const override;
 
-  const std::vector<gfx::NativeViewAccessible> GetDescendants() const override;
+  const std::vector<gfx::NativeViewAccessible> GetUIADescendants()
+      const override;
 
   std::string GetLanguage() const override;
 
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 594cac6e..be99b57 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -554,22 +554,10 @@
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TEXTRANGE_GETENCLOSINGELEMENT);
   UIA_VALIDATE_TEXTRANGEPROVIDER_CALL_1_OUT(element);
 
-  AXNode* common_anchor = start_->LowestCommonAnchor(*end_);
-  DCHECK(common_anchor);
-  if (!common_anchor)
+  AXPlatformNodeWin* enclosing_node = GetLowestAccessibleCommonPlatformNode();
+  if (!enclosing_node)
     return UIA_E_ELEMENTNOTAVAILABLE;
 
-  const AXTreeID tree_id = common_anchor->tree()->GetAXTreeID();
-  const AXNode::AXID node_id = common_anchor->id();
-  AXPlatformNodeWin* enclosing_node =
-      static_cast<AXPlatformNodeWin*>(AXPlatformNode::FromNativeViewAccessible(
-          GetDelegate(tree_id, node_id)->GetNativeViewAccessible()));
-  DCHECK(enclosing_node);
-  // If this node has an ancestor that is a control type, use that as the
-  // enclosing element.
-  enclosing_node = enclosing_node->GetLowestAccessibleElement();
-  DCHECK(enclosing_node);
-
   while (enclosing_node->GetData().IsIgnored() ||
          enclosing_node->GetData().role == ax::mojom::Role::kInlineTextBox) {
     AXPlatformNodeWin* parent = static_cast<AXPlatformNodeWin*>(
@@ -893,7 +881,7 @@
     delegate = node->GetDelegate();
   }
   if (delegate->GetChildCount())
-    descendants = delegate->GetDescendants();
+    descendants = delegate->GetUIADescendants();
 
   SAFEARRAY* safe_array =
       SafeArrayCreateVector(VT_UNKNOWN, 0, descendants.size());
@@ -1265,4 +1253,21 @@
     end_->SnapToMaxTextOffsetIfBeyond();
 }
 
+AXPlatformNodeWin*
+AXPlatformNodeTextRangeProviderWin::GetLowestAccessibleCommonPlatformNode()
+    const {
+  AXNode* common_anchor = start_->LowestCommonAnchor(*end_);
+  if (!common_anchor)
+    return nullptr;
+
+  const AXTreeID tree_id = common_anchor->tree()->GetAXTreeID();
+  const AXNode::AXID node_id = common_anchor->id();
+  AXPlatformNodeWin* platform_node =
+      static_cast<AXPlatformNodeWin*>(AXPlatformNode::FromNativeViewAccessible(
+          GetDelegate(tree_id, node_id)->GetNativeViewAccessible()));
+  DCHECK(platform_node);
+
+  return platform_node->GetLowestAccessibleElement();
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
index 8a1f598c..6d5ce34a 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.h
@@ -160,6 +160,7 @@
   void RemoveFocusFromPreviousSelectionIfNeeded(
       const AXNodeRange& new_selection);
   void ValidateStartAndEndPositions();
+  AXPlatformNodeWin* GetLowestAccessibleCommonPlatformNode() const;
 
   Microsoft::WRL::ComPtr<AXPlatformNodeWin> owner_;
   AXPositionInstance start_;
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index 9cd57e8..4e863fe 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -3284,141 +3284,197 @@
 TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderGetChildren) {
   // Set up ax tree with the following structure:
   //
-  // root
-  // |
-  // document(ignored)_________________________
-  // |                         |              |
-  // text_node1___             text_node2     ignored_text
-  // |            |
-  // text_node3   text_node4
-  ui::AXNodeData root_data;
-  root_data.id = 1;
-  root_data.role = ax::mojom::Role::kRootWebArea;
+  // ++1 kRootWebArea
+  // ++++2 kDocument ignored
+  // ++++++3 kStaticText
+  // ++++++++4 kInlineTextBox
+  // ++++++++5 kInlineTextBox
+  // ++++++6 kStaticText
+  // ++++++7 kStaticText ignored
+  // ++++++8 kButton
+  // ++++++++9  kImage
+  // ++++++++10 kStaticText
 
-  ui::AXNodeData document_data;
-  document_data.id = 2;
-  document_data.role = ax::mojom::Role::kDocument;
-  document_data.AddState(ax::mojom::State::kIgnored);
-  root_data.child_ids.push_back(document_data.id);
+  AXNodeData root_1;
+  AXNodeData document_2;
+  AXNodeData static_text_3;
+  AXNodeData inline_box_4;
+  AXNodeData inline_box_5;
+  AXNodeData static_text_6;
+  AXNodeData static_text_7;
+  AXNodeData button_8;
+  AXNodeData image_9;
+  AXNodeData static_text_10;
 
-  ui::AXNodeData text_node1;
-  text_node1.id = 3;
-  text_node1.role = ax::mojom::Role::kStaticText;
-  document_data.child_ids.push_back(text_node1.id);
+  root_1.id = 1;
+  document_2.id = 2;
+  static_text_3.id = 3;
+  inline_box_4.id = 4;
+  inline_box_5.id = 5;
+  static_text_6.id = 6;
+  static_text_7.id = 7;
+  button_8.id = 8;
+  image_9.id = 9;
+  static_text_10.id = 10;
 
-  ui::AXNodeData text_node2;
-  text_node2.id = 4;
-  text_node2.role = ax::mojom::Role::kStaticText;
-  document_data.child_ids.push_back(text_node2.id);
+  root_1.role = ax::mojom::Role::kRootWebArea;
+  root_1.child_ids = {document_2.id};
 
-  ui::AXNodeData ignored_text;
-  ignored_text.id = 5;
-  ignored_text.role = ax::mojom::Role::kStaticText;
-  ignored_text.AddState(ax::mojom::State::kIgnored);
-  document_data.child_ids.push_back(ignored_text.id);
+  document_2.role = ax::mojom::Role::kDocument;
+  document_2.AddState(ax::mojom::State::kIgnored);
+  document_2.child_ids = {static_text_3.id, static_text_6.id, static_text_7.id,
+                          button_8.id};
 
-  ui::AXNodeData text_node3;
-  text_node3.id = 6;
-  text_node3.role = ax::mojom::Role::kStaticText;
-  text_node1.child_ids.push_back(text_node3.id);
+  static_text_3.role = ax::mojom::Role::kStaticText;
+  static_text_3.child_ids = {inline_box_4.id, inline_box_5.id};
 
-  ui::AXNodeData text_node4;
-  text_node4.id = 7;
-  text_node4.role = ax::mojom::Role::kStaticText;
-  text_node1.child_ids.push_back(text_node4.id);
+  inline_box_4.role = ax::mojom::Role::kInlineTextBox;
+
+  inline_box_5.role = ax::mojom::Role::kInlineTextBox;
+
+  static_text_6.role = ax::mojom::Role::kStaticText;
+
+  static_text_7.role = ax::mojom::Role::kStaticText;
+  static_text_7.AddState(ax::mojom::State::kIgnored);
+
+  button_8.role = ax::mojom::Role::kButton;
+  // Hack: This attribute is needed to be able to get a text range provider
+  // located on this element (see AXPlatformNodeWin::GetPatternProvider).
+  button_8.AddBoolAttribute(ax::mojom::BoolAttribute::kEditableRoot, true);
+  button_8.child_ids = {image_9.id, static_text_10.id};
+
+  image_9.role = ax::mojom::Role::kImage;
+
+  static_text_10.role = ax::mojom::Role::kStaticText;
 
   ui::AXTreeUpdate update;
   ui::AXTreeData tree_data;
   tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
   update.tree_data = tree_data;
   update.has_tree_data = true;
-  update.root_id = root_data.id;
-  update.nodes.push_back(root_data);
-  update.nodes.push_back(document_data);
-  update.nodes.push_back(text_node1);
-  update.nodes.push_back(text_node2);
-  update.nodes.push_back(ignored_text);
-  update.nodes.push_back(text_node3);
-  update.nodes.push_back(text_node4);
+  update.root_id = root_1.id;
+  update.nodes.push_back(root_1);
+  update.nodes.push_back(document_2);
+  update.nodes.push_back(static_text_3);
+  update.nodes.push_back(inline_box_4);
+  update.nodes.push_back(inline_box_5);
+  update.nodes.push_back(static_text_6);
+  update.nodes.push_back(static_text_7);
+  update.nodes.push_back(button_8);
+  update.nodes.push_back(image_9);
+  update.nodes.push_back(static_text_10);
 
   Init(update);
 
   // Set up variables from the tree for testing.
-  AXNode* document_node = GetRootAsAXNode()->children()[0];
-  AXNode* node1 = document_node->children()[0];
-  AXNode* node2 = document_node->children()[1];
-  AXNode* node3 = node1->children()[0];
-  AXNode* node4 = node1->children()[1];
+  AXNode* document_2_node = GetRootAsAXNode()->children()[0];
+  AXNode* static_text_3_node = document_2_node->children()[0];
+  AXNode* inline_box_4_node = static_text_3_node->children()[0];
+  AXNode* inline_box_5_node = static_text_3_node->children()[1];
+  AXNode* static_text_6_node = document_2_node->children()[1];
+  AXNode* button_8_node = document_2_node->children()[3];
 
-  ComPtr<IRawElementProviderSimple> document_node_raw =
-      QueryInterfaceFromNode<IRawElementProviderSimple>(document_node);
-  ComPtr<IRawElementProviderSimple> text_node_raw1 =
-      QueryInterfaceFromNode<IRawElementProviderSimple>(node1);
-  ComPtr<IRawElementProviderSimple> text_node_raw2 =
-      QueryInterfaceFromNode<IRawElementProviderSimple>(node2);
-  ComPtr<IRawElementProviderSimple> text_node_raw3 =
-      QueryInterfaceFromNode<IRawElementProviderSimple>(node3);
-  ComPtr<IRawElementProviderSimple> text_node_raw4 =
-      QueryInterfaceFromNode<IRawElementProviderSimple>(node4);
+  ComPtr<IRawElementProviderSimple> document_2_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(document_2_node);
+  ComPtr<IRawElementProviderSimple> static_text_3_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(static_text_3_node);
+  ComPtr<IRawElementProviderSimple> inline_box_4_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(inline_box_4_node);
+  ComPtr<IRawElementProviderSimple> inline_box_5_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(inline_box_5_node);
+  ComPtr<IRawElementProviderSimple> static_text_6_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(static_text_6_node);
+  ComPtr<IRawElementProviderSimple> button_8_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(button_8_node);
 
-  // Test text_node3 - leaf nodes should have no children.
   ComPtr<ITextProvider> text_provider;
-  EXPECT_HRESULT_SUCCEEDED(
-      text_node_raw3->GetPatternProvider(UIA_TextPatternId, &text_provider));
-
   ComPtr<ITextRangeProvider> text_range_provider;
-  EXPECT_HRESULT_SUCCEEDED(
-      text_provider->get_DocumentRange(&text_range_provider));
-
   base::win::ScopedSafearray children;
-  EXPECT_HRESULT_SUCCEEDED(
-      text_range_provider->GetChildren(children.Receive()));
-
   std::vector<ComPtr<IRawElementProviderSimple>> expected_values = {};
 
-  EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+  // Test inline_box_4 - a leaf node should have no children.
+  {
+    EXPECT_HRESULT_SUCCEEDED(inline_box_4_raw->GetPatternProvider(
+        UIA_TextPatternId, &text_provider));
 
-  // Test text_node2 - leaf nodes should have no children.
-  EXPECT_HRESULT_SUCCEEDED(
-      text_node_raw2->GetPatternProvider(UIA_TextPatternId, &text_provider));
+    EXPECT_HRESULT_SUCCEEDED(
+        text_provider->get_DocumentRange(&text_range_provider));
 
-  EXPECT_HRESULT_SUCCEEDED(
-      text_provider->get_DocumentRange(&text_range_provider));
+    EXPECT_HRESULT_SUCCEEDED(
+        text_range_provider->GetChildren(children.Receive()));
 
-  EXPECT_HRESULT_SUCCEEDED(
-      text_range_provider->GetChildren(children.Receive()));
+    expected_values = {};
 
-  EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+    EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+  }
 
-  // Test text_node1 - children should include text_node3 and text_node4.
-  EXPECT_HRESULT_SUCCEEDED(
-      text_node_raw1->GetPatternProvider(UIA_TextPatternId, &text_provider));
+  // Test static_text_6 - a leaf node should have no children.
+  {
+    EXPECT_HRESULT_SUCCEEDED(static_text_6_raw->GetPatternProvider(
+        UIA_TextPatternId, &text_provider));
 
-  EXPECT_HRESULT_SUCCEEDED(
-      text_provider->get_DocumentRange(&text_range_provider));
+    EXPECT_HRESULT_SUCCEEDED(
+        text_provider->get_DocumentRange(&text_range_provider));
 
-  EXPECT_HRESULT_SUCCEEDED(
-      text_range_provider->GetChildren(children.Receive()));
+    EXPECT_HRESULT_SUCCEEDED(
+        text_range_provider->GetChildren(children.Receive()));
 
-  expected_values = {text_node_raw3, text_node_raw4};
+    expected_values = {};
 
-  EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+    EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+  }
 
-  // Test root_node - children should include the entire left subtree and
-  // the entire right subtree.
-  EXPECT_HRESULT_SUCCEEDED(
-      document_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
+  // Test static_text_3 - children should include inline_box_4 and inline_box_5.
+  {
+    EXPECT_HRESULT_SUCCEEDED(static_text_3_raw->GetPatternProvider(
+        UIA_TextPatternId, &text_provider));
 
-  EXPECT_HRESULT_SUCCEEDED(
-      text_provider->get_DocumentRange(&text_range_provider));
+    EXPECT_HRESULT_SUCCEEDED(
+        text_provider->get_DocumentRange(&text_range_provider));
 
-  EXPECT_HRESULT_SUCCEEDED(
-      text_range_provider->GetChildren(children.Receive()));
+    EXPECT_HRESULT_SUCCEEDED(
+        text_range_provider->GetChildren(children.Receive()));
 
-  expected_values = {text_node_raw1, text_node_raw3, text_node_raw4,
-                     text_node_raw2};
+    expected_values = {inline_box_4_raw, inline_box_5_raw};
 
-  EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+    EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+  }
+
+  // Test button_8 - a button should never expose its children.
+  {
+    EXPECT_HRESULT_SUCCEEDED(
+        button_8_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(
+        text_provider->get_DocumentRange(&text_range_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(
+        text_range_provider->GetChildren(children.Receive()));
+
+    expected_values = {};
+
+    EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+  }
+
+  // Test document_2 - children should not include ignored nodes and nodes under
+  // a node that should hide its children.
+  {
+    EXPECT_HRESULT_SUCCEEDED(
+        document_2_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(
+        text_provider->get_DocumentRange(&text_range_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(
+        text_range_provider->GetChildren(children.Receive()));
+
+    expected_values = {
+        static_text_3_raw, inline_box_4_raw, inline_box_5_raw,
+        static_text_6_raw, button_8_raw,
+    };
+
+    EXPECT_UIA_VT_UNKNOWN_SAFEARRAY_EQ(children.Get(), expected_values);
+  }
 }
 
 TEST_F(AXPlatformNodeTextRangeProviderTest,
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index f52be88f..f1639ee 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -6902,15 +6902,19 @@
 }
 
 bool AXPlatformNodeWin::ShouldHideChildrenForUIA() const {
-  switch (GetData().role) {
-    case ax::mojom::Role::kButton:
-    case ax::mojom::Role::kImage:
-    case ax::mojom::Role::kGraphicsSymbol:
+  auto role = GetData().role;
+
+  if (HasPresentationalChildren(role))
+    return true;
+
+  switch (role) {
+    // Other elements that are expected by UIA to hide their children without
+    // having "Children Presentational: True".
+    //
+    // TODO(bebeaudr): We might be able to remove ax::mojom::Role::kLink once
+    // http://crbug.com/1054514 is fixed. Links should not have to hide their
+    // children.
     case ax::mojom::Role::kLink:
-    case ax::mojom::Role::kMath:
-    case ax::mojom::Role::kProgressIndicator:
-    case ax::mojom::Role::kScrollBar:
-    case ax::mojom::Role::kSlider:
     case ax::mojom::Role::kTextField:
       return true;
     default:
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index 0bdf842..c2c3948 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -865,28 +865,51 @@
   return GetOrCreate(tree_, node_->children()[size_t{index}]);
 }
 
-// Recursive helper function for GetDescendants. Aggregates all of the
+// Recursive helper function for GetUIADescendants. Aggregates all of the
 // descendants for a given node within the descendants vector.
-void TestAXNodeWrapper::Descendants(
+void TestAXNodeWrapper::UIADescendants(
     const AXNode* node,
     std::vector<gfx::NativeViewAccessible>* descendants) const {
+  if (ShouldHideChildrenForUIA(node))
+    return;
+
   for (auto it = node->UnignoredChildrenBegin();
        it != node->UnignoredChildrenEnd(); ++it) {
     descendants->emplace_back(ax_platform_node()
                                   ->GetDelegate()
                                   ->GetFromNodeID(it->id())
                                   ->GetNativeViewAccessible());
-    Descendants(it.get(), descendants);
+    UIADescendants(it.get(), descendants);
   }
 }
 
-const std::vector<gfx::NativeViewAccessible> TestAXNodeWrapper::GetDescendants()
-    const {
+const std::vector<gfx::NativeViewAccessible>
+TestAXNodeWrapper::GetUIADescendants() const {
   std::vector<gfx::NativeViewAccessible> descendants;
-  Descendants(node_, &descendants);
+  UIADescendants(node_, &descendants);
   return descendants;
 }
 
+// static
+// Needs to stay in sync with AXPlatformNodeWin::ShouldHideChildrenForUIA.
+bool TestAXNodeWrapper::ShouldHideChildrenForUIA(const AXNode* node) {
+  if (!node)
+    return false;
+
+  auto role = node->data().role;
+
+  if (ui::HasPresentationalChildren(role))
+    return true;
+
+  switch (role) {
+    case ax::mojom::Role::kLink:
+    case ax::mojom::Role::kTextField:
+      return true;
+    default:
+      return false;
+  }
+}
+
 gfx::RectF TestAXNodeWrapper::GetInlineTextRect(const int start_offset,
                                                 const int end_offset) const {
   DCHECK(start_offset >= 0 && end_offset >= 0 && start_offset <= end_offset);
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index 145bba8..0a1b710 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -134,7 +134,8 @@
   bool IsOrderedSet() const override;
   base::Optional<int> GetPosInSet() const override;
   base::Optional<int> GetSetSize() const override;
-  const std::vector<gfx::NativeViewAccessible> GetDescendants() const override;
+  const std::vector<gfx::NativeViewAccessible> GetUIADescendants()
+      const override;
   gfx::RectF GetLocation() const;
   int InternalChildCount() const;
   TestAXNodeWrapper* InternalGetChild(int index) const;
@@ -154,8 +155,10 @@
                                     int32_t focus_offset);
 
   TestAXNodeWrapper* HitTestSyncInternal(int x, int y);
-  void Descendants(const AXNode* node,
-                   std::vector<gfx::NativeViewAccessible>* descendants) const;
+  void UIADescendants(
+      const AXNode* node,
+      std::vector<gfx::NativeViewAccessible>* descendants) const;
+  static bool ShouldHideChildrenForUIA(const AXNode* node);
 
   // Return the bounds of inline text in this node's coordinate system (which is
   // relative to its container node specified in AXRelativeBounds).
diff --git a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
index 317bd36..1d9e441 100644
--- a/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
+++ b/ui/android/java/src/org/chromium/ui/base/ViewAndroidDelegate.java
@@ -14,8 +14,8 @@
 import android.view.PointerIcon;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
 import android.view.inputmethod.InputConnection;
-import android.widget.FrameLayout.LayoutParams;
 import android.widget.ImageView;
 
 import androidx.annotation.VisibleForTesting;
@@ -131,17 +131,19 @@
 
     /**
      * Set the anchor view to specified position and size (all units in px).
-     * @param view The anchor view that needs to be positioned.
+     * @param anchorView The view that needs to be positioned. This must be the result of a previous
+     *         call to {@link acquireView} which has not yet been removed via {@link removeView}.
      * @param x X coordinate of the top left corner of the anchor view.
      * @param y Y coordinate of the top left corner of the anchor view.
      * @param width The width of the anchor view.
      * @param height The height of the anchor view.
      */
     @CalledByNative
-    public void setViewPosition(
-            View view, float x, float y, float width, float height, int leftMargin, int topMargin) {
+    public void setViewPosition(View anchorView, float x, float y, float width, float height,
+            int leftMargin, int topMargin) {
         ViewGroup containerView = getContainerView();
         if (containerView == null) return;
+        assert anchorView.getParent() == containerView;
 
         int widthInt = Math.round(width);
         int heightInt = Math.round(height);
@@ -155,10 +157,12 @@
         if (widthInt + startMargin > containerView.getWidth()) {
             widthInt = containerView.getWidth() - startMargin;
         }
-        LayoutParams lp = new LayoutParams(widthInt, heightInt);
-        MarginLayoutParamsCompat.setMarginStart(lp, startMargin);
-        lp.topMargin = topMargin;
-        view.setLayoutParams(lp);
+        MarginLayoutParams mlp = (MarginLayoutParams) anchorView.getLayoutParams();
+        mlp.width = widthInt;
+        mlp.height = heightInt;
+        MarginLayoutParamsCompat.setMarginStart(mlp, startMargin);
+        mlp.topMargin = topMargin;
+        anchorView.setLayoutParams(mlp);
     }
 
     /**
diff --git a/ui/android/view_android_unittests.cc b/ui/android/view_android_unittests.cc
index 2a72742..d42e19b64 100644
--- a/ui/android/view_android_unittests.cc
+++ b/ui/android/view_android_unittests.cc
@@ -275,58 +275,71 @@
 
 class Observer : public ViewAndroidObserver {
  public:
-  Observer() : attached_(false) {}
+  Observer() : attached_(false), destroyed_(false) {}
 
   void OnAttachedToWindow() override { attached_ = true; }
 
   void OnDetachedFromWindow() override { attached_ = false; }
 
+  void OnViewAndroidDestroyed() override { destroyed_ = true; }
+
   bool attached_;
+  bool destroyed_;
 };
 
 TEST(ViewAndroidTest, Observer) {
   std::unique_ptr<WindowAndroid> window(WindowAndroid::CreateForTesting());
-  ViewAndroid top;
-  ViewAndroid bottom;
 
   Observer top_observer;
   Observer bottom_observer;
-
-  top.AddObserver(&top_observer);
-  bottom.AddObserver(&bottom_observer);
-
-  top.AddChild(&bottom);
-
-  EXPECT_FALSE(top_observer.attached_);
-  EXPECT_FALSE(bottom_observer.attached_);
-
-  // Views in a tree all get notified of 'attached' event.
-  window->AddChild(&top);
-  EXPECT_TRUE(top_observer.attached_);
-  EXPECT_TRUE(bottom_observer.attached_);
-
-  // Observer, upon addition, does not get notified of the current
-  // attached state.
   Observer top_observer2;
-  top.AddObserver(&top_observer2);
-  EXPECT_FALSE(top_observer2.attached_);
 
-  bottom.RemoveFromParent();
-  EXPECT_FALSE(bottom_observer.attached_);
-  top.RemoveFromParent();
-  EXPECT_FALSE(top_observer.attached_);
+  {
+    ViewAndroid top;
+    ViewAndroid bottom;
 
-  window->AddChild(&top);
-  EXPECT_TRUE(top_observer.attached_);
+    top.AddObserver(&top_observer);
+    bottom.AddObserver(&bottom_observer);
 
-  // View, upon addition to a tree in the attached state, should be notified.
-  top.AddChild(&bottom);
-  EXPECT_TRUE(bottom_observer.attached_);
+    top.AddChild(&bottom);
 
-  // Views in a tree all get notified of 'detached' event.
-  top.RemoveFromParent();
-  EXPECT_FALSE(top_observer.attached_);
-  EXPECT_FALSE(bottom_observer.attached_);
+    EXPECT_FALSE(top_observer.attached_);
+    EXPECT_FALSE(bottom_observer.attached_);
+
+    // Views in a tree all get notified of 'attached' event.
+    window->AddChild(&top);
+    EXPECT_TRUE(top_observer.attached_);
+    EXPECT_TRUE(bottom_observer.attached_);
+
+    // Observer, upon addition, does not get notified of the current
+    // attached state.
+    top.AddObserver(&top_observer2);
+    EXPECT_FALSE(top_observer2.attached_);
+
+    bottom.RemoveFromParent();
+    EXPECT_FALSE(bottom_observer.attached_);
+    top.RemoveFromParent();
+    EXPECT_FALSE(top_observer.attached_);
+
+    window->AddChild(&top);
+    EXPECT_TRUE(top_observer.attached_);
+
+    // View, upon addition to a tree in the attached state, should be notified.
+    top.AddChild(&bottom);
+    EXPECT_TRUE(bottom_observer.attached_);
+
+    // Views in a tree all get notified of 'detached' event.
+    top.RemoveFromParent();
+    EXPECT_FALSE(top_observer.attached_);
+    EXPECT_FALSE(bottom_observer.attached_);
+
+    // Remove the second top observer to test the destruction notification.
+    top.RemoveObserver(&top_observer2);
+  }
+
+  EXPECT_TRUE(top_observer.destroyed_);
+  EXPECT_FALSE(top_observer2.destroyed_);
+  EXPECT_TRUE(bottom_observer.destroyed_);
 }
 
 TEST(ViewAndroidTest, WindowAndroidDestructionDetachesAllViewAndroid) {
@@ -350,6 +363,9 @@
 
   EXPECT_FALSE(top_observer.attached_);
   EXPECT_FALSE(bottom_observer.attached_);
+
+  top.RemoveObserver(&top_observer);
+  bottom.RemoveObserver(&bottom_observer);
 }
 
 }  // namespace ui
diff --git a/ui/compositor/animation_metrics_recorder.cc b/ui/compositor/animation_metrics_recorder.cc
index bc671104..2c9d7b77 100644
--- a/ui/compositor/animation_metrics_recorder.cc
+++ b/ui/compositor/animation_metrics_recorder.cc
@@ -4,6 +4,7 @@
 
 #include "ui/compositor/animation_metrics_recorder.h"
 
+#include "base/logging.h"
 #include "ui/compositor/animation_metrics_reporter.h"
 
 namespace ui {
@@ -14,21 +15,40 @@
 
 AnimationMetricsRecorder::~AnimationMetricsRecorder() = default;
 
+void AnimationMetricsRecorder::OnAnimatorAttached(
+    base::Optional<int> frame_number) {
+  DCHECK(frame_number.has_value());
+  if (start_frame_number_.has_value() || animator_detached_after_start_)
+    return;
+
+  start_frame_number_ = frame_number;
+}
+
+void AnimationMetricsRecorder::OnAnimatorDetached() {
+  animator_detached_after_start_ = true;
+  start_frame_number_.reset();
+}
+
 void AnimationMetricsRecorder::OnAnimationStart(
-    int start_frame_number,
+    base::Optional<int> start_frame_number,
     base::TimeTicks effective_start_time,
     base::TimeDelta duration) {
   start_frame_number_ = start_frame_number;
   effective_start_time_ = effective_start_time;
   duration_ = duration;
+  animator_detached_after_start_ = false;
 }
 
-void AnimationMetricsRecorder::OnAnimationEnd(int end_frame_number,
-                                              float refresh_rate) {
+void AnimationMetricsRecorder::OnAnimationEnd(
+    base::Optional<int> end_frame_number,
+    float refresh_rate) {
   DCHECK(reporter_);
 
-  if (duration_.is_zero() || end_frame_number <= start_frame_number_)
+  if (duration_.is_zero() || !start_frame_number_.has_value() ||
+      !end_frame_number.has_value()) {
     return;
+  }
+  DCHECK_GE(end_frame_number.value(), start_frame_number_.value());
 
   base::TimeDelta elapsed = base::TimeTicks::Now() - effective_start_time_;
   if (elapsed < duration_)
@@ -38,7 +58,7 @@
   const float frame_interval =
       base::Time::kMillisecondsPerSecond / refresh_rate;
   const float actual_duration =
-      (end_frame_number - start_frame_number_) * frame_interval;
+      (end_frame_number.value() - start_frame_number_.value()) * frame_interval;
   if (duration_.InMillisecondsF() - actual_duration >= frame_interval)
     smoothness = 100 * (actual_duration / duration_.InMillisecondsF());
   reporter_->Report(smoothness);
diff --git a/ui/compositor/animation_metrics_recorder.h b/ui/compositor/animation_metrics_recorder.h
index c9c16bf1..fbb581ea 100644
--- a/ui/compositor/animation_metrics_recorder.h
+++ b/ui/compositor/animation_metrics_recorder.h
@@ -5,6 +5,7 @@
 #ifndef UI_COMPOSITOR_ANIMATION_METRICS_RECORDER_H_
 #define UI_COMPOSITOR_ANIMATION_METRICS_RECORDER_H_
 
+#include "base/optional.h"
 #include "base/time/time.h"
 
 namespace ui {
@@ -14,24 +15,37 @@
 class AnimationMetricsRecorder {
  public:
   explicit AnimationMetricsRecorder(AnimationMetricsReporter* reporter);
-  ~AnimationMetricsRecorder();
-
   AnimationMetricsRecorder(const AnimationMetricsRecorder&) = delete;
   AnimationMetricsRecorder& operator=(const AnimationMetricsRecorder&) = delete;
+  ~AnimationMetricsRecorder();
 
-  void OnAnimationStart(int start_frame_number,
+  // Called when the animator is attached to/detached from a Compositor to
+  // update |start_frame_number_|.
+  void OnAnimatorAttached(base::Optional<int> frame_number);
+  void OnAnimatorDetached();
+
+  void OnAnimationStart(base::Optional<int> start_frame_number,
                         base::TimeTicks effective_start_time,
                         base::TimeDelta duration);
-  void OnAnimationEnd(int end_frame_number, float refresh_rate);
+  void OnAnimationEnd(base::Optional<int> end_frame_number, float refresh_rate);
 
  private:
   AnimationMetricsReporter* const reporter_;
 
   // Variables set at the start of an animation which are required to compute
   // the smoothness when the animation ends.
-  int start_frame_number_ = 0;
+  // |start_frame_number_| is the frame number in relevant Compositor when
+  // the animation starts. If not set, it means the animator and its Layer
+  // is not attached to a Compositor when the animation starts, or is
+  // detached from the Compositor partway through the animation.
+  base::Optional<int> start_frame_number_;
   base::TimeTicks effective_start_time_;
   base::TimeDelta duration_;
+
+  // Whether animator is detached from Compositor partway through the animation.
+  // If it is true, no metrics is reported because the number of frames could
+  // not be counted correctly in such case.
+  bool animator_detached_after_start_ = false;
 };
 
 }  // namespace ui
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 31b8979a..73ce83d8 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -1517,9 +1517,12 @@
   return compositor ? compositor->layer_animator_collection() : nullptr;
 }
 
-int Layer::GetFrameNumber() const {
-  const Compositor* compositor = GetCompositor();
-  return compositor ? compositor->activated_frame_count() : 0;
+base::Optional<int> Layer::GetFrameNumber() const {
+  if (const Compositor* compositor = GetCompositor()) {
+    return compositor->activated_frame_count();
+  }
+
+  return base::nullopt;
 }
 
 float Layer::GetRefreshRate() const {
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 50dc513..95efd1e 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -559,7 +559,7 @@
   cc::Layer* GetCcLayer() const override;
   LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() override;
   LayerAnimatorCollection* GetLayerAnimatorCollection() override;
-  int GetFrameNumber() const override;
+  base::Optional<int> GetFrameNumber() const override;
   float GetRefreshRate() const override;
 
   // Creates a corresponding composited layer for |type_|.
diff --git a/ui/compositor/layer_animation_delegate.h b/ui/compositor/layer_animation_delegate.h
index 04a15ca..9626869 100644
--- a/ui/compositor/layer_animation_delegate.h
+++ b/ui/compositor/layer_animation_delegate.h
@@ -5,6 +5,7 @@
 #ifndef UI_COMPOSITOR_LAYER_ANIMATION_DELEGATE_H_
 #define UI_COMPOSITOR_LAYER_ANIMATION_DELEGATE_H_
 
+#include "base/optional.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/compositor/compositor_export.h"
 #include "ui/compositor/property_change_reason.h"
@@ -59,7 +60,9 @@
   virtual cc::Layer* GetCcLayer() const = 0;
   virtual LayerAnimatorCollection* GetLayerAnimatorCollection() = 0;
   virtual LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() = 0;
-  virtual int GetFrameNumber() const = 0;
+  // Returns base::nullopt if the frame number is not available, e.g. when
+  // Layer is not attached to a Compositor. Otherwise, returns the frame number.
+  virtual base::Optional<int> GetFrameNumber() const = 0;
   virtual float GetRefreshRate() const = 0;
 
  protected:
diff --git a/ui/compositor/layer_animation_element.cc b/ui/compositor/layer_animation_element.cc
index 4d45757..9da426ae6 100644
--- a/ui/compositor/layer_animation_element.cc
+++ b/ui/compositor/layer_animation_element.cc
@@ -691,6 +691,17 @@
   }
 }
 
+void LayerAnimationElement::OnAnimatorAttached(
+    LayerAnimationDelegate* delegate) {
+  if (animation_metrics_recorder_)
+    animation_metrics_recorder_->OnAnimatorAttached(delegate->GetFrameNumber());
+}
+
+void LayerAnimationElement::OnAnimatorDetached() {
+  if (animation_metrics_recorder_)
+    animation_metrics_recorder_->OnAnimatorDetached();
+}
+
 bool LayerAnimationElement::IsThreaded(LayerAnimationDelegate* delegate) const {
   return false;
 }
diff --git a/ui/compositor/layer_animation_element.h b/ui/compositor/layer_animation_element.h
index 431d118..016a98f 100644
--- a/ui/compositor/layer_animation_element.h
+++ b/ui/compositor/layer_animation_element.h
@@ -196,8 +196,14 @@
   // Assigns the target value to |target|.
   void GetTargetValue(TargetValue* target) const;
 
+  // Sets the reporter to report animation metrics if |reporter| is not null.
+  // Otherwise, cancels the metric reporting.
   void SetAnimationMetricsReporter(AnimationMetricsReporter* reporter);
 
+  // Called when the animator is attached to/detached from a Compositor.
+  void OnAnimatorAttached(LayerAnimationDelegate* delegate);
+  void OnAnimatorDetached();
+
   // The properties that the element modifies.
   AnimatableProperties properties() const { return properties_; }
 
diff --git a/ui/compositor/layer_animation_sequence.cc b/ui/compositor/layer_animation_sequence.cc
index 62edb9b..0699507 100644
--- a/ui/compositor/layer_animation_sequence.cc
+++ b/ui/compositor/layer_animation_sequence.cc
@@ -245,6 +245,17 @@
   }
 }
 
+void LayerAnimationSequence::OnAnimatorAttached(
+    LayerAnimationDelegate* delegate) {
+  for (auto& element : elements_)
+    element->OnAnimatorAttached(delegate);
+}
+
+void LayerAnimationSequence::OnAnimatorDetached() {
+  for (auto& element : elements_)
+    element->OnAnimatorDetached();
+}
+
 void LayerAnimationSequence::SetAnimationMetricsReporter(
     AnimationMetricsReporter* reporter) {
   animation_metrics_reporter_ = reporter;
diff --git a/ui/compositor/layer_animation_sequence.h b/ui/compositor/layer_animation_sequence.h
index a3cd39a..89f575d 100644
--- a/ui/compositor/layer_animation_sequence.h
+++ b/ui/compositor/layer_animation_sequence.h
@@ -127,6 +127,10 @@
   // Called when the animator is destroyed.
   void OnAnimatorDestroyed();
 
+  // Called when the animator is attached to/detached from a Compositor.
+  void OnAnimatorAttached(LayerAnimationDelegate* delegate);
+  void OnAnimatorDetached();
+
   // Sets |animation_metrics_reporter_| and passes it to all |elements_|.
   void SetAnimationMetricsReporter(AnimationMetricsReporter* reporter);
 
diff --git a/ui/compositor/layer_animator.cc b/ui/compositor/layer_animator.cc
index 9f2eb41..839d5f21 100644
--- a/ui/compositor/layer_animator.cc
+++ b/ui/compositor/layer_animator.cc
@@ -164,6 +164,9 @@
 
   DCHECK(delegate_->GetCcLayer());
   AttachLayerToAnimation(delegate_->GetCcLayer()->id());
+
+  for (auto& layer_animation_sequence : animation_queue_)
+    layer_animation_sequence->OnAnimatorAttached(delegate());
 }
 
 void LayerAnimator::DetachLayerAndTimeline(Compositor* compositor) {
@@ -174,6 +177,9 @@
 
   DetachLayerFromAnimation();
   timeline->DetachAnimation(animation_);
+
+  for (auto& layer_animation_sequence : animation_queue_)
+    layer_animation_sequence->OnAnimatorDetached();
 }
 
 void LayerAnimator::AttachLayerToAnimation(int layer_id) {
diff --git a/ui/compositor/layer_animator_unittest.cc b/ui/compositor/layer_animator_unittest.cc
index 0388f9a..bed3555 100644
--- a/ui/compositor/layer_animator_unittest.cc
+++ b/ui/compositor/layer_animator_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_mock_clock_override.h"
 #include "base/test/task_environment.h"
+#include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/mutator_host.h"
@@ -134,10 +135,21 @@
         property);
   }
 
+  void Wait() {
+    if (animations_completed_)
+      return;
+
+    base::RunLoop run_loop;
+    quit_wait_loop_ = run_loop.QuitClosure();
+    run_loop.Run();
+  }
+
  private:
   // ImplicitAnimationObserver implementation
   void OnImplicitAnimationsCompleted() override {
     animations_completed_ = true;
+    if (quit_wait_loop_)
+      std::move(quit_wait_loop_).Run();
   }
 
   bool RequiresNotificationWhenAnimatorDestroyed() const override {
@@ -147,6 +159,8 @@
   bool animations_completed_;
   bool notify_when_animator_destructed_;
 
+  base::OnceClosure quit_wait_loop_;
+
   DISALLOW_COPY_AND_ASSIGN(TestImplicitAnimationObserver);
 };
 
@@ -3394,6 +3408,9 @@
   TestLayerAnimationDelegate delegate;
   scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate));
 
+  // Simulates that Layer has attached to a Compositor.
+  delegate.SetFrameNumber(0);
+
   std::unique_ptr<ui::LayerAnimationElement> animation_element =
       ui::LayerAnimationElement::CreateColorElement(SK_ColorRED,
                                                     kAnimationDuration);
@@ -3436,6 +3453,9 @@
   TestLayerAnimationDelegate delegate;
   scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate));
 
+  // Simulates that Layer has attached to a Compositor.
+  delegate.SetFrameNumber(0);
+
   std::unique_ptr<ui::LayerAnimationElement> animation_element =
       ui::LayerAnimationElement::CreateColorElement(SK_ColorRED,
                                                     kAnimationDuration);
@@ -3467,6 +3487,72 @@
   EXPECT_EQ(reporter.value(), expected);
 }
 
+// Tests that metrics is reported correctly as not smooth when animation starts
+// before Layer is attached to a Compositor.
+TEST(LayerAnimatorTest,
+     ReportMetricsNotSmoothForAnimationStartsBeforeAttached) {
+  base::test::TaskEnvironment task_environment_(
+      base::test::TaskEnvironment::MainThreadType::UI);
+  const bool enable_pixel_output = false;
+  TestContextFactories context_factories(enable_pixel_output);
+  const gfx::Rect bounds(10, 10, 100, 100);
+  std::unique_ptr<TestCompositorHost> host(
+      TestCompositorHost::Create(bounds, context_factories.GetContextFactory(),
+                                 context_factories.GetContextFactoryPrivate()));
+  host->Show();
+
+  Compositor* compositor = host->GetCompositor();
+  Layer root;
+  compositor->SetRootLayer(&root);
+
+  constexpr base::TimeDelta kAnimationDuration =
+      base::TimeDelta::FromMilliseconds(50);
+
+  // Draw enough frames so that missing the start frame number would cause the
+  // reporter to always report 100% smoothness. 4 times of the expected
+  // animation frames because somehow the refresh rate changes from 60fps to
+  // 200fps when reporting.
+  const float frame_interval =
+      base::Time::kMillisecondsPerSecond / compositor->refresh_rate();
+  const int kStartFrameNumber =
+      static_cast<int>(kAnimationDuration.InMillisecondsF() / frame_interval) *
+      4;
+  while (compositor->activated_frame_count() < kStartFrameNumber) {
+    compositor->ScheduleFullRedraw();
+    EXPECT_TRUE(ui::WaitForNextFrameToBePresented(compositor));
+  }
+
+  Layer layer;
+  layer.SetBounds(gfx::Rect(0, 0, 5, 5));
+
+  TestImplicitAnimationObserver waiter(false);
+  TestMetricsReporter reporter;
+  {
+    // Starts an animation before |layer| is attached.
+    ScopedLayerAnimationSettings settings(layer.GetAnimator());
+    settings.SetAnimationMetricsReporter(&reporter);
+    settings.AddObserver(&waiter);
+    settings.SetTransitionDuration(kAnimationDuration);
+    layer.SetBounds(gfx::Rect(0, 0, 50, 50));
+  }
+
+  // Attaches |layer|.
+  root.Add(&layer);
+
+  // Blocks UI thread for kAnimationDuration to make animation not smooth.
+  base::TimeTicks start = base::TimeTicks::Now();
+  do {
+    base::PlatformThread::Sleep(kAnimationDuration);
+  } while (base::TimeTicks::Now() - start < kAnimationDuration);
+
+  // Waits for the animation to finish.
+  waiter.Wait();
+
+  // Metrics should be reported as not smooth.
+  EXPECT_TRUE(reporter.report_called());
+  EXPECT_LT(reporter.value(), 100);
+}
+
 class LayerOwnerAnimationObserver : public LayerAnimationObserver {
  public:
   explicit LayerOwnerAnimationObserver(LayerAnimator* animator)
diff --git a/ui/compositor/test/test_layer_animation_delegate.cc b/ui/compositor/test/test_layer_animation_delegate.cc
index 0be8b89..f924984 100644
--- a/ui/compositor/test/test_layer_animation_delegate.cc
+++ b/ui/compositor/test/test_layer_animation_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "ui/compositor/test/test_layer_animation_delegate.h"
 
+#include "base/optional.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/layer.h"
 
@@ -49,7 +50,8 @@
   last_property_change_reason_is_set_ = false;
 }
 
-void TestLayerAnimationDelegate::SetFrameNumber(int frame_number) {
+void TestLayerAnimationDelegate::SetFrameNumber(
+    base::Optional<int> frame_number) {
   frame_number_ = frame_number;
 }
 
@@ -187,7 +189,7 @@
   return &threaded_delegate_;
 }
 
-int TestLayerAnimationDelegate::GetFrameNumber() const {
+base::Optional<int> TestLayerAnimationDelegate::GetFrameNumber() const {
   return frame_number_;
 }
 
diff --git a/ui/compositor/test/test_layer_animation_delegate.h b/ui/compositor/test/test_layer_animation_delegate.h
index 6ed1469..b218d09 100644
--- a/ui/compositor/test/test_layer_animation_delegate.h
+++ b/ui/compositor/test/test_layer_animation_delegate.h
@@ -42,8 +42,10 @@
 
   // Sets the current frame number to be returned by GetFrameNumber. This can be
   // used to simulate receiving acks of frame submission, in order to test
-  // advancing of animations.
-  void SetFrameNumber(int frame_number);
+  // advancing of animations. It can also be used to simulate the availability
+  // of the frame number when Layer is attached to, or detached from a frame
+  // number source, i.e. Compositor.
+  void SetFrameNumber(base::Optional<int> frame_number);
 
   // Implementation of LayerAnimationDelegate
   void SetBoundsFromAnimation(const gfx::Rect& bounds,
@@ -80,7 +82,7 @@
   ui::Layer* GetLayer() override;
   cc::Layer* GetCcLayer() const override;
   LayerThreadedAnimationDelegate* GetThreadedAnimationDelegate() override;
-  int GetFrameNumber() const override;
+  base::Optional<int> GetFrameNumber() const override;
   float GetRefreshRate() const override;
 
  private:
@@ -102,7 +104,7 @@
   gfx::Rect clip_rect_;
   gfx::RoundedCornersF rounded_corners_;
   scoped_refptr<cc::Layer> cc_layer_;
-  int frame_number_ = 0;
+  base::Optional<int> frame_number_;
 
   // Allow copy and assign.
 };
diff --git a/ui/gfx/native_widget_types.h b/ui/gfx/native_widget_types.h
index 2fa243c..b222304 100644
--- a/ui/gfx/native_widget_types.h
+++ b/ui/gfx/native_widget_types.h
@@ -102,7 +102,7 @@
 #endif
 class SkBitmap;
 
-#if defined(USE_X11)
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 extern "C" {
 struct _AtkObject;
 typedef struct _AtkObject AtkObject;
@@ -203,16 +203,15 @@
 #elif defined(OS_MACOSX)
 typedef NSFont* NativeFont;
 typedef id NativeViewAccessible;
-#else  // Android, Linux, Chrome OS, etc.
+#elif defined(OS_LINUX) && !defined(OS_CHROMEOS)
 // Linux doesn't have a native font type.
-#if defined(USE_X11)
 typedef AtkObject* NativeViewAccessible;
 #else
+// Android, Chrome OS, etc.
 typedef struct _UnimplementedNativeViewAccessible
     UnimplementedNativeViewAccessible;
 typedef UnimplementedNativeViewAccessible* NativeViewAccessible;
 #endif
-#endif
 
 // A constant value to indicate that gfx::NativeCursor refers to no cursor.
 #if defined(USE_AURA)
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js
index 0708d532..6e43f129 100644
--- a/ui/login/display_manager.js
+++ b/ui/login/display_manager.js
@@ -418,8 +418,7 @@
           $('version-labels').hidden = !$('version-labels').hidden;
       } else if (name == ACCELERATOR_RESET) {
         if (currentStepId == SCREEN_OOBE_RESET) {
-          $('reset').send(
-              login.Screen.CALLBACK_USER_ACTED, USER_ACTION_ROLLBACK_TOGGLED);
+          $('reset').userActed(USER_ACTION_ROLLBACK_TOGGLED);
         } else if (attributes.resetAllowed ||
             RESET_AVAILABLE_SCREEN_GROUP.indexOf(currentStepId) != -1) {
           chrome.send('toggleResetScreen');
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 515bf92..aed8bfc 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -772,13 +772,6 @@
           "widget/desktop_aura/x11_topmost_window_finder.cc",
           "widget/desktop_aura/x11_whole_screen_move_loop.cc",
         ]
-        if (use_atk) {
-          sources += [
-            "accessibility/view_ax_platform_node_delegate_auralinux.cc",
-            "accessibility/view_ax_platform_node_delegate_auralinux.h",
-          ]
-          configs += [ "//build/config/linux/atk" ]
-        }
       } else if (is_win) {
         public += [ "widget/desktop_aura/desktop_window_tree_host_win.h" ]
         sources += [
@@ -816,6 +809,13 @@
             [ "widget/desktop_aura/desktop_window_tree_host_platform.cc" ]
         deps += [ "//ui/platform_window/extensions" ]
       }
+      if (use_atk) {
+        sources += [
+          "accessibility/view_ax_platform_node_delegate_auralinux.cc",
+          "accessibility/view_ax_platform_node_delegate_auralinux.h",
+        ]
+        configs += [ "//build/config/linux/atk" ]
+      }
     }
   }
 
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index 5ab7e83..cb4df9b 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -186,7 +186,7 @@
       HighlightPathGenerator::GetRoundRectForView(this);
   if (!clipping_data)
     return false;
-  ink_drop_layer->SetClipRect(gfx::ToNearestRect(clipping_data->bounds));
+  ink_drop_layer->SetClipRect(gfx::ToEnclosingRect(clipping_data->bounds));
   ink_drop_layer->SetRoundedCornerRadius(
       gfx::RoundedCornersF(clipping_data->corner_radius));
   ink_drop_layer->SetIsFastRoundedCorner(true);
diff --git a/ui/views/controls/highlight_path_generator.cc b/ui/views/controls/highlight_path_generator.cc
index 5179a4d..cca14ec 100644
--- a/ui/views/controls/highlight_path_generator.cc
+++ b/ui/views/controls/highlight_path_generator.cc
@@ -42,8 +42,8 @@
 
   const float corner_radius = round_rect->corner_radius;
   return SkPath().addRoundRect(
-      gfx::RectToSkRect(gfx::ToNearestRect(round_rect->bounds)), corner_radius,
-      corner_radius);
+      gfx::RectToSkRect(gfx::ToEnclosingRect(round_rect->bounds)),
+      corner_radius, corner_radius);
 }
 
 base::Optional<HighlightPathGenerator::RoundRect>
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index 7494e52..c8a0139e 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -197,19 +197,22 @@
       <div id="rowsContainer">
         <div class="row">
           <cr-button class="digit-button" on-tap="onNumberTap_" value="1"
-              id="digitButton1" disabled="[[disabled]]" circle-ripple>
+              id="digitButton1" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard1')]]</inner-text>
             <inner-text class="letter empty"
                 hidden="[[!enableLetters]]">ABC</inner-text>
           </cr-button>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="2"
-              id="digitButton2" disabled="[[disabled]]" circle-ripple>
+              id="digitButton2" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard2')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">ABC</inner-text>
           </cr-button>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="3"
-              id="digitButton3" disabled="[[disabled]]" circle-ripple>
+              id="digitButton3" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard3')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">DEF</inner-text>
@@ -217,19 +220,22 @@
         </div>
         <div class="row">
           <cr-button class="digit-button" on-tap="onNumberTap_" value="4"
-              id="digitButton4" disabled="[[disabled]]" circle-ripple>
+              id="digitButton4" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard4')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">GHI</inner-text>
           </cr-button>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="5"
-              id="digitButton5" disabled="[[disabled]]" circle-ripple>
+              id="digitButton5" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard5')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">JKL</inner-text>
           </cr-button>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="6"
-              id="digitButton6" disabled="[[disabled]]" circle-ripple>
+              id="digitButton6" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard6')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">MNO</inner-text>
@@ -237,19 +243,22 @@
         </div>
         <div class="row">
           <cr-button class="digit-button" on-tap="onNumberTap_" value="7"
-              id="digitButton7" disabled="[[disabled]]" circle-ripple>
+              id="digitButton7" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard7')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">PQRS</inner-text>
           </cr-button>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="8"
-              id="digitButton8" disabled="[[disabled]]" circle-ripple>
+              id="digitButton8" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard8')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">TUV</inner-text>
           </cr-button>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="9"
-              id="digitButton9" disabled="[[disabled]]" circle-ripple>
+              id="digitButton9" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard9')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">WXYZ</inner-text>
@@ -258,7 +267,8 @@
         <div class="row bottom-row">
           <div class="digit-button"></div>
           <cr-button class="digit-button" on-tap="onNumberTap_" value="0"
-              id="digitButton0" disabled="[[disabled]]" circle-ripple>
+              id="digitButton0" disabled="[[disabled]]" circle-ripple
+              custom-tab-index="-1">
             <inner-text class="number">[[i18n('pinKeyboard0')]]</inner-text>
             <inner-text class="letter"
                 hidden="[[!enableLetters]]">+</inner-text>
@@ -271,7 +281,8 @@
               on-pointerout="clearAndReset_"
               on-pointerup="onBackspacePointerUp_"
               on-contextmenu="onBackspaceContextMenu_"
-              title="[[i18n('pinKeyboardDeleteAccessibleName')]]">
+              title="[[i18n('pinKeyboardDeleteAccessibleName')]]"
+              custom-tab-index="-1">
           </cr-icon-button>
         </div>
       </div>
diff --git a/ui/webui/resources/cr_elements/cr_button/cr_button.js b/ui/webui/resources/cr_elements/cr_button/cr_button.js
index 12df8ec..6ec5d722 100644
--- a/ui/webui/resources/cr_elements/cr_button/cr_button.js
+++ b/ui/webui/resources/cr_elements/cr_button/cr_button.js
@@ -23,6 +23,14 @@
     },
 
     /**
+     * Use this property in order to configure the "tabindex" attribute.
+     */
+    customTabIndex: {
+      type: Number,
+      observer: 'applyTabIndex_',
+    },
+
+    /**
      * Flag used for formatting ripples on circle shaped cr-buttons.
      * @private
      */
@@ -79,7 +87,7 @@
 
   /**
    * @param {boolean} newValue
-   * @param {boolean} oldValue
+   * @param {boolean|undefined} oldValue
    * @private
    */
   disabledChanged_(newValue, oldValue) {
@@ -90,7 +98,19 @@
       this.blur();
     }
     this.setAttribute('aria-disabled', Boolean(this.disabled));
-    this.setAttribute('tabindex', this.disabled ? -1 : 0);
+    this.applyTabIndex_();
+  },
+
+  /**
+   * Updates the tabindex HTML attribute to the actual value.
+   * @private
+   */
+  applyTabIndex_() {
+    let value = this.customTabIndex;
+    if (value === undefined) {
+      value = this.disabled ? -1 : 0;
+    }
+    this.setAttribute('tabindex', value);
   },
 
   /**
diff --git a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
index df323de1..e8ae600 100644
--- a/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
+++ b/ui/webui/resources/cr_elements/cr_icon_button/cr_icon_button.js
@@ -60,6 +60,14 @@
       observer: 'disabledChanged_',
     },
 
+    /**
+     * Use this property in order to configure the "tabindex" attribute.
+     */
+    customTabIndex: {
+      type: Number,
+      observer: 'applyTabIndex_',
+    },
+
     ironIcon: {
       type: String,
       observer: 'onIronIconChanged_',
@@ -120,7 +128,19 @@
       this.blur();
     }
     this.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
-    this.setAttribute('tabindex', this.disabled ? '-1' : '0');
+    this.applyTabIndex_();
+  },
+
+  /**
+   * Updates the tabindex HTML attribute to the actual value.
+   * @private
+   */
+  applyTabIndex_() {
+    let value = this.customTabIndex;
+    if (value === undefined) {
+      value = this.disabled ? -1 : 0;
+    }
+    this.setAttribute('tabindex', value);
   },
 
   /**