diff --git a/DEPS b/DEPS
index c310e5c..a954c518 100644
--- a/DEPS
+++ b/DEPS
@@ -195,7 +195,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '61003cde76882c43ab593a5fd889fc90b7d10abf',
+  'skia_revision': 'aa64c352b349a779e98d903eda594dd0ce502736',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -211,11 +211,11 @@
   # 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': '0aca3ca9427d1d2aa8d8e706de62360c61af3598',
+  'swiftshader_revision': '139f5c351e1ff62e465526d4d2f40d3b08a27169',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'daef088d069f8d49d8ba668f475dd21ca0e5c087',
+  'pdfium_revision': 'e21911cc1c77d39dbc51001845bbfce2783e6514',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -258,7 +258,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': 'e9879d106b95b2c9f0b0fd6efe8f1e5186f64ada',
+  'catapult_revision': 'cd2eebd327e35c839149f7a4d888b046d628df12',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -266,7 +266,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': '5a23b943626939e5bcc6c1dfddbbebd2c531e151',
+  'devtools_frontend_revision': '46abe35c6554f1534495b880031b5aa72df4b60e',
   # 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.
@@ -318,7 +318,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': '793a07e366362f2e3cdf07e144c1eff8de18efba',
+  'dawn_revision': '1c25198384e7ecfebbd3ac84203d3b1d46a0e228',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -901,7 +901,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '80d095c4dc414872855f79e4a66995c988912891',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b073999c6f90103a36a923e63ae8cf7a5c9c6c8c',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1332,7 +1332,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': '1wcgrsmCBOkqmqgVO1zI-z2_mk9oLiVRGlx-INget24C'
+              'version': 'PL87Lj_q7GOEzYJ2eJIJAzMtQbuLWVnmjDQPqfu2O64C'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1486,7 +1486,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '294729f33cbfac484a4b77c29dd42f03ffe65fa2',
+    Var('webrtc_git') + '/src.git' + '@' + '3326535126e435f1ba647885ce43a8f0f3d317eb',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1558,7 +1558,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6dc612511e7b53778e59f30b96f71f1af0ab7ca4',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@bba04cfcf31213de56b43886680cabc65635b69a',
     'condition': 'checkout_src_internal',
   },
 
@@ -1566,7 +1566,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'P-2JwDZls8V1LNOlmHoxgPlVs68icpTYuJyIwCQQ-okC',
+        'version': 'IzAbSag8-IGOAkL2GSMql94a4unzsZtFfkhskHgLSCkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1577,7 +1577,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'TGyqQ0i-VwfJWUWkmWGzsrQ0c-MmVg_nP2-LKMWnCQYC',
+        'version': 'cT9lCWDvv6veb3QkbQk0BRcglTeLjCZ2W35O2YG3ez0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/accessibility/accessibility_controller_impl.cc b/ash/accessibility/accessibility_controller_impl.cc
index fd513d0..b5ae43b7 100644
--- a/ash/accessibility/accessibility_controller_impl.cc
+++ b/ash/accessibility/accessibility_controller_impl.cc
@@ -1060,18 +1060,20 @@
   switch_access_bubble_controller_->ShowMenu(anchor, actions_to_show);
 }
 
+void AccessibilityControllerImpl::StartPointScan() {
+  if (::switches::IsSwitchAccessPointScanningEnabled()) {
+    if (!point_scan_controller_)
+      point_scan_controller_.reset(new PointScanController());
+
+    point_scan_controller_->Start();
+  }
+}
+
 void AccessibilityControllerImpl::
     DisablePolicyRecommendationRestorerForTesting() {
   Shell::Get()->policy_recommendation_restorer()->DisableForTesting();
 }
 
-void AccessibilityControllerImpl::StartPointScanning() {
-  if (!point_scan_controller_)
-    point_scan_controller_.reset(new PointScanController());
-
-  point_scan_controller_->Start();
-}
-
 bool AccessibilityControllerImpl::IsStickyKeysSettingVisibleInTray() {
   return sticky_keys().IsVisibleInTray();
 }
@@ -1724,8 +1726,6 @@
   switch_access_bubble_controller_ =
       std::make_unique<SwitchAccessMenuBubbleController>();
   UpdateKeyCodesAfterSwitchAccessEnabled();
-  if (::switches::IsSwitchAccessPointScanningEnabled())
-    StartPointScanning();
   if (skip_switch_access_notification_) {
     skip_switch_access_notification_ = false;
     return;
diff --git a/ash/accessibility/accessibility_controller_impl.h b/ash/accessibility/accessibility_controller_impl.h
index 609d3fee..214427b6 100644
--- a/ash/accessibility/accessibility_controller_impl.h
+++ b/ash/accessibility/accessibility_controller_impl.h
@@ -351,6 +351,7 @@
   void ShowSwitchAccessBackButton(const gfx::Rect& anchor) override;
   void ShowSwitchAccessMenu(const gfx::Rect& anchor,
                             std::vector<std::string> actions_to_show) override;
+  void StartPointScan() override;
   void SetDictationActive(bool is_active) override;
   void ToggleDictationFromSource(DictationToggleSource source) override;
   void HandleAutoclickScrollableBoundsFound(
diff --git a/ash/app_list/views/privacy_info_view.cc b/ash/app_list/views/privacy_info_view.cc
index ddab3e9..e18b284 100644
--- a/ash/app_list/views/privacy_info_view.cc
+++ b/ash/app_list/views/privacy_info_view.cc
@@ -43,22 +43,26 @@
 constexpr int kCellSpacingDip = 18;
 constexpr int kIconSizeDip = 20;
 
-// Link view used inside the privacy notice.
-class PrivacyLinkView : public views::Link {
+// Text view used inside the privacy notice.
+class PrivacyTextView : public views::StyledLabel {
  public:
-  explicit PrivacyLinkView(const base::string16& title) : Link(title) {}
+  explicit PrivacyTextView(PrivacyInfoView* privacy_view)
+      : StyledLabel(), privacy_view_(privacy_view) {}
 
   // views::View:
   bool HandleAccessibleAction(const ui::AXActionData& action_data) override {
     switch (action_data.action) {
-      case ax::mojom::Action::kFocus:
-        // Do nothing because the search box needs to keep focus.
+      case ax::mojom::Action::kDoDefault:
+        privacy_view_->LinkClicked();
         return true;
       default:
         break;
     }
-    return views::Link::HandleAccessibleAction(action_data);
+    return views::StyledLabel::HandleAccessibleAction(action_data);
   }
+
+ private:
+  PrivacyInfoView* const privacy_view_;  // Not owned.
 };
 
 }  // namespace
@@ -97,7 +101,9 @@
   if (selected_action_ == Action::kCloseButton) {
     cc::PaintFlags flags;
     flags.setAntiAlias(true);
-    flags.setColor(SkColorSetA(gfx::kGoogleGrey900, 0x14));
+    flags.setColor(SkColorSetA(
+        AppListColorProvider::Get()->GetSearchResultViewHighlightColor(),
+        0x14));
     flags.setStyle(cc::PaintFlags::kFill_Style);
     canvas->DrawCircle(close_button_->bounds().CenterPoint(),
                        close_button_->width() / 2, flags);
@@ -147,7 +153,6 @@
         CloseButtonPressed();
         break;
       case Action::kNone:
-      case Action::kDefault:
         break;
     }
   }
@@ -155,14 +160,8 @@
 
 void PrivacyInfoView::SelectInitialResultAction(bool reverse_tab_order) {
   if (!reverse_tab_order) {
-    if (is_default_result()) {
-      // Hold the selection but do nothing. This is so that the text view is not
-      // selected immediately after the launcher opens.
-      selected_action_ = Action::kDefault;
-    } else {
-      selected_action_ = Action::kTextLink;
-      text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
-    }
+    selected_action_ = Action::kTextLink;
+    text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
   } else {
     selected_action_ = Action::kCloseButton;
     close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
@@ -174,48 +173,20 @@
 }
 
 bool PrivacyInfoView::SelectNextResultAction(bool reverse_tab_order) {
-  // There are three selection elements: default -> text view -> close button.
-  // The default selection is not traversed if selection is caused by user
-  // action.
   bool action_changed = false;
 
-  if (!reverse_tab_order) {
-    switch (selected_action_) {
-      case Action::kDefault:
-        // Move selection forward from default to the text view.
-        selected_action_ = Action::kTextLink;
-        text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
-                                             true);
-        action_changed = true;
-        break;
-      case Action::kTextLink:
-        // Move selection forward from the text view to the close button.
-        selected_action_ = Action::kCloseButton;
-        close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
-                                                true);
-        action_changed = true;
-        break;
-      case Action::kNone:
-      case Action::kCloseButton:
-        break;
-    }
+  // There are two traversal elements, the text view and close button.
+  if (!reverse_tab_order && selected_action_ == Action::kTextLink) {
+    // Move selection forward from the text view to the close button.
+    selected_action_ = Action::kCloseButton;
+    close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+    action_changed = true;
+  } else if (reverse_tab_order && selected_action_ == Action::kCloseButton) {
+    // Move selection backward from the close button to the text view.
+    selected_action_ = Action::kTextLink;
+    text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+    action_changed = true;
   } else {
-    switch (selected_action_) {
-      case Action::kCloseButton:
-        // Move selection backward from the close button to the text view.
-        selected_action_ = Action::kTextLink;
-        text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
-                                             true);
-        action_changed = true;
-        break;
-      case Action::kNone:
-      case Action::kDefault:
-      case Action::kTextLink:
-        break;
-    }
-  }
-
-  if (!action_changed) {
     selected_action_ = Action::kNone;
   }
 
@@ -226,9 +197,17 @@
 }
 
 void PrivacyInfoView::NotifyA11yResultSelected() {
-  // Do not notify when this view is selected by default. Notifications for the
-  // child views are handled by SelectInitialResultAction and
-  // SelectNextResultAction.
+  switch (selected_action_) {
+    case Action::kTextLink:
+      text_view_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
+      break;
+    case Action::kCloseButton:
+      close_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
+                                              true);
+      break;
+    case Action::kNone:
+      break;
+  }
 }
 
 void PrivacyInfoView::ButtonPressed(views::Button* sender,
@@ -277,13 +256,12 @@
   size_t offset;
   const base::string16 text =
       l10n_util::GetStringFUTF16(info_string_id_, link, &offset);
-  text_view_ = AddChildView(std::make_unique<views::StyledLabel>());
+  text_view_ = AddChildView(std::make_unique<PrivacyTextView>(this));
   text_view_->SetText(text);
   text_view_->SetAutoColorReadabilityEnabled(false);
-  // Assign a container role to the text view so that ChromeVox focuses on
-  // the inner text elements individually.
-  text_view_->GetViewAccessibility().OverrideRole(
-      ax::mojom::Role::kGenericContainer);
+  text_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
+  // Make the whole text view behave as a link for accessibility.
+  text_view_->GetViewAccessibility().OverrideRole(ax::mojom::Role::kLink);
 
   views::StyledLabel::RangeStyleInfo style;
   style.override_color = AppListColorProvider::Get()->GetSearchBoxTextColor();
@@ -294,7 +272,7 @@
   // manually because default focus handling remains on the search box.
   views::StyledLabel::RangeStyleInfo link_style;
   link_style.disable_line_wrapping = true;
-  auto custom_view = std::make_unique<PrivacyLinkView>(link);
+  auto custom_view = std::make_unique<views::Link>(link);
   custom_view->set_callback(base::BindRepeating(&PrivacyInfoView::LinkClicked,
                                                 base::Unretained(this)));
   custom_view->SetEnabledColor(gfx::kGoogleBlue700);
diff --git a/ash/app_list/views/privacy_info_view.h b/ash/app_list/views/privacy_info_view.h
index 79f305d..49c814a9 100644
--- a/ash/app_list/views/privacy_info_view.h
+++ b/ash/app_list/views/privacy_info_view.h
@@ -50,7 +50,7 @@
   PrivacyInfoView(int info_string_id, int link_string_id);
 
  private:
-  enum class Action { kNone, kDefault, kTextLink, kCloseButton };
+  enum class Action { kNone, kTextLink, kCloseButton };
 
   void InitLayout();
   void InitInfoIcon();
diff --git a/ash/app_list/views/search_box_view_unittest.cc b/ash/app_list/views/search_box_view_unittest.cc
index 488fa18..75f8e5b 100644
--- a/ash/app_list/views/search_box_view_unittest.cc
+++ b/ash/app_list/views/search_box_view_unittest.cc
@@ -667,11 +667,7 @@
   EXPECT_TRUE(selection->is_default_result());
   EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
 
-  // The privacy view should have two additional non-default actions.
-  KeyPress(ui::VKEY_TAB);
-  selection = selection_controller->selected_result();
-  EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
-
+  // The privacy view should have one additional action.
   KeyPress(ui::VKEY_TAB);
   selection = selection_controller->selected_result();
   EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
@@ -681,26 +677,7 @@
   ASSERT_TRUE(selection->result());
   EXPECT_EQ(selection->result()->title(), base::ASCIIToUTF16("test"));
 
-  // Move focus forward to the close button and then the privacy view again.
-  // The privacy view should now have only two actions.
-  KeyPress(ui::VKEY_TAB);
-  selection = selection_controller->selected_result();
-  EXPECT_FALSE(selection);
-
-  KeyPress(ui::VKEY_TAB);
-  selection = selection_controller->selected_result();
-  EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
-
-  KeyPress(ui::VKEY_TAB);
-  selection = selection_controller->selected_result();
-  EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
-
-  KeyPress(ui::VKEY_TAB);
-  selection = selection_controller->selected_result();
-  ASSERT_TRUE(selection->result());
-  EXPECT_EQ(selection->result()->title(), base::ASCIIToUTF16("test"));
-
-  // When navigating backwards, the privacy notice should have two actions.
+  // The privacy notice should also have two actions when navigating backwards.
   KeyPress(ui::VKEY_TAB, /*is_shift_down=*/true);
   selection = selection_controller->selected_result();
   EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
@@ -740,7 +717,6 @@
   // Navigate to the close button and press enter. The privacy info should no
   // longer be shown.
   KeyPress(ui::VKEY_TAB);
-  KeyPress(ui::VKEY_TAB);
   KeyPress(ui::VKEY_RETURN);
   EXPECT_FALSE(view_delegate()->ShouldShowAssistantPrivacyInfo());
 }
@@ -769,13 +745,13 @@
       selection_controller->selected_result();
   EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
 
-  // The privacy view should have two additional actions. Tab to the next
-  // privacy view action.
+  // The privacy view should have one additional action. Tab to the next privacy
+  // view action.
   KeyPress(ui::VKEY_TAB);
   selection = selection_controller->selected_result();
   EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
 
-  // Create a new search result. The privacy view should only have one action
+  // Create a new search result. The privacy view should have no actions
   // remaining.
   CreateSearchResult(ash::SearchResultDisplayType::kList, 0.5,
                      base::ASCIIToUTF16("testing"), base::string16());
@@ -783,10 +759,6 @@
 
   KeyPress(ui::VKEY_TAB);
   selection = selection_controller->selected_result();
-  EXPECT_EQ(selection, privacy_container_view->GetResultViewAt(0));
-
-  KeyPress(ui::VKEY_TAB);
-  selection = selection_controller->selected_result();
   ASSERT_TRUE(selection);
   EXPECT_EQ(selection->result()->title(), base::ASCIIToUTF16("test"));
 }
diff --git a/ash/capture_mode/capture_label_view.cc b/ash/capture_mode/capture_label_view.cc
index e35aef1..0c7d776 100644
--- a/ash/capture_mode/capture_label_view.cc
+++ b/ash/capture_mode/capture_label_view.cc
@@ -13,6 +13,7 @@
 #include "base/i18n/number_formatting.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/text_constants.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
@@ -52,6 +53,14 @@
   label_button_->SetPaintToLayer();
   label_button_->layer()->SetFillsBoundsOpaquely(false);
   label_button_->SetEnabledTextColors(text_color);
+  label_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  label_button_->SetNotifyEnterExitOnChild(true);
+
+  label_button_->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  const auto ripple_attributes =
+      color_provider->GetRippleAttributes(background_color);
+  label_button_->SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  label_button_->SetInkDropBaseColor(ripple_attributes.base_color);
 
   label_ = AddChildView(std::make_unique<views::Label>(base::string16()));
   label_->SetPaintToLayer();
@@ -157,9 +166,7 @@
 }
 
 void CaptureLabelView::Layout() {
-  gfx::Rect label_button_bounds = GetLocalBounds();
-  label_button_bounds.ClampToCenteredSize(label_button_->GetPreferredSize());
-  label_button_->SetBoundsRect(label_button_bounds);
+  label_button_->SetBoundsRect(GetLocalBounds());
 
   gfx::Rect label_bounds = GetLocalBounds();
   label_bounds.ClampToCenteredSize(label_->GetPreferredSize());
@@ -203,4 +210,7 @@
   label_->SetText(base::FormatNumber(timeout_count_down_--));
 }
 
+BEGIN_METADATA(CaptureLabelView, views::View)
+END_METADATA
+
 }  // namespace ash
diff --git a/ash/capture_mode/capture_label_view.h b/ash/capture_mode/capture_label_view.h
index 7c10ae0..9898ab4 100644
--- a/ash/capture_mode/capture_label_view.h
+++ b/ash/capture_mode/capture_label_view.h
@@ -24,6 +24,8 @@
 class ASH_EXPORT CaptureLabelView : public views::View,
                                     public views::ButtonListener {
  public:
+  METADATA_HEADER(CaptureLabelView);
+
   explicit CaptureLabelView(CaptureModeSession* capture_mode_session);
   CaptureLabelView(const CaptureLabelView&) = delete;
   CaptureLabelView& operator=(const CaptureLabelView&) = delete;
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index d7bfedf..a61b0fd 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -71,6 +71,11 @@
     6,
     SkColorSetARGB(38, 0, 0, 0));
 
+// The minimum padding on each side of the capture region. If the capture button
+// cannot be placed in the center of the capture region and maintain this
+// padding, it will be placed below or above the capture region.
+constexpr int kCaptureRegionMinimumPaddingDp = 16;
+
 // Mouse cursor warping is disabled when the capture source is a custom region.
 // Sets the mouse warp status to |enable| and return the original value.
 bool SetMouseWarpEnabled(bool enable) {
@@ -137,8 +142,9 @@
 views::Widget::InitParams CreateWidgetParams(aura::Window* parent,
                                              const gfx::Rect& bounds,
                                              const std::string& name) {
-  views::Widget::InitParams params(
-      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+  // Use a popup widget to get transient properties, such as not needing to
+  // click on the widget first to get capture before receiving events.
+  views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
   params.parent = parent;
@@ -363,8 +369,8 @@
     return;
 
   gfx::Point location = event->location();
-  aura::Window* source = static_cast<aura::Window*>(event->target());
-  aura::Window::ConvertPointToTarget(source, current_root_, &location);
+  aura::Window* event_target = static_cast<aura::Window*>(event->target());
+  aura::Window::ConvertPointToTarget(event_target, current_root_, &location);
   const bool is_event_on_capture_bar =
       CaptureModeBarView::GetBounds(current_root_).Contains(location);
 
@@ -381,7 +387,7 @@
       case ui::ET_TOUCH_PRESSED:
       case ui::ET_TOUCH_MOVED: {
         gfx::Point screen_location(event->location());
-        ::wm::ConvertPointToScreen(source, &screen_location);
+        ::wm::ConvertPointToScreen(event_target, &screen_location);
         capture_window_observer_->UpdateSelectedWindowAtPosition(
             screen_location);
         break;
@@ -398,7 +404,7 @@
   }
 
   // Let the capture button handle any events it can handle first.
-  if (ShouldCaptureLabelHandleEvent(location))
+  if (ShouldCaptureLabelHandleEvent(event_target))
     return;
 
   // Allow events that are located on the capture mode bar to pass through so we
@@ -687,24 +693,57 @@
   // For fullscreen and window capture mode, the capture label is placed in the
   // middle of the screen. For region capture mode, if it's in select phase, the
   // capture label is also placed in the middle of the screen, and if it's in
-  // fine tune phase, the capture label is placed in middle of the capture
-  // region.
+  // fine tune phase, the capture label is ideally placed in the middle of the
+  // capture region. If it cannot fit, then it will be placed slightly above or
+  // below the capture region.
   gfx::Rect bounds(current_root_->bounds());
   const gfx::Rect capture_region = controller_->user_capture_region();
+  const gfx::Size preferred_size =
+      capture_label_widget_->GetContentsView()->GetPreferredSize();
   if (controller_->source() == CaptureModeSource::kRegion &&
       !is_selecting_region_ && !capture_region.IsEmpty()) {
     bounds = capture_region;
+
+    // The capture region must be at least the size of |preferred_size| plus
+    // some padding for the capture label to be centered inside it.
+    gfx::Size capture_region_min_size = preferred_size;
+    capture_region_min_size.Enlarge(kCaptureRegionMinimumPaddingDp,
+                                    kCaptureRegionMinimumPaddingDp);
+    if (bounds.width() > capture_region_min_size.width() &&
+        bounds.height() > capture_region_min_size.height()) {
+      bounds.ClampToCenteredSize(preferred_size);
+    } else {
+      // The capture region is too small for the capture label to be inside it.
+      // Align |bounds| so that its horizontal centerpoint aligns with the
+      // capture regions centerpoint.
+      bounds.set_size(preferred_size);
+      bounds.set_x(capture_region.CenterPoint().x() -
+                   preferred_size.width() / 2);
+
+      // Try to put the capture label slightly below the capture region. If it
+      // does not fully fit in the root window bounds, place the capture label
+      // slightly above.
+      const int under_region_label_y =
+          capture_region.bottom() + kCaptureButtonDistanceFromRegionDp;
+      if (under_region_label_y + preferred_size.height() <
+          current_root_->bounds().bottom()) {
+        bounds.set_y(under_region_label_y);
+      } else {
+        bounds.set_y(capture_region.y() - kCaptureButtonDistanceFromRegionDp -
+                     preferred_size.height());
+      }
+    }
+  } else {
+    bounds.ClampToCenteredSize(preferred_size);
   }
-  bounds.ClampToCenteredSize(
-      capture_label_widget_->GetContentsView()->GetPreferredSize());
+
   capture_label_widget_->SetBounds(bounds);
 }
 
 bool CaptureModeSession::ShouldCaptureLabelHandleEvent(
-    const gfx::Point& location_in_root) {
+    aura::Window* event_target) {
   if (!capture_label_widget_ ||
-      !capture_label_widget_->GetNativeWindow()->bounds().Contains(
-          location_in_root)) {
+      capture_label_widget_->GetNativeWindow() != event_target) {
     return false;
   }
 
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h
index ca47e45..9ef22bd 100644
--- a/ash/capture_mode/capture_mode_session.h
+++ b/ash/capture_mode/capture_mode_session.h
@@ -48,6 +48,10 @@
   // The vertical distance from the size label to the custom capture region.
   static constexpr int kSizeLabelYDistanceFromRegionDp = 8;
 
+  // The vertical distance of the capture button from the capture region, if the
+  // capture button does not fit inside the capture region.
+  static constexpr int kCaptureButtonDistanceFromRegionDp = 24;
+
   aura::Window* current_root() const { return current_root_; }
   CaptureModeBarView* capture_mode_bar_view() const {
     return capture_mode_bar_view_;
@@ -82,6 +86,10 @@
   void OnTabletModeStarted() override;
   void OnTabletModeEnded() override;
 
+  views::Widget* capture_label_widget_for_testing() const {
+    return capture_label_widget_.get();
+  }
+
  private:
   // Gets the bounds of current window selected for |kWindow| capture source.
   gfx::Rect GetSelectedWindowBounds() const;
@@ -132,8 +140,12 @@
   // Updates the capture label widget.
   void UpdateCaptureLabelWidget();
   void UpdateCaptureLabelWidgetBounds();
-  // Returns true if the capture label should handle the event.
-  bool ShouldCaptureLabelHandleEvent(const gfx::Point& location_in_root);
+
+  // Returns true if the capture label should handle the event. |event_target|
+  // is the window which is receiving the event. The capture label should handle
+  // the event if its associated window is |event_target| and its capture button
+  // child is visible.
+  bool ShouldCaptureLabelHandleEvent(aura::Window* event_target);
 
   CaptureModeController* const controller_;
 
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 5f11ce50..39c1cad 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -536,6 +536,44 @@
   EXPECT_EQ(nullptr, GetDimensionsLabelWindow());
 }
 
+TEST_F(CaptureModeTest, CaptureRegionCaptureButtonLocation) {
+  UpdateDisplay("800x800");
+
+  auto* controller = StartImageRegionCapture();
+
+  // Select a large region. Verify that the capture button widget is centered.
+  SelectRegion(gfx::Rect(100, 100, 600, 600));
+
+  views::Widget* capture_button_widget =
+      controller->capture_mode_session()->capture_label_widget_for_testing();
+  ASSERT_TRUE(capture_button_widget);
+  aura::Window* capture_button_window =
+      capture_button_widget->GetNativeWindow();
+  EXPECT_EQ(gfx::Point(400, 400),
+            capture_button_window->bounds().CenterPoint());
+
+  // Drag the bottom corner so that the region is too small to fit the capture
+  // button. Verify that the button is aligned horizontally and placed below the
+  // region.
+  auto* event_generator = GetEventGenerator();
+  event_generator->DragMouseTo(gfx::Point(120, 120));
+  EXPECT_EQ(gfx::Rect(100, 100, 20, 20), controller->user_capture_region());
+  EXPECT_EQ(110, capture_button_window->bounds().CenterPoint().x());
+  const int distance_from_region =
+      CaptureModeSession::kCaptureButtonDistanceFromRegionDp;
+  EXPECT_EQ(120 + distance_from_region, capture_button_window->bounds().y());
+
+  // Click inside the region to drag the entire region to the bottom of the
+  // screen. Verify that the button is aligned horizontally and placed above the
+  // region.
+  event_generator->set_current_screen_location(gfx::Point(110, 110));
+  event_generator->DragMouseTo(gfx::Point(110, 790));
+  EXPECT_EQ(gfx::Rect(100, 780, 20, 20), controller->user_capture_region());
+  EXPECT_EQ(110, capture_button_window->bounds().CenterPoint().x());
+  EXPECT_EQ(780 - distance_from_region,
+            capture_button_window->bounds().bottom());
+}
+
 TEST_F(CaptureModeTest, WindowCapture) {
   // Create 2 windows that overlap with each other.
   const gfx::Rect bounds1(0, 0, 200, 200);
diff --git a/ash/public/cpp/accessibility_controller.h b/ash/public/cpp/accessibility_controller.h
index 9c08f80..1cd380a2 100644
--- a/ash/public/cpp/accessibility_controller.h
+++ b/ash/public/cpp/accessibility_controller.h
@@ -80,6 +80,9 @@
       const gfx::Rect& bounds,
       std::vector<std::string> actions_to_show) = 0;
 
+  // Start Switch Access point scanning
+  virtual void StartPointScan() = 0;
+
   // Set whether dictation is active.
   virtual void SetDictationActive(bool is_active) = 0;
 
diff --git a/ash/public/cpp/caption_buttons/frame_size_button.cc b/ash/public/cpp/caption_buttons/frame_size_button.cc
index 5c863e7..e67f494 100644
--- a/ash/public/cpp/caption_buttons/frame_size_button.cc
+++ b/ash/public/cpp/caption_buttons/frame_size_button.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "ash/public/cpp/caption_buttons/snap_controller.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/i18n/rtl.h"
 #include "base/metrics/user_metrics.h"
 #include "chromeos/ui/base/window_properties.h"
@@ -91,7 +90,7 @@
     DCHECK_EQ(window_, window);
     if ((key == chromeos::kIsShowingInOverviewKey &&
          window_->GetProperty(chromeos::kIsShowingInOverviewKey)) ||
-        key == kWindowStateTypeKey) {
+        key == chromeos::kWindowStateTypeKey) {
       // If the window is put in overview while we're in waiting-for-snapping
       // mode, or the window's window state has changed, cancel the snap.
       size_button_->CancelSnap();
diff --git a/ash/public/cpp/default_frame_header.cc b/ash/public/cpp/default_frame_header.cc
index 440cce0..b205f83f 100644
--- a/ash/public/cpp/default_frame_header.cc
+++ b/ash/public/cpp/default_frame_header.cc
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/logging.h"  // DCHECK
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "third_party/skia/include/core/SkPath.h"
 #include "ui/gfx/canvas.h"
@@ -106,8 +107,8 @@
 // DefaultFrameHeader, protected:
 
 void DefaultFrameHeader::DoPaintHeader(gfx::Canvas* canvas) {
-  int corner_radius = IsNormalWindowStateType(
-                          GetTargetWindow()->GetProperty(kWindowStateTypeKey))
+  int corner_radius = IsNormalWindowStateType(GetTargetWindow()->GetProperty(
+                          chromeos::kWindowStateTypeKey))
                           ? kTopCornerRadiusWhenRestored
                           : 0;
 
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index 84fd5fca..f1b6092d 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -84,9 +84,6 @@
                              kWindowPinTypeKey,
                              WindowPinType::kNone)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowPositionManagedTypeKey, false)
-DEFINE_UI_CLASS_PROPERTY_KEY(chromeos::WindowStateType,
-                             kWindowStateTypeKey,
-                             chromeos::WindowStateType::kDefault)
 
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowPipTypeKey, false)
 
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h
index 2d92595..6febe80 100644
--- a/ash/public/cpp/window_properties.h
+++ b/ash/public/cpp/window_properties.h
@@ -239,10 +239,6 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
     kWindowPositionManagedTypeKey;
 
-// A property key to indicate ash's extended window state.
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<
-    chromeos::WindowStateType>* const kWindowStateTypeKey;
-
 // A property key to indicate pip window state.
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
     kWindowPipTypeKey;
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc
index b7ae214..7f8e6cfe 100644
--- a/ash/system/message_center/notifier_settings_view.cc
+++ b/ash/system/message_center/notifier_settings_view.cc
@@ -482,12 +482,12 @@
     app_badging_view->SetBorder(
         views::CreateSolidSidedBorder(0, 0, 0, 1, kTopBorderColor));
     header_view->AddChildView(std::move(app_badging_view));
-  }
 
-  // Separator between toggle button rows.
-  auto separator = std::make_unique<views::Separator>();
-  separator->SetColor(separator_color);
-  header_view->AddChildView(std::move(separator));
+    // Separator between toggle button rows.
+    auto separator = std::make_unique<views::Separator>();
+    separator->SetColor(separator_color);
+    header_view->AddChildView(std::move(separator));
+  }
 
   // Row for the quiet mode toggle button.
   auto quiet_mode_icon = std::make_unique<views::ImageView>();
@@ -552,8 +552,7 @@
         kNotificationCenterDoNotDisturbOnIcon, kMenuIconSize, icon_color));
   } else {
     quiet_mode_icon_->SetImage(gfx::CreateVectorIcon(
-        kNotificationCenterDoNotDisturbOffIcon, kMenuIconSize,
-        AshColorProvider::GetDisabledColor(icon_color)));
+        kNotificationCenterDoNotDisturbOffIcon, kMenuIconSize, icon_color));
   }
 }
 
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 045fdcd..40c8f93b 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -107,7 +107,7 @@
 constexpr gfx::Insets kUnifiedSliderRowPadding(0, 16, 8, 16);
 constexpr gfx::Insets kUnifiedSliderBubblePadding(12, 0, 4, 0);
 constexpr gfx::Insets kUnifiedSliderPadding(0, 16);
-constexpr gfx::Insets kMicGainSliderViewPadding(0, 52, 0, 0);
+constexpr gfx::Insets kMicGainSliderViewPadding(0, 52, 8, 0);
 constexpr gfx::Insets kMicGainSliderPadding(0, 8, 0, 48);
 constexpr int kMicGainSliderViewSpacing = 8;
 
@@ -213,7 +213,8 @@
 constexpr int kUnifiedTopShortcutButtonDefaultSpacing = 16;
 constexpr int kUnifiedTopShortcutButtonMinSpacing = 4;
 
-// Constants used in the title row of a detailed view in UnifiedSystemTray.
+// Constants used in the detailed view in UnifiedSystemTray.
+constexpr gfx::Insets kUnifiedDetailedViewPadding(0, 0, 8, 0);
 constexpr gfx::Insets kUnifiedDetailedViewTitlePadding(0, 0, 0, 16);
 constexpr int kUnifiedDetailedViewTitleRowHeight = 64;
 
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index dec7cf2..1ad1d1b5 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -271,7 +271,7 @@
 TrayDetailedView::TrayDetailedView(DetailedViewDelegate* delegate)
     : delegate_(delegate) {
   box_layout_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical));
+      views::BoxLayout::Orientation::kVertical, kUnifiedDetailedViewPadding));
   SetBackground(views::CreateSolidBackground(
       delegate_->GetBackgroundColor().value_or(SK_ColorTRANSPARENT)));
 }
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 31ed30d..d094ae1 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -30,6 +30,7 @@
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/layout_manager.h"
@@ -621,9 +622,9 @@
     window_->SetProperty(aura::client::kShowStateKey, new_window_state);
   }
 
-  if (GetStateType() != window_->GetProperty(kWindowStateTypeKey)) {
+  if (GetStateType() != window_->GetProperty(chromeos::kWindowStateTypeKey)) {
     base::AutoReset<bool> resetter(&ignore_property_change_, true);
-    window_->SetProperty(kWindowStateTypeKey, GetStateType());
+    window_->SetProperty(chromeos::kWindowStateTypeKey, GetStateType());
   }
 
   // sync up current window show state with PinType property.
@@ -898,10 +899,10 @@
     }
     return;
   }
-  if (key == kWindowStateTypeKey) {
+  if (key == chromeos::kWindowStateTypeKey) {
     if (!ignore_property_change_) {
       // This change came from somewhere else. Revert it.
-      window->SetProperty(kWindowStateTypeKey, GetStateType());
+      window->SetProperty(chromeos::kWindowStateTypeKey, GetStateType());
     }
     return;
   }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 142c0252..859ccb21 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2135,6 +2135,7 @@
       "trace_event/optional_trace_event.h",
       "trace_event/process_memory_dump.cc",
       "trace_event/process_memory_dump.h",
+      "trace_event/task_execution_macros.h",
       "trace_event/thread_instruction_count.cc",
       "trace_event/thread_instruction_count.h",
       "trace_event/trace_arguments.cc",
diff --git a/base/base_paths_fuchsia.cc b/base/base_paths_fuchsia.cc
index a7ea21a..933bcd9 100644
--- a/base/base_paths_fuchsia.cc
+++ b/base/base_paths_fuchsia.cc
@@ -26,11 +26,11 @@
       return true;
     case DIR_APP_DATA:
     case DIR_CACHE:
-      *result = base::FilePath(base::fuchsia::kPersistedDataDirectoryPath);
+      *result = base::FilePath(base::kPersistedDataDirectoryPath);
       return true;
     case DIR_ASSETS:
     case DIR_SOURCE_ROOT:
-      *result = base::FilePath(base::fuchsia::kPackageRootDirectoryPath);
+      *result = base::FilePath(base::kPackageRootDirectoryPath);
       return true;
   }
   return false;
diff --git a/base/fuchsia/file_utils.cc b/base/fuchsia/file_utils.cc
index 3abce00a..1a7a63c 100644
--- a/base/fuchsia/file_utils.cc
+++ b/base/fuchsia/file_utils.cc
@@ -17,14 +17,13 @@
 #include "base/macros.h"
 
 namespace base {
-namespace fuchsia {
 
 const char kPersistedDataDirectoryPath[] = "/data";
 const char kPersistedCacheDirectoryPath[] = "/cache";
 const char kServiceDirectoryPath[] = "/svc";
 const char kPackageRootDirectoryPath[] = "/pkg";
 
-fidl::InterfaceHandle<::fuchsia::io::Directory> OpenDirectory(
+fidl::InterfaceHandle<::fuchsia::io::Directory> OpenDirectoryHandle(
     const base::FilePath& path) {
   ScopedFD fd(open(path.value().c_str(), O_DIRECTORY | O_RDONLY));
   if (!fd.is_valid()) {
@@ -45,5 +44,20 @@
   return fidl::InterfaceHandle<::fuchsia::io::Directory>(std::move(channel));
 }
 
+// TODO(crbug.com/1073821): Remove this block when out-of-tree callers have been
+// changed to use the non-fuchsia-sub-namespace version.
+namespace fuchsia {
+
+const char kPersistedDataDirectoryPath[] = "/data";
+const char kPersistedCacheDirectoryPath[] = "/cache";
+const char kServiceDirectoryPath[] = "/svc";
+const char kPackageRootDirectoryPath[] = "/pkg";
+
+fidl::InterfaceHandle<::fuchsia::io::Directory> OpenDirectory(
+    const base::FilePath& path) {
+  return ::base::OpenDirectoryHandle(path);
+}
+
 }  // namespace fuchsia
+
 }  // namespace base
diff --git a/base/fuchsia/file_utils.h b/base/fuchsia/file_utils.h
index 35b08c5..8e3a197 100644
--- a/base/fuchsia/file_utils.h
+++ b/base/fuchsia/file_utils.h
@@ -11,7 +11,6 @@
 #include "base/files/file_path.h"
 
 namespace base {
-namespace fuchsia {
 
 // Persisted data directory, i.e. /data . Returned as DIR_APP_DATA from
 // PathService.
@@ -28,10 +27,22 @@
 
 // Returns fuchsia.io.Directory for the specified |path| or null InterfaceHandle
 // if the path doesn't exist or it's not a directory.
+BASE_EXPORT fidl::InterfaceHandle<::fuchsia::io::Directory> OpenDirectoryHandle(
+    const base::FilePath& path);
+
+// TODO(crbug.com/1073821): Remove this block when out-of-tree callers have been
+// changed to use the non-fuchsia-sub-namespace version.
+namespace fuchsia {
+
+BASE_EXPORT extern const char kPersistedDataDirectoryPath[];
+BASE_EXPORT extern const char kPersistedCacheDirectoryPath[];
+BASE_EXPORT extern const char kServiceDirectoryPath[];
+BASE_EXPORT extern const char kPackageRootDirectoryPath[];
 BASE_EXPORT fidl::InterfaceHandle<::fuchsia::io::Directory> OpenDirectory(
     const base::FilePath& path);
 
 }  // namespace fuchsia
+
 }  // namespace base
 
 #endif  // BASE_FUCHSIA_FILE_UTILS_H_
diff --git a/base/fuchsia/file_utils_unittest.cc b/base/fuchsia/file_utils_unittest.cc
index 810732b..5a43e24 100644
--- a/base/fuchsia/file_utils_unittest.cc
+++ b/base/fuchsia/file_utils_unittest.cc
@@ -9,7 +9,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
-namespace fuchsia {
 
 class OpenDirectoryTest : public testing::Test {
  protected:
@@ -22,23 +21,32 @@
 };
 
 TEST_F(OpenDirectoryTest, Open) {
-  auto dir = OpenDirectory(temp_dir.GetPath());
+  auto dir = OpenDirectoryHandle(temp_dir.GetPath());
   ASSERT_TRUE(dir);
 }
 
-// OpenDirectory() should fail when opening a directory that doesn't exist.
+// OpenDirectoryHandle() should fail when opening a directory that doesn't
+// exist.
 TEST_F(OpenDirectoryTest, OpenNonExistent) {
-  auto dir = OpenDirectory(temp_dir.GetPath().AppendASCII("non_existent"));
+  auto dir =
+      OpenDirectoryHandle(temp_dir.GetPath().AppendASCII("non_existent"));
   ASSERT_FALSE(dir);
 }
 
-// OpenDirectory() should open only directories.
+// OpenDirectoryHandle() should open only directories.
 TEST_F(OpenDirectoryTest, OpenFile) {
   auto file_path = temp_dir.GetPath().AppendASCII("test_file");
   ASSERT_TRUE(WriteFile(file_path, "foo"));
-  auto dir = OpenDirectory(file_path);
+  auto dir = OpenDirectoryHandle(file_path);
   ASSERT_FALSE(dir);
 }
 
-}  // namespace fuchsia
+// TODO(crbug.com/1073821): Remove this test.
+// Tests the deprecated ::base::fuchsia::OpenDirectory() function works until
+// all callers have been removed.
+TEST_F(OpenDirectoryTest, OpenTransitional) {
+  auto dir = ::base::fuchsia::OpenDirectory(temp_dir.GetPath());
+  ASSERT_TRUE(dir);
+}
+
 }  // namespace base
diff --git a/base/numerics/safe_conversions.h b/base/numerics/safe_conversions.h
index 7c1c265..34f4cb85 100644
--- a/base/numerics/safe_conversions.h
+++ b/base/numerics/safe_conversions.h
@@ -13,7 +13,7 @@
 
 #include "base/numerics/safe_conversions_impl.h"
 
-#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
+#if defined(__ARMEL__) && !defined(__native_client__)
 #include "base/numerics/safe_conversions_arm_impl.h"
 #define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
 #else
diff --git a/base/process/launch_fuchsia.cc b/base/process/launch_fuchsia.cc
index 17ea46f..fe39f485 100644
--- a/base/process/launch_fuchsia.cc
+++ b/base/process/launch_fuchsia.cc
@@ -165,7 +165,7 @@
     environ_modifications["PWD"] = cwd.value();
   }
 
-  std::unique_ptr<char* []> new_environ;
+  std::unique_ptr<char*[]> new_environ;
   if (!environ_modifications.empty()) {
     char* const empty_environ = nullptr;
     char* const* old_environ =
@@ -193,7 +193,7 @@
 
     for (const auto& path_to_clone : options.paths_to_clone) {
       fidl::InterfaceHandle<::fuchsia::io::Directory> directory =
-          base::fuchsia::OpenDirectory(path_to_clone);
+          base::OpenDirectoryHandle(path_to_clone);
       if (!directory) {
         LOG(WARNING) << "Could not open handle for path: " << path_to_clone;
         return base::Process();
diff --git a/base/process/process_util_unittest.cc b/base/process/process_util_unittest.cc
index 24ed3d1f..5593524 100644
--- a/base/process/process_util_unittest.cc
+++ b/base/process/process_util_unittest.cc
@@ -266,12 +266,11 @@
   // Attach the tempdir to "data", but also try to duplicate the existing "data"
   // directory.
   options.paths_to_clone.push_back(
-      base::FilePath(base::fuchsia::kPersistedDataDirectoryPath));
+      base::FilePath(base::kPersistedDataDirectoryPath));
   options.paths_to_clone.push_back(base::FilePath("/tmp"));
   options.paths_to_transfer.push_back(
-      {FilePath(base::fuchsia::kPersistedDataDirectoryPath),
-       base::fuchsia::OpenDirectory(
-           base::FilePath(tmpdir_with_staged.GetPath()))
+      {FilePath(base::kPersistedDataDirectoryPath),
+       base::OpenDirectoryHandle(base::FilePath(tmpdir_with_staged.GetPath()))
            .TakeChannel()
            .release()});
 
@@ -310,7 +309,7 @@
 
   // Mount the tempdir to "/foo".
   zx::channel tmp_channel =
-      base::fuchsia::OpenDirectory(new_tmpdir.GetPath()).TakeChannel();
+      base::OpenDirectoryHandle(new_tmpdir.GetPath()).TakeChannel();
 
   ASSERT_TRUE(tmp_channel.is_valid());
   LaunchOptions options;
@@ -641,7 +640,7 @@
 #endif
 TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
   const std::string signal_file =
-    ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
+      ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
   remove(signal_file.c_str());
   Process process = SpawnChild("CrashingChildProcess");
   ASSERT_TRUE(process.IsValid());
@@ -705,7 +704,7 @@
 #endif
 TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusSigKill) {
   const std::string signal_file =
-    ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
+      ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
   remove(signal_file.c_str());
   Process process = SpawnChild("KilledChildProcess");
   ASSERT_TRUE(process.IsValid());
@@ -742,7 +741,7 @@
 // test might not be relevant anyway.
 TEST_F(ProcessUtilTest, GetTerminationStatusSigTerm) {
   const std::string signal_file =
-    ProcessUtilTest::GetSignalFilePath(kSignalFileTerm);
+      ProcessUtilTest::GetSignalFilePath(kSignalFileTerm);
   remove(signal_file.c_str());
   Process process = SpawnChild("TerminatedChildProcess");
   ASSERT_TRUE(process.IsValid());
@@ -972,11 +971,13 @@
 //
 // Atomically replaces |guard|/|guardflags| with |nguard|/|nguardflags| on |fd|.
 int change_fdguard_np(int fd,
-                      const guardid_t *guard, u_int guardflags,
-                      const guardid_t *nguard, u_int nguardflags,
-                      int *fdflagsp) {
-  return syscall(SYS_change_fdguard_np, fd, guard, guardflags,
-                 nguard, nguardflags, fdflagsp);
+                      const guardid_t* guard,
+                      u_int guardflags,
+                      const guardid_t* nguard,
+                      u_int nguardflags,
+                      int* fdflagsp) {
+  return syscall(SYS_change_fdguard_np, fd, guard, guardflags, nguard,
+                 nguardflags, fdflagsp);
 }
 
 // Attempt to set a file-descriptor guard on |fd|.  In case of success, remove
@@ -1036,8 +1037,8 @@
     }
   }
 
-  int written = HANDLE_EINTR(write(write_pipe, &num_open_files,
-                                   sizeof(num_open_files)));
+  int written =
+      HANDLE_EINTR(write(write_pipe, &num_open_files, sizeof(num_open_files)));
   DCHECK_EQ(static_cast<size_t>(written), sizeof(num_open_files));
   int ret = IGNORE_EINTR(close(write_pipe));
   DPCHECK(ret == 0);
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 3cf448d6..a62b35b 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -332,7 +332,7 @@
   // that we can install a different /data.
   new_options.spawn_flags = FDIO_SPAWN_CLONE_STDIO | FDIO_SPAWN_CLONE_JOB;
 
-  const base::FilePath kDataPath(base::fuchsia::kPersistedDataDirectoryPath);
+  const base::FilePath kDataPath(base::kPersistedDataDirectoryPath);
 
   // Clone all namespace entries from the current process, except /data, which
   // is overridden below.
@@ -375,7 +375,7 @@
   // Bind the new test subdirectory to /data in the child process' namespace.
   new_options.paths_to_transfer.push_back(
       {kDataPath,
-       base::fuchsia::OpenDirectory(nested_data_path).TakeChannel().release()});
+       base::OpenDirectoryHandle(nested_data_path).TakeChannel().release()});
 #endif  // defined(OS_FUCHSIA)
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
diff --git a/base/trace_event/base_tracing.h b/base/trace_event/base_tracing.h
index c5831f237d..ae3c60e7 100644
--- a/base/trace_event/base_tracing.h
+++ b/base/trace_event/base_tracing.h
@@ -16,8 +16,10 @@
 // Update the check in //base/PRESUBMIT.py when adding new headers here.
 // TODO(crbug/1006541): Switch to perfetto for trace event implementation.
 #include "base/trace_event/blame_context.h"
+#include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/memory_allocator_dump_guid.h"
 #include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/task_execution_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "base/trace_event/typed_macros.h"
diff --git a/base/trace_event/common/trace_event_common.h b/base/trace_event/common/trace_event_common.h
index 28b7275..120481f3 100644
--- a/base/trace_event/common/trace_event_common.h
+++ b/base/trace_event/common/trace_event_common.h
@@ -969,6 +969,7 @@
 #define TRACE_TASK_EXECUTION(run_function, task) \
   INTERNAL_TRACE_TASK_EXECUTION(run_function, task)
 
+// Special trace event macro to trace log messages.
 #define TRACE_LOG_MESSAGE(file, message, line) \
   INTERNAL_TRACE_LOG_MESSAGE(file, message, line)
 
diff --git a/base/trace_event/task_execution_macros.h b/base/trace_event/task_execution_macros.h
new file mode 100644
index 0000000..156bcf0
--- /dev/null
+++ b/base/trace_event/task_execution_macros.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 BASE_TRACE_EVENT_TASK_EXECUTION_MACROS_H_
+#define BASE_TRACE_EVENT_TASK_EXECUTION_MACROS_H_
+
+#include "base/location.h"
+#include "base/trace_event/heap_profiler.h"
+#include "base/trace_event/typed_macros.h"
+#include "third_party/perfetto/include/perfetto/tracing/track_event_interned_data_index.h"
+#include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/log_message.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/source_location.pbzero.h"
+#include "third_party/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h"
+
+namespace {
+
+// TrackEventInternedDataIndex expects the same data structure to be used for
+// all interned fields with the same field number. We can't use base::Location
+// for log event's location since base::Location uses program counter based
+// location.
+struct TraceSourceLocation {
+  const char* function_name = nullptr;
+  const char* file_name = nullptr;
+  int line_number = 0;
+
+  bool operator==(const TraceSourceLocation& other) const {
+    return file_name == other.file_name &&
+           function_name == other.function_name &&
+           line_number == other.line_number;
+  }
+};
+
+}  // namespace
+
+namespace std {
+
+template <>
+struct ::std::hash<TraceSourceLocation> {
+  std::size_t operator()(const TraceSourceLocation& loc) const {
+    return base::FastHash(base::as_bytes(base::make_span(&loc, 1)));
+  }
+};
+
+}  // namespace std
+
+namespace {
+
+struct InternedSourceLocation
+    : public perfetto::TrackEventInternedDataIndex<
+          InternedSourceLocation,
+          perfetto::protos::pbzero::InternedData::kSourceLocationsFieldNumber,
+          TraceSourceLocation> {
+  static void Add(perfetto::protos::pbzero::InternedData* interned_data,
+                  size_t iid,
+                  const TraceSourceLocation& location) {
+    auto* msg = interned_data->add_source_locations();
+    msg->set_iid(iid);
+    if (location.file_name != nullptr)
+      msg->set_file_name(location.file_name);
+    if (location.function_name != nullptr)
+      msg->set_function_name(location.function_name);
+    // TODO(ssid): Add line number once it is whitelisted in internal proto.
+    // TODO(ssid): Add program counter to the proto fields when
+    // !BUILDFLAG(ENABLE_LOCATION_SOURCE).
+    // TODO(http://crbug.com760702) remove file name and just pass the program
+    // counter to the heap profiler macro.
+    // TODO(ssid): Consider writing the program counter of the current task
+    // (from the callback function pointer) instead of location that posted the
+    // task.
+  }
+};
+
+struct InternedLogMessage
+    : public perfetto::TrackEventInternedDataIndex<
+          InternedLogMessage,
+          perfetto::protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
+          std::string> {
+  static void Add(perfetto::protos::pbzero::InternedData* interned_data,
+                  size_t iid,
+                  const std::string& log_message) {
+    auto* msg = interned_data->add_log_message_body();
+    msg->set_iid(iid);
+    msg->set_body(log_message);
+  }
+};
+
+}  // namespace
+
+// Implementation detail: internal macro to trace a task execution with the
+// location where it was posted from.
+#define INTERNAL_TRACE_TASK_EXECUTION(run_function, task)                 \
+  TRACE_EVENT("toplevel", run_function, [&](perfetto::EventContext ctx) { \
+    ctx.event()->set_task_execution()->set_posted_from_iid(               \
+        InternedSourceLocation::Get(&ctx,                                 \
+                                    {(task).posted_from.function_name(),  \
+                                     (task).posted_from.file_name(),      \
+                                     (task).posted_from.line_number()})); \
+  });                                                                     \
+  TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION INTERNAL_TRACE_EVENT_UID( \
+      task_event)((task).posted_from.file_name());                        \
+  TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER                     \
+  INTERNAL_TRACE_EVENT_UID(task_pc_event)                                 \
+  ((task).posted_from.program_counter());
+
+// Implementation detail: internal macro to trace a log message, with the source
+// location of the log statement.
+#define INTERNAL_TRACE_LOG_MESSAGE(file, message, line)                        \
+  TRACE_EVENT_INSTANT(                                                         \
+      "log", "LogMessage", TRACE_EVENT_SCOPE_THREAD,                           \
+      [&](perfetto::EventContext ctx) {                                        \
+        perfetto::protos::pbzero::LogMessage* log =                            \
+            ctx.event()->set_log_message();                                    \
+        log->set_source_location_iid(InternedSourceLocation::Get(              \
+            &ctx,                                                              \
+            TraceSourceLocation{/*function_name=*/nullptr, file, line}));      \
+        log->set_body_iid(InternedLogMessage::Get(&ctx, message.as_string())); \
+      });
+
+#endif  // BASE_TRACE_EVENT_TASK_EXECUTION_MACROS_H_
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index 6c6a6c0..a0cd28cf 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -22,7 +22,6 @@
 #include "base/time/time_override.h"
 #include "base/trace_event/builtin_categories.h"
 #include "base/trace_event/common/trace_event_common.h"
-#include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/log_message.h"
 #include "base/trace_event/thread_instruction_count.h"
 #include "base/trace_event/trace_arguments.h"
@@ -390,45 +389,6 @@
     }                                                                \
   } while (0)
 
-#define INTERNAL_TRACE_LOG_MESSAGE(file, message, line)                        \
-  TRACE_EVENT_INSTANT1(                                                        \
-      "log", "LogMessage",                                                     \
-      TRACE_EVENT_FLAG_TYPED_PROTO_ARGS | TRACE_EVENT_SCOPE_THREAD, "message", \
-      std::make_unique<base::trace_event::LogMessage>(file, message, line))
-
-#if BUILDFLAG(ENABLE_LOCATION_SOURCE)
-
-// Implementation detail: internal macro to trace a task execution with the
-// location where it was posted from.
-//
-// This implementation is for when location sources are available.
-// TODO(ssid): The program counter of the current task should be added here.
-#define INTERNAL_TRACE_TASK_EXECUTION(run_function, task)                      \
-  INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLAGS(                                  \
-      "toplevel", run_function, TRACE_EVENT_FLAG_TYPED_PROTO_ARGS, "src_file", \
-      (task).posted_from.file_name(), "src_func",                              \
-      (task).posted_from.function_name());                                     \
-  TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION INTERNAL_TRACE_EVENT_UID(      \
-      task_event)((task).posted_from.file_name());                             \
-  TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER                          \
-  INTERNAL_TRACE_EVENT_UID(task_pc_event)((task).posted_from.program_counter());
-
-#else
-
-// TODO(http://crbug.com760702) remove file name and just pass the program
-// counter to the heap profiler macro.
-// TODO(ssid): The program counter of the current task should be added here.
-#define INTERNAL_TRACE_TASK_EXECUTION(run_function, task)                 \
-  INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLAGS(                             \
-      "toplevel", run_function, TRACE_EVENT_FLAG_TYPED_PROTO_ARGS, "src", \
-      (task).posted_from.ToString())                                      \
-  TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION INTERNAL_TRACE_EVENT_UID( \
-      task_event)((task).posted_from.file_name());                        \
-  TRACE_HEAP_PROFILER_API_SCOPED_WITH_PROGRAM_COUNTER                     \
-  INTERNAL_TRACE_EVENT_UID(task_pc_event)((task).posted_from.program_counter());
-
-#endif
-
 namespace trace_event_internal {
 
 // Specify these values when the corresponding argument of AddTraceEvent is not
diff --git a/build/fuchsia/aemu_target.py b/build/fuchsia/aemu_target.py
index 684b019..f9fc6af 100644
--- a/build/fuchsia/aemu_target.py
+++ b/build/fuchsia/aemu_target.py
@@ -20,10 +20,19 @@
 class AemuTarget(qemu_target.QemuTarget):
   EMULATOR_NAME = 'aemu'
 
-  def __init__(self, out_dir, target_cpu, system_log_file, cpu_cores,
-               require_kvm, ram_size_mb, enable_graphics, hardware_gpu):
-    super(AemuTarget, self).__init__(out_dir, target_cpu, system_log_file,
-                                     cpu_cores, require_kvm, ram_size_mb)
+  def __init__(self,
+               out_dir,
+               target_cpu,
+               system_log_file,
+               cpu_cores,
+               require_kvm,
+               ram_size_mb,
+               enable_graphics,
+               hardware_gpu,
+               fuchsia_out_dir=None):
+    super(AemuTarget,
+          self).__init__(out_dir, target_cpu, system_log_file, cpu_cores,
+                         require_kvm, ram_size_mb, fuchsia_out_dir)
 
     # TODO(crbug.com/1000907): Enable AEMU for arm64.
     if platform.machine() == 'aarch64':
diff --git a/build/fuchsia/common_args.py b/build/fuchsia/common_args.py
index 6b3916c..5a8ce9eb 100644
--- a/build/fuchsia/common_args.py
+++ b/build/fuchsia/common_args.py
@@ -43,6 +43,11 @@
                            'subclass of Target that will be used. Only '
                            'needed if device specific operations such as '
                            'paving is required.')
+  device_args.add_argument('--fuchsia-out-dir',
+                           help='Path to a Fuchsia build output directory. '
+                           'Setting the GN arg '
+                           '"default_fuchsia_build_dir_for_installation" '
+                           'will cause it to be passed here.')
 
 
 def _GetTargetClass(args):
@@ -107,11 +112,6 @@
   common_args.add_argument('--verbose', '-v', default=False,
                            action='store_true',
                            help='Enable debug-level logging.')
-  common_args.add_argument('--fuchsia-out-dir',
-                           nargs='+',
-                           help='Path to a Fuchsia build output directory. '
-                           'If more than one outdir is supplied, the last one '
-                           'in the sequence will be used.')
 
 
 def ConfigureLogging(args):
@@ -150,10 +150,12 @@
   known_args, _ = target_arg_parser.parse_known_args()
   target_args = vars(known_args)
 
-  # target_cpu is needed to determine target type, so we need to add
-  # it here to the args used to initialize the Target.
+  # target_cpu is needed to determine target type, and fuchsia_out_dir
+  # is needed for devices with Fuchsia built from source code.
   target_args.update({'target_cpu': module_args.target_cpu})
+  target_args.update({'fuchsia_out_dir': module_args.fuchsia_out_dir})
 
   if additional_args:
     target_args.update(additional_args)
+
   return target_class(**target_args)
diff --git a/build/fuchsia/deploy_to_amber_repo.py b/build/fuchsia/deploy_to_amber_repo.py
index 02dcf25..80ac2fe 100755
--- a/build/fuchsia/deploy_to_amber_repo.py
+++ b/build/fuchsia/deploy_to_amber_repo.py
@@ -38,21 +38,16 @@
   parser = argparse.ArgumentParser()
   parser.add_argument('--package', action='append', required=True,
                       help='Paths to packages to install.')
-  parser.add_argument('--fuchsia-out-dir', nargs='+',
+  parser.add_argument('--fuchsia-out-dir',
+                      required=True,
                       help='Path to a Fuchsia build output directory. '
-                           'If more than one outdir is supplied, the last one '
-                           'in the sequence will be used.')
+                      'Setting the GN arg '
+                      '"default_fuchsia_build_dir_for_installation" '
+                      'will cause it to be passed here.')
   args = parser.parse_args()
   assert args.package
 
-  if not args.fuchsia_out_dir or len(args.fuchsia_out_dir) == 0:
-    sys.stderr.write('No Fuchsia build output directory was specified.\n' +
-                     'To resolve this, Use the commandline argument ' +
-                     '--fuchsia-out-dir\nor set the GN arg ' +
-                     '"default_fuchsia_build_dir_for_installation".\n')
-    return 1
-
-  fuchsia_out_dir = os.path.expanduser(args.fuchsia_out_dir.pop())
+  fuchsia_out_dir = os.path.expanduser(args.fuchsia_out_dir)
   repo = amber_repo.ExternalAmberRepo(
       os.path.join(fuchsia_out_dir, 'amber-files'))
   print('Installing packages and symbols in Amber repo %s...' % repo.GetPath())
diff --git a/build/fuchsia/device_target.py b/build/fuchsia/device_target.py
index f68dbff1..17310d5 100644
--- a/build/fuchsia/device_target.py
+++ b/build/fuchsia/device_target.py
@@ -89,8 +89,6 @@
     self._host = host
     self._port = port
     self._fuchsia_out_dir = None
-    if fuchsia_out_dir:
-      self._fuchsia_out_dir = os.path.expanduser(fuchsia_out_dir)
     self._node_name = node_name
     self._os_check = os_check
     self._amber_repo = None
@@ -98,7 +96,7 @@
     if self._host and self._node_name:
       raise Exception('Only one of "--host" or "--name" can be specified.')
 
-    if self._fuchsia_out_dir:
+    if fuchsia_out_dir:
       if ssh_config:
         raise Exception('Only one of "--fuchsia-out-dir" or "--ssh_config" can '
                         'be specified.')
diff --git a/build/fuchsia/emu_target.py b/build/fuchsia/emu_target.py
index 440f1ab7..22726cb 100644
--- a/build/fuchsia/emu_target.py
+++ b/build/fuchsia/emu_target.py
@@ -16,12 +16,15 @@
 
 
 class EmuTarget(target.Target):
-  def __init__(self, out_dir, target_cpu, system_log_file):
+  def __init__(self, out_dir, target_cpu, system_log_file, fuchsia_out_dir):
     """out_dir: The directory which will contain the files that are
                    generated to support the emulator deployment.
     target_cpu: The emulated target CPU architecture.
                 Can be 'x64' or 'arm64'."""
 
+    # fuchsia_out_dir is unused by emulator targets.
+    del fuchsia_out_dir
+
     super(EmuTarget, self).__init__(out_dir, target_cpu)
     self._emu_process = None
     self._system_log_file = system_log_file
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a3a002f8..ba36bfc 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201009.3.1
+0.20201010.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a3a002f8..ba36bfc 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201009.3.1
+0.20201010.1.1
diff --git a/build/fuchsia/qemu_target.py b/build/fuchsia/qemu_target.py
index 6c858233..d43e9d3 100644
--- a/build/fuchsia/qemu_target.py
+++ b/build/fuchsia/qemu_target.py
@@ -40,9 +40,16 @@
 class QemuTarget(emu_target.EmuTarget):
   EMULATOR_NAME = 'qemu'
 
-  def __init__(self, out_dir, target_cpu, system_log_file, cpu_cores,
-               require_kvm, ram_size_mb):
-    super(QemuTarget, self).__init__(out_dir, target_cpu, system_log_file)
+  def __init__(self,
+               out_dir,
+               target_cpu,
+               system_log_file,
+               cpu_cores,
+               require_kvm,
+               ram_size_mb,
+               fuchsia_out_dir=None):
+    super(QemuTarget, self).__init__(out_dir, target_cpu, system_log_file,
+                                     fuchsia_out_dir)
     self._cpu_cores=cpu_cores
     self._require_kvm=require_kvm
     self._ram_size_mb=ram_size_mb
diff --git a/cc/raster/synchronous_task_graph_runner.cc b/cc/raster/synchronous_task_graph_runner.cc
index b7e0e94..03a5a46 100644
--- a/cc/raster/synchronous_task_graph_runner.cc
+++ b/cc/raster/synchronous_task_graph_runner.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/threading/simple_thread.h"
+#include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/trace_event.h"
 
 namespace cc {
diff --git a/chrome/VERSION b/chrome/VERSION
index a311285..3ad2607 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=88
 MINOR=0
-BUILD=4288
+BUILD=4289
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 8f36311..5847138f 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -418,6 +418,7 @@
     "//components/media_router/browser/android:java",
     "//components/messages/android:factory_java",
     "//components/messages/android:java",
+    "//components/messages/android:manager_java",
     "//components/minidump_uploader:minidump_uploader_java",
     "//components/module_installer/android:module_installer_java",
     "//components/module_installer/android:module_interface_java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index a70a738..e1c6796 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -81,7 +81,7 @@
 import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
 import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.components.feature_engagement.EventConstants;
-import org.chromium.components.messages.MessageQueueManager;
+import org.chromium.components.messages.ManagedMessageDispatcher;
 import org.chromium.components.messages.MessagesFactory;
 import org.chromium.content_public.browser.ActionModeCallbackHelper;
 import org.chromium.content_public.browser.LoadUrlParams;
@@ -158,7 +158,7 @@
     private ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     private final OneshotSupplier<StartSurface> mStartSurfaceSupplier;
     @Nullable
-    private MessageQueueManager mMessageQueueManager;
+    private ManagedMessageDispatcher mMessageDispatcher;
 
     /**
      * Create a new {@link RootUiCoordinator} for the given activity.
@@ -215,11 +215,6 @@
         mOverviewModeBehaviorSupplier.onAvailable(
                 mCallbackController.makeCancelable(this::setOverviewModeBehavior));
         mStartSurfaceSupplier = startSurfaceSupplier;
-
-        if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE)) {
-            mMessageQueueManager =
-                    MessagesFactory.createMessageQueueManager(mActivity.getWindowAndroid());
-        }
     }
 
     // TODO(pnoland, crbug.com/865801): remove this in favor of wiring it directly.
@@ -234,9 +229,9 @@
 
         mActivity.getLayoutManagerSupplier().removeObserver(mLayoutManagerSupplierCallback);
 
-        if (mMessageQueueManager != null) {
-            mMessageQueueManager.destroy();
-            mMessageQueueManager = null;
+        if (mMessageDispatcher != null) {
+            MessagesFactory.detachMessageDispatcher(mMessageDispatcher);
+            mMessageDispatcher = null;
         }
 
         if (mOverlayPanelManager != null) {
@@ -321,6 +316,12 @@
 
         initFindToolbarManager();
         initializeToolbar();
+        if (CachedFeatureFlags.isEnabled(ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE)) {
+            mMessageDispatcher = MessagesFactory.createMessageDispatcher(
+                    mActivity.findViewById(R.id.message_container));
+            MessagesFactory.attachMessageDispatcher(
+                    mActivity.getWindowAndroid(), mMessageDispatcher);
+        }
     }
 
     @Override
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
index 9a5fda7d..e1418d9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentTabsPageTest.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.ntp;
 
 import android.accounts.Account;
-import android.support.test.InstrumentationRegistry;
+import android.app.Activity;
 import android.view.View;
 
 import androidx.test.filters.LargeTest;
@@ -21,7 +21,6 @@
 
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -35,7 +34,6 @@
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_public.browser.test.util.TestTouchUtils;
 import org.chromium.content_public.common.ContentUrlConstants;
 import org.chromium.url.GURL;
 
@@ -86,7 +84,6 @@
     @Test
     @MediumTest
     @Feature({"RecentTabsPage"})
-    @FlakyTest(message = "crbug.com/1075804")
     public void testRecentlyClosedTabs() throws ExecutionException {
         mPage = loadRecentTabsPage();
         // Set a recently closed tab and confirm a view is rendered for it.
@@ -96,7 +93,8 @@
         View view = waitForView(title);
 
         // Clear the recently closed tabs with the context menu and confirm the view is gone.
-        invokeContextMenu(view, RecentTabsRowAdapter.RecentlyClosedTabsGroup.ID_REMOVE_ALL);
+        openContextMenuAndInvokeItem(mActivityTestRule.getActivity(), view,
+                RecentTabsRowAdapter.RecentlyClosedTabsGroup.ID_REMOVE_ALL);
         Assert.assertEquals(0, mManager.getRecentlyClosedTabs(1).size());
         waitForViewToDisappear(title);
     }
@@ -193,10 +191,13 @@
         });
     }
 
-    private void invokeContextMenu(View view, int contextMenuItemId) throws ExecutionException {
-        TestTouchUtils.performLongClickOnMainSync(
-                InstrumentationRegistry.getInstrumentation(), view);
-        Assert.assertTrue(InstrumentationRegistry.getInstrumentation().invokeContextMenuAction(
-                mActivityTestRule.getActivity(), contextMenuItemId, 0));
+    private static void openContextMenuAndInvokeItem(
+            final Activity activity, final View view, final int itemId) {
+        // IMPLEMENTATION NOTE: Instrumentation.invokeContextMenuAction would've been much simpler,
+        // but it requires the View to be focused which is hard to achieve in touch mode.
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            view.performLongClick();
+            activity.getWindow().performContextMenuIdentifierAction(itemId, 0);
+        });
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
index c21e78e..7edcca5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
@@ -25,7 +25,6 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisableIf;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.blink.mojom.MhtmlLoadResult;
@@ -211,7 +210,6 @@
 
     @Test
     @SmallTest
-    @DisabledTest(message = "crbug.com/786237")
     public void testShowOfflineSnackbarIfNecessary() throws Exception {
         // Arrange - build a mock controller for sensing.
         OfflinePageUtils.setSnackbarDurationForTesting(1000);
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 3afb55c..708a905 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-88.0.4285.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-88.0.4288.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d7758580..3afcc3c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3669,9 +3669,6 @@
     {"arc-usb-host", flag_descriptions::kArcUsbHostName,
      flag_descriptions::kArcUsbHostDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(arc::kUsbHostFeature)},
-    {"arc-usb-storage-ui", flag_descriptions::kArcUsbStorageUIName,
-     flag_descriptions::kArcUsbStorageUIDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(arc::kUsbStorageUIFeature)},
 #endif  // OS_CHROMEOS
 #if defined(OS_WIN)
     {"enable-winrt-sensor-implementation",
diff --git a/chrome/browser/accessibility/accessibility_extension_api.cc b/chrome/browser/accessibility/accessibility_extension_api.cc
index f218223..12525d2 100644
--- a/chrome/browser/accessibility/accessibility_extension_api.cc
+++ b/chrome/browser/accessibility/accessibility_extension_api.cc
@@ -501,6 +501,21 @@
   return RespondNow(NoArguments());
 }
 
+ExtensionFunction::ResponseAction
+AccessibilityPrivateEnablePointScanFunction::Run() {
+  std::unique_ptr<accessibility_private::EnablePointScan::Params> params =
+      accessibility_private::EnablePointScan::Params::Create(*args_);
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  if (params->enabled) {
+    ash::AccessibilityController::Get()->StartPointScan();
+  }
+
+  // TODO(crbug.com/1061537): Implement (!params->enabled) to disable point scan
+
+  return RespondNow(NoArguments());
+}
+
 AccessibilityPrivateGetBatteryDescriptionFunction::
     AccessibilityPrivateGetBatteryDescriptionFunction() {}
 
diff --git a/chrome/browser/accessibility/accessibility_extension_api.h b/chrome/browser/accessibility/accessibility_extension_api.h
index 2c95b9f..99fa27b 100644
--- a/chrome/browser/accessibility/accessibility_extension_api.h
+++ b/chrome/browser/accessibility/accessibility_extension_api.h
@@ -159,6 +159,15 @@
                              ACCESSIBILITY_PRIVATE_UPDATESWITCHACCESSBUBBLE)
 };
 
+// API function that is called to start or end point scanning of the
+// Switch Access extension.
+class AccessibilityPrivateEnablePointScanFunction : public ExtensionFunction {
+  ~AccessibilityPrivateEnablePointScanFunction() override {}
+  ResponseAction Run() override;
+  DECLARE_EXTENSION_FUNCTION("accessibilityPrivate.enablePointScan",
+                             ACCESSIBILITY_PRIVATE_ENABLEPOINTSCAN)
+};
+
 // API function that is called to get the device's battery status as a string.
 class AccessibilityPrivateGetBatteryDescriptionFunction
     : public ExtensionFunction {
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 1416f14..608ac5f7 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -79,6 +79,7 @@
     "//chrome/browser/apps/platform_apps",
     "//chrome/browser/apps/platform_apps/api",
     "//chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_error_page",
+    "//chrome/browser/chromeos/nearby:bluetooth_adapter_util",
     "//chrome/browser/chromeos/power/ml/smart_dim",
     "//chrome/browser/chromeos/wilco_dtc_supportd:mojo_utils",
     "//chrome/browser/devtools",
@@ -209,6 +210,7 @@
     "//chromeos/services/multidevice_setup/public/cpp:auth_token_validator",
     "//chromeos/services/multidevice_setup/public/cpp:oobe_completion_tracker",
     "//chromeos/services/multidevice_setup/public/cpp:prefs",
+    "//chromeos/services/nearby/public/mojom",
     "//chromeos/services/network_config:in_process_instance",
     "//chromeos/services/network_config/public/cpp:cpp",
     "//chromeos/services/network_health/public/mojom",
@@ -1901,6 +1903,10 @@
     "multidevice_setup/multidevice_setup_service_factory.h",
     "multidevice_setup/oobe_completion_tracker_factory.cc",
     "multidevice_setup/oobe_completion_tracker_factory.h",
+    "nearby/nearby_connections_dependencies_provider.cc",
+    "nearby/nearby_connections_dependencies_provider.h",
+    "nearby/nearby_connections_dependencies_provider_factory.cc",
+    "nearby/nearby_connections_dependencies_provider_factory.h",
     "net/client_cert_filter_chromeos.cc",
     "net/client_cert_filter_chromeos.h",
     "net/client_cert_store_chromeos.cc",
diff --git a/chrome/browser/chromeos/browser_context_keyed_service_factories.cc b/chrome/browser/chromeos/browser_context_keyed_service_factories.cc
index 87b3d4bc..6031f8f 100644
--- a/chrome/browser/chromeos/browser_context_keyed_service_factories.cc
+++ b/chrome/browser/chromeos/browser_context_keyed_service_factories.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/chromeos/kerberos/kerberos_credentials_manager_factory.h"
 #include "chrome/browser/chromeos/launcher_search_provider/launcher_search_provider_service_factory.h"
 #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_factory.h"
+#include "chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/phonehub/phone_hub_manager_factory.h"
 #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_service_factory.h"
@@ -71,6 +72,7 @@
   ash::HoldingSpaceKeyedServiceFactory::GetInstance();
   KerberosCredentialsManagerFactory::GetInstance();
   launcher_search_provider::ServiceFactory::GetInstance();
+  nearby::NearbyConnectionsDependenciesProviderFactory::GetInstance();
   OwnerSettingsServiceChromeOSFactory::GetInstance();
   phonehub::PhoneHubManagerFactory::GetInstance();
   platform_keys::KeyPermissionsServiceFactory::GetInstance();
diff --git a/chrome/browser/chromeos/crosapi/account_manager_ash.cc b/chrome/browser/chromeos/crosapi/account_manager_ash.cc
index abd93f6..c74ee21 100644
--- a/chrome/browser/chromeos/crosapi/account_manager_ash.cc
+++ b/chrome/browser/chromeos/crosapi/account_manager_ash.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/callback.h"
+#include "base/notreached.h"
 #include "chromeos/components/account_manager/account_manager.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
@@ -17,9 +18,12 @@
     mojo::PendingReceiver<mojom::AccountManager> receiver)
     : account_manager_(account_manager), receiver_(this, std::move(receiver)) {
   DCHECK(account_manager_);
+  account_manager_->AddObserver(this);
 }
 
-AccountManagerAsh::~AccountManagerAsh() = default;
+AccountManagerAsh::~AccountManagerAsh() {
+  account_manager_->RemoveObserver(this);
+}
 
 void AccountManagerAsh::IsInitialized(IsInitializedCallback callback) {
   std::move(callback).Run(account_manager_->IsInitialized());
@@ -32,4 +36,79 @@
   std::move(callback).Run(std::move(receiver));
 }
 
+void AccountManagerAsh::OnTokenUpserted(
+    const chromeos::AccountManager::Account& account) {
+  for (auto& observer : observers_)
+    observer->OnTokenUpserted(ToMojoAccount(account));
+}
+
+void AccountManagerAsh::OnAccountRemoved(
+    const chromeos::AccountManager::Account& account) {
+  for (auto& observer : observers_)
+    observer->OnAccountRemoved(ToMojoAccount(account));
+}
+
+// static
+chromeos::AccountManager::Account AccountManagerAsh::FromMojoAccount(
+    const mojom::AccountPtr& mojom_account) {
+  chromeos::AccountManager::Account account;
+
+  account.key.id = mojom_account->key->id;
+  account.key.account_type =
+      FromMojoAccountType(mojom_account->key->account_type);
+  account.raw_email = mojom_account->raw_email;
+
+  return account;
+}
+
+// static
+mojom::AccountPtr AccountManagerAsh::ToMojoAccount(
+    const chromeos::AccountManager::Account& account) {
+  mojom::AccountPtr mojom_account = mojom::Account::New();
+
+  mojom_account->key = mojom::AccountKey::New();
+  mojom_account->key->id = account.key.id;
+  mojom_account->key->account_type =
+      ToMojoAccountType(account.key.account_type);
+  mojom_account->raw_email = account.raw_email;
+
+  return mojom_account;
+}
+
+// static
+chromeos::account_manager::AccountType AccountManagerAsh::FromMojoAccountType(
+    const mojom::AccountType& account_type) {
+  switch (account_type) {
+    case mojom::AccountType::kUnspecified:
+      return chromeos::account_manager::AccountType::ACCOUNT_TYPE_UNSPECIFIED;
+    case mojom::AccountType::kGaia:
+      return chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA;
+    case mojom::AccountType::kActiveDirectory:
+      return chromeos::account_manager::AccountType::
+          ACCOUNT_TYPE_ACTIVE_DIRECTORY;
+    default:
+      // Ash does not know about this new account type. Don't consider this as
+      // as error to preserve forwards compatibility with lacros.
+      LOG(WARNING) << "Unknown account type: " << account_type;
+      return chromeos::account_manager::AccountType::ACCOUNT_TYPE_UNSPECIFIED;
+  }
+}
+
+// static
+mojom::AccountType AccountManagerAsh::ToMojoAccountType(
+    const chromeos::account_manager::AccountType& account_type) {
+  switch (account_type) {
+    case chromeos::account_manager::AccountType::ACCOUNT_TYPE_UNSPECIFIED:
+      return mojom::AccountType::kUnspecified;
+    case chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA:
+      return mojom::AccountType::kGaia;
+    case chromeos::account_manager::AccountType::ACCOUNT_TYPE_ACTIVE_DIRECTORY:
+      return mojom::AccountType::kActiveDirectory;
+  }
+}
+
+void AccountManagerAsh::FlushMojoForTesting() {
+  observers_.FlushForTesting();
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/account_manager_ash.h b/chrome/browser/chromeos/crosapi/account_manager_ash.h
index c8965cb2..fb25c7c 100644
--- a/chrome/browser/chromeos/crosapi/account_manager_ash.h
+++ b/chrome/browser/chromeos/crosapi/account_manager_ash.h
@@ -5,21 +5,19 @@
 #ifndef CHROME_BROWSER_CHROMEOS_CROSAPI_ACCOUNT_MANAGER_ASH_H_
 #define CHROME_BROWSER_CHROMEOS_CROSAPI_ACCOUNT_MANAGER_ASH_H_
 
+#include "chromeos/components/account_manager/account_manager.h"
 #include "chromeos/crosapi/mojom/account_manager.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 
-namespace chromeos {
-class AccountManager;
-}  // namespace chromeos
-
 namespace crosapi {
 
 // Implements the |crosapi::mojom::AccountManager| interface in ash-chrome.
 // It enables lacros-chrome to interact with accounts stored in the Chrome OS
 // Account Manager.
-class AccountManagerAsh : public mojom::AccountManager {
+class AccountManagerAsh : public mojom::AccountManager,
+                          public chromeos::AccountManager::Observer {
  public:
   AccountManagerAsh(chromeos::AccountManager* account_manager,
                     mojo::PendingReceiver<mojom::AccountManager> receiver);
@@ -31,8 +29,27 @@
   void IsInitialized(IsInitializedCallback callback) override;
   void AddObserver(AddObserverCallback callback) override;
 
+  // chromeos::AccountManager::Observer:
+  void OnTokenUpserted(
+      const chromeos::AccountManager::Account& account) override;
+  void OnAccountRemoved(
+      const chromeos::AccountManager::Account& account) override;
+
  private:
   friend class AccountManagerAshTest;
+  friend class TestAccountManagerObserver;
+
+  // Following util functions are static for ease of testing.
+  static chromeos::AccountManager::Account FromMojoAccount(
+      const mojom::AccountPtr& mojom_account);
+  static mojom::AccountPtr ToMojoAccount(
+      const chromeos::AccountManager::Account& account);
+  static chromeos::account_manager::AccountType FromMojoAccountType(
+      const mojom::AccountType& account_type);
+  static mojom::AccountType ToMojoAccountType(
+      const chromeos::account_manager::AccountType& account_type);
+
+  void FlushMojoForTesting();
 
   chromeos::AccountManager* const account_manager_;
   mojo::Receiver<mojom::AccountManager> receiver_;
diff --git a/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc b/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc
index 77fad22..836a8a7 100644
--- a/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc
+++ b/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc
@@ -6,13 +6,17 @@
 
 #include <cstddef>
 #include <memory>
+#include <type_traits>
+#include <utility>
 
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/task_environment.h"
 #include "chromeos/components/account_manager/account_manager.h"
+#include "chromeos/components/account_manager/tokens.pb.h"
 #include "chromeos/crosapi/mojom/account_manager.mojom-test-utils.h"
 #include "chromeos/crosapi/mojom/account_manager.mojom.h"
+#include "components/prefs/testing_pref_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -21,6 +25,66 @@
 
 namespace crosapi {
 
+namespace {
+
+const char kFakeGaiaId[] = "fake-gaia-id";
+const char kFakeEmail[] = "fake_email@example.com";
+const char kFakeToken[] = "fake-token";
+
+}  // namespace
+
+class TestAccountManagerObserver
+    : public mojom::AccountManagerObserverInterceptorForTesting {
+ public:
+  TestAccountManagerObserver() : receiver_(this) {}
+
+  TestAccountManagerObserver(const TestAccountManagerObserver&) = delete;
+  TestAccountManagerObserver& operator=(const TestAccountManagerObserver&) =
+      delete;
+  ~TestAccountManagerObserver() override = default;
+
+  void Observe(
+      mojom::AccountManagerAsyncWaiter* const account_manager_async_waiter) {
+    mojo::PendingReceiver<mojom::AccountManagerObserver> receiver;
+    account_manager_async_waiter->AddObserver(&receiver);
+    receiver_.Bind(std::move(receiver));
+  }
+
+  int GetNumOnTokenUpsertedCalls() { return num_token_upserted_calls_; }
+
+  chromeos::AccountManager::Account GetLastUpsertedAccount() {
+    return last_upserted_account_;
+  }
+
+  int GetNumOnAccountRemovedCalls() { return num_account_removed_calls_; }
+
+  chromeos::AccountManager::Account GetLastRemovedAccount() {
+    return last_removed_account_;
+  }
+
+ private:
+  // mojom::AccountManagerObserverInterceptorForTesting override:
+  AccountManagerObserver* GetForwardingInterface() override { return this; }
+
+  // mojom::AccountManagerObserverInterceptorForTesting override:
+  void OnTokenUpserted(mojom::AccountPtr account) override {
+    ++num_token_upserted_calls_;
+    last_upserted_account_ = AccountManagerAsh::FromMojoAccount(account);
+  }
+
+  // mojom::AccountManagerObserverInterceptorForTesting override:
+  void OnAccountRemoved(mojom::AccountPtr account) override {
+    ++num_account_removed_calls_;
+    last_removed_account_ = AccountManagerAsh::FromMojoAccount(account);
+  }
+
+  int num_token_upserted_calls_ = 0;
+  int num_account_removed_calls_ = 0;
+  chromeos::AccountManager::Account last_upserted_account_;
+  chromeos::AccountManager::Account last_removed_account_;
+  mojo::Receiver<mojom::AccountManagerObserver> receiver_;
+};
+
 class AccountManagerAshTest : public ::testing::Test {
  public:
   AccountManagerAshTest() = default;
@@ -39,11 +103,15 @@
 
   void RunAllPendingTasks() { task_environment_.RunUntilIdle(); }
 
+  void FlushMojoForTesting() { account_manager_ash_->FlushMojoForTesting(); }
+
   // Returns |true| if initialization was successful.
   bool InitializeAccountManager() {
     base::RunLoop run_loop;
     account_manager_.InitializeInEphemeralMode(
         test_url_loader_factory_.GetSafeWeakWrapper(), run_loop.QuitClosure());
+    account_manager_.SetPrefService(&pref_service_);
+    account_manager_.RegisterPrefs(pref_service_.registry());
     run_loop.Run();
     return account_manager_.IsInitialized();
   }
@@ -54,10 +122,13 @@
     return account_manager_async_waiter_.get();
   }
 
+  chromeos::AccountManager* account_manager() { return &account_manager_; }
+
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
 
   network::TestURLLoaderFactory test_url_loader_factory_;
+  TestingPrefServiceSimple pref_service_;
   chromeos::AccountManager account_manager_;
   mojo::Remote<mojom::AccountManager> remote_;
   std::unique_ptr<AccountManagerAsh> account_manager_ash_;
@@ -96,4 +167,38 @@
   EXPECT_EQ(0, GetNumObservers());
 }
 
+TEST_F(AccountManagerAshTest, LacrosObserversAreNotifiedOnAccountUpdates) {
+  const chromeos::AccountManager::AccountKey kTestAccountKey{
+      kFakeGaiaId, chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA};
+  ASSERT_TRUE(InitializeAccountManager());
+  TestAccountManagerObserver observer;
+  observer.Observe(account_manager_async_waiter());
+  ASSERT_EQ(1, GetNumObservers());
+
+  EXPECT_EQ(0, observer.GetNumOnTokenUpsertedCalls());
+  account_manager()->UpsertAccount(kTestAccountKey, kFakeEmail, kFakeToken);
+  FlushMojoForTesting();
+  EXPECT_EQ(1, observer.GetNumOnTokenUpsertedCalls());
+  EXPECT_EQ(kTestAccountKey, observer.GetLastUpsertedAccount().key);
+  EXPECT_EQ(kFakeEmail, observer.GetLastUpsertedAccount().raw_email);
+}
+
+TEST_F(AccountManagerAshTest, LacrosObserversAreNotifiedOnAccountRemovals) {
+  const chromeos::AccountManager::AccountKey kTestAccountKey{
+      kFakeGaiaId, chromeos::account_manager::AccountType::ACCOUNT_TYPE_GAIA};
+  ASSERT_TRUE(InitializeAccountManager());
+  TestAccountManagerObserver observer;
+  observer.Observe(account_manager_async_waiter());
+  ASSERT_EQ(1, GetNumObservers());
+  account_manager()->UpsertAccount(kTestAccountKey, kFakeEmail, kFakeToken);
+  FlushMojoForTesting();
+
+  EXPECT_EQ(0, observer.GetNumOnAccountRemovedCalls());
+  account_manager()->RemoveAccount(kTestAccountKey);
+  FlushMojoForTesting();
+  EXPECT_EQ(1, observer.GetNumOnAccountRemovedCalls());
+  EXPECT_EQ(kTestAccountKey, observer.GetLastRemovedAccount().key);
+  EXPECT_EQ(kFakeEmail, observer.GetLastRemovedAccount().raw_email);
+}
+
 }  // namespace crosapi
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index a5c7f16..62a522ef 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -123,6 +123,7 @@
 #include "chromeos/services/assistant/public/cpp/assistant_service.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "chromeos/settings/cros_settings_names.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 #include "components/policy/core/browser/policy_conversions.h"
@@ -812,7 +813,8 @@
                             chromeos::WindowStateType expected_type,
                             base::OnceCallback<void(bool)> callback)
       : expected_type_(expected_type), callback_(std::move(callback)) {
-    DCHECK_NE(window->GetProperty(ash::kWindowStateTypeKey), expected_type_);
+    DCHECK_NE(window->GetProperty(chromeos::kWindowStateTypeKey),
+              expected_type_);
     scoped_observer_.Add(window);
   }
   ~WindowStateChangeObserver() override {}
@@ -822,8 +824,8 @@
                                const void* key,
                                intptr_t old) override {
     DCHECK(scoped_observer_.IsObserving(window));
-    if (key == ash::kWindowStateTypeKey &&
-        window->GetProperty(ash::kWindowStateTypeKey) == expected_type_) {
+    if (key == chromeos::kWindowStateTypeKey &&
+        window->GetProperty(chromeos::kWindowStateTypeKey) == expected_type_) {
       scoped_observer_.RemoveAll();
       std::move(callback_).Run(/*success=*/true);
     }
@@ -3584,7 +3586,7 @@
     window_info.window_type = GetAppWindowType(
         static_cast<ash::AppType>(window->GetProperty(aura::client::kAppType)));
     window_info.state_type =
-        ToWindowStateType(window->GetProperty(ash::kWindowStateTypeKey));
+        ToWindowStateType(window->GetProperty(chromeos::kWindowStateTypeKey));
     window_info.bounds_in_root =
         ToBoundsDictionary(window->GetBoundsInRootWindow());
     window_info.target_bounds = ToBoundsDictionary(window->GetTargetBounds());
@@ -3715,7 +3717,7 @@
 
   chromeos::WindowStateType expected_state =
       GetExpectedWindowState(params->change.event_type);
-  if (window->GetProperty(ash::kWindowStateTypeKey) == expected_state) {
+  if (window->GetProperty(chromeos::kWindowStateTypeKey) == expected_state) {
     if (params->change.fail_if_no_change &&
         *(params->change.fail_if_no_change)) {
       return RespondNow(
@@ -4081,6 +4083,40 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// AutotestPrivateActivateAdjacentDesksToTargetIndexFunction
+///////////////////////////////////////////////////////////////////////////////
+
+AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
+    AutotestPrivateActivateAdjacentDesksToTargetIndexFunction() = default;
+AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
+    ~AutotestPrivateActivateAdjacentDesksToTargetIndexFunction() = default;
+
+ExtensionFunction::ResponseAction
+AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::Run() {
+  std::unique_ptr<
+      api::autotest_private::ActivateAdjacentDesksToTargetIndex::Params>
+      params(api::autotest_private::ActivateAdjacentDesksToTargetIndex::Params::
+                 Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  if (!ash::AutotestDesksApi().ActivateAdjacentDesksToTargetIndex(
+          params->index,
+          base::BindOnce(
+              &AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
+                  OnAnimationComplete,
+              this))) {
+    return RespondNow(OneArgument(std::make_unique<base::Value>(false)));
+  }
+
+  return RespondLater();
+}
+
+void AutotestPrivateActivateAdjacentDesksToTargetIndexFunction::
+    OnAnimationComplete() {
+  Respond(OneArgument(std::make_unique<base::Value>(true)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // AutotestPrivateMouseClickFunction
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
index 5d96a90..6818901 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.h
@@ -1109,6 +1109,21 @@
   void OnAnimationComplete();
 };
 
+class AutotestPrivateActivateAdjacentDesksToTargetIndexFunction
+    : public ExtensionFunction {
+ public:
+  AutotestPrivateActivateAdjacentDesksToTargetIndexFunction();
+  DECLARE_EXTENSION_FUNCTION(
+      "autotestPrivate.activateAdjacentDesksToTargetIndex",
+      AUTOTESTPRIVATE_ACTIVATEADJACENTDESKSTOTARGETINDEX)
+
+ private:
+  ~AutotestPrivateActivateAdjacentDesksToTargetIndexFunction() override;
+  ResponseAction Run() override;
+
+  void OnAnimationComplete();
+};
+
 class AutotestPrivateMouseClickFunction : public ExtensionFunction {
  public:
   AutotestPrivateMouseClickFunction();
diff --git a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
index 6b6cb62..b277dfd 100644
--- a/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
+++ b/chrome/browser/chromeos/kerberos/kerberos_credentials_manager.cc
@@ -935,7 +935,7 @@
   // settings. Consider creating a standalone reauth dialog.
   chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
       primary_profile_,
-      chromeos::settings::mojom::kKerberosSubpagePath +
+      chromeos::settings::mojom::kKerberosAccountsSubpagePath +
           std::string("?kerberos_reauth=") +
           net::EscapeQueryParamValue(principal_name, false /* use_plus */));
 
diff --git a/chrome/browser/chromeos/nearby/BUILD.gn b/chrome/browser/chromeos/nearby/BUILD.gn
new file mode 100644
index 0000000..9e6f030e
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/BUILD.gn
@@ -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.
+
+# Note: This target is separated from the rest of //chrome/browser/chromeos so
+# that it can depend on the bluetooth::mojom::Adapter interface, which is only
+# visible to a limited set of clients.
+source_set("bluetooth_adapter_util") {
+  sources = [
+    "bluetooth_adapter_util.cc",
+    "bluetooth_adapter_util.h",
+  ]
+
+  deps = [
+    "//base",
+    "//device/bluetooth",
+    "//device/bluetooth:deprecated_experimental_mojo",
+    "//mojo/public/cpp/bindings",
+  ]
+}
diff --git a/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc b/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc
new file mode 100644
index 0000000..0ce9a81
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/bluetooth_adapter_util.cc
@@ -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.
+
+#include "chrome/browser/chromeos/nearby/bluetooth_adapter_util.h"
+
+#include <memory>
+#include <utility>
+
+#include "device/bluetooth/adapter.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+namespace chromeos {
+namespace nearby {
+
+void MakeSelfOwnedBluetoothAdapter(
+    mojo::PendingReceiver<bluetooth::mojom::Adapter> pending_receiver,
+    scoped_refptr<device::BluetoothAdapter> adapter) {
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<bluetooth::Adapter>(std::move(adapter)),
+      std::move(pending_receiver));
+}
+
+}  // namespace nearby
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/nearby/bluetooth_adapter_util.h b/chrome/browser/chromeos/nearby/bluetooth_adapter_util.h
new file mode 100644
index 0000000..a889eef
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/bluetooth_adapter_util.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_NEARBY_BLUETOOTH_ADAPTER_UTIL_H_
+#define CHROME_BROWSER_CHROMEOS_NEARBY_BLUETOOTH_ADAPTER_UTIL_H_
+
+#include "base/memory/ref_counted.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+
+namespace bluetooth {
+namespace mojom {
+class Adapter;
+}  // namespace mojom
+}  // namespace bluetooth
+
+namespace device {
+class BluetoothAdapter;
+}  // namespace device
+
+namespace chromeos {
+namespace nearby {
+
+// Note: This helper function is implemented in its own file so that it can
+// depend on the bluetooth::mojom::Adapter interface, which is only visible to a
+// limited set of clients.
+void MakeSelfOwnedBluetoothAdapter(
+    mojo::PendingReceiver<bluetooth::mojom::Adapter> pending_receiver,
+    scoped_refptr<device::BluetoothAdapter> adapter);
+
+}  // namespace nearby
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_NEARBY_BLUETOOTH_ADAPTER_UTIL_H_
diff --git a/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.cc b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.cc
new file mode 100644
index 0000000..231a960
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.cc
@@ -0,0 +1,138 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.h"
+
+#include "chrome/browser/chromeos/nearby/bluetooth_adapter_util.h"
+#include "chrome/browser/nearby_sharing/webrtc_signaling_messenger.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sharing/webrtc/ice_config_fetcher.h"
+#include "chrome/browser/sharing/webrtc/sharing_mojo_service.h"
+#include "content/public/browser/storage_partition.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "net/base/network_isolation_key.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+#include "services/network/public/mojom/p2p_trusted.mojom.h"
+
+namespace chromeos {
+namespace nearby {
+namespace {
+
+template <typename T>
+struct MojoPipe {
+  mojo::PendingRemote<T> remote;
+  mojo::PendingReceiver<T> receiver{remote.InitWithNewPipeAndPassReceiver()};
+};
+
+class P2PTrustedSocketManagerClientImpl
+    : public network::mojom::P2PTrustedSocketManagerClient {
+ public:
+  explicit P2PTrustedSocketManagerClientImpl(
+      mojo::PendingRemote<network::mojom::P2PTrustedSocketManager>
+          socket_manager)
+      : socket_manager_(std::move(socket_manager)) {}
+  ~P2PTrustedSocketManagerClientImpl() override = default;
+
+  // network::mojom::P2PTrustedSocketManagerClient:
+  void InvalidSocketPortRangeRequested() override { NOTIMPLEMENTED(); }
+  void DumpPacket(const std::vector<uint8_t>& packet_header,
+                  uint64_t packet_length,
+                  bool incoming) override {
+    NOTIMPLEMENTED();
+  }
+
+ private:
+  mojo::Remote<network::mojom::P2PTrustedSocketManager> socket_manager_;
+};
+
+}  // namespace
+
+NearbyConnectionsDependenciesProvider::NearbyConnectionsDependenciesProvider(
+    Profile* profile,
+    signin::IdentityManager* identity_manager)
+    : profile_(profile), identity_manager_(identity_manager) {
+  DCHECK(identity_manager_);
+}
+
+NearbyConnectionsDependenciesProvider::
+    ~NearbyConnectionsDependenciesProvider() = default;
+
+location::nearby::connections::mojom::NearbyConnectionsDependenciesPtr
+NearbyConnectionsDependenciesProvider::GetDependencies() {
+  if (shut_down_)
+    return nullptr;
+
+  auto dependencies = location::nearby::connections::mojom::
+      NearbyConnectionsDependencies::New();
+
+  if (device::BluetoothAdapterFactory::IsBluetoothSupported())
+    dependencies->bluetooth_adapter = GetBluetoothAdapterPendingRemote();
+  else
+    dependencies->bluetooth_adapter = mojo::NullRemote();
+
+  dependencies->webrtc_dependencies = GetWebRtcDependencies();
+
+  return dependencies;
+}
+
+void NearbyConnectionsDependenciesProvider::Shutdown() {
+  shut_down_ = true;
+}
+
+mojo::PendingRemote<bluetooth::mojom::Adapter>
+NearbyConnectionsDependenciesProvider::GetBluetoothAdapterPendingRemote() {
+  mojo::PendingRemote<bluetooth::mojom::Adapter> pending_adapter;
+  device::BluetoothAdapterFactory::Get()->GetAdapter(
+      base::BindOnce(&MakeSelfOwnedBluetoothAdapter,
+                     pending_adapter.InitWithNewPipeAndPassReceiver()));
+
+  return pending_adapter;
+}
+
+location::nearby::connections::mojom::WebRtcDependenciesPtr
+NearbyConnectionsDependenciesProvider::GetWebRtcDependencies() {
+  auto* network_context =
+      content::BrowserContext::GetDefaultStoragePartition(profile_)
+          ->GetNetworkContext();
+
+  MojoPipe<network::mojom::P2PTrustedSocketManagerClient> socket_manager_client;
+  MojoPipe<network::mojom::P2PTrustedSocketManager> trusted_socket_manager;
+  MojoPipe<network::mojom::P2PSocketManager> socket_manager;
+  MojoPipe<network::mojom::MdnsResponder> mdns_responder;
+
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<P2PTrustedSocketManagerClientImpl>(
+          std::move(trusted_socket_manager.remote)),
+      std::move(socket_manager_client.receiver));
+
+  // Create socket manager.
+  network_context->CreateP2PSocketManager(
+      net::NetworkIsolationKey::CreateTransient(),
+      std::move(socket_manager_client.remote),
+      std::move(trusted_socket_manager.receiver),
+      std::move(socket_manager.receiver));
+
+  // Create mdns responder.
+  network_context->CreateMdnsResponder(std::move(mdns_responder.receiver));
+
+  // Create ice config fetcher.
+  auto url_loader_factory = profile_->GetURLLoaderFactory();
+  MojoPipe<sharing::mojom::IceConfigFetcher> ice_config_fetcher;
+  mojo::MakeSelfOwnedReceiver(
+      std::make_unique<IceConfigFetcher>(url_loader_factory),
+      std::move(ice_config_fetcher.receiver));
+
+  MojoPipe<sharing::mojom::WebRtcSignalingMessenger> messenger;
+  mojo::MakeSelfOwnedReceiver(std::make_unique<WebRtcSignalingMessenger>(
+                                  identity_manager_, url_loader_factory),
+                              std::move(messenger.receiver));
+
+  return location::nearby::connections::mojom::WebRtcDependencies::New(
+      std::move(socket_manager.remote), std::move(mdns_responder.remote),
+      std::move(ice_config_fetcher.remote), std::move(messenger.remote));
+}
+
+}  // namespace nearby
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.h b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.h
new file mode 100644
index 0000000..f07b7f2
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_NEARBY_NEARBY_CONNECTIONS_DEPENDENCIES_PROVIDER_H_
+#define CHROME_BROWSER_CHROMEOS_NEARBY_NEARBY_CONNECTIONS_DEPENDENCIES_PROVIDER_H_
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/services/nearby/public/mojom/sharing.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class Profile;
+
+namespace signin {
+class IdentityManager;
+}  // namespace signin
+
+namespace chromeos {
+namespace nearby {
+
+// Provides dependencies required to initialize Nearby Connections. Implemented
+// as a KeyedService because WebRTC dependencies are linked to the user's
+// identity.
+class NearbyConnectionsDependenciesProvider : public KeyedService {
+ public:
+  NearbyConnectionsDependenciesProvider(
+      Profile* profile,
+      signin::IdentityManager* identity_manager);
+  ~NearbyConnectionsDependenciesProvider() override;
+
+  // Note: Returns null during session shutdown.
+  location::nearby::connections::mojom::NearbyConnectionsDependenciesPtr
+  GetDependencies();
+
+ private:
+  // KeyedService:
+  void Shutdown() override;
+
+  mojo::PendingRemote<bluetooth::mojom::Adapter>
+  GetBluetoothAdapterPendingRemote();
+
+  location::nearby::connections::mojom::WebRtcDependenciesPtr
+  GetWebRtcDependencies();
+
+  bool shut_down_ = false;
+
+  Profile* profile_;
+  signin::IdentityManager* identity_manager_;
+
+  base::WeakPtrFactory<NearbyConnectionsDependenciesProvider> weak_ptr_factory_{
+      this};
+};
+
+}  // namespace nearby
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_NEARBY_NEARBY_CONNECTIONS_DEPENDENCIES_PROVIDER_H_
diff --git a/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.cc b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.cc
new file mode 100644
index 0000000..a867239
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.h"
+
+#include "chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+namespace chromeos {
+namespace nearby {
+
+// static
+NearbyConnectionsDependenciesProvider*
+NearbyConnectionsDependenciesProviderFactory::GetForProfile(Profile* profile) {
+  return static_cast<NearbyConnectionsDependenciesProvider*>(
+      NearbyConnectionsDependenciesProviderFactory::GetInstance()
+          ->GetServiceForBrowserContext(profile, /*create=*/true));
+}
+
+// static
+NearbyConnectionsDependenciesProviderFactory*
+NearbyConnectionsDependenciesProviderFactory::GetInstance() {
+  return base::Singleton<NearbyConnectionsDependenciesProviderFactory>::get();
+}
+
+NearbyConnectionsDependenciesProviderFactory::
+    NearbyConnectionsDependenciesProviderFactory()
+    : BrowserContextKeyedServiceFactory(
+          "NearbyConnectionsDependenciesProvider",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(IdentityManagerFactory::GetInstance());
+}
+
+NearbyConnectionsDependenciesProviderFactory::
+    ~NearbyConnectionsDependenciesProviderFactory() = default;
+
+KeyedService*
+NearbyConnectionsDependenciesProviderFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return new NearbyConnectionsDependenciesProvider(
+      profile, IdentityManagerFactory::GetForProfile(profile));
+}
+
+bool NearbyConnectionsDependenciesProviderFactory::
+    ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+}  // namespace nearby
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.h b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.h
new file mode 100644
index 0000000..b6fd189a
--- /dev/null
+++ b/chrome/browser/chromeos/nearby/nearby_connections_dependencies_provider_factory.h
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_NEARBY_NEARBY_CONNECTIONS_DEPENDENCIES_PROVIDER_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_NEARBY_NEARBY_CONNECTIONS_DEPENDENCIES_PROVIDER_FACTORY_H_
+
+#include "base/memory/singleton.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class Profile;
+
+namespace chromeos {
+namespace nearby {
+
+class NearbyConnectionsDependenciesProvider;
+
+class NearbyConnectionsDependenciesProviderFactory
+    : public BrowserContextKeyedServiceFactory {
+ public:
+  static NearbyConnectionsDependenciesProvider* GetForProfile(Profile* profile);
+
+  static NearbyConnectionsDependenciesProviderFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<
+      NearbyConnectionsDependenciesProviderFactory>;
+
+  NearbyConnectionsDependenciesProviderFactory();
+  NearbyConnectionsDependenciesProviderFactory(
+      const NearbyConnectionsDependenciesProviderFactory&) = delete;
+  NearbyConnectionsDependenciesProviderFactory& operator=(
+      const NearbyConnectionsDependenciesProviderFactory&) = delete;
+  ~NearbyConnectionsDependenciesProviderFactory() override;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+};
+
+}  // namespace nearby
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_NEARBY_NEARBY_CONNECTIONS_DEPENDENCIES_PROVIDER_FACTORY_H_
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 41a09a6..84c26c2 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -198,11 +198,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "arc-usb-storage-ui",
-    "owners": [ "fukino" ],
-    "expiry_milestone": 78
-  },
-  {
     "name": "ash-debug-shortcuts",
     "owners": [ "//ash/OWNERS" ],
     // Used by developers for debugging and to dump extra information to logs
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 122df7a..2e00bf1 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3602,10 +3602,6 @@
 const char kArcUsbHostDescription[] =
     "Allow Android apps to use USB host feature on ChromeOS devices.";
 
-const char kArcUsbStorageUIName[] = "Enable ARC USB Storage UI";
-const char kArcUsbStorageUIDescription[] =
-    "Enable experimental UI for controlling ARC access to USB storage devices.";
-
 const char kAshEnablePipRoundedCornersName[] =
     "Enable Picture-in-Picture rounded corners.";
 const char kAshEnablePipRoundedCornersDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index db42c69..f6bb6810f 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2077,9 +2077,6 @@
 extern const char kArcUsbHostName[];
 extern const char kArcUsbHostDescription[];
 
-extern const char kArcUsbStorageUIName[];
-extern const char kArcUsbStorageUIDescription[];
-
 extern const char kAshEnablePipRoundedCornersName[];
 extern const char kAshEnablePipRoundedCornersDescription[];
 
diff --git a/chrome/browser/nearby_sharing/nearby_receive_manager.cc b/chrome/browser/nearby_sharing/nearby_receive_manager.cc
index 6456396..b36983b 100644
--- a/chrome/browser/nearby_sharing/nearby_receive_manager.cc
+++ b/chrome/browser/nearby_sharing/nearby_receive_manager.cc
@@ -29,9 +29,9 @@
                   << TransferMetadata::StatusToString(
                          transfer_metadata.status());
 
+  NotifyOnTransferUpdate(share_target, transfer_metadata);
   if (TransferMetadata::Status::kAwaitingLocalConfirmation == status) {
     share_targets_map_.insert_or_assign(share_target.id, share_target);
-    NotifyOnIncomingShare(share_target, transfer_metadata.token());
   } else if (transfer_metadata.is_final_status()) {
     share_targets_map_.erase(share_target.id);
   }
@@ -109,10 +109,10 @@
   }
 }
 
-void NearbyReceiveManager::NotifyOnIncomingShare(
+void NearbyReceiveManager::NotifyOnTransferUpdate(
     const ShareTarget& share_target,
-    const base::Optional<std::string>& connection_token) {
+    const TransferMetadata& metadata) {
   for (auto& remote : observers_set_) {
-    remote->OnIncomingShare(share_target, connection_token);
+    remote->OnTransferUpdate(share_target, metadata.ToMojo());
   }
 }
diff --git a/chrome/browser/nearby_sharing/nearby_receive_manager.h b/chrome/browser/nearby_sharing/nearby_receive_manager.h
index 88e97d3..4c6ffe3b 100644
--- a/chrome/browser/nearby_sharing/nearby_receive_manager.h
+++ b/chrome/browser/nearby_sharing/nearby_receive_manager.h
@@ -50,9 +50,8 @@
   void OnShutdown() override {}
 
  private:
-  void NotifyOnIncomingShare(
-      const ShareTarget& share_target,
-      const base::Optional<std::string>& connection_token);
+  void NotifyOnTransferUpdate(const ShareTarget& share_target,
+                              const TransferMetadata& metadata);
 
   NearbySharingService* nearby_sharing_service_;
 
diff --git a/chrome/browser/nearby_sharing/nearby_receive_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_receive_manager_unittest.cc
index 1c44d02..e973611 100644
--- a/chrome/browser/nearby_sharing/nearby_receive_manager_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_receive_manager_unittest.cc
@@ -23,14 +23,14 @@
     in_high_visibility_ = in_high_visibility;
   }
 
-  void OnIncomingShare(
+  void OnTransferUpdate(
       const ShareTarget& share_target,
-      const base::Optional<std::string>& connection_token) override {
+      nearby_share::mojom::TransferMetadataPtr metadata) override {
     last_share_target_ = share_target;
-    last_connection_token_ = connection_token;
+    last_metadata_ = *metadata;
   }
 
-  base::Optional<std::string> last_connection_token_;
+  base::Optional<nearby_share::mojom::TransferMetadata> last_metadata_;
   base::Optional<bool> in_high_visibility_;
   ShareTarget last_share_target_;
   mojo::Receiver<nearby_share::mojom::ReceiveObserver> receiver_{this};
@@ -164,8 +164,8 @@
   receive_manager_.OnTransferUpdate(share_target_, transfer_metadata_);
   FlushMojoMessages();
   EXPECT_EQ(share_target_.id, observer_.last_share_target_.id);
-  ASSERT_TRUE(observer_.last_connection_token_.has_value());
-  EXPECT_EQ("1234", observer_.last_connection_token_);
+  ASSERT_TRUE(observer_.last_metadata_.has_value());
+  EXPECT_EQ("1234", observer_.last_metadata_->token);
 
   ExpectAccept();
   bool success = false;
@@ -179,8 +179,8 @@
   receive_manager_.OnTransferUpdate(share_target_, transfer_metadata_);
   FlushMojoMessages();
   EXPECT_EQ(share_target_.id, observer_.last_share_target_.id);
-  ASSERT_TRUE(observer_.last_connection_token_.has_value());
-  EXPECT_EQ("1234", observer_.last_connection_token_);
+  ASSERT_TRUE(observer_.last_metadata_.has_value());
+  EXPECT_EQ("1234", observer_.last_metadata_->token);
 
   ExpectReject();
   bool success = false;
@@ -195,8 +195,8 @@
   receive_manager_.OnTransferUpdate(share_target_, transfer_metadata_);
   FlushMojoMessages();
   EXPECT_EQ(share_target_.id, observer_.last_share_target_.id);
-  ASSERT_TRUE(observer_.last_connection_token_.has_value());
-  EXPECT_EQ("1234", observer_.last_connection_token_);
+  ASSERT_TRUE(observer_.last_metadata_.has_value());
+  EXPECT_EQ("1234", observer_.last_metadata_->token);
 
   // Simulate the sender canceling before we accept the share target and causing
   // the accept to fail before hitting the service.
@@ -217,8 +217,8 @@
   receive_manager_.OnTransferUpdate(share_target_, transfer_metadata_);
   FlushMojoMessages();
   EXPECT_EQ(share_target_.id, observer_.last_share_target_.id);
-  ASSERT_TRUE(observer_.last_connection_token_.has_value());
-  EXPECT_EQ("1234", observer_.last_connection_token_);
+  ASSERT_TRUE(observer_.last_metadata_.has_value());
+  EXPECT_EQ("1234", observer_.last_metadata_->token);
 
   // Simulate the sender canceling before we reject the share target and causing
   // the reject to fail before hitting the service.
diff --git a/chrome/browser/nearby_sharing/transfer_metadata.cc b/chrome/browser/nearby_sharing/transfer_metadata.cc
index 41fb855a..9b88e1a 100644
--- a/chrome/browser/nearby_sharing/transfer_metadata.cc
+++ b/chrome/browser/nearby_sharing/transfer_metadata.cc
@@ -4,6 +4,7 @@
 
 #include <utility>
 
+#include "chrome/browser/nearby_sharing/logging/logging.h"
 #include "chrome/browser/nearby_sharing/transfer_metadata.h"
 
 // static
@@ -63,6 +64,58 @@
   }
 }
 
+// static
+nearby_share::mojom::TransferStatus TransferMetadata::StatusToMojo(
+    Status status) {
+  switch (status) {
+    case Status::kUnknown:
+      return nearby_share::mojom::TransferStatus::kUnknown;
+    case Status::kConnecting:
+      return nearby_share::mojom::TransferStatus::kConnecting;
+    case Status::kAwaitingLocalConfirmation:
+      return nearby_share::mojom::TransferStatus::kAwaitingLocalConfirmation;
+    case Status::kAwaitingRemoteAcceptance:
+      return nearby_share::mojom::TransferStatus::kAwaitingRemoteAcceptance;
+    case Status::kAwaitingRemoteAcceptanceFailed:
+      return nearby_share::mojom::TransferStatus::
+          kAwaitingRemoteAcceptanceFailed;
+    case Status::kInProgress:
+      return nearby_share::mojom::TransferStatus::kInProgress;
+    case Status::kComplete:
+      return nearby_share::mojom::TransferStatus::kComplete;
+    case Status::kFailed:
+      return nearby_share::mojom::TransferStatus::kFailed;
+    case Status::kRejected:
+      return nearby_share::mojom::TransferStatus::kRejected;
+    case Status::kCancelled:
+      return nearby_share::mojom::TransferStatus::kCancelled;
+    case Status::kTimedOut:
+      return nearby_share::mojom::TransferStatus::kTimedOut;
+    case Status::kMediaUnavailable:
+      return nearby_share::mojom::TransferStatus::kMediaUnavailable;
+    case Status::kNotEnoughSpace:
+      return nearby_share::mojom::TransferStatus::kNotEnoughSpace;
+    case Status::kUnsupportedAttachmentType:
+      return nearby_share::mojom::TransferStatus::kUnsupportedAttachmentType;
+    case Status::kMediaDownloading:
+    case Status::kExternalProviderLaunched:
+      // These statuses are not used anywhere.
+      NOTREACHED();
+      return nearby_share::mojom::TransferStatus::kUnknown;
+  }
+  NOTREACHED();
+}
+
+nearby_share::mojom::TransferMetadataPtr TransferMetadata::ToMojo() const {
+  auto mojo_transfer_metadata = nearby_share::mojom::TransferMetadata::New();
+  mojo_transfer_metadata->status = StatusToMojo(status());
+  mojo_transfer_metadata->progress = progress();
+  mojo_transfer_metadata->token = token();
+  mojo_transfer_metadata->is_original = is_original();
+  mojo_transfer_metadata->is_final_status = is_final_status();
+  return mojo_transfer_metadata;
+}
+
 TransferMetadata::TransferMetadata(Status status,
                                    float progress,
                                    base::Optional<std::string> token,
diff --git a/chrome/browser/nearby_sharing/transfer_metadata.h b/chrome/browser/nearby_sharing/transfer_metadata.h
index af1520bc..f13123f85 100644
--- a/chrome/browser/nearby_sharing/transfer_metadata.h
+++ b/chrome/browser/nearby_sharing/transfer_metadata.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/optional.h"
+#include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom.h"
 #include "url/gurl.h"
 
 // Metadata about an ongoing transfer. Wraps transient data like status and
@@ -38,6 +39,8 @@
 
   static std::string StatusToString(TransferMetadata::Status status);
 
+  static nearby_share::mojom::TransferStatus StatusToMojo(Status status);
+
   TransferMetadata(Status status,
                    float progress,
                    base::Optional<std::string> token,
@@ -62,6 +65,8 @@
   // True if this |TransferMetadata| is the last status for this transfer.
   bool is_final_status() const { return is_final_status_; }
 
+  nearby_share::mojom::TransferMetadataPtr ToMojo() const;
+
  private:
   Status status_;
   float progress_;
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.cc b/chrome/browser/policy/messaging_layer/public/report_client.cc
index 1f9f80c..0a3f4200 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.cc
+++ b/chrome/browser/policy/messaging_layer/public/report_client.cc
@@ -168,32 +168,23 @@
     return;
   }
 
-  class ProcessRecordContext : public TaskRunnerContext<bool> {
-   public:
-    ProcessRecordContext(
-        EncryptedRecord record,
-        std::vector<EncryptedRecord>* records,
-        base::OnceCallback<void(bool)> processed_callback,
-        scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner)
-        : TaskRunnerContext<bool>(std::move(processed_callback),
-                                  sequenced_task_runner),
-          records_(records),
-          record_(std::move(record)) {}
+  sequenced_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](std::vector<EncryptedRecord>* records, EncryptedRecord record,
+             base::OnceCallback<void(bool)> processed_cb) {
+            records->emplace_back(std::move(record));
+            std::move(processed_cb).Run(true);
+          },
+          base::Unretained(encrypted_records_.get()),
+          std::move(data.ValueOrDie()), std::move(processed_cb)));
+}
 
-   private:
-    ~ProcessRecordContext() override = default;
-
-    void OnStart() override {
-      records_->emplace_back(std::move(record_));
-      Response(true);
-    }
-
-    std::vector<EncryptedRecord>* const records_;
-    const EncryptedRecord record_;
-  };
-
-  Start<ProcessRecordContext>(data.ValueOrDie(), encrypted_records_.get(),
-                              std::move(processed_cb), sequenced_task_runner_);
+void ReportingClient::Uploader::ProcessGap(
+    SequencingInformation start,
+    uint64_t count,
+    base::OnceCallback<void(bool)> processed_cb) {
+  LOG(FATAL) << "Gap not implemented yet";
 }
 
 void ReportingClient::Uploader::Completed(Status final_status) {
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.h b/chrome/browser/policy/messaging_layer/public/report_client.h
index d6c9a03..03db7d10 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.h
+++ b/chrome/browser/policy/messaging_layer/public/report_client.h
@@ -227,6 +227,9 @@
 
     void ProcessRecord(StatusOr<EncryptedRecord> data,
                        base::OnceCallback<void(bool)> processed_cb) override;
+    void ProcessGap(SequencingInformation start,
+                    uint64_t count,
+                    base::OnceCallback<void(bool)> processed_cb) override;
 
     void Completed(Status final_status) override;
 
diff --git a/chrome/browser/policy/messaging_layer/storage/storage.cc b/chrome/browser/policy/messaging_layer/storage/storage.cc
index ea6314b..7bb64c5 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage.cc
@@ -138,6 +138,13 @@
                                       std::move(processed_cb));
   }
 
+  void ProcessGap(SequencingInformation start,
+                  uint64_t count,
+                  base::OnceCallback<void(bool)> processed_cb) override {
+    storage_interface_->ProcessGap(std::move(start), count,
+                                   std::move(processed_cb));
+  }
+
   void Completed(Status final_status) override {
     storage_interface_->Completed(final_status);
   }
diff --git a/chrome/browser/policy/messaging_layer/storage/storage.h b/chrome/browser/policy/messaging_layer/storage/storage.h
index 073d51cc..cbe929a9 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage.h
+++ b/chrome/browser/policy/messaging_layer/storage/storage.h
@@ -28,28 +28,8 @@
 // according to the priority.
 class Storage : public base::RefCountedThreadSafe<Storage> {
  public:
-  // Interface for Upload, which must be implemented by an object returned by
-  // |StartUpload| callback (see below).
-  // Every time Storage starts an upload (by timer or immediately after Write)
-  // it uses this interface to hand available records over to the actual
-  // uploader. Storage takes ownership of it and automatically discards after
-  // |Completed| returns. Similar to StorageQueue::UploaderInterface, but with
-  // added priority parameter.
-  class UploaderInterface {
-   public:
-    virtual ~UploaderInterface() = default;
-
-    // Unserializes every record and hands ownership over for processing (e.g.
-    // to add to the network message). Expects |processed_cb| to be called after
-    // the record or error status has been processed, with true if next record
-    // needs to be delivered and false if the Uploader should stop.
-    virtual void ProcessRecord(StatusOr<EncryptedRecord> record,
-                               base::OnceCallback<void(bool)> processed_cb) = 0;
-
-    // Finalizes the upload (e.g. sends the message to the server and gets
-    // response).
-    virtual void Completed(Status final_status) = 0;
-  };
+  // Interface for Upload, forwarding to StorageQueue::UploaderInterface.
+  using UploaderInterface = StorageQueue::UploaderInterface;
 
   // Callback type for UploadInterface provider for specified queue.
   using StartUploadCb =
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_queue.cc b/chrome/browser/policy/messaging_layer/storage/storage_queue.cc
index 06a1cd1..1f9564f2 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_queue.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_queue.cc
@@ -656,6 +656,7 @@
   void CallCurrentRecord(uint64_t generation_id,
                          uint64_t seq_number,
                          base::StringPiece blob) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(read_sequence_checker_);
     google::protobuf::io::ArrayInputStream stream(  // Zero-copy stream.
         blob.data(), blob.size());
     EncryptedRecord encrypted_record;
@@ -1030,6 +1031,7 @@
 
 Status StorageQueue::RemoveConfirmedData(uint64_t seq_number) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(storage_queue_sequence_checker_);
+  // Update first available number, if new one is higher.
   if (first_seq_number_ <= seq_number) {
     first_seq_number_ = seq_number + 1;
   }
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_queue.h b/chrome/browser/policy/messaging_layer/storage/storage_queue.h
index 72ca81e7..fab33bb 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_queue.h
+++ b/chrome/browser/policy/messaging_layer/storage/storage_queue.h
@@ -113,6 +113,14 @@
     virtual void ProcessRecord(StatusOr<EncryptedRecord> record,
                                base::OnceCallback<void(bool)> processed_cb) = 0;
 
+    // Makes a note of a gap [start, start + count). Expects |processed_cb| to
+    // be called after the record or error status has been processed, with true
+    // if next record needs to be delivered and false if the Uploader should
+    // stop.
+    virtual void ProcessGap(SequencingInformation start,
+                            uint64_t count,
+                            base::OnceCallback<void(bool)> processed_cb) = 0;
+
     // Finalizes the upload (e.g. sends the message to server and gets
     // response). Called always, regardless of whether there were errors.
     virtual void Completed(Status final_status) = 0;
@@ -161,14 +169,15 @@
   // |uploader|->ProcessRecord (keeping ownership of the buffer) and resuming
   // after result callback returns 'true'. Only files that have been closed are
   // included in reading; |Upload| makes sure to close the last writeable file
-  // and create a new one before starting to send records to the |uploader|. If
-  // the monotonic order of sequencing is broken, INTERNAL error Status is
-  // reported. |Upload| can be stopped after any record by returning 'false' to
-  // |processed_cb| callback - in that case |Upload| will behave as if the end
-  // of data has been reached. While one or more |Upload|s are active, files can
-  // be added to the StorageQueue but cannot be deleted. If processing of the
-  // record takes significant time, |uploader| implementation should be offset
-  // to another thread to avoid locking StorageQueue.
+  // and create a new one before starting to send records to the |uploader|.
+  // If some records are not available or corrupt, |uploader|->ProcessGap is
+  // called. If the monotonic order of sequencing is broken, INTERNAL error
+  // Status is reported. |Upload| can be stopped after any record by returning
+  // 'false' to |processed_cb| callback - in that case |Upload| will behave as
+  // if the end of data has been reached. While one or more |Upload|s are
+  // active, files can be added to the StorageQueue but cannot be deleted. If
+  // processing of the record takes significant time, |uploader| implementation
+  // should be offset to another thread to avoid locking StorageQueue.
   // Helper methods: SwitchLastFileIfNotEmpty, CollectFilesForUpload.
   void Flush();
 
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc b/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc
index 1dca6c30..f5d442e 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_queue_unittest.cc
@@ -150,6 +150,12 @@
                           wrapped_record.record().data()));
   }
 
+  void ProcessGap(SequencingInformation start,
+                  uint64_t count,
+                  base::OnceCallback<void(bool)> processed_cb) override {
+    LOG(FATAL) << "Gap not implemented yet";
+  }
+
   void Completed(Status status) override { UploadComplete(status); }
 
   MOCK_METHOD(bool, UploadRecord, (uint64_t, base::StringPiece), (const));
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc b/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
index 8b36550..8cd7b8a 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
@@ -157,6 +157,12 @@
                           wrapped_record.record().data()));
   }
 
+  void ProcessGap(SequencingInformation start,
+                  uint64_t count,
+                  base::OnceCallback<void(bool)> processed_cb) override {
+    LOG(FATAL) << "Gap not implemented yet";
+  }
+
   void Completed(Status status) override { UploadComplete(status); }
 
   MOCK_METHOD(bool,
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index e691466..df09b21b 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -54,10 +54,8 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
-#include "extensions/browser/extension_registry.h"
-#include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
-#include "extensions/common/value_builder.h"
+#include "extensions/buildflags/buildflags.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
 #include "net/dns/mock_host_resolver.h"
@@ -76,6 +74,14 @@
 #include "chromeos/constants/chromeos_switches.h"
 #endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/extension_protocols.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/value_builder.h"
+#endif
+
 namespace {
 
 // A helper class which creates a SimpleURLLoader with an expected final status
@@ -143,17 +149,21 @@
 
   void Watch(Profile* profile) { observed_profiles_.Add(profile); }
 
+  bool destroyed() const { return destroyed_; }
+
+  void WaitForDestruction() { run_loop_.Run(); }
+
+ private:
   // ProfileObserver:
   void OnProfileWillBeDestroyed(Profile* profile) override {
     DCHECK(!destroyed_) << "Double profile destruction";
     destroyed_ = true;
+    run_loop_.Quit();
     observed_profiles_.Remove(profile);
   }
 
-  bool destroyed() const { return destroyed_; }
-
- private:
   bool destroyed_ = false;
+  base::RunLoop run_loop_;
   ScopedObserver<Profile, ProfileObserver> observed_profiles_{this};
 
   DISALLOW_COPY_AND_ASSIGN(ProfileDestructionWatcher);
@@ -583,7 +593,6 @@
 
 // The following tests make sure that it's safe to shut down while one of the
 // Profile's URLLoaderFactories is in use by a SimpleURLLoader.
-
 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                        SimpleURLLoaderUsingMainContextDuringShutdown) {
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -595,7 +604,6 @@
 
 // The following tests make sure that it's safe to destroy an incognito profile
 // while one of the its URLLoaderFactory is in use by a SimpleURLLoader.
-
 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
                        SimpleURLLoaderUsingMainContextDuringIncognitoTeardown) {
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -609,7 +617,64 @@
           .get());
 }
 
-// Verifies the cache directory supports multiple profiles when it's overriden
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+// Regression test for https://crbug.com/1136214 - verification that
+// ExtensionURLLoaderFactory won't hit a use-after-free bug when used after
+// a Profile has been torn down already.
+IN_PROC_BROWSER_TEST_F(ProfileBrowserTest,
+                       ExtensionURLLoaderFactoryAfterIncognitoTeardown) {
+  // Create a mojo::Remote to ExtensionURLLoaderFactory for the incognito
+  // profile.
+  Browser* incognito_browser =
+      OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
+  Profile* incognito_profile = incognito_browser->profile();
+  mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
+  url_loader_factory.Bind(extensions::CreateExtensionNavigationURLLoaderFactory(
+      incognito_profile, base::kInvalidUkmSourceId,
+      false /* is_web_view_request */));
+
+  // Verify that the factory works fine while the profile is still alive.
+  // We don't need to test with a real extension URL - it is sufficient to
+  // verify that the factory responds with ERR_BLOCKED_BY_CLIENT that indicates
+  // a missing extension.
+  GURL missing_extension_url("chrome-extension://no-such-extension/blah");
+  {
+    SimpleURLLoaderHelper simple_loader_helper(url_loader_factory.get(),
+                                               missing_extension_url,
+                                               net::ERR_BLOCKED_BY_CLIENT);
+    simple_loader_helper.WaitForCompletion();
+  }
+
+  {
+    // Start monitoring |incognito_profile| for shutdown.
+    ProfileManager* profile_manager = g_browser_process->profile_manager();
+    EXPECT_TRUE(profile_manager->IsValidProfile(incognito_profile));
+    ProfileDestructionWatcher watcher;
+    watcher.Watch(incognito_profile);
+
+    // Close all incognito tabs, starting profile shutdown.
+    incognito_browser->tab_strip_model()->CloseAllTabs();
+
+    // ProfileDestructionWatcher waits for
+    // BrowserContext::NotifyWillBeDestroyed, but after the RunLoop unwinds, the
+    // profile should already be gone - let's assert this below (since this
+    // ensures that |simple_loader_helper2| really tests what needs to be
+    // tested).
+    watcher.WaitForDestruction();
+    EXPECT_FALSE(profile_manager->IsValidProfile(incognito_profile));
+  }
+
+  // Verify that the factory doesn't crash (https://crbug.com/1136214), but
+  // instead SimpleURLLoaderImpl::OnMojoDisconnect reports net::ERR_FAILED.
+  {
+    SimpleURLLoaderHelper simple_loader_helper2(
+        url_loader_factory.get(), missing_extension_url, net::ERR_FAILED);
+    simple_loader_helper2.WaitForCompletion();
+  }
+}
+#endif
+
+// Verifies the cache directory supports multiple profiles when it's overridden
 // by group policy or command line switches.
 IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, DiskCacheDirOverride) {
   base::ScopedAllowBlockingForTesting allow_blocking;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js
index 6483c17..2234668d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/keyboard_handler_test.js
@@ -45,22 +45,23 @@
     assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
 
     // Send the pass through command: Search+Shift+Escape.
-    const search = this.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
+    const search =
+        TestUtils.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
     keyboardHandler.onKeyDown(search);
     assertEquals(1, keyboardHandler.eatenKeyDowns_.size);
     assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
     assertEquals('no_pass_through', keyboardHandler.passThroughState_);
     assertUndefined(ChromeVox.passThroughMode);
 
-    const searchShift =
-        this.createMockKeyEvent(KeyCode.SHIFT, {metaKey: true, shiftKey: true});
+    const searchShift = TestUtils.createMockKeyEvent(
+        KeyCode.SHIFT, {metaKey: true, shiftKey: true});
     keyboardHandler.onKeyDown(searchShift);
     assertEquals(2, keyboardHandler.eatenKeyDowns_.size);
     assertEquals(0, keyboardHandler.passedThroughKeyDowns_.size);
     assertEquals('no_pass_through', keyboardHandler.passThroughState_);
     assertUndefined(ChromeVox.passThroughMode);
 
-    const searchShiftEsc = this.createMockKeyEvent(
+    const searchShiftEsc = TestUtils.createMockKeyEvent(
         KeyCode.ESCAPE, {metaKey: true, shiftKey: true});
     keyboardHandler.onKeyDown(searchShiftEsc);
     assertEquals(3, keyboardHandler.eatenKeyDowns_.size);
@@ -97,7 +98,7 @@
     assertEquals('pending_shortcut_keyups', keyboardHandler.passThroughState_);
     assertTrue(ChromeVox.passThroughMode);
 
-    const searchCtrl = this.createMockKeyEvent(
+    const searchCtrl = TestUtils.createMockKeyEvent(
         KeyCode.CONTROL, {metaKey: true, ctrlKey: true});
     keyboardHandler.onKeyDown(searchCtrl);
     assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
@@ -106,7 +107,7 @@
     assertTrue(ChromeVox.passThroughMode);
 
     const searchCtrlM =
-        this.createMockKeyEvent(KeyCode.M, {metaKey: true, ctrlKey: true});
+        TestUtils.createMockKeyEvent(KeyCode.M, {metaKey: true, ctrlKey: true});
     keyboardHandler.onKeyDown(searchCtrlM);
     assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
     assertEquals(3, keyboardHandler.passedThroughKeyDowns_.size);
@@ -144,17 +145,18 @@
 
         // Send some random keys; ensure the pass through state variables never
         // change.
-        const search = this.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
+        const search =
+            TestUtils.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
         keyboardHandler.onKeyDown(search);
         assertNoPassThrough();
 
-        const searchShift = this.createMockKeyEvent(
+        const searchShift = TestUtils.createMockKeyEvent(
             KeyCode.SHIFT, {metaKey: true, shiftKey: true});
         keyboardHandler.onKeyDown(searchShift);
         assertNoPassThrough();
 
-        const searchShiftM =
-            this.createMockKeyEvent(KeyCode.M, {metaKey: true, shiftKey: true});
+        const searchShiftM = TestUtils.createMockKeyEvent(
+            KeyCode.M, {metaKey: true, shiftKey: true});
         keyboardHandler.onKeyDown(searchShiftM);
         assertNoPassThrough();
 
@@ -167,15 +169,15 @@
         keyboardHandler.onKeyUp(search);
         assertNoPassThrough();
 
-        keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.A));
+        keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.A));
         assertNoPassThrough();
 
         keyboardHandler.onKeyDown(
-            this.createMockKeyEvent(KeyCode.A, {altKey: true}));
+            TestUtils.createMockKeyEvent(KeyCode.A, {altKey: true}));
         assertNoPassThrough();
 
         keyboardHandler.onKeyUp(
-            this.createMockKeyEvent(KeyCode.A, {altKey: true}));
+            TestUtils.createMockKeyEvent(KeyCode.A, {altKey: true}));
         assertNoPassThrough();
       });
     });
@@ -185,23 +187,24 @@
     function() {
       this.runWithLoadedTree('<p>test</p>', function() {
         // Send a few key downs.
-        const search = this.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
+        const search =
+            TestUtils.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
         keyboardHandler.onKeyDown(search);
         assertEquals(1, keyboardHandler.eatenKeyDowns_.size);
 
-        const searchShift = this.createMockKeyEvent(
+        const searchShift = TestUtils.createMockKeyEvent(
             KeyCode.SHIFT, {metaKey: true, shiftKey: true});
         keyboardHandler.onKeyDown(searchShift);
         assertEquals(2, keyboardHandler.eatenKeyDowns_.size);
 
-        const searchShiftM =
-            this.createMockKeyEvent(KeyCode.M, {metaKey: true, shiftKey: true});
+        const searchShiftM = TestUtils.createMockKeyEvent(
+            KeyCode.M, {metaKey: true, shiftKey: true});
         keyboardHandler.onKeyDown(searchShiftM);
         assertEquals(3, keyboardHandler.eatenKeyDowns_.size);
 
         // Now, send a key down, but no modifiers set, which is impossible to
         // actually press. This key is not eaten.
-        const m = this.createMockKeyEvent(KeyCode.M, {});
+        const m = TestUtils.createMockKeyEvent(KeyCode.M, {});
         keyboardHandler.onKeyDown(m);
         assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
 
@@ -220,26 +223,27 @@
         ChromeVox.passThroughMode = true;
 
         // Send a few key downs (which are passed through).
-        const search = this.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
+        const search =
+            TestUtils.createMockKeyEvent(KeyCode.SEARCH, {metaKey: true});
         keyboardHandler.onKeyDown(search);
         assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
         assertEquals(1, keyboardHandler.passedThroughKeyDowns_.size);
 
-        const searchShift = this.createMockKeyEvent(
+        const searchShift = TestUtils.createMockKeyEvent(
             KeyCode.SHIFT, {metaKey: true, shiftKey: true});
         keyboardHandler.onKeyDown(searchShift);
         assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
         assertEquals(2, keyboardHandler.passedThroughKeyDowns_.size);
 
-        const searchShiftM =
-            this.createMockKeyEvent(KeyCode.M, {metaKey: true, shiftKey: true});
+        const searchShiftM = TestUtils.createMockKeyEvent(
+            KeyCode.M, {metaKey: true, shiftKey: true});
         keyboardHandler.onKeyDown(searchShiftM);
         assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
         assertEquals(3, keyboardHandler.passedThroughKeyDowns_.size);
 
         // Now, send a key down, but no modifiers set, which is impossible to
         // actually press. This is passed through, so the count resets to 1.
-        const m = this.createMockKeyEvent(KeyCode.M, {});
+        const m = TestUtils.createMockKeyEvent(KeyCode.M, {});
         keyboardHandler.onKeyDown(m);
         assertEquals(0, keyboardHandler.eatenKeyDowns_.size);
         assertEquals(1, keyboardHandler.passedThroughKeyDowns_.size);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js
index 7a0ef71..5613428 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/user_action_monitor_test.js
@@ -69,7 +69,8 @@
     const keySequenceActionOne = UserActionMonitor.Action.fromActionInfo(
         {type: 'key_sequence', value: {keys: {keyCode: [KeyCode.SPACE]}}});
     const keySequenceActionTwo = new UserActionMonitor.Action(
-        'key_sequence', new KeySequence(this.createMockKeyEvent(KeyCode.A)));
+        'key_sequence',
+        new KeySequence(TestUtils.createMockKeyEvent(KeyCode.A)));
     const gestureActionOne = UserActionMonitor.Action.fromActionInfo(
         {type: 'gesture', value: 'swipeUp1'});
     const gestureActionTwo =
@@ -146,7 +147,7 @@
 
     try {
       monitor.onKeySequence(
-          new KeySequence(this.createMockKeyEvent(KeyCode.SPACE)));
+          new KeySequence(TestUtils.createMockKeyEvent(KeyCode.SPACE)));
       assertTrue(false);  // Shouldn't execute.
     } catch (error) {
       assertEquals(
@@ -227,14 +228,14 @@
     const onFinished = () => finished = true;
 
     ChromeVoxState.instance.createUserActionMonitor(actions, onFinished);
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.LEFT));
-    keyboardHandler.onKeyUp(this.createMockKeyEvent(KeyCode.LEFT));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.LEFT));
+    keyboardHandler.onKeyUp(TestUtils.createMockKeyEvent(KeyCode.LEFT));
     assertFalse(finished);
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.RIGHT));
-    keyboardHandler.onKeyUp(this.createMockKeyEvent(KeyCode.RIGHT));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.RIGHT));
+    keyboardHandler.onKeyUp(TestUtils.createMockKeyEvent(KeyCode.RIGHT));
     assertFalse(finished);
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.SPACE));
-    keyboardHandler.onKeyUp(this.createMockKeyEvent(KeyCode.SPACE));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.SPACE));
+    keyboardHandler.onKeyUp(TestUtils.createMockKeyEvent(KeyCode.SPACE));
     assertTrue(finished);
   });
 });
@@ -252,21 +253,21 @@
     const onFinished = () => finished = true;
 
     ChromeVoxState.instance.createUserActionMonitor(actions, onFinished);
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.O));
-    keyboardHandler.onKeyUp(this.createMockKeyEvent(KeyCode.O));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.O));
+    keyboardHandler.onKeyUp(TestUtils.createMockKeyEvent(KeyCode.O));
     assertFalse(finished);
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.B));
-    keyboardHandler.onKeyUp(this.createMockKeyEvent(KeyCode.B));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.B));
+    keyboardHandler.onKeyUp(TestUtils.createMockKeyEvent(KeyCode.B));
     assertFalse(finished);
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.SEARCH));
-    keyboardHandler.onKeyUp(this.createMockKeyEvent(KeyCode.SEARCH));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.SEARCH));
+    keyboardHandler.onKeyUp(TestUtils.createMockKeyEvent(KeyCode.SEARCH));
     assertFalse(finished);
     keyboardHandler.onKeyDown(
-        this.createMockKeyEvent(KeyCode.O, {searchKeyHeld: true}));
+        TestUtils.createMockKeyEvent(KeyCode.O, {searchKeyHeld: true}));
     assertFalse(finished);
     keyboardHandler.onKeyUp(
-        this.createMockKeyEvent(KeyCode.O, {searchKeyHeld: true}));
-    keyboardHandler.onKeyDown(this.createMockKeyEvent(KeyCode.B));
+        TestUtils.createMockKeyEvent(KeyCode.O, {searchKeyHeld: true}));
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(KeyCode.B));
     assertTrue(finished);
   });
 });
@@ -294,10 +295,10 @@
     ];
     const onFinished = () => finished = true;
 
-    const altShiftLSequence = new KeySequence(
-        this.createMockKeyEvent(KeyCode.L, {altKey: true, shiftKey: true}));
-    const altShiftSSequence = new KeySequence(
-        this.createMockKeyEvent(KeyCode.S, {altKey: true, shiftKey: true}));
+    const altShiftLSequence = new KeySequence(TestUtils.createMockKeyEvent(
+        KeyCode.L, {altKey: true, shiftKey: true}));
+    const altShiftSSequence = new KeySequence(TestUtils.createMockKeyEvent(
+        KeyCode.S, {altKey: true, shiftKey: true}));
     let monitor;
     mockFeedback
         .call(() => {
@@ -340,13 +341,13 @@
     const onFinished = () => finished = true;
 
     const nextObject =
-        this.createMockKeyEvent(KeyCode.RIGHT, {searchKeyHeld: true});
+        TestUtils.createMockKeyEvent(KeyCode.RIGHT, {searchKeyHeld: true});
     const nextLine =
-        this.createMockKeyEvent(KeyCode.DOWN, {searchKeyHeld: true});
+        TestUtils.createMockKeyEvent(KeyCode.DOWN, {searchKeyHeld: true});
     const previousObject =
-        this.createMockKeyEvent(KeyCode.LEFT, {searchKeyHeld: true});
+        TestUtils.createMockKeyEvent(KeyCode.LEFT, {searchKeyHeld: true});
     const previousLine =
-        this.createMockKeyEvent(KeyCode.UP, {searchKeyHeld: true});
+        TestUtils.createMockKeyEvent(KeyCode.UP, {searchKeyHeld: true});
 
     ChromeVoxState.instance.createUserActionMonitor(actions, onFinished);
     mockFeedback.expectSpeech('Start')
@@ -402,15 +403,15 @@
     assertFalse(closed);
     assertFalse(finished);
     keyboardHandler.onKeyDown(
-        this.createMockKeyEvent(KeyCode.CONTROL, {ctrlKey: true}));
+        TestUtils.createMockKeyEvent(KeyCode.CONTROL, {ctrlKey: true}));
+    assertFalse(closed);
+    assertFalse(finished);
+    keyboardHandler.onKeyDown(TestUtils.createMockKeyEvent(
+        KeyCode.ALT, {ctrlKey: true, altKey: true}));
     assertFalse(closed);
     assertFalse(finished);
     keyboardHandler.onKeyDown(
-        this.createMockKeyEvent(KeyCode.ALT, {ctrlKey: true, altKey: true}));
-    assertFalse(closed);
-    assertFalse(finished);
-    keyboardHandler.onKeyDown(
-        this.createMockKeyEvent(KeyCode.Z, {ctrlKey: true, altKey: true}));
+        TestUtils.createMockKeyEvent(KeyCode.Z, {ctrlKey: true, altKey: true}));
     assertTrue(closed);
     // |finished| remains false since we didn't press the expected key sequence.
     assertFalse(finished);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
index 8b28022..4e6b0b04c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/tutorial_test.js
@@ -500,7 +500,7 @@
       return ChromeVoxState.instance.getCurrentRange().start.node;
     };
     const simulateKeyPress = (keyCode, opt_modifiers) => {
-      const keyEvent = this.createMockKeyEvent(keyCode, opt_modifiers);
+      const keyEvent = TestUtils.createMockKeyEvent(keyCode, opt_modifiers);
       keyboardHandler.onKeyDown(keyEvent);
       keyboardHandler.onKeyUp(keyEvent);
     };
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js
index 8078800..5a42485 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/testing/common.js
@@ -41,6 +41,31 @@
     }
     return stringified.replace(/^[^\/]+\/\*!?/, '').replace(/\*\/[^\/]+$/, '');
   }
+
+  /**
+   * Create a mock event object.
+   * @param {number} keyCode
+   * @param {{altGraphKey: boolean=,
+   *         altKey: boolean=,
+   *         ctrlKey: boolean=,
+   *         metaKey: boolean=,
+   *         searchKeyHeld: boolean=,
+   *         shiftKey: boolean=,
+   *         stickyMode: boolean=,
+   *         prefixKey: boolean=}=} opt_modifiers
+   * @return {Object} The mock event.
+   */
+  static createMockKeyEvent(keyCode, opt_modifiers) {
+    const modifiers = opt_modifiers === undefined ? {} : opt_modifiers;
+    const keyEvent = {};
+    keyEvent.keyCode = keyCode;
+    for (const key in modifiers) {
+      keyEvent[key] = modifiers[key];
+    }
+    keyEvent.preventDefault = _ => {};
+    keyEvent.stopPropagation = _ => {};
+    return keyEvent;
+  }
 }
 
 
diff --git a/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js b/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js
index 98d88a7..11b9973 100644
--- a/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/common/event_generator_test.js
@@ -10,34 +10,39 @@
 /** Test fixture for array_util.js. */
 EventGeneratorTest = class extends ChromeVoxNextE2ETest {};
 
-TEST_F('EventGeneratorTest', 'MouseEventsProcessedSequentially', function() {
-  const mouseEventLog = [];
-  chrome.accessibilityPrivate.sendSyntheticMouseEvent = (event) =>
-      mouseEventLog.push(event);
+// Fails on ChromeOS - https://crbug.com/1136991
+TEST_F(
+    'EventGeneratorTest', 'DISABLED_MouseEventsProcessedSequentially',
+    function() {
+      const mouseEventLog = [];
+      chrome.accessibilityPrivate.sendSyntheticMouseEvent = (event) =>
+          mouseEventLog.push(event);
 
-  // Set a 1ms delay so that a timeout is set between the press and release.
-  EventGenerator.sendMouseClick(100, 100, /*delayMs=*/ 1);
-  assertEquals(1, mouseEventLog.length, 'First event should be synchronous');
+      // Set a 1ms delay so that a timeout is set between the press and release.
+      EventGenerator.sendMouseClick(100, 100, /*delayMs=*/ 1);
+      assertEquals(
+          1, mouseEventLog.length, 'First event should be synchronous');
 
-  EventGenerator.sendMouseClick(200, 200, /*delayMs=*/ 1);
-  assertEquals(
-      1, mouseEventLog.length,
-      'Second mouse click shouldn\'t start until first has finished');
+      EventGenerator.sendMouseClick(200, 200, /*delayMs=*/ 1);
+      assertEquals(
+          1, mouseEventLog.length,
+          'Second mouse click shouldn\'t start until first has finished');
 
-  const checkEventLog = () => {
-    assertEquals(
-        4, mouseEventLog.length, 'Both click events should have completed.');
-    assertEquals('press', mouseEventLog[0].type);
-    assertEquals('release', mouseEventLog[1].type);
-    assertEquals(mouseEventLog[0].x, mouseEventLog[1].x);
-    assertEquals(mouseEventLog[0].y, mouseEventLog[1].y);
+      const checkEventLog = () => {
+        assertEquals(
+            4, mouseEventLog.length,
+            'Both click events should have completed.');
+        assertEquals('press', mouseEventLog[0].type);
+        assertEquals('release', mouseEventLog[1].type);
+        assertEquals(mouseEventLog[0].x, mouseEventLog[1].x);
+        assertEquals(mouseEventLog[0].y, mouseEventLog[1].y);
 
-    assertEquals('press', mouseEventLog[2].type);
-    assertEquals('release', mouseEventLog[3].type);
-    assertEquals(mouseEventLog[2].x, mouseEventLog[3].x);
-    assertEquals(mouseEventLog[2].y, mouseEventLog[3].y);
-  };
-  // Experimentally, the code takes 13ms to set all timeouts on a development
-  // machine. Wait 150ms to increase stability.
-  setTimeout(this.newCallback(checkEventLog), 150);
-});
+        assertEquals('press', mouseEventLog[2].type);
+        assertEquals('release', mouseEventLog[3].type);
+        assertEquals(mouseEventLog[2].x, mouseEventLog[3].x);
+        assertEquals(mouseEventLog[2].y, mouseEventLog[3].y);
+      };
+      // Experimentally, the code takes 13ms to set all timeouts on a
+      // development machine. Wait 150ms to increase stability.
+      setTimeout(this.newCallback(checkEventLog), 150);
+    });
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index e985578..ef16932 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -152,6 +152,7 @@
    * @private
    */
   addGlobalActions_(actions) {
+    actions.push(SwitchAccessMenuAction.POINT_SCAN);
     actions.push(SwitchAccessMenuAction.SETTINGS);
     return actions;
   }
@@ -193,6 +194,8 @@
       case SwitchAccessMenuAction.SETTINGS:
         chrome.accessibilityPrivate.openSettingsSubpage(
             'manageAccessibility/switchAccess');
+      case SwitchAccessMenuAction.POINT_SCAN:
+        chrome.accessibilityPrivate.enablePointScan(true);
         return true;
       default:
         return false;
diff --git a/chrome/browser/resources/read_later/app.html b/chrome/browser/resources/read_later/app.html
index e1cc65b..4732bbc 100644
--- a/chrome/browser/resources/read_later/app.html
+++ b/chrome/browser/resources/read_later/app.html
@@ -1,32 +1,28 @@
 <style include="mwb-shared-style">
   #header {
     align-items: center;
-    font-size: 15px;
+    display: flex;
+    font-size: var(--mwb-primary-text-font-size);
+    height: var(--mwb-item-height);
     margin: 0;
-    padding: var(--mwb-list-item-vertical-margin)
-             var(--mwb-list-item-horizontal-margin);
+    padding-inline-start: var(--mwb-list-item-horizontal-margin);
   }
 
   #read-later-list {
-    max-height: 458px;
+    max-height: 444px;
     overflow-x: hidden;
     overflow-y: auto;
   }
 
   .sub-heading {
+    align-items: center;
+    border-bottom: 1px solid #dbdbdb;
     color: var(--cr-secondary-text-color);
-    font-size: var(--mwb-secondary-text-font-size);
-    padding: var(--mwb-list-item-vertical-margin)
-             0
-             calc(var(--mwb-list-item-vertical-margin) / 2)
-             var(--mwb-list-item-horizontal-margin);
-  }
-
-  .sub-heading::after {
-    border-bottom: 1px solid var(--cr-secondary-text-color);
-    content: '';
-    display: block;
-    margin-top: calc(var(--mwb-list-item-vertical-margin) / 2);
+    display: flex;
+    font-size: 11px;
+    height: 24px;
+    margin-inline-start: var(--mwb-list-item-horizontal-margin);
+    margin-top: 4px;
   }
 </style>
 
diff --git a/chrome/browser/resources/read_later/read_later_item.html b/chrome/browser/resources/read_later/read_later_item.html
index b3af5194..4a7ee2b 100644
--- a/chrome/browser/resources/read_later/read_later_item.html
+++ b/chrome/browser/resources/read_later/read_later_item.html
@@ -1,11 +1,14 @@
 <style>
-  :host(:hover) .button-container,
-  :host(.selected) .button-container {
+  .button-container {
     display: flex;
+    margin-inline-start: calc(var(--mwb-icon-size) / 2);
+    overflow: hidden;
+    width: 0;
   }
 
-  .button-container {
-    display: none;
+  :host(:hover) .button-container {
+    overflow: visible;
+    width: auto;
   }
 
   .text-container {
@@ -33,9 +36,24 @@
   }
 
   cr-icon-button {
-    --cr-icon-button-margin-end: calc(var(--mwb-icon-size) / 4);
-    --cr-icon-button-margin-start: calc(var(--mwb-icon-size) / 4);
-    --cr-icon-button-size: var(--mwb-icon-size);
+    border-radius: 50%;
+    --cr-icon-button-fill-color: transparent;
+    --cr-icon-button-icon-size: var(--mwb-icon-size);
+    --cr-icon-button-size: 24px;
+    --cr-icon-button-margin-start: 0;
+    --cr-icon-button-margin-end: 0;
+  }
+
+  :host(:hover) cr-icon-button {
+    --cr-icon-button-fill-color: var(--google-grey-refresh-700);
+  }
+
+  cr-icon-button:hover {
+    background-color: rgba(var(--google-grey-900-rgb), .1);
+  }
+
+  #deleteButton {
+    margin-inline-start: calc(var(--cr-icon-button-size) / 2);
   }
 </style>
 
@@ -47,14 +65,14 @@
 </div>
 <div class="button-container">
   <cr-icon-button id="updateStatusButton"
-      aria-label="[[getUpdateStatusButtonTooltip_(
-      '$i18n{tooltipMarkAsUnread}', '$i18n{tooltipMarkAsRead}', data.read)]]"
+      aria-label="[[getUpdateStatusButtonTooltip_('$i18n{tooltipMarkAsUnread}',
+      '$i18n{tooltipMarkAsRead}', data.read)]]" disable-ripple
       iron-icon="cr:check" title="[[getUpdateStatusButtonTooltip_(
       '$i18n{tooltipMarkAsUnread}', '$i18n{tooltipMarkAsRead}', data.read)]]"
       on-click="onUpdateStatusClick_">
   </cr-icon-button>
   <cr-icon-button id="deleteButton" aria-label="$i18n{tooltipDelete}"
-      iron-icon="cr:close" title="$i18n{tooltipDelete}"
+      disable-ripple iron-icon="cr:close" title="$i18n{tooltipDelete}"
       on-click="onItemDeleteClick_">
   </cr-icon-button>
 </div>
diff --git a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.js b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.js
index 13355a816..116928e 100644
--- a/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/nearby_share_page/nearby_share_receive_dialog.js
@@ -119,7 +119,7 @@
       // reasons (timeout, user cancel, etc). During a receive transfer, it
       // happens before we start connecting (because we need to stop
       // advertising) so we need to wait a bit to see if we see an
-      // onIncomingShare event within a reasonable timeout. This is the normal
+      // onTransferUpdate event within a reasonable timeout. This is the normal
       // case and it should happen quickly when it is a real connection. In the
       // timeout case, we are just exiting high visibility normally and can
       // close for now, and the small timeout won't impact UX. Ideally we should
@@ -129,16 +129,19 @@
   },
 
   /**
-   * Mojo callback called when a shareTarget is requesting an incoming share
-   * and the user must manually confirm.
+   * Mojo callback when transfer status changes.
    * @param {!nearbyShare.mojom.ShareTarget} shareTarget
-   * @param {?string} connectionToken
+   * @param {!nearbyShare.mojom.TransferMetadata} metadata
    */
-  onIncomingShare(shareTarget, connectionToken) {
-    clearTimeout(this.closeTimeoutId_);
-    this.shareTarget = shareTarget;
-    this.connectionToken = connectionToken;
-    this.showConfirmPage();
+  onTransferUpdate(shareTarget, metadata) {
+    if (metadata.status ===
+        nearbyShare.mojom.TransferStatus.kAwaitingLocalConfirmation) {
+      clearTimeout(this.closeTimeoutId_);
+      this.shareTarget = shareTarget;
+      this.connectionToken =
+          (metadata && metadata.token) ? metadata.token : null;
+      this.showConfirmPage();
+    }
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
index db665d9..1c73133 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
@@ -34,6 +34,7 @@
 js_library("account_manager") {
   deps = [
     "..:deep_linking_behavior",
+    "..:metrics_recorder",
     "..:os_route",
     "../..:router",
     "../../people_page:account_manager_browser_proxy",
@@ -259,6 +260,7 @@
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.m.js" ]
   deps = [
     "..:deep_linking_behavior.m",
+    "..:metrics_recorder.m",
     "..:os_route.m",
     "../..:router.m",
     "../../people_page:account_manager_browser_proxy.m",
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
index 1e3aec1..9ccd38e9 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -12,6 +12,7 @@
 <link rel="import" href="../localized_link/localized_link.html">
 <link rel="import" href="../../i18n_setup.html">
 <link rel="import" href="../deep_linking_behavior.html">
+<link rel="import" href="../metrics_recorder.html">
 <link rel="import" href="../os_route.html">
 <link rel="import" href="../../router.html">
 <link rel="import" href="../../settings_shared_css.html">
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
index ff8f287..5460fd6 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
@@ -161,6 +161,9 @@
    * @private
    */
   addAccount_(event) {
+    settings.recordSettingChange(
+        chromeos.settings.mojom.Setting.kAddAccount,
+        {intValue: this.accounts_.length + 1});
     this.browserProxy_.addAccount();
   },
 
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index 92d5e32ca..ae24519 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -115,7 +115,8 @@
           r.OS_PEOPLE, mojom.MANAGE_OTHER_PEOPLE_SUBPAGE_PATH,
           Subpage.kManageOtherPeople);
       r.KERBEROS_ACCOUNTS = createSubpage(
-          r.OS_PEOPLE, mojom.KERBEROS_SUBPAGE_PATH, Subpage.kKerberos);
+          r.OS_PEOPLE, mojom.KERBEROS_ACCOUNTS_SUBPAGE_PATH,
+          Subpage.kKerberosAccounts);
     }
 
     // Device section.
diff --git a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
index 66ee1a6..bd7b78df 100644
--- a/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_navigation_observer_browsertest.cc
@@ -2290,81 +2290,81 @@
 IN_PROC_BROWSER_TEST_F(SBNavigationObserverBrowserTest,
                        VerifyNumberOfRecentNavigationsToCollect) {
   EXPECT_EQ(0, CountOfRecentNavigationsToAppend(
-                   /*sber=*/false, /*incognito=*/false,
+                   /*extended_reporting_enabled=*/false, /*is_incognito=*/false,
                    SafeBrowsingNavigationObserverManager::SUCCESS));
   EXPECT_EQ(0, CountOfRecentNavigationsToAppend(
-                   /*sber=*/false, /*incognito=*/true,
+                   /*extended_reporting_enabled=*/false, /*is_incognito=*/true,
                    SafeBrowsingNavigationObserverManager::SUCCESS));
   EXPECT_EQ(0,
             CountOfRecentNavigationsToAppend(
-                /*sber=*/false, /*incognito=*/false,
+                /*extended_reporting_enabled=*/false, /*is_incognito=*/false,
                 SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_PAGE));
   EXPECT_EQ(0,
             CountOfRecentNavigationsToAppend(
-                /*sber=*/false, /*incognito=*/true,
+                /*extended_reporting_enabled=*/false, /*is_incognito=*/true,
                 SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_PAGE));
   EXPECT_EQ(
       0, CountOfRecentNavigationsToAppend(
-             /*sber=*/false, /*incognito=*/false,
+             /*extended_reporting_enabled=*/false, /*is_incognito=*/false,
              SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_REFERRER));
   EXPECT_EQ(
       0, CountOfRecentNavigationsToAppend(
-             /*sber=*/false, /*incognito=*/true,
+             /*extended_reporting_enabled=*/false, /*is_incognito=*/true,
              SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_REFERRER));
   EXPECT_EQ(0, CountOfRecentNavigationsToAppend(
-                   /*sber=*/false, /*incognito=*/false,
+                   /*extended_reporting_enabled=*/false, /*is_incognito=*/false,
                    SafeBrowsingNavigationObserverManager::INVALID_URL));
   EXPECT_EQ(0, CountOfRecentNavigationsToAppend(
-                   /*sber=*/false, /*incognito=*/true,
+                   /*extended_reporting_enabled=*/false, /*is_incognito=*/true,
                    SafeBrowsingNavigationObserverManager::INVALID_URL));
   EXPECT_EQ(
       0,
       CountOfRecentNavigationsToAppend(
-          /*sber=*/false, /*incognito=*/false,
+          /*extended_reporting_enabled=*/false, /*is_incognito=*/false,
           SafeBrowsingNavigationObserverManager::NAVIGATION_EVENT_NOT_FOUND));
   EXPECT_EQ(
       0,
       CountOfRecentNavigationsToAppend(
-          /*sber=*/false, /*incognito=*/true,
+          /*extended_reporting_enabled=*/false, /*is_incognito=*/true,
           SafeBrowsingNavigationObserverManager::NAVIGATION_EVENT_NOT_FOUND));
 
   EXPECT_EQ(5, CountOfRecentNavigationsToAppend(
-                   /*sber=*/true, /*incognito=*/false,
+                   /*extended_reporting_enabled=*/true, /*is_incognito=*/false,
                    SafeBrowsingNavigationObserverManager::SUCCESS));
   EXPECT_EQ(0, CountOfRecentNavigationsToAppend(
-                   /*sber=*/true, /*incognito=*/true,
+                   /*extended_reporting_enabled=*/true, /*is_incognito=*/true,
                    SafeBrowsingNavigationObserverManager::SUCCESS));
   EXPECT_EQ(5,
             CountOfRecentNavigationsToAppend(
-                /*sber=*/true, /*incognito=*/false,
+                /*extended_reporting_enabled=*/true, /*is_incognito=*/false,
                 SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_PAGE));
   EXPECT_EQ(0,
             CountOfRecentNavigationsToAppend(
-                /*sber=*/true, /*incognito=*/true,
+                /*extended_reporting_enabled=*/true, /*is_incognito=*/true,
                 SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_PAGE));
   EXPECT_EQ(
       0, CountOfRecentNavigationsToAppend(
-             /*sber=*/true, /*incognito=*/false,
+             /*extended_reporting_enabled=*/true, /*is_incognito=*/false,
              SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_REFERRER));
   EXPECT_EQ(
       0, CountOfRecentNavigationsToAppend(
-             /*sber=*/true, /*incognito=*/true,
+             /*extended_reporting_enabled=*/true, /*is_incognito=*/true,
              SafeBrowsingNavigationObserverManager::SUCCESS_LANDING_REFERRER));
   EXPECT_EQ(5, CountOfRecentNavigationsToAppend(
-                   /*sber=*/true, /*incognito=*/false,
+                   /*extended_reporting_enabled=*/true, /*is_incognito=*/false,
                    SafeBrowsingNavigationObserverManager::INVALID_URL));
   EXPECT_EQ(0, CountOfRecentNavigationsToAppend(
-                   /*sber=*/true, /*incognito=*/true,
+                   /*extended_reporting_enabled=*/true, /*is_incognito=*/true,
                    SafeBrowsingNavigationObserverManager::INVALID_URL));
   EXPECT_EQ(
       5,
       CountOfRecentNavigationsToAppend(
-          /*sber=*/true, /*incognito=*/false,
+          /*extended_reporting_enabled=*/true, /*is_incognito=*/false,
           SafeBrowsingNavigationObserverManager::NAVIGATION_EVENT_NOT_FOUND));
   EXPECT_EQ(
       0,
       CountOfRecentNavigationsToAppend(
-          /*sber=*/true, /*incognito=*/true,
+          /*extended_reporting_enabled=*/true, /*is_incognito=*/true,
           SafeBrowsingNavigationObserverManager::NAVIGATION_EVENT_NOT_FOUND));
 }
 
diff --git a/chrome/browser/ui/app_list/app_list_test_util.cc b/chrome/browser/ui/app_list/app_list_test_util.cc
index 7534fa3..40f1b99 100644
--- a/chrome/browser/ui/app_list/app_list_test_util.cc
+++ b/chrome/browser/ui/app_list/app_list_test_util.cc
@@ -44,8 +44,7 @@
   InitializeInstalledExtensionService(pref_path, source_install_dir);
   service_->Init();
 
-  if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions))
-    ConfigureWebAppProvider();
+  ConfigureWebAppProvider();
 
   // Let any async services complete their set-up.
   base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.cc b/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.cc
index 1cf14e2..9b43169 100644
--- a/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.cc
+++ b/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.cc
@@ -47,6 +47,8 @@
     const gfx::Rect& anchor,
     std::vector<std::string> actions) {}
 
+void FakeAccessibilityController::StartPointScan() {}
+
 void FakeAccessibilityController::SetDictationActive(bool is_active) {}
 
 void FakeAccessibilityController::ToggleDictationFromSource(
diff --git a/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.h b/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.h
index a290d34..16c442e 100644
--- a/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.h
+++ b/chrome/browser/ui/ash/accessibility/fake_accessibility_controller.h
@@ -33,6 +33,7 @@
   void ShowSwitchAccessBackButton(const gfx::Rect& anchor) override;
   void ShowSwitchAccessMenu(const gfx::Rect& anchor,
                             std::vector<std::string> actions) override;
+  void StartPointScan() override;
   void SetDictationActive(bool is_active) override;
   void ToggleDictationFromSource(ash::DictationToggleSource source) override;
   void HandleAutoclickScrollableBoundsFound(
diff --git a/chrome/browser/ui/ash/ash_test_util.cc b/chrome/browser/ui/ash/ash_test_util.cc
index 1f8801e..5e21914 100644
--- a/chrome/browser/ui/ash/ash_test_util.cc
+++ b/chrome/browser/ui/ash/ash_test_util.cc
@@ -4,8 +4,8 @@
 
 #include "chrome/browser/ui/ash/ash_test_util.h"
 
-#include "ash/public/cpp/window_properties.h"
 #include "base/run_loop.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/window.h"
@@ -30,14 +30,14 @@
   void OnWindowPropertyChanged(aura::Window* window,
                                const void* key,
                                intptr_t old) override {
-    if (key == ash::kWindowStateTypeKey && IsSnapped())
+    if (key == chromeos::kWindowStateTypeKey && IsSnapped())
       run_loop_.Quit();
   }
 
   void Wait() { run_loop_.Run(); }
 
   bool IsSnapped() const {
-    return window_->GetProperty(ash::kWindowStateTypeKey) == type_;
+    return window_->GetProperty(chromeos::kWindowStateTypeKey) == type_;
   }
 
  private:
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 6fbc768..60de8eb 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -122,7 +122,7 @@
      {ChromePage::HELP, chromeos::settings::mojom::kAboutChromeOsSectionPath},
      {ChromePage::INTERNET, chromeos::settings::mojom::kNetworkSectionPath},
      {ChromePage::KERBEROSACCOUNTS,
-      chromeos::settings::mojom::kKerberosSubpagePath},
+      chromeos::settings::mojom::kKerberosAccountsSubpagePath},
      {ChromePage::KEYBOARDOVERLAY,
       chromeos::settings::mojom::kKeyboardSubpagePath},
      {ChromePage::KNOWNNETWORKS,
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index d5914ba2..ed69316 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -383,7 +383,8 @@
       base_url.Resolve(chromeos::settings::mojom::kNetworkSectionPath));
   TestOpenChromePage(
       ChromePage::KERBEROSACCOUNTS,
-      base_url.Resolve(chromeos::settings::mojom::kKerberosSubpagePath));
+      base_url.Resolve(
+          chromeos::settings::mojom::kKerberosAccountsSubpagePath));
   TestOpenChromePage(
       ChromePage::KNOWNNETWORKS,
       base_url.Resolve(chromeos::settings::mojom::kKnownNetworksSubpagePath));
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
index 0c1d02c9..c9c2f6a3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
@@ -91,7 +91,10 @@
 void HoldingSpaceDownloadsDelegate::OnDownloadCreated(
     content::DownloadManager* manager,
     download::DownloadItem* item) {
-  download_item_observer_.Add(item);
+  // Ignore `OnDownloadCreated()` events prior to `manager` initialization. For
+  // those events we bind any observers necessary in `OnManagerInitialized()`.
+  if (!is_restoring_persistence() && manager->IsManagerInitialized())
+    download_item_observer_.Add(item);
 }
 
 void HoldingSpaceDownloadsDelegate::OnDownloadUpdated(
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_item_controller.cc
index c8c9f314..509c3e7 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_item_controller.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_item_controller.h"
 
-#include "ash/public/cpp/window_properties.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/chromeos/arc/pip/arc_pip_bridge.h"
@@ -12,6 +11,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/favicon/content/content_favicon_driver.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
@@ -45,7 +45,7 @@
     // showing the menu on the shelf icon.
     for (ui::BaseWindow* window : windows()) {
       aura::Window* native_window = window->GetNativeWindow();
-      if (native_window->GetProperty(ash::kWindowStateTypeKey) ==
+      if (native_window->GetProperty(chromeos::kWindowStateTypeKey) ==
           chromeos::WindowStateType::kPip) {
         Profile* profile = ChromeLauncherController::instance()->profile();
         arc::ArcPipBridge* pip_bridge =
diff --git a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc b/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
index 90f0bdc0..8ee5569a 100644
--- a/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
+++ b/chrome/browser/ui/views/frame/browser_frame_header_ash.cc
@@ -8,9 +8,9 @@
 #include "ash/public/cpp/caption_buttons/frame_caption_button_container_view.h"
 #include "ash/public/cpp/frame_utils.h"
 #include "ash/public/cpp/tablet_mode.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/check.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -177,7 +177,8 @@
       appearance_provider_->GetFrameHeaderOverlayImage(active);
 
   chromeos::WindowStateType state_type =
-      target_widget()->GetNativeWindow()->GetProperty(ash::kWindowStateTypeKey);
+      target_widget()->GetNativeWindow()->GetProperty(
+          chromeos::kWindowStateTypeKey);
   int corner_radius = chromeos::IsNormalWindowStateType(state_type)
                           ? ash::kTopCornerRadiusWhenRestored
                           : 0;
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
index 9b14c58..7cf7213 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
 #include "chrome/browser/ui/views/chrome_view_class_properties.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/immersive_mode_controller.h"
@@ -804,8 +805,27 @@
 
 TabStripUILayout WebUITabStripContainerView::GetLayout() {
   DCHECK(tab_contents_container_);
-  return TabStripUILayout::CalculateForWebViewportSize(
-      tab_contents_container_->size());
+
+  gfx::Size tab_contents_size = tab_contents_container_->size();
+
+  // Because some pages can display the bookmark bar even when the bookmark bar
+  // is disabled (e.g. NTP) and some pages never display the bookmark bar (e.g.
+  // crashed tab pages, pages in guest browser windows), we will always reserve
+  // room for the bookmarks bar so that the size and shape of the effective
+  // viewport doesn't change.
+  //
+  // This may cause the thumbnail to crop off the extreme right and left edge of
+  // the image in some cases, but a very slight crop is preferable to constantly
+  // changing thumbnail sizes.
+  //
+  // See: crbug.com/1066652 for more info
+  const int max_bookmark_height = GetLayoutConstant(BOOKMARK_BAR_HEIGHT);
+  const views::View* bookmarks = browser_view_->bookmark_bar();
+  const int bookmark_bar_height =
+      (bookmarks && bookmarks->GetVisible()) ? bookmarks->height() : 0;
+  tab_contents_size.Enlarge(0, -(max_bookmark_height - bookmark_bar_height));
+
+  return TabStripUILayout::CalculateForWebViewportSize(tab_contents_size);
 }
 
 SkColor WebUITabStripContainerView::GetColor(int id) const {
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
index 6cb317f..51d32b6 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller.cc
@@ -101,7 +101,7 @@
   DCHECK(context);
   chromeos::WindowStateType type =
       GetWindowForTabDraggingProperties(context)->GetProperty(
-          ash::kWindowStateTypeKey);
+          chromeos::kWindowStateTypeKey);
   return type == chromeos::WindowStateType::kLeftSnapped ||
          type == chromeos::WindowStateType::kRightSnapped;
 }
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share.mojom b/chrome/browser/ui/webui/nearby_share/nearby_share.mojom
index 895425b..b97ed422 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share.mojom
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share.mojom
@@ -40,17 +40,55 @@
   OnShareTargetLost(ShareTarget share_target);
 };
 
-// Status of the current transfer.
+// Status of the current transfer. This mirrors the Status enum at
+// chrome/browser/nearby_sharing/transfer_metadata.h, but drops the unused
+// statuses kMediaDownloading and kExternalProviderLaunched.
 enum TransferStatus {
+  kUnknown,
+  // A remote device is connecting.
+  kConnecting,
   // The user of the local device needs to accept or reject the transfer. This
   // involves showing a token on both devices to be compared by the user.
   kAwaitingLocalConfirmation,
   // The remote device needs to accept the transfer. We still need to show the
   // token on this device so the other user can compare it.
   kAwaitingRemoteAcceptance,
+  // The connection was lost before the remote accepted/rejected.
+  kAwaitingRemoteAcceptanceFailed,
   // The current transfer is now in progress after both users accepted the
   // transfer on their devices.
   kInProgress,
+  // The current transfer is complete and successful.
+  kComplete,
+  // The current transfer failed.
+  kFailed,
+  // The remote device rejected the transfer.
+  kRejected,
+  // The remote device cancelled the transfer.
+  kCancelled,
+  // The connection to a remote device timed out.
+  kTimedOut,
+  // The attachment could not be found, or the payload could not be created.
+  kMediaUnavailable,
+  // Not enough disk space to store the payload.
+  kNotEnoughSpace,
+  // The file type is not supported for sharing.
+  kUnsupportedAttachmentType,
+};
+
+// Metadata associated with the current transfer.
+struct TransferMetadata {
+  // Status of the current transfer.
+  TransferStatus status;
+  // Represents transfer progress as a percentage.
+  float progress;
+  // Represents the UKey2 token from Nearby Connections. Null if no
+  // UKey2 comparison is needed for this transfer.
+  string? token;
+  // Indicates whether this metadata has been seen before.
+  bool is_original;
+  // Indicates whether this is the last status for this transfer.
+  bool is_final_status;
 };
 
 // Interface to be notified about transfer updates. Includes an optional token
@@ -141,11 +179,8 @@
   // changed and the new value is provided.
   OnHighVisibilityChanged(bool in_high_visibility);
 
-  // Called when a share request has arrived and the user must be asked to
-  // accept or reject the call. This will only trigger when the receive manager
-  // is in high visibility, otherwise the notification manager (background
-  // receive surface) will handle the events.
-  OnIncomingShare(ShareTarget share_target, string? connection_token);
+  // Called when the status of the current transfer changes.
+  OnTransferUpdate(ShareTarget share_target, TransferMetadata metadata);
 };
 
 // Allows the caller to observe changes to or query high visibility, or
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
index 2d64d88..fda6238 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
@@ -59,7 +59,7 @@
   kSecurityAndSignIn = 304,
   kFingerprint = 305,
   kManageOtherPeople = 306,
-  kKerberos = 307,
+  kKerberosAccounts = 307,
 
   // Device section.
   kPointers = 400,
@@ -162,7 +162,7 @@
 const string kSecurityAndSignInSubpagePath = "lockScreen";
 const string kFingerprintSubpagePath = "lockScreen/fingerprint";
 const string kManageOtherPeopleSubpagePath = "accounts";
-const string kKerberosSubpagePath = "kerberosAccounts";
+const string kKerberosAccountsSubpagePath = "kerberosAccounts";
 
 // Device section.
 const string kDeviceSectionPath = "device";
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
index dc36534..e2d65d3 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
@@ -46,7 +46,7 @@
       chromeos::settings::mojom::kSecurityAndSignInSubpagePath,
       chromeos::settings::mojom::kFingerprintSubpagePath,
       chromeos::settings::mojom::kManageOtherPeopleSubpagePath,
-      chromeos::settings::mojom::kKerberosSubpagePath,
+      chromeos::settings::mojom::kKerberosAccountsSubpagePath,
 
       // Device section.
       chromeos::settings::mojom::kDeviceSectionPath,
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index f2f4bd2..74bf141 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -7,6 +7,7 @@
 #include "ash/public/cpp/ash_features.h"
 #include "base/bind.h"
 #include "base/i18n/number_formatting.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
@@ -235,25 +236,25 @@
 const std::vector<SearchConcept>& GetKerberosSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags({
       {IDS_OS_SETTINGS_TAG_KERBEROS_ADD,
-       mojom::kKerberosSubpagePath,
+       mojom::kKerberosAccountsSubpagePath,
        mojom::SearchResultIcon::kAvatar,
        mojom::SearchResultDefaultRank::kMedium,
        mojom::SearchResultType::kSetting,
        {.setting = mojom::Setting::kAddKerberosTicket}},
       {IDS_OS_SETTINGS_TAG_KERBEROS_REMOVE,
-       mojom::kKerberosSubpagePath,
+       mojom::kKerberosAccountsSubpagePath,
        mojom::SearchResultIcon::kAvatar,
        mojom::SearchResultDefaultRank::kMedium,
        mojom::SearchResultType::kSetting,
        {.setting = mojom::Setting::kRemoveKerberosTicket}},
       {IDS_OS_SETTINGS_TAG_KERBEROS,
-       mojom::kKerberosSubpagePath,
+       mojom::kKerberosAccountsSubpagePath,
        mojom::SearchResultIcon::kAvatar,
        mojom::SearchResultDefaultRank::kMedium,
        mojom::SearchResultType::kSubpage,
-       {.subpage = mojom::Subpage::kKerberos}},
+       {.subpage = mojom::Subpage::kKerberosAccounts}},
       {IDS_OS_SETTINGS_TAG_KERBEROS_ACTIVE,
-       mojom::kKerberosSubpagePath,
+       mojom::kKerberosAccountsSubpagePath,
        mojom::SearchResultIcon::kAvatar,
        mojom::SearchResultDefaultRank::kMedium,
        mojom::SearchResultType::kSetting,
@@ -920,8 +921,15 @@
 
 bool PeopleSection::LogMetric(mojom::Setting setting,
                               base::Value& value) const {
-  // Unimplemented.
-  return false;
+  switch (setting) {
+    case mojom::Setting::kAddAccount:
+      base::UmaHistogramCounts1000("ChromeOS.Settings.People.AddAccountCount",
+                                   value.GetInt());
+      return true;
+
+    default:
+      return false;
+  }
 }
 
 void PeopleSection::RegisterHierarchy(HierarchyGenerator* generator) const {
@@ -1010,17 +1018,18 @@
                             kManageOtherPeopleSettings, generator);
 
   // Kerberos.
-  generator->RegisterTopLevelSubpage(
-      IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE, mojom::Subpage::kKerberos,
-      mojom::SearchResultIcon::kAvatar, mojom::SearchResultDefaultRank::kMedium,
-      mojom::kKerberosSubpagePath);
+  generator->RegisterTopLevelSubpage(IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE,
+                                     mojom::Subpage::kKerberosAccounts,
+                                     mojom::SearchResultIcon::kAvatar,
+                                     mojom::SearchResultDefaultRank::kMedium,
+                                     mojom::kKerberosAccountsSubpagePath);
   static constexpr mojom::Setting kKerberosSettings[] = {
       mojom::Setting::kAddKerberosTicket,
       mojom::Setting::kRemoveKerberosTicket,
       mojom::Setting::kSetActiveKerberosTicket,
   };
-  RegisterNestedSettingBulk(mojom::Subpage::kKerberos, kKerberosSettings,
-                            generator);
+  RegisterNestedSettingBulk(mojom::Subpage::kKerberosAccounts,
+                            kKerberosSettings, generator);
 }
 
 void PeopleSection::FetchAccounts() {
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
index bf0fcf6..6d5e320e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker.h
@@ -39,7 +39,9 @@
   // SettingsUserActionTracker();
   friend class SettingsUserActionTrackerTest;
   FRIEND_TEST_ALL_PREFIXES(SettingsUserActionTrackerTest,
-                           TestRecordSettingChanged);
+                           TestRecordSettingChangedBool);
+  FRIEND_TEST_ALL_PREFIXES(SettingsUserActionTrackerTest,
+                           TestRecordSettingChangedInt);
 
   // mojom::UserActionRecorder:
   void RecordPageFocus() override;
diff --git a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
index de415ce..d545b37f 100644
--- a/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/settings_user_action_tracker_unittest.cc
@@ -33,6 +33,8 @@
   void SetUp() override {
     fake_hierarchy_.AddSettingMetadata(mojom::Section::kBluetooth,
                                        mojom::Setting::kBluetoothOnOff);
+    fake_hierarchy_.AddSettingMetadata(mojom::Section::kPeople,
+                                       mojom::Setting::kAddAccount);
   }
 
   base::HistogramTester histogram_tester_;
@@ -41,7 +43,7 @@
   SettingsUserActionTracker tracker_;
 };
 
-TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChanged) {
+TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedBool) {
   // Record that the bluetooth enabled setting was toggled off.
   tracker_.RecordSettingChangeWithDetails(
       mojom::Setting::kBluetoothOnOff,
@@ -63,5 +65,26 @@
               mojom::Setting::kBluetoothOnOff);
 }
 
+TEST_F(SettingsUserActionTrackerTest, TestRecordSettingChangedInt) {
+  // Record that the user tried to add a 3rd account.
+  tracker_.RecordSettingChangeWithDetails(
+      mojom::Setting::kAddAccount, mojom::SettingChangeValue::NewIntValue(3));
+
+  // The umbrella metric for which setting was changed should be updated. Note
+  // that kAddAccount has enum value of 300.
+  histogram_tester_.ExpectTotalCount("ChromeOS.Settings.SettingChanged",
+                                     /*count=*/1);
+  histogram_tester_.ExpectBucketCount("ChromeOS.Settings.SettingChanged",
+                                      /*sample=*/300,
+                                      /*count=*/1);
+
+  // The LogMetric fn in the People section should have been called.
+  const FakeOsSettingsSection* people_section =
+      static_cast<const FakeOsSettingsSection*>(
+          fake_sections_.GetSection(mojom::Section::kPeople));
+  EXPECT_TRUE(people_section->logged_metrics().back() ==
+              mojom::Setting::kAddAccount);
+}
+
 }  // namespace settings.
 }  // namespace chromeos.
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 22cf1ad..7b143b8 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1602266368-0e61a032fa7157fa29bc804c0d7295bad87c6193.profdata
+chrome-linux-master-1602329287-d9e640c0f4fb081a95bb071b07a3e8b9abe79208.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 0cd4f90..cc69157 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1602266368-92d2928e0040ad47ba60bbcac401f36efe1994de.profdata
+chrome-mac-master-1602329287-377f3782d4c02db29be26bdaf518b1d3cd99dfb6.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index c5cc205..abc7604 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1602201537-a14eb3c04e9257cb13970f54aebda7b1af96d2f4.profdata
+chrome-win32-master-1602329287-4430a8255045839af70a229385d0ebd7f7aeb9aa.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 5734b6b8..6342722 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1602244639-9ed9777b412f549b6770ee430e4768c83e1e2c1e.profdata
+chrome-win64-master-1602319457-99315dd935bfb1b8d29d7c96af31a8803cbd1a16.profdata
diff --git a/chrome/common/extensions/api/accessibility_private.json b/chrome/common/extensions/api/accessibility_private.json
index 1ef1a1e..f1aba0f 100644
--- a/chrome/common/extensions/api/accessibility_private.json
+++ b/chrome/common/extensions/api/accessibility_private.json
@@ -332,6 +332,19 @@
         "platforms": ["chromeos"]
       },
       {
+        "name": "enablePointScan",
+        "type": "function",
+        "description": "Enables or disables point scanning in Switch Access.",
+        "parameters": [
+          {
+            "name": "enabled",
+            "type": "boolean",
+            "description": "True for start point scanning, false for end point scanning."
+          }
+        ],
+        "platforms": ["chromeos"]
+      },
+      {
         "name": "setNativeChromeVoxArcSupportForCurrentApp",
         "type": "function",
         "description": "Sets current ARC app to use native ARC support.",
diff --git a/chrome/common/extensions/api/autotest_private.idl b/chrome/common/extensions/api/autotest_private.idl
index 36a7111..4d83442 100644
--- a/chrome/common/extensions/api/autotest_private.idl
+++ b/chrome/common/extensions/api/autotest_private.idl
@@ -986,6 +986,14 @@
     // cannot be removed.
     static void removeActiveDesk(DesksCallback callback);
 
+    // Activates the desk at the given |index| by chaining multiple
+    // activate-desk animations.
+    // |index|: the zero-based index of the desk desired to be activated.
+    // |callback|: called indicating success when the animation completes, or
+    // failure when the desk at |index| is already the active desk.
+    static void activateAdjacentDesksToTargetIndex(long index,
+                                                   DesksCallback callback);
+
     // Create mouse events to cause a mouse click.
     // |button|: the mouse button for the click event.
     // |callback|: called after the mouse click finishes.
diff --git a/chrome/test/data/webui/settings/chromeos/fake_receive_manager.js b/chrome/test/data/webui/settings/chromeos/fake_receive_manager.js
index 47ad209..6a9e4a62 100644
--- a/chrome/test/data/webui/settings/chromeos/fake_receive_manager.js
+++ b/chrome/test/data/webui/settings/chromeos/fake_receive_manager.js
@@ -40,7 +40,14 @@
 
     simulateShareTargetArrival(name, connectionToken) {
       const target = {id: {low: 1, high: 2}, name: name, type: 1};
-      this.observer_.onIncomingShare(target, connectionToken);
+      const metadata = {
+        'status': nearbyShare.mojom.TransferStatus.kAwaitingLocalConfirmation,
+        progress: 0.0,
+        token: connectionToken,
+        is_original: true,
+        is_final_status: false
+      };
+      this.observer_.onTransferUpdate(target, metadata);
       return target;
     }
 
diff --git a/chrome/test/data/webui/settings/chromeos/nearby_share_receive_dialog_tests.js b/chrome/test/data/webui/settings/chromeos/nearby_share_receive_dialog_tests.js
index a35a7b88..e1b891ba 100644
--- a/chrome/test/data/webui/settings/chromeos/nearby_share_receive_dialog_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/nearby_share_receive_dialog_tests.js
@@ -128,7 +128,7 @@
         });
 
     test(
-        'unregister surface, onIncomingShare, does not close dialog',
+        'unregister surface, OnTransferUpdate, does not close dialog',
         async function() {
           // When attached we enter high visibility mode by default
           assertTrue(isVisible('nearby-share-high-visibility-page'));
diff --git a/chrome/tools/build/linux/FILES.cfg b/chrome/tools/build/linux/FILES.cfg
index 54b6b6a..204419e1 100644
--- a/chrome/tools/build/linux/FILES.cfg
+++ b/chrome/tools/build/linux/FILES.cfg
@@ -154,20 +154,6 @@
     'buildtype': ['official'],
     'archive': 'remoting-me2me-host-linux.zip',
   },
-  # Remoting symbols:
-  {
-    'filename': 'remote_assistance_host.debug',
-    'arch': ['64bit'],
-    'buildtype': ['dev', 'official'],
-    'archive': 'remoting-debug-info.zip',
-  },
-  {
-    # Include all debug symbols that start with `remoting`.
-    'filename': 'remoting*.debug',
-    'arch': ['64bit'],
-    'buildtype': ['dev', 'official'],
-    'archive': 'remoting-debug-info.zip',
-  },
   # Breakpad symbols:
   {
     'filename': 'chrome.breakpad.x64',
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 2d52f83c..7ca713c 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -37,6 +37,7 @@
 #include "chromecast/browser/cast_browser_context.h"
 #include "chromecast/browser/cast_browser_process.h"
 #include "chromecast/browser/cast_content_browser_client.h"
+#include "chromecast/browser/cast_extension_url_loader_factory.h"
 #include "chromecast/browser/cast_feature_list_creator.h"
 #include "chromecast/browser/cast_system_memory_pressure_evaluator.h"
 #include "chromecast/browser/cast_system_memory_pressure_evaluator_adjuster.h"
@@ -384,6 +385,7 @@
   extensions::EnsureBrowserContextKeyedServiceFactoriesBuilt();
 
   extensions::CastExtensionSystemFactory::GetInstance();
+  CastExtensionURLLoaderFactory::EnsureShutdownNotifierFactoryBuilt();
 }
 #endif
 
diff --git a/chromecast/browser/cast_extension_url_loader_factory.cc b/chromecast/browser/cast_extension_url_loader_factory.cc
index 71b8353..0fdd582 100644
--- a/chromecast/browser/cast_extension_url_loader_factory.cc
+++ b/chromecast/browser/cast_extension_url_loader_factory.cc
@@ -10,11 +10,13 @@
 #include <vector>
 
 #include "base/strings/strcat.h"
+#include "chromecast/browser/extensions/cast_extension_system_factory.h"
 #include "chromecast/common/cast_redirect_manifest_handler.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_factory.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
@@ -181,6 +183,16 @@
           content::BrowserContext::GetDefaultStoragePartition(browser_context)
               ->GetURLLoaderFactoryForBrowserProcess()) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  // base::Unretained is safe below, because lifetime of
+  // |browser_context_shutdown_subscription_| guarantees that
+  // OnBrowserContextDestroyed won't be called after |this| is destroyed.
+  browser_context_shutdown_subscription_ =
+      BrowserContextShutdownNotifierFactory::GetInstance()
+          ->Get(browser_context)
+          ->Subscribe(base::BindRepeating(
+              &CastExtensionURLLoaderFactory::OnBrowserContextDestroyed,
+              base::Unretained(this)));
 }
 
 CastExtensionURLLoaderFactory::~CastExtensionURLLoaderFactory() = default;
@@ -234,13 +246,43 @@
       network_factory_);
 }
 
+void CastExtensionURLLoaderFactory::OnBrowserContextDestroyed() {
+  // When the BrowserContext gets destroyed, |this| factory is not able to serve
+  // any more requests.
+  DisconnectReceiversAndDestroy();
+}
+
+// static
+CastExtensionURLLoaderFactory::BrowserContextShutdownNotifierFactory*
+CastExtensionURLLoaderFactory::BrowserContextShutdownNotifierFactory::
+    GetInstance() {
+  static base::NoDestructor<BrowserContextShutdownNotifierFactory> s_factory;
+  return s_factory.get();
+}
+
+CastExtensionURLLoaderFactory::BrowserContextShutdownNotifierFactory::
+    BrowserContextShutdownNotifierFactory()
+    : BrowserContextKeyedServiceShutdownNotifierFactory(
+          "CastExtensionURLLoaderFactory::"
+          "BrowserContextShutdownNotifierFactory") {
+  DependsOn(extensions::ExtensionRegistryFactory::GetInstance());
+  DependsOn(extensions::CastExtensionSystemFactory::GetInstance());
+}
+
 // static
 mojo::PendingRemote<network::mojom::URLLoaderFactory>
 CastExtensionURLLoaderFactory::Create(
     content::BrowserContext* browser_context,
     mojo::PendingRemote<network::mojom::URLLoaderFactory> extension_factory) {
+  DCHECK(browser_context);
+
   mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
 
+  // Return an unbound |pending_remote| if the |browser_context| has already
+  // started shutting down.
+  if (browser_context->ShutdownStarted())
+    return pending_remote;
+
   // The CastExtensionURLLoaderFactory will delete itself when there are no more
   // receivers - see the NonNetworkURLLoaderFactoryBase::OnDisconnect method.
   new CastExtensionURLLoaderFactory(
@@ -250,5 +292,10 @@
   return pending_remote;
 }
 
+// static
+void CastExtensionURLLoaderFactory::EnsureShutdownNotifierFactoryBuilt() {
+  BrowserContextShutdownNotifierFactory::GetInstance();
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_extension_url_loader_factory.h b/chromecast/browser/cast_extension_url_loader_factory.h
index b3c1c0c..f39c9ef 100644
--- a/chromecast/browser/cast_extension_url_loader_factory.h
+++ b/chromecast/browser/cast_extension_url_loader_factory.h
@@ -5,7 +5,12 @@
 #ifndef CHROMECAST_BROWSER_CAST_EXTENSION_URL_LOADER_FACTORY_H_
 #define CHROMECAST_BROWSER_CAST_EXTENSION_URL_LOADER_FACTORY_H_
 
+#include <memory>
+
 #include "base/macros.h"
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
 #include "content/public/browser/non_network_url_loader_factory_base.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -44,6 +49,8 @@
       content::BrowserContext* browser_context,
       mojo::PendingRemote<network::mojom::URLLoaderFactory> extension_factory);
 
+  static void EnsureShutdownNotifierFactoryBuilt();
+
  private:
   ~CastExtensionURLLoaderFactory() override;
 
@@ -65,10 +72,31 @@
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
       override;
 
+  void OnBrowserContextDestroyed();
+
+  class BrowserContextShutdownNotifierFactory
+      : public BrowserContextKeyedServiceShutdownNotifierFactory {
+   public:
+    static BrowserContextShutdownNotifierFactory* GetInstance();
+
+    // No copying.
+    BrowserContextShutdownNotifierFactory(
+        const BrowserContextShutdownNotifierFactory&) = delete;
+    BrowserContextShutdownNotifierFactory& operator=(
+        const BrowserContextShutdownNotifierFactory&) = delete;
+
+   private:
+    friend class base::NoDestructor<BrowserContextShutdownNotifierFactory>;
+    BrowserContextShutdownNotifierFactory();
+  };
+
   extensions::ExtensionRegistry* extension_registry_;
   mojo::Remote<network::mojom::URLLoaderFactory> extension_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> network_factory_;
 
+  std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
+      browser_context_shutdown_subscription_;
+
   DISALLOW_COPY_AND_ASSIGN(CastExtensionURLLoaderFactory);
 };
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 5544157..696f4d3 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13520.0.0
\ No newline at end of file
+13522.0.0
\ No newline at end of file
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 6c5d733..20f5bed 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-87-4265.0-1601890590-benchmark-87.0.4280.9-r1.orderfile.xz
+chromeos-chrome-orderfile-field-87-4265.0-1601890590-benchmark-87.0.4280.15-r1.orderfile.xz
diff --git a/chromeos/ui/base/window_properties.cc b/chromeos/ui/base/window_properties.cc
index 1fd45e46..57b76565 100644
--- a/chromeos/ui/base/window_properties.cc
+++ b/chromeos/ui/base/window_properties.cc
@@ -4,10 +4,14 @@
 
 #include "chromeos/ui/base/window_properties.h"
 
+#include "chromeos/ui/base/window_state_type.h"
 #include "ui/aura/window.h"
 
 namespace chromeos {
 
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsShowingInOverviewKey, false)
 
+DEFINE_UI_CLASS_PROPERTY_KEY(WindowStateType,
+                             kWindowStateTypeKey,
+                             WindowStateType::kDefault)
 }  // namespace chromeos
diff --git a/chromeos/ui/base/window_properties.h b/chromeos/ui/base/window_properties.h
index 9b12271..aab2a63 100644
--- a/chromeos/ui/base/window_properties.h
+++ b/chromeos/ui/base/window_properties.h
@@ -15,6 +15,8 @@
 
 namespace chromeos {
 
+enum class WindowStateType;
+
 // Shell-specific window property keys for use by ash and lacros clients.
 
 // Alphabetical sort.
@@ -23,6 +25,10 @@
 COMPONENT_EXPORT(CHROMEOS_UI_BASE)
 extern const aura::WindowProperty<bool>* const kIsShowingInOverviewKey;
 
+// A property key to indicate ash's extended window state.
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<WindowStateType>* const kWindowStateTypeKey;
+
 // Alphabetical sort.
 
 }  // namespace chromeos
diff --git a/components/heap_profiling/multi_process/test_driver.cc b/components/heap_profiling/multi_process/test_driver.cc
index cf61046..fb67ecb 100644
--- a/components/heap_profiling/multi_process/test_driver.cc
+++ b/components/heap_profiling/multi_process/test_driver.cc
@@ -17,6 +17,7 @@
 #include "base/stl_util.h"
 #include "base/test/bind_test_util.h"
 #include "base/threading/platform_thread.h"
+#include "base/trace_event/heap_profiler.h"
 #include "base/trace_event/heap_profiler_event_filter.h"
 #include "base/values.h"
 #include "build/build_config.h"
diff --git a/components/messages/android/BUILD.gn b/components/messages/android/BUILD.gn
index da349bd..e51ca03 100644
--- a/components/messages/android/BUILD.gn
+++ b/components/messages/android/BUILD.gn
@@ -11,7 +11,8 @@
     "java/src/org/chromium/components/messages/MessageBannerView.java",
     "java/src/org/chromium/components/messages/MessageBannerViewBinder.java",
     "java/src/org/chromium/components/messages/MessageContainer.java",
-    "java/src/org/chromium/components/messages/MessageQueueManager.java",
+    "java/src/org/chromium/components/messages/MessageDispatcher.java",
+    "java/src/org/chromium/components/messages/MessageDispatcherProvider.java",
     "java/src/org/chromium/components/messages/MessageStateHandler.java",
     "java/src/org/chromium/components/messages/SingleActionMessage.java",
   ]
@@ -38,6 +39,15 @@
   ]
 }
 
+# Build target for Messages manager code, that owns and initializes
+# MessageDispatcher.
+android_library("manager_java") {
+  sources = [
+    "java/src/org/chromium/components/messages/ManagedMessageDispatcher.java",
+  ]
+  deps = [ ":java" ]
+}
+
 static_library("feature_flags") {
   sources = [
     "messages_feature.cc",
@@ -79,6 +89,7 @@
 
   deps = [
     ":java",
+    ":manager_java",
     "//ui/android:ui_full_java",
   ]
 }
diff --git a/components/messages/android/internal/BUILD.gn b/components/messages/android/internal/BUILD.gn
index e31cdf6..3eac12b 100644
--- a/components/messages/android/internal/BUILD.gn
+++ b/components/messages/android/internal/BUILD.gn
@@ -6,12 +6,14 @@
 
 android_library("java") {
   sources = [
-    "java/src/org/chromium/components/messages/MessageQueueManagerImpl.java",
+    "java/src/org/chromium/components/messages/MessageDispatcherImpl.java",
+    "java/src/org/chromium/components/messages/MessageQueueManager.java",
     "java/src/org/chromium/components/messages/MessagesFactory.java",
   ]
 
   deps = [
     "..:java",
+    "..:manager_java",
     "//base:base_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//ui/android:ui_full_java",
@@ -22,7 +24,9 @@
   # Skip platform checks since Robolectric depends on requires_android targets.
   bypass_platform_checks = true
   testonly = true
-  sources = [ "java/src/org/chromium/components/messages/MessageQueueManagerImplTest.java" ]
+  sources = [
+    "java/src/org/chromium/components/messages/MessageQueueManagerTest.java",
+  ]
   deps = [
     ":java",
     "..:java",
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageDispatcherImpl.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageDispatcherImpl.java
new file mode 100644
index 0000000..8b0029d
--- /dev/null
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageDispatcherImpl.java
@@ -0,0 +1,36 @@
+// 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.components.messages;
+
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * This class implements public MessageDispatcher interface, delegating the actual work to
+ * MessageQueueManager.
+ */
+public class MessageDispatcherImpl implements ManagedMessageDispatcher {
+    private final MessageQueueManager mMessageQueueManager = new MessageQueueManager();
+    private final MessageContainer mMessageContainer;
+
+    /**
+     * Build a new message dispatcher
+     * @param messageContainer A container view for displaying message banners.
+     */
+    public MessageDispatcherImpl(MessageContainer messageContainer) {
+        mMessageContainer = messageContainer;
+    }
+
+    @Override
+    public void enqueueMessage(PropertyModel messageProperties) {
+        MessageStateHandler messageStateHandler =
+                new SingleActionMessage(mMessageContainer, messageProperties);
+        mMessageQueueManager.enqueueMessage(messageStateHandler, messageProperties);
+    }
+
+    @Override
+    public void dismissMessage(PropertyModel messageProperties) {
+        mMessageQueueManager.dismissMessage(messageProperties);
+    }
+}
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerImpl.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java
similarity index 64%
rename from components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerImpl.java
rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java
index 4a04b47..d3aae5c 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerImpl.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManager.java
@@ -6,10 +6,6 @@
 
 import androidx.annotation.Nullable;
 
-import org.chromium.base.UnownedUserData;
-import org.chromium.base.UnownedUserDataKey;
-import org.chromium.ui.base.WindowAndroid;
-
 import java.util.ArrayDeque;
 import java.util.HashMap;
 import java.util.Map;
@@ -19,42 +15,13 @@
  * A class managing the queue of messages. Its primary role is to decide when to show/hide current
  * message and which message to show next.
  */
-class MessageQueueManagerImpl implements MessageQueueManager, UnownedUserData {
-    private static final UnownedUserDataKey<MessageQueueManagerImpl> KEY =
-            new UnownedUserDataKey<>(MessageQueueManagerImpl.class);
-
+class MessageQueueManager {
     private final Queue<MessageStateHandler> mMessageQueue = new ArrayDeque<>();
     private final Map<Object, MessageStateHandler> mMessageMap = new HashMap<>();
     @Nullable
     private MessageStateHandler mCurrentDisplayedMessage;
 
-    /**
-     * Get the activity's MessageQueueManager from the provided WindowAndroid.
-     * @param window The window to get the manager from.
-     * @return The activity's MessageQueueManager.
-     */
-    public static MessageQueueManagerImpl from(WindowAndroid window) {
-        return KEY.retrieveDataFromHost(window.getUnownedUserDataHost());
-    }
-
-    public MessageQueueManagerImpl() {}
-
-    /**
-     * Attaches MessageQueueManager to a given window. This window will be used later to retrieve
-     * activity's MessageQueueManager.
-     * @param window The window to attach to.
-     */
-    public void attachToWindowAndroid(WindowAndroid window) {
-        KEY.attachToHost(window.getUnownedUserDataHost(), this);
-    }
-
-    /**
-     * Destroys MessageQueueManager, detaching it from the WindowAndroid it was attached to.
-     */
-    @Override
-    public void destroy() {
-        KEY.detachFromAllHosts(this);
-    }
+    public MessageQueueManager() {}
 
     /**
      * Enqueues a message. Associates the message with its key; the key is used later to dismiss the
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerImplTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
similarity index 87%
rename from components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerImplTest.java
rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
index 928d08f..f45d7c6 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerImplTest.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageQueueManagerTest.java
@@ -17,10 +17,10 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
- * Unit tests for MessageQueueManagerImpl.
+ * Unit tests for MessageQueueManager.
  */
 @RunWith(BaseRobolectricTestRunner.class)
-public class MessageQueueManagerImplTest {
+public class MessageQueueManagerTest {
     /**
      * Tests lifecycle of a single message:
      *   - enqueueMessage() calls show()
@@ -29,7 +29,7 @@
     @Test
     @SmallTest
     public void testEnqueueMessage() {
-        MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
+        MessageQueueManager queueManager = new MessageQueueManager();
         MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
         MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
 
@@ -52,7 +52,7 @@
     @Test
     @SmallTest
     public void testOneMessageShownAtATime() {
-        MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
+        MessageQueueManager queueManager = new MessageQueueManager();
         MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
         MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
 
@@ -74,7 +74,7 @@
     @Test
     @SmallTest
     public void testDismissBeforeShow() {
-        MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
+        MessageQueueManager queueManager = new MessageQueueManager();
         MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
         MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
 
@@ -98,7 +98,7 @@
     @Test(expected = IllegalStateException.class)
     @SmallTest
     public void testEnqueueDuplicateKey() {
-        MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
+        MessageQueueManager queueManager = new MessageQueueManager();
         MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
         MessageStateHandler m2 = Mockito.mock(MessageStateHandler.class);
         Object key = new Object();
@@ -113,7 +113,7 @@
     @Test
     @SmallTest
     public void testDismissMessageTwice() {
-        MessageQueueManagerImpl queueManager = new MessageQueueManagerImpl();
+        MessageQueueManager queueManager = new MessageQueueManager();
         MessageStateHandler m1 = Mockito.mock(MessageStateHandler.class);
         queueManager.enqueueMessage(m1, m1);
         queueManager.dismissMessage(m1);
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/MessagesFactory.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessagesFactory.java
index 176cdc00..4a1c5845 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/MessagesFactory.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/MessagesFactory.java
@@ -9,13 +9,29 @@
 /** A factory for constructing different Messages related objects. */
 public class MessagesFactory {
     /**
-     * Creates an instance of MessageQueueManager and attaches it to WindowAndroid.
-     * @param windowAndroid The WindowAndroid to attach MessageQueueManager to.
-     * @return The constructed MessageQueueManager.
+     * Creates an instance of ManagedMessageDispatcher.
+     * @return The constructed ManagedMessageDispatcher.
      */
-    public static MessageQueueManager createMessageQueueManager(WindowAndroid windowAndroid) {
-        MessageQueueManagerImpl messageQueueManager = new MessageQueueManagerImpl();
-        messageQueueManager.attachToWindowAndroid(windowAndroid);
-        return messageQueueManager;
+    public static ManagedMessageDispatcher createMessageDispatcher(MessageContainer container) {
+        return new MessageDispatcherImpl(container);
     }
-}
\ No newline at end of file
+
+    /**
+     * Attaches MessageDispatcher as UnownedUserData to WindowAndroid making it accessible to
+     * components outside of chrome/android.
+     * @param windowAndroid The WindowAndroid to attach ManagedMessageDispatcher to.
+     * @param messageDispatcher The MessageDispatcher to attach.
+     */
+    public static void attachMessageDispatcher(
+            WindowAndroid windowAndroid, ManagedMessageDispatcher messageDispatcher) {
+        MessageDispatcherProvider.attach(windowAndroid, messageDispatcher);
+    }
+
+    /**
+     * Detaches MessageDispatcher from WindowAndroid.
+     * @param messageDispatcher The MessageDispatcher to detach from WindowAndroid.
+     */
+    public static void detachMessageDispatcher(ManagedMessageDispatcher messageDispatcher) {
+        MessageDispatcherProvider.detach(messageDispatcher);
+    }
+}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/ManagedMessageDispatcher.java b/components/messages/android/java/src/org/chromium/components/messages/ManagedMessageDispatcher.java
new file mode 100644
index 0000000..7718970
--- /dev/null
+++ b/components/messages/android/java/src/org/chromium/components/messages/ManagedMessageDispatcher.java
@@ -0,0 +1,11 @@
+// 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.components.messages;
+
+/**
+ * An interface for the MessageDispatcher owning object.
+ */
+public interface ManagedMessageDispatcher
+        extends MessageDispatcher, MessageDispatcherProvider.Unowned {}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageDispatcher.java b/components/messages/android/java/src/org/chromium/components/messages/MessageDispatcher.java
new file mode 100644
index 0000000..bbadd2d
--- /dev/null
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageDispatcher.java
@@ -0,0 +1,26 @@
+// 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.components.messages;
+
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * The public interface for Messages. To interact with messages, feature should obtain a reference
+ * to MessageDispatcher through MessageDispatcherProvider and call methods of MessageDispatcher.
+ */
+public interface MessageDispatcher {
+    /**
+     * Enqueues a message defined by its properties.
+     * @param messageProperties The PropertyModel with message's visual properties.
+     */
+    void enqueueMessage(PropertyModel messageProperties);
+
+    /**
+     * Dismisses a message referenced by its PropertyModel. Hdes the message if it is currently
+     * displayed. Displays the next message in the queue if available.
+     * @param messageProperties The PropertyModel of the messageto be dismissed.
+     */
+    void dismissMessage(PropertyModel messageProperties);
+}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageDispatcherProvider.java b/components/messages/android/java/src/org/chromium/components/messages/MessageDispatcherProvider.java
new file mode 100644
index 0000000..906dff5f
--- /dev/null
+++ b/components/messages/android/java/src/org/chromium/components/messages/MessageDispatcherProvider.java
@@ -0,0 +1,38 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.messages;
+
+import org.chromium.base.UnownedUserData;
+import org.chromium.base.UnownedUserDataKey;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * The class that handles association of MessageDispatcher with WindowAndroid and retrieval of the
+ * associated MessageDispatcher.
+ */
+public class MessageDispatcherProvider {
+    /** An interface that allows a MessageDispatcher to be associated with an unowned data host. */
+    interface Unowned extends MessageDispatcher, UnownedUserData {}
+
+    /** The key used to bind the MessageDispatcher to the unowned data host. */
+    private static final UnownedUserDataKey<Unowned> KEY = new UnownedUserDataKey<>(Unowned.class);
+
+    /**
+     * Retrieves the shared MessageDispatcher from the provided WindowAndroid.
+     * @param windowAndroid The window to retrieve MessageDispatcher from.
+     * @return An instance of MessageDispatcher associated with the window.
+     */
+    public static MessageDispatcher from(WindowAndroid windowAndroid) {
+        return KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost());
+    }
+
+    static void attach(WindowAndroid windowAndroid, Unowned controller) {
+        KEY.attachToHost(windowAndroid.getUnownedUserDataHost(), controller);
+    }
+
+    static void detach(Unowned controller) {
+        KEY.detachFromAllHosts(controller);
+    }
+}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageQueueManager.java b/components/messages/android/java/src/org/chromium/components/messages/MessageQueueManager.java
deleted file mode 100644
index 799d9d1..0000000
--- a/components/messages/android/java/src/org/chromium/components/messages/MessageQueueManager.java
+++ /dev/null
@@ -1,16 +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.components.messages;
-
-/**
- * The public interface for managing the queue of messages. The embedder should retain reference to
- * MessageQueueManager and destroy it when the Activity gets destroyed.
- */
-public interface MessageQueueManager {
-    /**
-     * Destroys MessageQueueManager, detaching it from the WindowAndroid it was attached to.
-     */
-    void destroy();
-}
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc
index 6cd32c8..d2117a29 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -108,6 +108,12 @@
   done_ = true;
   matches_.clear();
 
+  if (!base::FeatureList::IsEnabled(
+          omnibox::kOmniboxLocalZeroSuggestForAuthenticatedUsers) &&
+      client_->IsAuthenticated()) {
+    return;
+  }
+
   // Allow local history query suggestions only when the user is not in an
   // off-the-record context.
   if (client_->IsOffTheRecord())
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc b/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
index 3b9850b..3f91155 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
@@ -245,14 +245,13 @@
       "Omnibox.LocalHistoryZeroSuggest.AsyncDeleteTime", 0);
 }
 
-// Tests that suggestions are returned only user is not in an off-the-record
-// context, regardless of the user's authentication state.
+// Tests that suggestions are returned only if user is not in an off-the-record
+// context.
 TEST_F(LocalHistoryZeroSuggestProviderTest, Incognito) {
   LoadURLs({
       {default_search_provider(), "hello world", "&foo=bar", 1},
   });
 
-  EXPECT_CALL(*client_.get(), IsAuthenticated()).Times(0);
   EXPECT_CALL(*client_.get(), IsOffTheRecord())
       .Times(2)
       .WillOnce(testing::Return(true))
@@ -265,6 +264,64 @@
   ExpectMatches({{"hello world", 500}});
 }
 
+// Tests that suggestions are returned regardless of the authentication state
+// when omnibox::kOmniboxLocalZeroSuggestForAuthenticatedUsers is enabled.
+#if defined(OS_IOS)
+// Tests that enable additional features fail on iOS.
+#define MAYBE_ZeroSuggestForAuthenticatedUsers_Enabled \
+  DISABLED_ZeroSuggestForAuthenticatedUsers_Enabled
+#else
+#define MAYBE_ZeroSuggestForAuthenticatedUsers_Enabled \
+  ZeroSuggestForAuthenticatedUsers_Enabled
+#endif
+TEST_F(LocalHistoryZeroSuggestProviderTest,
+       MAYBE_ZeroSuggestForAuthenticatedUsers_Enabled) {
+  LoadURLs({
+      {default_search_provider(), "hello world", "&foo=bar", 1},
+  });
+
+  scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list_->InitAndEnableFeature(
+      omnibox::kOmniboxLocalZeroSuggestForAuthenticatedUsers);
+
+  EXPECT_CALL(*client_.get(), IsAuthenticated()).Times(0);
+
+  StartProviderAndWaitUntilDone();
+  ExpectMatches({{"hello world", 500}});
+}
+
+// Tests that suggestions are returned for signed-out users only when
+// when omnibox::kOmniboxLocalZeroSuggestForAuthenticatedUsers is disabled.
+#if defined(OS_IOS)
+// Tests that enable additional features fail on iOS.
+#define MAYBE_ZeroSuggestForAuthenticatedUsers_Disabled \
+  DISABLED_ZeroSuggestForAuthenticatedUsers_Disabled
+#else
+#define MAYBE_ZeroSuggestForAuthenticatedUsers_Disabled \
+  ZeroSuggestForAuthenticatedUsers_Disabled
+#endif
+TEST_F(LocalHistoryZeroSuggestProviderTest,
+       MAYBE_ZeroSuggestForAuthenticatedUsers_Disabled) {
+  LoadURLs({
+      {default_search_provider(), "hello world", "&foo=bar", 1},
+  });
+
+  scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list_->InitAndDisableFeature(
+      omnibox::kOmniboxLocalZeroSuggestForAuthenticatedUsers);
+
+  EXPECT_CALL(*client_.get(), IsAuthenticated())
+      .Times(2)
+      .WillOnce(testing::Return(true))
+      .WillOnce(testing::Return(false));
+
+  StartProviderAndWaitUntilDone();
+  ExpectMatches({});
+
+  StartProviderAndWaitUntilDone();
+  ExpectMatches({{"hello world", 500}});
+}
+
 // Tests that suggestions are returned only if FeatureFlags is configured
 // to return local history suggestions in the NTP.
 #if defined(OS_IOS)
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index ecbb75c0..df6dcd9 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -203,6 +203,13 @@
 const base::Feature kOmniboxLocalZeroSuggestAgeThreshold{
     "OmniboxLocalZeroSuggestAgeThreshold", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// If enabled, enables local zero-prefix suggestions for signed in users.
+// Local zero-prefix suggestions are enabled for signed in users by default. We
+// will be experimenting with DISABLING this behavior.
+const base::Feature kOmniboxLocalZeroSuggestForAuthenticatedUsers{
+    "OmniboxLocalZeroSuggestForAuthenticatedUsers",
+    base::FEATURE_ENABLED_BY_DEFAULT};
+
 // If enabled, ranks the local zero-prefix suggestions based on frecency
 // (combined frequency and recency).
 const base::Feature kOmniboxLocalZeroSuggestFrecencyRanking{
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 79e3d4a..81e63ef 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -58,6 +58,7 @@
 // On-Focus Suggestions a.k.a. ZeroSuggest.
 extern const base::Feature kClobberTriggersContextualWebZeroSuggest;
 extern const base::Feature kOmniboxLocalZeroSuggestAgeThreshold;
+extern const base::Feature kOmniboxLocalZeroSuggestForAuthenticatedUsers;
 extern const base::Feature kOmniboxLocalZeroSuggestFrecencyRanking;
 extern const base::Feature kOmniboxTrendingZeroPrefixSuggestionsOnNTP;
 extern const base::Feature kOnFocusSuggestionsContextualWeb;
diff --git a/components/safe_browsing/content/web_ui/BUILD.gn b/components/safe_browsing/content/web_ui/BUILD.gn
index f176fdb..36709990 100644
--- a/components/safe_browsing/content/web_ui/BUILD.gn
+++ b/components/safe_browsing/content/web_ui/BUILD.gn
@@ -26,7 +26,6 @@
     "//components/safe_browsing/core/browser:referrer_chain_provider",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/safe_browsing/core/db:v4_local_database_manager",
-    "//components/safe_browsing/core/realtime:policy_engine",
     "//components/safe_browsing/core/web_ui:constants",
     "//components/strings:components_strings_grit",
     "//components/sync/protocol:protocol",
diff --git a/components/safe_browsing/content/web_ui/resources/safe_browsing.html b/components/safe_browsing/content/web_ui/resources/safe_browsing.html
index 75689ec..6329254 100644
--- a/components/safe_browsing/content/web_ui/resources/safe_browsing.html
+++ b/components/safe_browsing/content/web_ui/resources/safe_browsing.html
@@ -92,7 +92,6 @@
         </tabpanel>
         <tabpanel>
           <h2>RT Lookup Pings</h2>
-          <p id="rt-lookup-experiment-enabled" class="result-container"></p>
           <table id="rt-lookup-ping-list" class="request-response"></table>
         </tabpanel>
         <tabpanel>
diff --git a/components/safe_browsing/content/web_ui/resources/safe_browsing.js b/components/safe_browsing/content/web_ui/resources/safe_browsing.js
index f5ec1b4..c846c67 100644
--- a/components/safe_browsing/content/web_ui/resources/safe_browsing.js
+++ b/components/safe_browsing/content/web_ui/resources/safe_browsing.js
@@ -111,9 +111,6 @@
       addRTLookupResponse(result);
     });
 
-    cr.sendWithPromise('getRTLookupExperimentEnabled', [])
-        .then((enabled) => addRTLookupExperimentEnabled(enabled));
-
     cr.sendWithPromise('getLogMessages', []).then((logMessages) => {
       logMessages.forEach(function(message) {
         addLogMessage(message);
@@ -323,12 +320,6 @@
     }
   }
 
-  function addRTLookupExperimentEnabled(enabled) {
-    const enabledFormatted = $('rt-lookup-template').content.cloneNode(true);
-    enabledFormatted.querySelector('#experiment-bool').textContent = enabled;
-    $('rt-lookup-experiment-enabled').appendChild(enabledFormatted);
-  }
-
   function addLogMessage(result) {
     const logDiv = $('log-messages');
     const eventFormatted = "[" + (new Date(result["time"])).toLocaleString() +
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
index 68db04f..f124e1c 100644
--- a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
@@ -37,7 +37,6 @@
 #include "components/enterprise/common/proto/connectors.pb.h"
 #include "components/safe_browsing/core/proto/webprotect.pb.h"
 #endif
-#include "components/safe_browsing/core/realtime/policy_engine.h"
 #include "components/safe_browsing/core/web_ui/constants.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/user_prefs/user_prefs.h"
@@ -1992,17 +1991,6 @@
   ResolveJavascriptCallback(base::Value(callback_id), responses_sent);
 }
 
-void SafeBrowsingUIHandler::GetRTLookupExperimentEnabled(
-    const base::ListValue* args) {
-  base::ListValue value;
-  value.Append(base::Value(RealTimePolicyEngine::IsUrlLookupEnabled()));
-
-  AllowJavascript();
-  std::string callback_id;
-  args->GetString(0, &callback_id);
-  ResolveJavascriptCallback(base::Value(callback_id), value);
-}
-
 void SafeBrowsingUIHandler::GetReferrerChain(const base::ListValue* args) {
   std::string url_string;
   args->GetString(1, &url_string);
@@ -2240,10 +2228,6 @@
       base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupResponses,
                           base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
-      "getRTLookupExperimentEnabled",
-      base::BindRepeating(&SafeBrowsingUIHandler::GetRTLookupExperimentEnabled,
-                          base::Unretained(this)));
-  web_ui()->RegisterMessageCallback(
       "getLogMessages",
       base::BindRepeating(&SafeBrowsingUIHandler::GetLogMessages,
                           base::Unretained(this)));
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.h b/components/safe_browsing/content/web_ui/safe_browsing_ui.h
index e363c8c..2a619de 100644
--- a/components/safe_browsing/content/web_ui/safe_browsing_ui.h
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.h
@@ -128,10 +128,6 @@
   // currently open chrome://safe-browsing tab was opened.
   void GetRTLookupResponses(const base::ListValue* args);
 
-  // Show whether real time lookup experiment is enabled. This is useful for
-  // testing on Android, because it also takes memory threshold into account.
-  void GetRTLookupExperimentEnabled(const base::ListValue* args);
-
   // Get the current referrer chain for a given URL.
   void GetReferrerChain(const base::ListValue* args);
 
diff --git a/components/safe_browsing/core/realtime/policy_engine.h b/components/safe_browsing/core/realtime/policy_engine.h
index e69ae30..148bcd9 100644
--- a/components/safe_browsing/core/realtime/policy_engine.h
+++ b/components/safe_browsing/core/realtime/policy_engine.h
@@ -74,7 +74,6 @@
                                                 bool is_off_the_record);
 
   friend class SafeBrowsingService;
-  friend class SafeBrowsingUIHandler;
 
  private:
   static bool IsInExcludedCountry(const std::string& country_code);
diff --git a/components/services/heap_profiling/public/cpp/profiling_client.cc b/components/services/heap_profiling/public/cpp/profiling_client.cc
index ff58a17..d39fdb8 100644
--- a/components/services/heap_profiling/public/cpp/profiling_client.cc
+++ b/components/services/heap_profiling/public/cpp/profiling_client.cc
@@ -14,6 +14,7 @@
 #include "base/sampling_heap_profiler/sampling_heap_profiler.h"
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
 #include "base/trace_event/heap_profiler_event_filter.h"
 #include "base/trace_event/malloc_dump_provider.h"
 #include "base/trace_event/memory_dump_manager.h"
diff --git a/components/timers/alarm_timer_chromeos.cc b/components/timers/alarm_timer_chromeos.cc
index af98c065..bd3947c 100644
--- a/components/timers/alarm_timer_chromeos.cc
+++ b/components/timers/alarm_timer_chromeos.cc
@@ -17,6 +17,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/pending_task.h"
 #include "base/task/common/task_annotator.h"
+#include "base/trace_event/task_execution_macros.h"
 #include "base/trace_event/trace_event.h"
 
 namespace timers {
diff --git a/components/tracing/test/trace_event_perftest.cc b/components/tracing/test/trace_event_perftest.cc
index 5f93f59c..c2317ce 100644
--- a/components/tracing/test/trace_event_perftest.cc
+++ b/components/tracing/test/trace_event_perftest.cc
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
+#include "base/trace_event/task_execution_macros.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
 #include "perf_test_helpers.h"
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 62084d4..67a7d8b 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -418,6 +418,7 @@
         'pageshow',
         'freeze',
         'resume',
+        'unload',
       ];
       for (event_name of event_list) {
         let result = event_name;
@@ -3256,6 +3257,117 @@
                   "window.visibilitychange", "window.pageshow.persisted"));
 }
 
+// Track the events dispatched when a page is deemed ineligible for back-forward
+// cache after we've dispatched the 'pagehide' event with persisted set to true.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       EventsForPageIneligibleAfterPagehidePersisted) {
+  ASSERT_TRUE(CreateHttpsServer()->Start());
+  GURL url_1(https_server()->GetURL("a.com", "/title1.html"));
+  GURL url_2(https_server()->GetURL("a.com", "/title2.html"));
+
+  // 1) Navigate to |url_1|.
+  EXPECT_TRUE(NavigateToURL(shell(), url_1));
+  RenderFrameHostImpl* rfh_1 = current_frame_host();
+  RenderFrameDeletedObserver delete_observer_rfh_1(rfh_1);
+  // 2) Use WebRTC (a non-sticky blocklisted feature), so that we would still do
+  // a RFH swap on same-site navigation and fire the 'pagehide' event during
+  // commit of the new page with 'persisted' set to true, but the page will not
+  // be eligible for back-forward cache after commit.
+  EXPECT_TRUE(ExecJs(rfh_1, "new RTCPeerConnection()"));
+
+  EXPECT_TRUE(ExecJs(rfh_1, R"(
+    window.onpagehide = (e) => {
+      if (e.persisted) {
+        window.domAutomationController.send('pagehide.persisted');
+      }
+    }
+    document.onvisibilitychange = () => {
+      if (document.visibilityState == 'hidden') {
+        window.domAutomationController.send('visibilitychange.hidden');
+      }
+    }
+    window.onunload = () => {
+      window.domAutomationController.send('unload');
+    }
+  )"));
+
+  DOMMessageQueue dom_message_queue(shell()->web_contents());
+  // 3) Navigate to |url_2|.
+  EXPECT_TRUE(NavigateToURL(shell(), url_2));
+  // |rfh_1| will not get into the back-forward cache and eventually get deleted
+  // because it uses a blocklisted feature.
+  delete_observer_rfh_1.WaitUntilDeleted();
+
+  // Only the pagehide and visibilitychange events will be dispatched.
+  int num_messages_received = 0;
+  std::string expected_messages[] = {"\"pagehide.persisted\"",
+                                     "\"visibilitychange.hidden\""};
+  std::string message;
+  while (dom_message_queue.PopMessage(&message)) {
+    EXPECT_EQ(expected_messages[num_messages_received], message);
+    num_messages_received++;
+  }
+  EXPECT_EQ(num_messages_received, 2);
+}
+
+// Track the events dispatched when a page is deemed ineligible for back-forward
+// cache before we've dispatched the pagehide event on it.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       EventsForPageIneligibleBeforePagehide) {
+  ASSERT_TRUE(CreateHttpsServer()->Start());
+  GURL url_1(https_server()->GetURL("a.com", "/title1.html"));
+  GURL url_2(https_server()->GetURL("b.com", "/title2.html"));
+
+  // 1) Navigate to |url_1|.
+  EXPECT_TRUE(NavigateToURL(shell(), url_1));
+  RenderFrameHostImpl* rfh_1 = current_frame_host();
+  RenderFrameDeletedObserver delete_observer_rfh_1(rfh_1);
+  // 2) Use keyboard lock (a sticky blocklisted feature), so that the page is
+  // known to be ineligible for bfcache at commit time, before we dispatch the
+  // pagehide event.
+  EXPECT_TRUE(ExecJs(rfh_1, R"(
+    new Promise(resolve => {
+      navigator.keyboard.lock();
+      resolve();
+    });
+  )"));
+
+  EXPECT_TRUE(ExecJs(rfh_1, R"(
+    window.onpagehide = (e) => {
+      if (!e.persisted) {
+        window.domAutomationController.send('pagehide.not_persisted');
+      }
+    }
+    document.onvisibilitychange = () => {
+      if (document.visibilityState == 'hidden') {
+        window.domAutomationController.send('visibilitychange.hidden');
+      }
+    }
+    window.onunload = () => {
+      window.domAutomationController.send('unload');
+    }
+  )"));
+
+  DOMMessageQueue dom_message_queue(shell()->web_contents());
+  // 3) Navigate to |url_2|.
+  EXPECT_TRUE(NavigateToURL(shell(), url_2));
+  // |rfh_1| will not get into the back-forward cache and eventually get deleted
+  // because it uses a blocklisted feature.
+  delete_observer_rfh_1.WaitUntilDeleted();
+
+  // "pagehide", "visibilitychange", and "unload" events will be dispatched.
+  int num_messages_received = 0;
+  std::string expected_messages[] = {"\"pagehide.not_persisted\"",
+                                     "\"visibilitychange.hidden\"",
+                                     "\"unload\""};
+  std::string message;
+  while (dom_message_queue.PopMessage(&message)) {
+    EXPECT_EQ(expected_messages[num_messages_received], message);
+    num_messages_received++;
+  }
+  EXPECT_EQ(num_messages_received, 3);
+}
+
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, EvictPageWithInfiniteLoop) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
index 4fab4dc..604357f 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
@@ -157,38 +157,6 @@
   return false;
 }
 
-std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter(
-    const base::Optional<
-        std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>>& filters) {
-  // There isn't much support for GATT over BR/EDR from neither platforms nor
-  // devices so performing a Dual scan will find devices that the API is not
-  // able to interact with. To avoid wasting power and confusing users with
-  // devices they are not able to interact with, we only perform an LE Scan.
-  auto discovery_filter = std::make_unique<device::BluetoothDiscoveryFilter>(
-      device::BLUETOOTH_TRANSPORT_LE);
-
-  if (filters) {
-    for (const auto& filter : filters.value()) {
-      device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter;
-      bool useful_filter = false;
-      if (filter->services) {
-        device_filter.uuids =
-            base::flat_set<device::BluetoothUUID>(filter->services.value());
-        useful_filter = true;
-      }
-      if (filter->name) {
-        device_filter.name = filter->name.value();
-        useful_filter = true;
-      }
-      if (useful_filter) {
-        discovery_filter->AddDeviceFilter(device_filter);
-      }
-    }
-  }
-
-  return discovery_filter;
-}
-
 void StopDiscoverySession(
     std::unique_ptr<device::BluetoothDiscoverySession> discovery_session) {
   // Nothing goes wrong if the discovery session fails to stop, and we don't
@@ -568,4 +536,45 @@
   }
 }
 
+// static
+std::unique_ptr<device::BluetoothDiscoveryFilter>
+BluetoothDeviceChooserController::ComputeScanFilter(
+    const base::Optional<
+        std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>>& filters) {
+  // There isn't much support for GATT over BR/EDR from neither platforms nor
+  // devices so performing a Dual scan will find devices that the API is not
+  // able to interact with. To avoid wasting power and confusing users with
+  // devices they are not able to interact with, we only perform an LE Scan.
+  auto discovery_filter = std::make_unique<device::BluetoothDiscoveryFilter>(
+      device::BLUETOOTH_TRANSPORT_LE);
+
+  if (filters) {
+    for (const auto& filter : filters.value()) {
+      device::BluetoothDiscoveryFilter::DeviceInfoFilter device_filter;
+      // Keep track of whether this filter can be converted accurately.
+      bool has_supported_fields = false;
+      if (filter->services) {
+        device_filter.uuids =
+            base::flat_set<device::BluetoothUUID>(filter->services.value());
+        has_supported_fields = true;
+      }
+      if (filter->name) {
+        device_filter.name = filter->name.value();
+        has_supported_fields = true;
+      }
+
+      // If we don't have any supported fields in this filter then we cannot
+      // filter any devices as we don't want to filter out devices which would
+      // have passed these unsupported filter criteria.
+      if (!has_supported_fields) {
+        discovery_filter->ClearDeviceFilters();
+        return discovery_filter;
+      }
+      discovery_filter->AddDeviceFilter(device_filter);
+    }
+  }
+
+  return discovery_filter;
+}
+
 }  // namespace content
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.h b/content/browser/bluetooth/bluetooth_device_chooser_controller.h
index 746ad34..7f6909f 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.h
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.h
@@ -20,6 +20,7 @@
 class BluetoothAdapter;
 class BluetoothDevice;
 class BluetoothDiscoverySession;
+class BluetoothDiscoveryFilter;
 }
 
 namespace content {
@@ -94,6 +95,10 @@
       TestScanDurationSetting setting =
           TestScanDurationSetting::IMMEDIATE_TIMEOUT);
 
+  static std::unique_ptr<device::BluetoothDiscoveryFilter> ComputeScanFilter(
+      const base::Optional<
+          std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>>& filters);
+
  private:
   // Populates the chooser with the GATT connected devices.
   void PopulateConnectedDevices();
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller_unittest.cc b/content/browser/bluetooth/bluetooth_device_chooser_controller_unittest.cc
index cb23903..39213de1 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller_unittest.cc
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller_unittest.cc
@@ -3,8 +3,13 @@
 // found in the LICENSE file.
 
 #include "content/browser/bluetooth/bluetooth_device_chooser_controller.h"
+#include "device/bluetooth/bluetooth_discovery_filter.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+constexpr char kHeartRateUUIDString[] = "0000180d-0000-1000-8000-00805f9b34fb";
+constexpr char kBatteryServiceUUIDString[] =
+    "0000180f-0000-1000-8000-00805f9b34fb";
+
 namespace content {
 
 class BluetoothDeviceChooserControllerTest : public testing::Test {};
@@ -36,4 +41,129 @@
       4, BluetoothDeviceChooserController::CalculateSignalStrengthLevel(127));
 }
 
+TEST_F(BluetoothDeviceChooserControllerTest, ComputeScanFilterTest) {
+  // No supported OS level filters have a name prefix filter. Since filters are
+  // unioned we must not filter any devices if we have a name prefix filter.
+  {
+    auto filter = blink::mojom::WebBluetoothLeScanFilter::New();
+    filter->name_prefix = "test name prefix";
+
+    std::vector<blink::mojom::WebBluetoothLeScanFilterPtr> scan_filters;
+    scan_filters.emplace_back(std::move(filter));
+
+    std::unique_ptr<device::BluetoothDiscoveryFilter> resulting_filter =
+        BluetoothDeviceChooserController::ComputeScanFilter(
+            std::move(scan_filters));
+
+    const base::flat_set<device::BluetoothDiscoveryFilter::DeviceInfoFilter>*
+        device_info_filters = resulting_filter->GetDeviceFilters();
+
+    EXPECT_TRUE(device_info_filters->empty());
+  }
+  // Ensures ComputeScanFilter adds all UUIDs to the BluetoothDiscoveryFilter
+  // that it creates
+  {
+    std::vector<device::BluetoothUUID> services;
+    services.emplace_back(device::BluetoothUUID(kHeartRateUUIDString));
+    services.emplace_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+    auto filter = blink::mojom::WebBluetoothLeScanFilter::New();
+    filter->services = std::move(services);
+
+    std::vector<blink::mojom::WebBluetoothLeScanFilterPtr> scan_filters;
+    scan_filters.emplace_back(std::move(filter));
+
+    std::unique_ptr<device::BluetoothDiscoveryFilter> resulting_filter =
+        BluetoothDeviceChooserController::ComputeScanFilter(
+            std::move(scan_filters));
+
+    const base::flat_set<device::BluetoothDiscoveryFilter::DeviceInfoFilter>*
+        device_info_filters = resulting_filter->GetDeviceFilters();
+
+    EXPECT_TRUE(device_info_filters->begin()->uuids.contains(
+        device::BluetoothUUID(kHeartRateUUIDString)));
+    EXPECT_TRUE(device_info_filters->begin()->uuids.contains(
+        device::BluetoothUUID(kBatteryServiceUUIDString)));
+    EXPECT_TRUE(device_info_filters->begin()->name.empty());
+  }
+  // Ensures ComputeScanFilter adds the correct name to the
+  // BluetoothDiscoveryFilter that it creates
+  {
+    auto filter = blink::mojom::WebBluetoothLeScanFilter::New();
+    filter->name = "test name";
+
+    std::vector<blink::mojom::WebBluetoothLeScanFilterPtr> scan_filters;
+    scan_filters.emplace_back(std::move(filter));
+
+    std::unique_ptr<device::BluetoothDiscoveryFilter> resulting_filter =
+        BluetoothDeviceChooserController::ComputeScanFilter(
+            std::move(scan_filters));
+
+    const base::flat_set<device::BluetoothDiscoveryFilter::DeviceInfoFilter>*
+        device_info_filters = resulting_filter->GetDeviceFilters();
+
+    EXPECT_TRUE(device_info_filters->begin()->uuids.empty());
+    EXPECT_EQ(device_info_filters->begin()->name, "test name");
+  }
+  // Ensures ComputeScanFilter adds both the name and the UUIDs to the
+  // BluetoothDiscoveryFilter that it creates
+  {
+    std::vector<device::BluetoothUUID> services;
+    services.emplace_back(device::BluetoothUUID(kHeartRateUUIDString));
+    services.emplace_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+    auto filter = blink::mojom::WebBluetoothLeScanFilter::New();
+    filter->services = std::move(services);
+    filter->name = "test name";
+
+    std::vector<blink::mojom::WebBluetoothLeScanFilterPtr> scan_filters;
+    scan_filters.emplace_back(std::move(filter));
+
+    std::unique_ptr<device::BluetoothDiscoveryFilter> resulting_filter =
+        BluetoothDeviceChooserController::ComputeScanFilter(
+            std::move(scan_filters));
+
+    const base::flat_set<device::BluetoothDiscoveryFilter::DeviceInfoFilter>*
+        device_info_filters = resulting_filter->GetDeviceFilters();
+
+    EXPECT_TRUE(device_info_filters->begin()->uuids.contains(
+        device::BluetoothUUID(kHeartRateUUIDString)));
+    EXPECT_TRUE(device_info_filters->begin()->uuids.contains(
+        device::BluetoothUUID(kBatteryServiceUUIDString)));
+    EXPECT_EQ(device_info_filters->begin()->name, "test name");
+  }
+  // Ensures we will not filter any devices if we have at least one
+  // name_prefix only filter even with other filters present.
+  {
+    std::vector<device::BluetoothUUID> services;
+    services.emplace_back(device::BluetoothUUID(kHeartRateUUIDString));
+    std::vector<device::BluetoothUUID> services2;
+    services2.emplace_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+    auto filter = blink::mojom::WebBluetoothLeScanFilter::New();
+    filter->services = std::move(services);
+    filter->name = "test name";
+
+    auto prefix_filter = blink::mojom::WebBluetoothLeScanFilter::New();
+    prefix_filter->name_prefix = "test name prefix";
+
+    auto filter2 = blink::mojom::WebBluetoothLeScanFilter::New();
+    filter2->services = std::move(services2);
+    filter2->name = "test name2";
+
+    std::vector<blink::mojom::WebBluetoothLeScanFilterPtr> scan_filters;
+    scan_filters.emplace_back(std::move(filter));
+    scan_filters.emplace_back(std::move(prefix_filter));
+
+    std::unique_ptr<device::BluetoothDiscoveryFilter> resulting_filter =
+        BluetoothDeviceChooserController::ComputeScanFilter(
+            std::move(scan_filters));
+
+    const base::flat_set<device::BluetoothDiscoveryFilter::DeviceInfoFilter>*
+        device_info_filters = resulting_filter->GetDeviceFilters();
+
+    EXPECT_TRUE(device_info_filters->empty());
+  }
+}
+
 }  // namespace content
\ No newline at end of file
diff --git a/content/browser/hid/hid_service.cc b/content/browser/hid/hid_service.cc
index e82e310..da81e01 100644
--- a/content/browser/hid/hid_service.cc
+++ b/content/browser/hid/hid_service.cc
@@ -183,22 +183,25 @@
   WebContents* web_contents =
       WebContents::FromRenderFrameHost(render_frame_host());
 
-  base::EraseIf(watcher_ids_, [&](const auto& watcher_entry) {
-    const auto* device_info =
-        delegate->GetDeviceInfo(web_contents, watcher_entry.first);
-    if (!device_info)
-      return true;
+  size_t watchers_removed =
+      base::EraseIf(watcher_ids_, [&](const auto& watcher_entry) {
+        const auto* device_info =
+            delegate->GetDeviceInfo(web_contents, watcher_entry.first);
+        if (!device_info)
+          return true;
 
-    if (delegate->HasDevicePermission(web_contents, origin(), *device_info)) {
-      return false;
-    }
+        if (delegate->HasDevicePermission(web_contents, origin(),
+                                          *device_info)) {
+          return false;
+        }
 
-    watchers_.Remove(watcher_entry.second);
-    return true;
-  });
+        watchers_.Remove(watcher_entry.second);
+        return true;
+      });
 
   // If needed decrement the active frame count.
-  OnWatcherRemoved(false /* cleanup_watcher_ids */);
+  if (watchers_removed)
+    OnWatcherRemoved(/*cleanup_watcher_ids=*/false);
 }
 
 void HidService::FinishGetDevices(
diff --git a/content/browser/hid/hid_service_unittest.cc b/content/browser/hid/hid_service_unittest.cc
index d352460..22d3277 100644
--- a/content/browser/hid/hid_service_unittest.cc
+++ b/content/browser/hid/hid_service_unittest.cc
@@ -388,4 +388,19 @@
   EXPECT_FALSE(connection.is_connected());
 }
 
+TEST_F(HidServiceTest, RevokeDevicePermissionWithoutConnection) {
+  NavigateAndCommit(GURL(kTestUrl));
+
+  mojo::Remote<blink::mojom::HidService> service;
+  contents()->GetMainFrame()->GetHidService(
+      service.BindNewPipeAndPassReceiver());
+
+  // Simulate user revoking permission.
+  url::Origin origin = url::Origin::Create(GURL(kTestUrl));
+  hid_delegate().OnPermissionRevoked(origin, origin);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(contents()->IsConnectedToHidDevice());
+}
+
 }  // namespace content
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index 81f9d4a..b439fb4 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -379,6 +379,22 @@
         mAllowedMenuItems = allowedMenuItems;
     }
 
+    @Override
+    public int getAllowedMenuItemIfAny(ActionMode mode, MenuItem item) {
+        if (!isActionModeValid()) return 0;
+
+        int id = item.getItemId();
+        int groupId = item.getGroupId();
+        if (id == R.id.select_action_menu_share) {
+            return MENU_ITEM_SHARE;
+        } else if (id == R.id.select_action_menu_web_search) {
+            return MENU_ITEM_WEB_SEARCH;
+        } else if (groupId == R.id.select_action_menu_text_processing_menus) {
+            return MENU_ITEM_PROCESS_TEXT;
+        }
+        return 0;
+    }
+
     @VisibleForTesting
     @CalledByNative
     public void showSelectionMenu(int left, int top, int right, int bottom, int handleHeight,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java b/content/public/android/java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java
index d95fc55..863a741 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/ActionModeCallbackHelper.java
@@ -100,7 +100,14 @@
      * Set the action mode menu items allowed on the content.
      * @param allowedMenuItems bit field of item-flag mapping.
      */
-    public abstract void setAllowedMenuItems(int menItems);
+    public abstract void setAllowedMenuItems(int allowedMenuItems);
+
+    /**
+     * If the passed in mode and menu matches one of the MENU_ITEM_* items, return it.
+     * Otherwise, return 0. Only call from inside the implementation of
+     * ActionMode.Callback#onActionItemClicked.
+     */
+    public abstract int getAllowedMenuItemIfAny(ActionMode mode, MenuItem item);
 
     /**
      * @see {@link ActionMode.Callback#onCreateActionMode(ActionMode, Menu)}
diff --git a/content/public/browser/non_network_url_loader_factory_base.cc b/content/public/browser/non_network_url_loader_factory_base.cc
index f66b0eb..7bb7797 100644
--- a/content/public/browser/non_network_url_loader_factory_base.cc
+++ b/content/public/browser/non_network_url_loader_factory_base.cc
@@ -17,6 +17,19 @@
 
 NonNetworkURLLoaderFactoryBase::~NonNetworkURLLoaderFactoryBase() = default;
 
+void NonNetworkURLLoaderFactoryBase::DisconnectReceiversAndDestroy() {
+  // Clear |receivers_| to explicitly make sure that no further method
+  // invocations or disconnection notifications will happen.  (per the
+  // comment of mojo::ReceiverSet::Clear)
+  receivers_.Clear();
+
+  // Similarly to OnDisconnect, if there are no more |receivers_|, then no
+  // instance methods of |this| can be called in the future (mojo methods Clone
+  // and CreateLoaderAndStart should be the only public entrypoints).
+  // Therefore, it is safe to delete |this| at this point.
+  delete this;
+}
+
 void NonNetworkURLLoaderFactoryBase::Clone(
     mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -27,8 +40,13 @@
 void NonNetworkURLLoaderFactoryBase::OnDisconnect() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
-  if (receivers_.empty())
+  if (receivers_.empty()) {
+    // If there are no more |receivers_|, then no instance methods of |this| can
+    // be called in the future (mojo methods Clone and CreateLoaderAndStart
+    // should be the only public entrypoints).  Therefore, it is safe to delete
+    // |this| at this point.
     delete this;
+  }
 }
 
 }  // namespace content
diff --git a/content/public/browser/non_network_url_loader_factory_base.h b/content/public/browser/non_network_url_loader_factory_base.h
index 67a16eea..591ce7e 100644
--- a/content/public/browser/non_network_url_loader_factory_base.h
+++ b/content/public/browser/non_network_url_loader_factory_base.h
@@ -39,6 +39,18 @@
 
   ~NonNetworkURLLoaderFactoryBase() override;
 
+  // Sometimes a derived class can no longer function, even when the set of
+  // |receivers_| is still non-empty.  This should be rare (typically the
+  // lifetime of users of mojo::Remote<network::mojom::URLLoaderFactory> should
+  // be shorter than whatever the factory depends on), but may happen in some
+  // corner cases (e.g. in a race between 1) BrowserContext destruction and 2)
+  // CreateLoaderAndStart mojo call).
+  //
+  // When a derived class gets notified that its dependencies got destroyed, it
+  // should call DisconnectReceiversAndDestroy to prevent any future calls to
+  // CreateLoaderAndStart.
+  void DisconnectReceiversAndDestroy();
+
   THREAD_CHECKER(thread_checker_);
 
  private:
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index 2c7f592..f04fcba 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -5,7 +5,8 @@
 #         mac
 #         win win10 win7 ]
 # tags: [ debug ]
-# tags: [ intel intel-0x3e92 intel-0xa2e intel-0x5912 intel-0xd26
+# tags: [ amd amd-0x699f
+#         intel intel-0x3e92 intel-0xa2e intel-0x5912 intel-0xd26
 #         nvidia nvidia-0x1cb3
 #         qualcomm-adreno-(tm)-418 ]
 # results: [ Failure RetryOnFailure Skip ]
@@ -103,17 +104,24 @@
 
 # Using YUY2 instead of NV12 on Windows 10 HD 630 GPUs due to NV12 support
 # showing up as SOFTWARE which causes zero copy to fail.
+crbug.com/1079393 [ win10 intel-0x5912 ] OverlayModeTraceTest_DirectComposition_Video_MP4_NV12 [ Failure ]
+crbug.com/1079393 [ win10 intel-0x5912 ] OverlayModeTraceTest_DirectComposition_Video_VP9_NV12 [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Underlay [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Underlay_DXVA [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Underlay_Fullsize [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4 [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_DXVA [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_FourColors_Aspect_4x3 [ Failure ]
+crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_FourColors_Rot_180 [ Failure ]
+crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_FourColors_Rot_270 [ Failure ]
+crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_FourColors_Rot_90 [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_Fullsize [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_NV12 [ Failure ]
+crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_MP4_VP_SCALING [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_VP9 [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_VP9_DXVA [ Failure ]
 crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_VP9_NV12 [ Failure ]
+crbug.com/1079393 [ win10 intel-0x5912 ] VideoPathTraceTest_DirectComposition_Video_VP9_VP_SCALING [ Failure ]
 
 # Fuchsia Flakes.
 crbug.com/1058255 [ fuchsia ] TraceTest_WebGLGreenTriangle_AA_Alpha [ Skip ]
@@ -138,3 +146,7 @@
 # BGRA swap chains don't consistently get promoted to overlays.
 crbug.com/1119491 [ win intel ] OverlayModeTraceTest_DirectComposition_Video_MP4_BGRA [ Skip ]
 crbug.com/1119491 [ win intel ] OverlayModeTraceTest_DirectComposition_Video_VP9_BGRA [ Skip ]
+
+# YUY2 swap chains don't work on AMD.
+crbug.com/1132381 [ win amd-0x699f ] VideoPathTraceTest_DirectComposition_Video_MP4_YUY2 [ Failure ]
+crbug.com/1132381 [ win amd-0x699f ] VideoPathTraceTest_DirectComposition_Video_VP9_YUY2 [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 3c66cd7..8229ad9 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -600,6 +600,7 @@
 crbug.com/1095679 [ android qualcomm-adreno-(tm)-418 no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ]
 crbug.com/1095679 [ android qualcomm-adreno-(tm)-418 no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
 crbug.com/1095679 [ android qualcomm-adreno-(tm)-418 no-passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
+crbug.com/1135785 [ android qualcomm-adreno-(tm)-418 no-passthrough ] conformance/textures/misc/texture-video-transparent.html [ RetryOnFailure ]
 # Timing out on this device for unknown reasons.
 crbug.com/1099148 [ android qualcomm-adreno-(tm)-418 ] deqp/data/gles2/shaders/swizzles.html [ Skip ]
 crbug.com/1122644 [ android qualcomm-adreno-(tm)-418 ] conformance/textures/misc/texture-upload-size.html [ Skip ]
diff --git a/device/bluetooth/BUILD.gn b/device/bluetooth/BUILD.gn
index 50a3a0c..15346647 100644
--- a/device/bluetooth/BUILD.gn
+++ b/device/bluetooth/BUILD.gn
@@ -59,8 +59,9 @@
     # Experimental implementation not ready for production use yet. See
     # public/mojom/README.md
 
-    # Single approved client:
+    # Approved clients:
     "//chrome/browser/ui/webui/bluetooth_internals:*",
+    "//chrome/browser/chromeos/nearby:bluetooth_adapter_util",
 
     # Implementation tests
     # Ideally only device_unittests, however android & fushia generate
diff --git a/device/bluetooth/bluetooth_discovery_filter.cc b/device/bluetooth/bluetooth_discovery_filter.cc
index 9ad3b1f..e5a885c7 100644
--- a/device/bluetooth/bluetooth_discovery_filter.cc
+++ b/device/bluetooth/bluetooth_discovery_filter.cc
@@ -95,6 +95,10 @@
   return &device_filters_;
 }
 
+void BluetoothDiscoveryFilter::ClearDeviceFilters() {
+  device_filters_.clear();
+}
+
 void BluetoothDiscoveryFilter::CopyFrom(
     const BluetoothDiscoveryFilter& filter) {
   transport_ = filter.transport_;
diff --git a/device/bluetooth/bluetooth_discovery_filter.h b/device/bluetooth/bluetooth_discovery_filter.h
index dade11d..ad2527f 100644
--- a/device/bluetooth/bluetooth_discovery_filter.h
+++ b/device/bluetooth/bluetooth_discovery_filter.h
@@ -93,6 +93,8 @@
   // Returns true if all fields in filter are empty
   bool IsDefault() const;
 
+  void ClearDeviceFilters();
+
   // Returns result of merging two filters together. If at least one of the
   // filters is NULL this will return an empty filter
   static std::unique_ptr<device::BluetoothDiscoveryFilter> Merge(
diff --git a/extensions/browser/browser_context_keyed_service_factories.cc b/extensions/browser/browser_context_keyed_service_factories.cc
index afdb3c8..176bf55 100644
--- a/extensions/browser/browser_context_keyed_service_factories.cc
+++ b/extensions/browser/browser_context_keyed_service_factories.cc
@@ -39,6 +39,7 @@
 #include "extensions/browser/event_router_factory.h"
 #include "extensions/browser/extension_message_filter.h"
 #include "extensions/browser/extension_prefs_factory.h"
+#include "extensions/browser/extension_protocols.h"
 #include "extensions/browser/guest_view/extensions_guest_view_message_filter.h"
 #include "extensions/browser/process_manager_factory.h"
 #include "extensions/browser/renderer_startup_helper.h"
@@ -83,6 +84,7 @@
   declarative_net_request::RulesMonitorService::GetFactoryInstance();
   DeclarativeUserScriptManagerFactory::GetInstance();
   DisplaySourceEventRouterFactory::GetInstance();
+  EnsureExtensionURLLoaderFactoryShutdownNotifierFactoryBuilt();
   EventRouterFactory::GetInstance();
   ExtensionMessageFilter::EnsureShutdownNotifierFactoryBuilt();
   ExtensionsGuestViewMessageFilter::EnsureShutdownNotifierFactoryBuilt();
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index ce31f93..5789324 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1580,6 +1580,8 @@
   FILEMANAGERPRIVATEINTERNAL_GETPDFTHUMBNAIL = 1517,
   AUTOTESTPRIVATE_REMOVEALLNOTIFICATIONS = 1518,
   VIRTUALKEYBOARDPRIVATE_OPENSUGGESTIONSETTINGS = 1519,
+  ACCESSIBILITY_PRIVATE_ENABLEPOINTSCAN = 1520,
+  AUTOTESTPRIVATE_ACTIVATEADJACENTDESKSTOTARGETINDEX = 1521,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/browser/extension_protocols.cc b/extensions/browser/extension_protocols.cc
index 34e8114..e4e1bf4 100644
--- a/extensions/browser/extension_protocols.cc
+++ b/extensions/browser/extension_protocols.cc
@@ -29,6 +29,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/no_destructor.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
@@ -38,6 +39,8 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/timer/elapsed_timer.h"
 #include "build/build_config.h"
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -53,6 +56,7 @@
 #include "extensions/browser/content_verify_job.h"
 #include "extensions/browser/extension_navigation_ui_data.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_registry_factory.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/extension_util.h"
 #include "extensions/browser/extensions_browser_client.h"
@@ -61,6 +65,7 @@
 #include "extensions/browser/info_map.h"
 #include "extensions/browser/media_router_extension_access_logger.h"
 #include "extensions/browser/process_map.h"
+#include "extensions/browser/process_map_factory.h"
 #include "extensions/browser/url_request_util.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
@@ -459,8 +464,15 @@
       base::UkmSourceId ukm_source_id,
       bool is_web_view_request,
       int render_process_id) {
+    DCHECK(browser_context);
+
     mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
 
+    // Return an unbound |pending_remote| if the |browser_context| has already
+    // started shutting down.
+    if (browser_context->ShutdownStarted())
+      return pending_remote;
+
     // Manages its own lifetime.
     new ExtensionURLLoaderFactory(
         browser_context, ukm_source_id, is_web_view_request, render_process_id,
@@ -469,6 +481,10 @@
     return pending_remote;
   }
 
+  static void EnsureShutdownNotifierFactoryBuilt() {
+    BrowserContextShutdownNotifierFactory::GetInstance();
+  }
+
  private:
   // Constructs ExtensionURLLoaderFactory bound to the |factory_receiver|.
   //
@@ -490,6 +506,16 @@
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     extension_info_map_ =
         extensions::ExtensionSystem::Get(browser_context_)->info_map();
+
+    // base::Unretained is safe below, because lifetime of
+    // |browser_context_shutdown_subscription_| guarantees that
+    // OnBrowserContextDestroyed won't be called after |this| is destroyed.
+    browser_context_shutdown_subscription_ =
+        BrowserContextShutdownNotifierFactory::GetInstance()
+            ->Get(browser_context)
+            ->Subscribe(base::BindRepeating(
+                &ExtensionURLLoaderFactory::OnBrowserContextDestroyed,
+                base::Unretained(this)));
   }
 
   ~ExtensionURLLoaderFactory() override = default;
@@ -506,6 +532,12 @@
       override {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+    if (browser_context_->ShutdownStarted()) {
+      mojo::Remote<network::mojom::URLLoaderClient>(std::move(client))
+          ->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
+      return;
+    }
+
     client =
         WrapWithMetricsIfNeeded(request.url, ukm_source_id_, std::move(client));
 
@@ -723,6 +755,38 @@
         /* allow_directory_listing */ false, std::move(response_headers));
   }
 
+  void OnBrowserContextDestroyed() {
+    // When |browser_context_| gets destroyed, |this| factory is not able to
+    // serve any more requests.
+    DisconnectReceiversAndDestroy();
+  }
+
+  class BrowserContextShutdownNotifierFactory
+      : public BrowserContextKeyedServiceShutdownNotifierFactory {
+   public:
+    static BrowserContextShutdownNotifierFactory* GetInstance() {
+      static base::NoDestructor<BrowserContextShutdownNotifierFactory>
+          s_factory;
+      return s_factory.get();
+    }
+
+    // No copying.
+    BrowserContextShutdownNotifierFactory(
+        const BrowserContextShutdownNotifierFactory&) = delete;
+    BrowserContextShutdownNotifierFactory& operator=(
+        const BrowserContextShutdownNotifierFactory&) = delete;
+
+   private:
+    friend class base::NoDestructor<BrowserContextShutdownNotifierFactory>;
+    BrowserContextShutdownNotifierFactory()
+        : BrowserContextKeyedServiceShutdownNotifierFactory(
+              "ExtensionURLLoaderFactory::"
+              "BrowserContextShutdownNotifierFactory") {
+      DependsOn(ExtensionRegistryFactory::GetInstance());
+      DependsOn(ProcessMapFactory::GetInstance());
+    }
+  };
+
   content::BrowserContext* browser_context_;
   bool is_web_view_request_;
   base::UkmSourceId ukm_source_id_;
@@ -733,6 +797,9 @@
   const int render_process_id_;
   scoped_refptr<extensions::InfoMap> extension_info_map_;
 
+  std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
+      browser_context_shutdown_subscription_;
+
   DISALLOW_COPY_AND_ASSIGN(ExtensionURLLoaderFactory);
 };
 
@@ -828,4 +895,8 @@
       browser_context, ukm_source_id, is_web_view_request, render_process_id);
 }
 
+void EnsureExtensionURLLoaderFactoryShutdownNotifierFactoryBuilt() {
+  ExtensionURLLoaderFactory::EnsureShutdownNotifierFactoryBuilt();
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/extension_protocols.h b/extensions/browser/extension_protocols.h
index 946774ba..7980d1f 100644
--- a/extensions/browser/extension_protocols.h
+++ b/extensions/browser/extension_protocols.h
@@ -77,6 +77,8 @@
 mojo::PendingRemote<network::mojom::URLLoaderFactory>
 CreateExtensionURLLoaderFactory(int render_process_id, int render_frame_id);
 
+void EnsureExtensionURLLoaderFactoryShutdownNotifierFactoryBuilt();
+
 }  // namespace extensions
 
 #endif  // EXTENSIONS_BROWSER_EXTENSION_PROTOCOLS_H_
diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc
index 7a69da7f..1126c48b 100644
--- a/extensions/common/url_pattern.cc
+++ b/extensions/common/url_pattern.cc
@@ -160,15 +160,9 @@
       match_subdomains_(false),
       port_("*") {
   ParseResult result = Parse(pattern);
-  if (result != ParseResult::kSuccess) {
-    const char* error_string = GetParseResultString(result);
-    // Temporarily add more logging to investigate why this code path is
-    // reached. For http://crbug.com/856948
-    LOG(ERROR) << "Invalid pattern was given " << pattern << " result "
-               << error_string;
-    NOTREACHED() << "URLPattern invalid: '" << pattern
-                 << "'; error: " << error_string;
-  }
+  DCHECK_EQ(ParseResult::kSuccess, result)
+      << "Parsing unexpectedly failed for pattern: " << pattern << ": "
+      << GetParseResultString(result);
 }
 
 URLPattern::URLPattern(const URLPattern& other) = default;
diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h
index 4547825..feda6fb 100644
--- a/extensions/common/url_pattern.h
+++ b/extensions/common/url_pattern.h
@@ -98,6 +98,7 @@
 
   // Convenience to construct a URLPattern from a string. If the string is not
   // known ahead of time, use Parse() instead, which returns success or failure.
+  // This method will DCHECK if parsing fails.
   URLPattern(int valid_schemes, base::StringPiece pattern);
 
   URLPattern();
diff --git a/fuchsia/cast_streaming/cast_message_port_impl.cc b/fuchsia/cast_streaming/cast_message_port_impl.cc
index 906f305..e362fe7 100644
--- a/fuchsia/cast_streaming/cast_message_port_impl.cc
+++ b/fuchsia/cast_streaming/cast_message_port_impl.cc
@@ -267,7 +267,7 @@
     client_->OnMessage(sender_id, message_namespace, str_message);
   } else if (message_namespace == kInjectNamespace) {
     SendInjectResponse(sender_id, str_message);
-  } else if (message_namespace == kSystemNamespace) {
+  } else if (message_namespace != kSystemNamespace) {
     // System messages are ignored, log messages from unknown namespaces.
     DVLOG(2) << "Unknown message from " << sender_id
              << ", namespace=" << message_namespace
diff --git a/fuchsia/cast_streaming/cast_streaming_session.cc b/fuchsia/cast_streaming/cast_streaming_session.cc
index 3e5d348a..3561e302 100644
--- a/fuchsia/cast_streaming/cast_streaming_session.cc
+++ b/fuchsia/cast_streaming/cast_streaming_session.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/notreached.h"
+#include "base/timer/timer.h"
 #include "components/openscreen_platform/network_context.h"
 #include "components/openscreen_platform/network_util.h"
 #include "components/openscreen_platform/task_runner.h"
@@ -26,6 +27,9 @@
 constexpr char kVideoCodecH264[] = "h264";
 constexpr char kVideoCodecVp8[] = "vp8";
 
+// Timeout to end the Session when no offer message is sent.
+constexpr base::TimeDelta kInitTimeout = base::TimeDelta::FromSeconds(5);
+
 }  // namespace
 
 namespace cast_streaming {
@@ -48,19 +52,24 @@
       : task_runner_(task_runner),
         environment_(&openscreen::Clock::now, &task_runner_),
         cast_message_port_impl_(std::move(message_port_request)),
-        // TODO(crbug.com/1087520): Add streaming session Constraints and
-        // DisplayDescription.
-        receiver_session_(this,
-                          &environment_,
-                          &cast_message_port_impl_,
-                          openscreen::cast::ReceiverSession::Preferences(
-                              {openscreen::cast::VideoCodec::kH264,
-                               openscreen::cast::VideoCodec::kVp8},
-                              {openscreen::cast::AudioCodec::kAac,
-                               openscreen::cast::AudioCodec::kOpus})),
         client_(client) {
     DCHECK(task_runner);
     DCHECK(client_);
+
+    // TODO(crbug.com/1087520): Add streaming session Constraints and
+    // DisplayDescription.
+    receiver_session_ = std::make_unique<openscreen::cast::ReceiverSession>(
+        this, &environment_, &cast_message_port_impl_,
+        openscreen::cast::ReceiverSession::Preferences(
+            {openscreen::cast::VideoCodec::kH264,
+             openscreen::cast::VideoCodec::kVp8},
+            {openscreen::cast::AudioCodec::kAac,
+             openscreen::cast::AudioCodec::kOpus}));
+
+    init_timeout_timer_.Start(
+        FROM_HERE, kInitTimeout,
+        base::BindOnce(&CastStreamingSession::Internal::OnInitializationTimeout,
+                       base::Unretained(this)));
   }
 
   ~Internal() final = default;
@@ -69,14 +78,22 @@
   Internal& operator=(const Internal&) = delete;
 
  private:
+  void OnInitializationTimeout() {
+    DVLOG(1) << __func__;
+    DCHECK(!is_initialized_);
+    client_->OnInitializationFailure();
+    is_initialized_ = true;
+  }
+
   // openscreen::cast::ReceiverSession::Client implementation.
   void OnNegotiated(
       const openscreen::cast::ReceiverSession* session,
       openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) final {
     DVLOG(1) << __func__;
-    DCHECK_EQ(session, &receiver_session_);
+    DCHECK_EQ(session, receiver_session_.get());
+    init_timeout_timer_.Stop();
 
-    if (initialized_called_) {
+    if (is_initialized_) {
       // TODO(crbug.com/1116185): Handle multiple offer messages properly.
       return;
     }
@@ -99,11 +116,15 @@
       }
 
       // Initialize the audio consumer.
+      // We can use unretained pointers here because StreamConsumer is owned by
+      // this object and |client_| is guaranteed to outlive this object.
       audio_consumer_ = std::make_unique<StreamConsumer>(
           receivers.audio->receiver, std::move(data_pipe_producer),
           base::BindRepeating(
               &CastStreamingSession::Client::OnAudioBufferReceived,
-              base::Unretained(client_)));
+              base::Unretained(client_)),
+          base::BindOnce(&CastStreamingSession::Internal::OnDataTimeout,
+                         base::Unretained(this)));
 
       // Gather data for the audio decoder config.
       media::ChannelLayout channel_layout =
@@ -143,11 +164,15 @@
       }
 
       // Initialize the video consumer.
+      // We can use unretained pointers here because StreamConsumer is owned by
+      // this object and |client_| is guaranteed to outlive this object.
       video_consumer_ = std::make_unique<StreamConsumer>(
           receivers.video->receiver, std::move(data_pipe_producer),
           base::BindRepeating(
               &CastStreamingSession::Client::OnVideoBufferReceived,
-              base::Unretained(client_)));
+              base::Unretained(client_)),
+          base::BindOnce(&CastStreamingSession::Internal::OnDataTimeout,
+                         base::Unretained(this)));
 
       // Gather data for the video decoder config.
       const std::string& video_codec =
@@ -193,14 +218,15 @@
       client_->OnInitializationSuccess(std::move(audio_stream_info),
                                        std::move(video_stream_info));
     }
-    initialized_called_ = true;
+    is_initialized_ = true;
   }
 
   // TODO(https://crbug.com/1116185): Handle |reason| and reset streams on a
   // new offer message.
   void OnReceiversDestroying(const openscreen::cast::ReceiverSession* session,
                              ReceiversDestroyingReason reason) final {
-    DCHECK_EQ(session, &receiver_session_);
+    // This can be called when |receiver_session_| is being destroyed, so we
+    // do not sanity-check |session| here.
     DVLOG(1) << __func__;
     audio_consumer_.reset();
     video_consumer_.reset();
@@ -209,20 +235,26 @@
 
   void OnError(const openscreen::cast::ReceiverSession* session,
                openscreen::Error error) final {
-    DCHECK_EQ(session, &receiver_session_);
+    DCHECK_EQ(session, receiver_session_.get());
     LOG(ERROR) << error;
-    if (!initialized_called_) {
+    if (!is_initialized_) {
       client_->OnInitializationFailure();
-      initialized_called_ = true;
+      is_initialized_ = true;
     }
   }
 
+  void OnDataTimeout() {
+    DVLOG(1) << __func__;
+    receiver_session_.reset();
+  }
+
   openscreen_platform::TaskRunner task_runner_;
   openscreen::cast::Environment environment_;
   CastMessagePortImpl cast_message_port_impl_;
-  openscreen::cast::ReceiverSession receiver_session_;
+  std::unique_ptr<openscreen::cast::ReceiverSession> receiver_session_;
+  base::OneShotTimer init_timeout_timer_;
 
-  bool initialized_called_ = false;
+  bool is_initialized_ = false;
   CastStreamingSession::Client* const client_;
   std::unique_ptr<openscreen::cast::Receiver::Consumer> audio_consumer_;
   std::unique_ptr<openscreen::cast::Receiver::Consumer> video_consumer_;
diff --git a/fuchsia/cast_streaming/stream_consumer.cc b/fuchsia/cast_streaming/stream_consumer.cc
index 353ac52..ff2a53f 100644
--- a/fuchsia/cast_streaming/stream_consumer.cc
+++ b/fuchsia/cast_streaming/stream_consumer.cc
@@ -10,9 +10,17 @@
 
 namespace cast_streaming {
 
+namespace {
+
+// Timeout to stop the Session when no data is received.
+constexpr base::TimeDelta kNoDataTimeout = base::TimeDelta::FromSeconds(10);
+
+}  // namespace
+
 StreamConsumer::StreamConsumer(openscreen::cast::Receiver* receiver,
                                mojo::ScopedDataPipeProducerHandle data_pipe,
-                               FrameReceivedCB frame_received_cb)
+                               FrameReceivedCB frame_received_cb,
+                               base::OnceClosure on_timeout)
     : receiver_(receiver),
       data_pipe_(std::move(data_pipe)),
       frame_received_cb_(std::move(frame_received_cb)),
@@ -27,7 +35,10 @@
                                               base::Unretained(this)));
   if (result != MOJO_RESULT_OK) {
     CloseDataPipeOnError();
+    return;
   }
+
+  data_timeout_timer_.Start(FROM_HERE, kNoDataTimeout, std::move(on_timeout));
 }
 
 StreamConsumer::~StreamConsumer() {
@@ -39,6 +50,7 @@
   receiver_->SetConsumer(nullptr);
   pipe_watcher_.Cancel();
   data_pipe_.reset();
+  data_timeout_timer_.Stop();
 }
 
 void StreamConsumer::OnPipeWritable(MojoResult result) {
@@ -72,6 +84,7 @@
 
 void StreamConsumer::OnFramesReady(int next_frame_buffer_size) {
   DCHECK(data_pipe_);
+  data_timeout_timer_.Reset();
 
   if (pending_buffer_remaining_bytes_ != 0) {
     // There already is a pending frame. Ignore this one for now.
diff --git a/fuchsia/cast_streaming/stream_consumer.h b/fuchsia/cast_streaming/stream_consumer.h
index eef5f14b..0fc3ffd 100644
--- a/fuchsia/cast_streaming/stream_consumer.h
+++ b/fuchsia/cast_streaming/stream_consumer.h
@@ -8,6 +8,7 @@
 #include <fuchsia/media/cpp/fidl.h>
 
 #include "base/callback.h"
+#include "base/timer/timer.h"
 #include "media/mojo/mojom/media_types.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
@@ -33,9 +34,11 @@
   // |receiver| sends frames to this object. It must outlive this object.
   // |frame_received_cb| is called on every new frame, after a new frame has
   // been written to |data_pipe|. On error, |data_pipe| will be closed.
+  // If no data is received for 10 seconds, |on_timeout| will be closed.
   StreamConsumer(openscreen::cast::Receiver* receiver,
                  mojo::ScopedDataPipeProducerHandle data_pipe,
-                 FrameReceivedCB frame_received_cb);
+                 FrameReceivedCB frame_received_cb,
+                 base::OnceClosure on_timeout);
   ~StreamConsumer() final;
 
   StreamConsumer(const StreamConsumer&) = delete;
@@ -70,6 +73,9 @@
 
   // Remaining bytes to write from |pending_buffer_| to |data_pipe_|.
   size_t pending_buffer_remaining_bytes_ = 0;
+
+  // Timer to trigger connection closure if no data is received for 10 seconds.
+  base::OneShotTimer data_timeout_timer_;
 };
 
 }  // namespace cast_streaming
diff --git a/fuchsia/engine/browser/cast_streaming_session_client.cc b/fuchsia/engine/browser/cast_streaming_session_client.cc
index 5b022f9..5c0508a 100644
--- a/fuchsia/engine/browser/cast_streaming_session_client.cc
+++ b/fuchsia/engine/browser/cast_streaming_session_client.cc
@@ -89,6 +89,13 @@
 
   // Tear down the Mojo connection.
   cast_streaming_receiver_.reset();
+
+  // Tear down all remaining Mojo objects if needed. This is necessary if the
+  // Cast Streaming Session ending was initiated by the receiver component.
+  if (audio_remote_)
+    audio_remote_.reset();
+  if (video_remote_)
+    video_remote_.reset();
 }
 
 void CastStreamingSessionClient::OnMojoDisconnect() {
diff --git a/fuchsia/engine/browser/content_directory_browsertest.cc b/fuchsia/engine/browser/content_directory_browsertest.cc
index aae0ab1..59906c8 100644
--- a/fuchsia/engine/browser/content_directory_browsertest.cc
+++ b/fuchsia/engine/browser/content_directory_browsertest.cc
@@ -83,13 +83,13 @@
     provider.set_name("testdata");
     base::FilePath pkg_path;
     base::PathService::Get(base::DIR_ASSETS, &pkg_path);
-    provider.set_directory(base::fuchsia::OpenDirectory(
+    provider.set_directory(base::OpenDirectoryHandle(
         pkg_path.AppendASCII("fuchsia/engine/test/data")));
     providers.emplace_back(std::move(provider));
 
     provider = {};
     provider.set_name("alternate");
-    provider.set_directory(base::fuchsia::OpenDirectory(
+    provider.set_directory(base::OpenDirectoryHandle(
         pkg_path.AppendASCII("fuchsia/engine/test/data")));
     providers.emplace_back(std::move(provider));
 
diff --git a/fuchsia/engine/context_provider_impl_unittest.cc b/fuchsia/engine/context_provider_impl_unittest.cc
index 27dea37..b395265 100644
--- a/fuchsia/engine/context_provider_impl_unittest.cc
+++ b/fuchsia/engine/context_provider_impl_unittest.cc
@@ -104,7 +104,7 @@
 fuchsia::web::CreateContextParams BuildCreateContextParams() {
   fuchsia::web::CreateContextParams output;
   zx_status_t result = fdio_service_connect(
-      base::fuchsia::kServiceDirectoryPath,
+      base::kServiceDirectoryPath,
       output.mutable_service_directory()->NewRequest().TakeChannel().release());
   ZX_CHECK(result == ZX_OK, result) << "Failed to open /svc";
   return output;
@@ -113,7 +113,7 @@
 fidl::InterfaceHandle<fuchsia::io::Directory> OpenCacheDirectory() {
   fidl::InterfaceHandle<fuchsia::io::Directory> cache_handle;
   zx_status_t result =
-      fdio_service_connect(base::fuchsia::kPersistedCacheDirectoryPath,
+      fdio_service_connect(base::kPersistedCacheDirectoryPath,
                            cache_handle.NewRequest().TakeChannel().release());
   ZX_CHECK(result == ZX_OK, result) << "Failed to open /cache";
   return cache_handle;
@@ -350,7 +350,7 @@
 
   // Pass a handle data dir to the context.
   create_params.set_data_directory(
-      base::fuchsia::OpenDirectory(profile_temp_dir.GetPath()));
+      base::OpenDirectoryHandle(profile_temp_dir.GetPath()));
 
   provider_ptr_->Create(std::move(create_params), context.NewRequest());
 
@@ -370,8 +370,7 @@
 
   // Pass in a handle to a file instead of a directory.
   CHECK(base::CreateTemporaryFile(&temp_file_path));
-  create_params.set_data_directory(
-      base::fuchsia::OpenDirectory(temp_file_path));
+  create_params.set_data_directory(base::OpenDirectoryHandle(temp_file_path));
 
   provider_ptr_->Create(std::move(create_params), context.NewRequest());
 
diff --git a/fuchsia/engine/renderer/cast_streaming_demuxer.cc b/fuchsia/engine/renderer/cast_streaming_demuxer.cc
index dc8413ca..2f7bc6de 100644
--- a/fuchsia/engine/renderer/cast_streaming_demuxer.cc
+++ b/fuchsia/engine/renderer/cast_streaming_demuxer.cc
@@ -63,7 +63,7 @@
       return;
 
     if (current_buffer_->end_of_stream()) {
-      std::move(pending_read_cb_).Run(Status::kAborted, nullptr);
+      std::move(pending_read_cb_).Run(Status::kError, nullptr);
       return;
     }
 
@@ -198,7 +198,6 @@
 
 CastStreamingDemuxer::~CastStreamingDemuxer() {
   DVLOG(1) << __func__;
-  DCHECK(media_task_runner_->BelongsToCurrentThread());
 
   if (was_initialization_successful_) {
     original_task_runner_->PostTask(
diff --git a/fuchsia/engine/test/web_engine_shell.cc b/fuchsia/engine/test/web_engine_shell.cc
index 1e77c90b..a2d06f69 100644
--- a/fuchsia/engine/test/web_engine_shell.cc
+++ b/fuchsia/engine/test/web_engine_shell.cc
@@ -156,7 +156,7 @@
   fuchsia::web::ContentDirectoryProvider content_directory;
   base::FilePath pkg_path;
   base::PathService::Get(base::DIR_ASSETS, &pkg_path);
-  content_directory.set_directory(base::fuchsia::OpenDirectory(
+  content_directory.set_directory(base::OpenDirectoryHandle(
       pkg_path.AppendASCII("fuchsia/engine/test/shell_data")));
   content_directory.set_name("shell-data");
   std::vector<fuchsia::web::ContentDirectoryProvider> content_directories;
@@ -168,8 +168,8 @@
   // embedder application. By passing a handle to this process' service
   // directory to the ContextProvider, we are allowing the Context access to the
   // same set of services available to this application.
-  create_context_params.set_service_directory(base::fuchsia::OpenDirectory(
-      base::FilePath(base::fuchsia::kServiceDirectoryPath)));
+  create_context_params.set_service_directory(
+      base::OpenDirectoryHandle(base::FilePath(base::kServiceDirectoryPath)));
 
   // Enable other WebEngine features.
   fuchsia::web::ContextFeatureFlags features =
@@ -188,12 +188,11 @@
   // DRM services require cdm_data_directory to be populated, so create a
   // directory under /data and use that as the cdm_data_directory.
   base::FilePath cdm_data_path =
-      base::FilePath(base::fuchsia::kPersistedDataDirectoryPath)
-          .Append("cdm_data");
+      base::FilePath(base::kPersistedDataDirectoryPath).Append("cdm_data");
   base::File::Error error;
   CHECK(base::CreateDirectoryAndGetError(cdm_data_path, &error)) << error;
   create_context_params.set_cdm_data_directory(
-      base::fuchsia::OpenDirectory(cdm_data_path));
+      base::OpenDirectoryHandle(cdm_data_path));
   CHECK(create_context_params.cdm_data_directory());
 
   base::RunLoop run_loop;
diff --git a/fuchsia/engine/web_engine_debug_integration_test.cc b/fuchsia/engine/web_engine_debug_integration_test.cc
index cda82e7..0576bf5 100644
--- a/fuchsia/engine/web_engine_debug_integration_test.cc
+++ b/fuchsia/engine/web_engine_debug_integration_test.cc
@@ -88,7 +88,7 @@
     ASSERT_FALSE(web_engine_path.empty());
 
     debug_dir_ = std::make_unique<sys::ServiceDirectory>(
-        base::fuchsia::OpenDirectory(web_engine_path.Append("out/debug")));
+        base::OpenDirectoryHandle(web_engine_path.Append("out/debug")));
     debug_dir_->Connect(debug_.NewRequest());
 
     // Attach the DevToolsListener. EnableDevTools has an acknowledgement
@@ -126,8 +126,8 @@
                                UserModeDebugging user_mode_debugging,
                                std::string url) {
     // Create a Context, a Frame and navigate it to |url|.
-    auto directory = base::fuchsia::OpenDirectory(
-        base::FilePath(base::fuchsia::kServiceDirectoryPath));
+    auto directory =
+        base::OpenDirectoryHandle(base::FilePath(base::kServiceDirectoryPath));
     if (!directory.is_valid())
       return;
 
diff --git a/fuchsia/engine/web_engine_integration_test_base.cc b/fuchsia/engine/web_engine_integration_test_base.cc
index 2b1e1f0..1f69f2d 100644
--- a/fuchsia/engine/web_engine_integration_test_base.cc
+++ b/fuchsia/engine/web_engine_integration_test_base.cc
@@ -35,7 +35,7 @@
   provider.set_name("testdata");
   base::FilePath pkg_path;
   CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
-  provider.set_directory(base::fuchsia::OpenDirectory(
+  provider.set_directory(base::OpenDirectoryHandle(
       pkg_path.AppendASCII("fuchsia/engine/test/data")));
   return provider;
 }
@@ -51,8 +51,8 @@
 fuchsia::web::CreateContextParams
 WebEngineIntegrationTestBase::DefaultContextParams() const {
   fuchsia::web::CreateContextParams create_params;
-  auto directory = base::fuchsia::OpenDirectory(
-      base::FilePath(base::fuchsia::kServiceDirectoryPath));
+  auto directory =
+      base::OpenDirectoryHandle(base::FilePath(base::kServiceDirectoryPath));
   EXPECT_TRUE(directory.is_valid());
   create_params.set_service_directory(std::move(directory));
   return create_params;
@@ -66,7 +66,7 @@
   provider.set_name("testdata");
   base::FilePath pkg_path;
   CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
-  provider.set_directory(base::fuchsia::OpenDirectory(
+  provider.set_directory(base::OpenDirectoryHandle(
       pkg_path.AppendASCII("fuchsia/engine/test/data")));
 
   create_params.mutable_content_directories()->emplace_back(
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc
index af4f3a3..d10501e 100644
--- a/fuchsia/runners/cast/cast_runner.cc
+++ b/fuchsia/runners/cast/cast_runner.cc
@@ -218,8 +218,8 @@
 
   // TODO(b/154204041) Migrate to using persistent data, and specifying an
   // explicit quota for CDM storage.
-  params.set_cdm_data_directory(base::fuchsia::OpenDirectory(
-      base::FilePath(base::fuchsia::kPersistedCacheDirectoryPath)));
+  params.set_cdm_data_directory(base::OpenDirectoryHandle(
+      base::FilePath(base::kPersistedCacheDirectoryPath)));
   CHECK(params.cdm_data_directory());
 
   const char kCastPlayreadyKeySystem[] = "com.chromecast.playready";
diff --git a/fuchsia/runners/cast/cast_runner_integration_test.cc b/fuchsia/runners/cast/cast_runner_integration_test.cc
index ee1b7563..3a64dc04 100644
--- a/fuchsia/runners/cast/cast_runner_integration_test.cc
+++ b/fuchsia/runners/cast/cast_runner_integration_test.cc
@@ -349,7 +349,7 @@
     provider.set_name("testdata");
     base::FilePath pkg_path;
     CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
-    provider.set_directory(base::fuchsia::OpenDirectory(
+    provider.set_directory(base::OpenDirectoryHandle(
         pkg_path.AppendASCII("fuchsia/runners/cast/testdata")));
     std::vector<fuchsia::web::ContentDirectoryProvider> providers;
     providers.emplace_back(std::move(provider));
@@ -405,8 +405,7 @@
         std::make_unique<sys::ServiceDirectory>(std::move(svc_directory));
 
     // Place the ServiceDirectory in the |flat_namespace|.
-    startup_info.flat_namespace.paths.emplace_back(
-        base::fuchsia::kServiceDirectoryPath);
+    startup_info.flat_namespace.paths.emplace_back(base::kServiceDirectoryPath);
     startup_info.flat_namespace.directories.emplace_back(
         directory.TakeChannel());
 
diff --git a/fuchsia/runners/cast/cast_streaming.cc b/fuchsia/runners/cast/cast_streaming.cc
index 88754ed..3f6156b 100644
--- a/fuchsia/runners/cast/cast_streaming.cc
+++ b/fuchsia/runners/cast/cast_streaming.cc
@@ -22,7 +22,7 @@
 
   fuchsia::web::ContentDirectoryProvider content_directory;
   content_directory.set_directory(
-      base::fuchsia::OpenDirectory(pkg_path.AppendASCII(kCastDataDirectory)));
+      base::OpenDirectoryHandle(pkg_path.AppendASCII(kCastDataDirectory)));
   content_directory.set_name(kCastStreamingContentDirectoryName);
   std::vector<fuchsia::web::ContentDirectoryProvider> content_directories;
   content_directories.emplace_back(std::move(content_directory));
diff --git a/fuchsia/runners/cast/data/receiver.html b/fuchsia/runners/cast/data/receiver.html
index 29a914e1..faf7eb4 100644
--- a/fuchsia/runners/cast/data/receiver.html
+++ b/fuchsia/runners/cast/data/receiver.html
@@ -17,7 +17,7 @@
 </head>
 
 <body>
-  <video src="data:cast_streaming_receiver" autoplay>
+  <video src="data:cast_streaming_receiver">
 
   <script>
     // The Cast Streaming session must stop when the stream is no longer visible. crbug.com/1111886
@@ -27,10 +27,10 @@
       }
     });
 
-    // TODO(crbug.com/1087528): This should not be necessary. Figure out why
-    // autoplay is not enough here.
     var video = document.querySelector('video');
-    video.play();
+    video.addEventListener('ended', window.close);
+    video.addEventListener('error', window.close);
+    video.play().catch(window.close);
   </script>
 </body>
 </html>
diff --git a/fuchsia/runners/web/main.cc b/fuchsia/runners/web/main.cc
index af1329c..477360c 100644
--- a/fuchsia/runners/web/main.cc
+++ b/fuchsia/runners/web/main.cc
@@ -29,23 +29,22 @@
       fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
       fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM);
 
-  create_context_params.set_service_directory(base::fuchsia::OpenDirectory(
-      base::FilePath(base::fuchsia::kServiceDirectoryPath)));
+  create_context_params.set_service_directory(
+      base::OpenDirectoryHandle(base::FilePath(base::kServiceDirectoryPath)));
   CHECK(create_context_params.service_directory());
 
-  create_context_params.set_data_directory(base::fuchsia::OpenDirectory(
-      base::FilePath(base::fuchsia::kPersistedDataDirectoryPath)));
+  create_context_params.set_data_directory(base::OpenDirectoryHandle(
+      base::FilePath(base::kPersistedDataDirectoryPath)));
   CHECK(create_context_params.data_directory());
 
   // DRM services require cdm_data_directory to be populated, so create a
   // directory under /data and use that as the cdm_data_directory.
   base::FilePath cdm_data_path =
-      base::FilePath(base::fuchsia::kPersistedDataDirectoryPath)
-          .Append("cdm_data");
+      base::FilePath(base::kPersistedDataDirectoryPath).Append("cdm_data");
   base::File::Error error;
   CHECK(base::CreateDirectoryAndGetError(cdm_data_path, &error)) << error;
   create_context_params.set_cdm_data_directory(
-      base::fuchsia::OpenDirectory(cdm_data_path));
+      base::OpenDirectoryHandle(cdm_data_path));
   CHECK(create_context_params.cdm_data_directory());
 
 #if BUILDFLAG(WEB_RUNNER_REMOTE_DEBUGGING_PORT) != 0
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 9eae539..95df932 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -4971,7 +4971,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -5004,7 +5004,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -5037,7 +5037,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -5348,7 +5348,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -6077,7 +6077,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -6108,7 +6108,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -6633,7 +6633,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -7657,7 +7657,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -7804,7 +7804,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -8674,7 +8674,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -8706,7 +8706,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -9424,7 +9424,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -9457,7 +9457,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10386,7 +10386,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10564,7 +10564,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10597,7 +10597,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10630,7 +10630,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10663,7 +10663,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10785,7 +10785,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10883,7 +10883,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10918,7 +10918,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -10953,7 +10953,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11019,7 +11019,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11085,7 +11085,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11120,7 +11120,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11155,7 +11155,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11190,7 +11190,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11225,7 +11225,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11260,7 +11260,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11293,7 +11293,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11384,7 +11384,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11446,7 +11446,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11479,7 +11479,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11512,7 +11512,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11544,7 +11544,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11576,7 +11576,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11609,7 +11609,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11642,7 +11642,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11675,7 +11675,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11766,7 +11766,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11799,7 +11799,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11832,7 +11832,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11865,7 +11865,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11927,7 +11927,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -11960,7 +11960,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12022,7 +12022,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12141,7 +12141,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12406,7 +12406,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12469,7 +12469,7 @@
       experimental: YES
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12503,7 +12503,7 @@
       experimental: YES
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12537,7 +12537,7 @@
       experimental: YES
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12682,7 +12682,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12714,7 +12714,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12800,7 +12800,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12833,7 +12833,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12892,7 +12892,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12925,7 +12925,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12958,7 +12958,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -12991,7 +12991,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13024,7 +13024,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13057,7 +13057,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13090,7 +13090,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13152,7 +13152,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13353,7 +13353,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13385,7 +13385,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13416,7 +13416,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13652,7 +13652,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13685,7 +13685,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13717,7 +13717,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13805,7 +13805,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
@@ -13866,7 +13866,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.resultdb.result_sink"
-        value: 30
+        value: 0
       }
       resultdb {
         enable: true
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 5be49c0..a6a5ad1 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -693,7 +693,7 @@
         # TODO(crbug.com/1108016): Move this kwarg to ci.builder(), after
         # ResultSink and result_adapter is confirmed to work.
         experiments = {
-            "chromium.resultdb.result_sink": 30,
+            "chromium.resultdb.result_sink": 0,
         },
         **kwargs
     )
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index 0ac4f31..3f25afa 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -233,6 +233,7 @@
     ":password_constants",
     "//base:base",
     "//base/test:test_support",
+    "//components/password_manager/core/common",
     "//components/strings:components_strings_grit",
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/ui/settings:settings_root_constants",
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.mm
index c74c413..e1901d4 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.mm
@@ -9,4 +9,4 @@
 #endif
 
 NSString* const kPasswordDetailsViewControllerId =
-    @"kPasswordDetailsViewControllerId";
+    @"PasswordDetailsTableViewId";
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
index 0ae4468b..3c89c5c 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.mm
@@ -210,6 +210,8 @@
     item.identifyingIcon = [[UIImage imageNamed:image]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
     item.identifyingIconEnabled = YES;
+    item.identifyingIconAccessibilityLabel =
+        l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_SHOW_BUTTON);
   }
   return item;
 }
diff --git a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
index 00d88eb..ba4d7ced 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_settings_egtest.mm
@@ -8,7 +8,9 @@
 
 #include "base/callback.h"
 #include "base/ios/ios_util.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/ui/settings/password/passwords_settings_app_interface.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
@@ -46,6 +48,7 @@
 // go here, the rest into the unittest.
 
 using chrome_test_util::ButtonWithAccessibilityLabel;
+using chrome_test_util::ButtonWithAccessibilityLabelId;
 using chrome_test_util::NavigationBarDoneButton;
 using chrome_test_util::SettingsDoneButton;
 using chrome_test_util::SettingsMenuBackButton;
@@ -58,6 +61,11 @@
 // it too high could result in scrolling way past the searched element.
 constexpr int kScrollAmount = 150;
 
+NSString* GetTextFieldForID(int category_id) {
+  return [NSString
+      stringWithFormat:@"%@_textField", l10n_util::GetNSString(category_id)];
+}
+
 // Returns the GREYElementInteraction* for the item on the password list with
 // the given |matcher|. It scrolls in |direction| if necessary to ensure that
 // the matched item is interactable.
@@ -84,22 +92,12 @@
     id<GREYMatcher> matcher) {
   return [[EarlGrey
       selectElementWithMatcher:grey_allOf(matcher, grey_interactable(), nil)]
-         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
-                                                  kScrollAmount)
+         usingSearchAction:grey_scrollToContentEdge(kGREYContentEdgeTop)
       onElementWithMatcher:grey_accessibilityID(kPasswordDetailsTableViewId)];
 }
 
 // Returns the GREYElementInteraction* for the item on the deletion alert
 // identified with the given |matcher|.
-GREYElementInteraction* GetInteractionForPasswordDetailDeletionAlert(
-    id<GREYMatcher> matcher) {
-  return [[EarlGrey
-      selectElementWithMatcher:grey_allOf(matcher, grey_interactable(), nil)]
-      inRoot:grey_accessibilityID(kPasswordDetailsDeletionAlertViewId)];
-}
-
-// Returns the GREYElementInteraction* for the item on the deletion alert
-// identified with the given |matcher|.
 GREYElementInteraction* GetInteractionForPasswordsExportConfirmAlert(
     id<GREYMatcher> matcher) {
   return [[EarlGrey
@@ -120,34 +118,6 @@
   return grey_accessibilityID(kPasswordsSearchBarId);
 }
 
-id<GREYMatcher> SiteHeader() {
-  return grey_allOf(
-      grey_accessibilityLabel(
-          l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_SITE)),
-      grey_accessibilityTrait(UIAccessibilityTraitHeader), nullptr);
-}
-
-id<GREYMatcher> UsernameHeader() {
-  return grey_allOf(
-      grey_accessibilityLabel(
-          l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME)),
-      grey_accessibilityTrait(UIAccessibilityTraitHeader), nullptr);
-}
-
-id<GREYMatcher> PasswordHeader() {
-  return grey_allOf(
-      grey_accessibilityLabel(
-          l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD)),
-      grey_accessibilityTrait(UIAccessibilityTraitHeader), nullptr);
-}
-
-id<GREYMatcher> FederationHeader() {
-  return grey_allOf(
-      grey_accessibilityLabel(
-          l10n_util::GetNSString(IDS_IOS_SHOW_PASSWORD_VIEW_FEDERATION)),
-      grey_accessibilityTrait(UIAccessibilityTraitHeader), nullptr);
-}
-
 GREYLayoutConstraint* Below() {
   return [GREYLayoutConstraint
       layoutConstraintWithAttribute:kGREYLayoutAttributeTop
@@ -158,39 +128,24 @@
 }
 
 // Matcher for the Copy site button in Password Details view.
-id<GREYMatcher> CopySiteButton() {
+id<GREYMatcher> PasswordDetailWebsite() {
   return grey_allOf(
-      ButtonWithAccessibilityLabel(
-          [NSString stringWithFormat:@"%@: %@",
-                                     l10n_util::GetNSString(
-                                         IDS_IOS_SHOW_PASSWORD_VIEW_SITE),
-                                     l10n_util::GetNSString(
-                                         IDS_IOS_SETTINGS_SITE_COPY_BUTTON)]),
-      grey_interactable(), nullptr);
+      grey_accessibilityID(GetTextFieldForID(IDS_IOS_SHOW_PASSWORD_VIEW_SITE)),
+      grey_kindOfClassName(@"UITextField"), nil);
 }
 
 // Matcher for the Copy username button in Password Details view.
-id<GREYMatcher> CopyUsernameButton() {
-  return grey_allOf(
-      ButtonWithAccessibilityLabel([NSString
-          stringWithFormat:@"%@: %@",
-                           l10n_util::GetNSString(
-                               IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME),
-                           l10n_util::GetNSString(
-                               IDS_IOS_SETTINGS_USERNAME_COPY_BUTTON)]),
-      grey_interactable(), nullptr);
+id<GREYMatcher> PasswordDetailUsername() {
+  return grey_allOf(grey_accessibilityID(
+                        GetTextFieldForID(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME)),
+                    grey_kindOfClassName(@"UITextField"), nil);
 }
 
 // Matcher for the Copy password button in Password Details view.
-id<GREYMatcher> CopyPasswordButton() {
-  return grey_allOf(
-      ButtonWithAccessibilityLabel([NSString
-          stringWithFormat:@"%@: %@",
-                           l10n_util::GetNSString(
-                               IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD),
-                           l10n_util::GetNSString(
-                               IDS_IOS_SETTINGS_PASSWORD_COPY_BUTTON)]),
-      grey_interactable(), nullptr);
+id<GREYMatcher> PasswordDetailPassword() {
+  return grey_allOf(grey_accessibilityID(
+                        GetTextFieldForID(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD)),
+                    grey_kindOfClassName(@"UITextField"), nil);
 }
 
 // Matcher for the Show password button in Password Details view.
@@ -202,8 +157,16 @@
 
 // Matcher for the Delete button in Password Details view.
 id<GREYMatcher> DeleteButton() {
+  return grey_allOf(
+      ButtonWithAccessibilityLabelId(IDS_IOS_SETTINGS_TOOLBAR_DELETE),
+      grey_not(grey_accessibilityTrait(UIAccessibilityTraitNotEnabled)),
+      nullptr);
+}
+
+// Matcher for the Delete button in Confirmation Alert for password deletion.
+id<GREYMatcher> DeleteConfirmationButton() {
   return grey_allOf(ButtonWithAccessibilityLabel(l10n_util::GetNSString(
-                        IDS_IOS_SETTINGS_PASSWORD_DELETE_BUTTON)),
+                        IDS_IOS_CONFIRM_PASSWORD_DELETION)),
                     grey_interactable(), nullptr);
 }
 
@@ -300,10 +263,24 @@
       performAction:grey_tap()];
 }
 
+void CopyPasswordDetailWithID(int detail_id) {
+  [GetInteractionForPasswordDetailItem(grey_allOf(
+      grey_accessibilityID(GetTextFieldForID(detail_id)),
+      grey_kindOfClassName(@"UITextField"), nil)) performAction:grey_tap()];
+
+  // Tap the context menu item for copying.
+  [[EarlGrey selectElementWithMatcher:PopUpMenuItemWithLabel(
+                                          IDS_IOS_SETTINGS_SITE_COPY_MENU_ITEM)]
+      performAction:grey_tap()];
+}
+
 }  // namespace
 
 // Various tests for the Save Passwords section of the settings.
-@interface PasswordsSettingsTestCase : ChromeTestCase
+@interface PasswordsSettingsTestCase : ChromeTestCase {
+  base::test::ScopedFeatureList _featureList;
+}
+
 @end
 
 @implementation PasswordsSettingsTestCase
@@ -319,6 +296,18 @@
   [super tearDown];
 }
 
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  AppLaunchConfiguration config;
+
+  // Password Check Feature is enabled for all tests. This is done because it
+  // is inefficient to use ensureAppLaunchedWithConfiguration for each test.
+  // This should be removed once test config is modified.
+  // TODO(crbug.com/1075494): Remove this once feature is launched.
+  config.features_enabled.push_back(password_manager::features::kPasswordCheck);
+
+  return config;
+}
+
 // Verifies the UI elements are accessible on the Passwords page.
 - (void)testAccessibilityOnPasswords {
   // Saving a form is needed for using the "password details" view.
@@ -356,13 +345,12 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
+  // Check the snackbar in case of successful reauthentication.
   [PasswordSettingsAppInterface setUpMockReauthenticationModule];
   [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                     ReauthenticationResult::kSuccess];
 
-  // Check the snackbar in case of successful reauthentication.
-  [GetInteractionForPasswordDetailItem(CopyPasswordButton())
-      performAction:grey_tap()];
+  CopyPasswordDetailWithID(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
 
   NSString* snackbarLabel =
       l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_COPIED_MESSAGE);
@@ -373,8 +361,8 @@
   // Check the snackbar in case of failed reauthentication.
   [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                     ReauthenticationResult::kFailure];
-  [GetInteractionForPasswordDetailItem(CopyPasswordButton())
-      performAction:grey_tap()];
+
+  CopyPasswordDetailWithID(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
 
   snackbarLabel =
       l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_WAS_NOT_COPIED_MESSAGE);
@@ -392,7 +380,7 @@
 
 // Checks that an attempt to show a password provides an appropriate feedback
 // when reauthentication succeeds.
-- (void)testShowPasswordToastAuthSucceeded {
+- (void)testShowPasswordAuthSucceeded {
   // Saving a form is needed for using the "password details" view.
   SaveExamplePasswordForm();
 
@@ -405,14 +393,12 @@
   [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                     ReauthenticationResult::kSuccess];
 
-  // Check the snackbar in case of successful reauthentication.
   [GetInteractionForPasswordDetailItem(ShowPasswordButton())
       performAction:grey_tap()];
 
-  // Check that the password is displayed.
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityLabel(@"concrete password")]
-      assertWithMatcher:grey_sufficientlyVisible()];
+  // Ensure that password is shown.
+  [GetInteractionForPasswordDetailItem(grey_textFieldValue(
+      @"concrete password")) assertWithMatcher:grey_notNil()];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
@@ -442,8 +428,7 @@
       performAction:grey_tap()];
 
   // Check that the password is not displayed.
-  [[EarlGrey
-      selectElementWithMatcher:grey_accessibilityLabel(@"concrete password")]
+  [[EarlGrey selectElementWithMatcher:grey_textFieldValue(@"concrete password")]
       assertWithMatcher:grey_nil()];
 
   // Note that there is supposed to be no message (cf. the case of the copy
@@ -468,8 +453,7 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(CopyUsernameButton())
-      performAction:grey_tap()];
+  CopyPasswordDetailWithID(IDS_IOS_SHOW_PASSWORD_VIEW_USERNAME);
   NSString* snackbarLabel =
       l10n_util::GetNSString(IDS_IOS_SETTINGS_USERNAME_WAS_COPIED_MESSAGE);
   // The tap checks the existence of the snackbar and also closes it.
@@ -494,8 +478,8 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(CopySiteButton())
-      performAction:grey_tap()];
+  CopyPasswordDetailWithID(IDS_IOS_SHOW_PASSWORD_VIEW_SITE);
+
   NSString* snackbarLabel =
       l10n_util::GetNSString(IDS_IOS_SETTINGS_SITE_WAS_COPIED_MESSAGE);
   // The tap checks the existence of the snackbar and also closes it.
@@ -521,11 +505,16 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(DeleteButton())
+  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
+  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
+                                    ReauthenticationResult::kSuccess];
+
+  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailDeletionAlert(ButtonWithAccessibilityLabel(
-      l10n_util::GetNSString(IDS_IOS_CONFIRM_PASSWORD_DELETION)))
+  [[EarlGrey selectElementWithMatcher:DeleteButton()] performAction:grey_tap()];
+
+  [[EarlGrey selectElementWithMatcher:DeleteConfirmationButton()]
       performAction:grey_tap()];
 
   // Wait until the alert and the detail view are dismissed.
@@ -576,11 +565,16 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(DeleteButton())
+  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
+  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
+                                    ReauthenticationResult::kSuccess];
+
+  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailDeletionAlert(ButtonWithAccessibilityLabel(
-      l10n_util::GetNSString(IDS_IOS_CONFIRM_PASSWORD_DELETION)))
+  [[EarlGrey selectElementWithMatcher:DeleteButton()] performAction:grey_tap()];
+
+  [[EarlGrey selectElementWithMatcher:DeleteConfirmationButton()]
       performAction:grey_tap()];
 
   // Wait until the alert and the detail view are dismissed.
@@ -623,11 +617,12 @@
 
   [GetInteractionForPasswordEntry(@"blocked.com") performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(DeleteButton())
+  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailDeletionAlert(ButtonWithAccessibilityLabel(
-      l10n_util::GetNSString(IDS_IOS_CONFIRM_PASSWORD_DELETION)))
+  [[EarlGrey selectElementWithMatcher:DeleteButton()] performAction:grey_tap()];
+
+  [[EarlGrey selectElementWithMatcher:DeleteConfirmationButton()]
       performAction:grey_tap()];
 
   // Wait until the alert and the detail view are dismissed.
@@ -671,11 +666,17 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(DeleteButton())
+  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
+  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
+                                    ReauthenticationResult::kSuccess];
+
+  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
       performAction:grey_tap()];
 
+  [[EarlGrey selectElementWithMatcher:DeleteButton()] performAction:grey_tap()];
+
   // Tap the alert's Cancel button to cancel.
-  if (base::ios::IsRunningOnOrLater(13, 2, 0) && [ChromeEarlGrey isIPadIdiom]) {
+  if ([ChromeEarlGrey isIPadIdiom]) {
     [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                             kPasswordDetailsTableViewId)]
         performAction:grey_tap()];
@@ -689,10 +690,10 @@
         performAction:grey_tap()];
   }
 
-  // Check that the current view is still the detail view, by locating the Copy
-  // button.
-  [[EarlGrey selectElementWithMatcher:CopyPasswordButton()]
-      assertWithMatcher:grey_sufficientlyVisible()];
+  // Check that the current view is still the detail view.
+  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
+                                          kPasswordDetailsTableViewId)]
+      assertWithMatcher:grey_notNil()];
 
   // Verify that the deletion did not happen.
   GREYAssertEqual(1u, [PasswordSettingsAppInterface passwordStoreResultsCount],
@@ -732,7 +733,7 @@
 
   // Check that the current view is not the detail view, by failing to locate
   // the Copy button.
-  [[EarlGrey selectElementWithMatcher:CopyPasswordButton()]
+  [[EarlGrey selectElementWithMatcher:PasswordDetailPassword()]
       assertWithMatcher:grey_nil()];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
@@ -741,77 +742,6 @@
       performAction:grey_tap()];
 }
 
-// Checks that attempts to copy the site via the context menu item provide an
-// appropriate feedback.
-- (void)testCopySiteMenuItem {
-  // Saving a form is needed for using the "password details" view.
-  SaveExamplePasswordForm();
-
-  OpenPasswordSettings();
-
-  [GetInteractionForPasswordEntry(@"example.com, concrete username")
-      performAction:grey_tap()];
-
-  // Tap the site cell to display the context menu.
-  [GetInteractionForPasswordDetailItem(grey_accessibilityLabel(
-      @"https://example.com/")) performAction:grey_tap()];
-
-  // Tap the context menu item for copying.
-  [[EarlGrey selectElementWithMatcher:PopUpMenuItemWithLabel(
-                                          IDS_IOS_SETTINGS_SITE_COPY_MENU_ITEM)]
-      performAction:grey_tap()];
-
-  // Check the snackbar.
-  NSString* snackbarLabel =
-      l10n_util::GetNSString(IDS_IOS_SETTINGS_SITE_WAS_COPIED_MESSAGE);
-  // The tap checks the existence of the snackbar and also closes it.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
-      performAction:grey_tap()];
-
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-      performAction:grey_tap()];
-}
-
-// Checks that attempts to copy the username via the context menu item provide
-// an appropriate feedback.
-- (void)testCopyUsernameMenuItem {
-  // Saving a form is needed for using the "password details" view.
-  SaveExamplePasswordForm();
-
-  OpenPasswordSettings();
-
-  [GetInteractionForPasswordEntry(@"example.com, concrete username")
-      performAction:grey_tap()];
-
-  // Tap the username cell to display the context menu.
-  [GetInteractionForPasswordDetailItem(
-      grey_accessibilityLabel(@"concrete username")) performAction:grey_tap()];
-
-  // Tap the context menu item for copying.
-  [[EarlGrey
-      selectElementWithMatcher:PopUpMenuItemWithLabel(
-                                   IDS_IOS_SETTINGS_USERNAME_COPY_MENU_ITEM)]
-      performAction:grey_tap()];
-
-  // Check the snackbar.
-  NSString* snackbarLabel =
-      l10n_util::GetNSString(IDS_IOS_SETTINGS_USERNAME_WAS_COPIED_MESSAGE);
-  // The tap checks the existence of the snackbar and also closes it.
-  [[EarlGrey selectElementWithMatcher:grey_accessibilityLabel(snackbarLabel)]
-      performAction:grey_tap()];
-
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-      performAction:grey_tap()];
-}
-
 // Checks that attempts to copy the password via the context menu item provide
 // an appropriate feedback.
 - (void)testCopyPasswordMenuItem {
@@ -860,62 +790,6 @@
       performAction:grey_tap()];
 }
 
-// Checks that attempts to show and hide the password via the context menu item
-// provide an appropriate feedback.
-- (void)testShowHidePasswordMenuItem {
-  if (![ChromeEarlGrey isIPadIdiom]) {
-    // TODO(crbug.com/1109644): Enable the test on iPhone once the bug is fixed.
-    EARL_GREY_TEST_DISABLED(@"Disabled for iPhone.");
-  }
-
-  // Saving a form is needed for using the "password details" view.
-  SaveExamplePasswordForm();
-
-  OpenPasswordSettings();
-
-  [GetInteractionForPasswordEntry(@"example.com, concrete username")
-      performAction:grey_tap()];
-
-  // Tap the password cell to display the context menu.
-  [GetInteractionForPasswordDetailItem(grey_text(kMaskedPassword))
-      performAction:grey_tap()];
-
-  // Make sure to capture the reauthentication module in a variable until the
-  // end of the test, otherwise it might get deleted too soon and break the
-  // functionality of copying and viewing passwords.
-  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
-  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
-                                    ReauthenticationResult::kSuccess];
-
-  // Tap the context menu item for showing.
-  [[EarlGrey
-      selectElementWithMatcher:PopUpMenuItemWithLabel(
-                                   IDS_IOS_SETTINGS_PASSWORD_SHOW_MENU_ITEM)]
-      performAction:grey_tap()];
-
-  // Tap the password cell to display the context menu again, and to check that
-  // the password was unmasked.
-  [GetInteractionForPasswordDetailItem(
-      grey_accessibilityLabel(@"concrete password")) performAction:grey_tap()];
-
-  // Tap the context menu item for hiding.
-  [[EarlGrey
-      selectElementWithMatcher:PopUpMenuItemWithLabel(
-                                   IDS_IOS_SETTINGS_PASSWORD_HIDE_MENU_ITEM)]
-      performAction:grey_tap()];
-
-  // Check that the password is masked again.
-  [GetInteractionForPasswordDetailItem(grey_text(kMaskedPassword))
-      performAction:grey_tap()];
-
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
-      performAction:grey_tap()];
-  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
-      performAction:grey_tap()];
-}
-
 // Checks that federated credentials have no password but show the federation.
 - (void)testFederated {
   GREYAssert([PasswordSettingsAppInterface
@@ -929,24 +803,26 @@
   [GetInteractionForPasswordEntry(@"example.com, federated username")
       performAction:grey_tap()];
 
-  // Check that the Site, Username, Federation and Delete Saved Password
-  // sections are there.
-  [GetInteractionForPasswordDetailItem(SiteHeader())
-      assertWithMatcher:grey_notNil()];
-  [GetInteractionForPasswordDetailItem(UsernameHeader())
-      assertWithMatcher:grey_notNil()];
-  // For federation check both the section header and content.
-  [GetInteractionForPasswordDetailItem(FederationHeader())
-      assertWithMatcher:grey_notNil()];
-  [GetInteractionForPasswordDetailItem(grey_text(@"famous.provider.net"))
-      assertWithMatcher:grey_notNil()];
-  [GetInteractionForPasswordDetailItem(DeleteButton())
-      assertWithMatcher:grey_notNil()];
+  // Check that the Site and Username are present and correct.
+  [[EarlGrey selectElementWithMatcher:PasswordDetailWebsite()]
+      assertWithMatcher:grey_textFieldValue(@"https://example.com/")];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
+      assertWithMatcher:grey_textFieldValue(@"federated username")];
 
   // Check that the password is not present.
-  [GetInteractionForPasswordDetailItem(PasswordHeader())
+  [[EarlGrey selectElementWithMatcher:PasswordDetailPassword()]
       assertWithMatcher:grey_nil()];
 
+  // Check that editing doesn't require reauth.
+  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
+  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
+                                    ReauthenticationResult::kFailure];
+  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
+      performAction:grey_tap()];
+  // Ensure delete button is present after entering editing mode.
+  [[EarlGrey selectElementWithMatcher:DeleteButton()]
+      assertWithMatcher:grey_notNil()];
+
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
@@ -965,44 +841,17 @@
   [GetInteractionForPasswordEntry(@"example.com, concrete username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(SiteHeader())
-      assertWithMatcher:grey_notNil()];
-  id<GREYMatcher> siteCell = grey_accessibilityLabel(@"https://example.com/");
-  [GetInteractionForPasswordDetailItem(siteCell)
-      assertWithMatcher:grey_layout(@[ Below() ], SiteHeader())];
-  [GetInteractionForPasswordDetailItem(CopySiteButton())
-      assertWithMatcher:grey_layout(@[ Below() ], siteCell)];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailWebsite()]
+      assertWithMatcher:grey_textFieldValue(@"https://example.com/")];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
+      assertWithMatcher:grey_textFieldValue(@"concrete username")];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailPassword()]
+      assertWithMatcher:grey_textFieldValue(kMaskedPassword)];
 
-  [GetInteractionForPasswordDetailItem(UsernameHeader())
-      assertWithMatcher:grey_layout(@[ Below() ], CopySiteButton())];
-  id<GREYMatcher> usernameCell = grey_accessibilityLabel(@"concrete username");
-  [GetInteractionForPasswordDetailItem(usernameCell)
-      assertWithMatcher:grey_layout(@[ Below() ], UsernameHeader())];
-  [GetInteractionForPasswordDetailItem(CopyUsernameButton())
-      assertWithMatcher:grey_layout(@[ Below() ], usernameCell)];
-
-  id<GREYMatcher> passwordHeader =
-      grey_allOf(PasswordHeader(),
-                 grey_kindOfClassName(@"UITableViewHeaderFooterView"), nil);
-  [GetInteractionForPasswordDetailItem(passwordHeader)
-      assertWithMatcher:grey_layout(@[ Below() ], CopyUsernameButton())];
-  id<GREYMatcher> passwordCell = grey_accessibilityLabel(
-      l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_HIDDEN_LABEL));
-  [GetInteractionForPasswordDetailItem(passwordCell)
-      assertWithMatcher:grey_layout(@[ Below() ], passwordHeader)];
-  [GetInteractionForPasswordDetailItem(CopyPasswordButton())
-      assertWithMatcher:grey_layout(@[ Below() ], passwordCell)];
-  [GetInteractionForPasswordDetailItem(ShowPasswordButton())
-      assertWithMatcher:grey_layout(@[ Below() ], CopyPasswordButton())];
-
-  [GetInteractionForPasswordDetailItem(DeleteButton())
-      assertWithMatcher:grey_layout(@[ Below() ], ShowPasswordButton())];
-
-  // Check that the federation block is not present. Match directly to also
-  // catch the case where the block would be present but not currently visible
-  // due to the scrolling state.
-  [[EarlGrey selectElementWithMatcher:FederationHeader()]
-      assertWithMatcher:grey_nil()];
+  [GetInteractionForPasswordDetailItem(PasswordDetailPassword())
+      assertWithMatcher:grey_layout(@[ Below() ], PasswordDetailUsername())];
+  [GetInteractionForPasswordDetailItem(PasswordDetailUsername())
+      assertWithMatcher:grey_layout(@[ Below() ], PasswordDetailWebsite())];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
@@ -1023,25 +872,11 @@
 
   [GetInteractionForPasswordEntry(@"example.com") performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(SiteHeader())
-      assertWithMatcher:grey_notNil()];
-  id<GREYMatcher> siteCell = grey_accessibilityLabel(@"https://example.com/");
-  [GetInteractionForPasswordDetailItem(siteCell)
-      assertWithMatcher:grey_layout(@[ Below() ], SiteHeader())];
-  [GetInteractionForPasswordDetailItem(CopySiteButton())
-      assertWithMatcher:grey_layout(@[ Below() ], siteCell)];
-
-  [GetInteractionForPasswordDetailItem(DeleteButton())
-      assertWithMatcher:grey_layout(@[ Below() ], CopySiteButton())];
-
-  // Check that the other blocks are not present. Match directly to also catch
-  // the case where those blocks would be present but not currently visible due
-  // to the scrolling state.
-  [[EarlGrey selectElementWithMatcher:UsernameHeader()]
+  [[EarlGrey selectElementWithMatcher:PasswordDetailWebsite()]
+      assertWithMatcher:grey_textFieldValue(@"https://example.com/")];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
       assertWithMatcher:grey_nil()];
-  [[EarlGrey selectElementWithMatcher:PasswordHeader()]
-      assertWithMatcher:grey_nil()];
-  [[EarlGrey selectElementWithMatcher:FederationHeader()]
+  [[EarlGrey selectElementWithMatcher:PasswordDetailPassword()]
       assertWithMatcher:grey_nil()];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
@@ -1066,37 +901,16 @@
   [GetInteractionForPasswordEntry(@"example.com, federated username")
       performAction:grey_tap()];
 
-  [GetInteractionForPasswordDetailItem(SiteHeader())
-      assertWithMatcher:grey_notNil()];
-  id<GREYMatcher> siteCell = grey_accessibilityLabel(@"https://example.com/");
-  [GetInteractionForPasswordDetailItem(siteCell)
-      assertWithMatcher:grey_layout(@[ Below() ], SiteHeader())];
-  [GetInteractionForPasswordDetailItem(CopySiteButton())
-      assertWithMatcher:grey_layout(@[ Below() ], siteCell)];
-
-  [GetInteractionForPasswordDetailItem(UsernameHeader())
-      assertWithMatcher:grey_layout(@[ Below() ], CopySiteButton())];
-  id<GREYMatcher> usernameCell = grey_accessibilityLabel(@"federated username");
-  [GetInteractionForPasswordDetailItem(usernameCell)
-      assertWithMatcher:grey_layout(@[ Below() ], UsernameHeader())];
-  [GetInteractionForPasswordDetailItem(CopyUsernameButton())
-      assertWithMatcher:grey_layout(@[ Below() ], usernameCell)];
-
-  [GetInteractionForPasswordDetailItem(FederationHeader())
-      assertWithMatcher:grey_layout(@[ Below() ], CopyUsernameButton())];
-  id<GREYMatcher> federationCell = grey_text(@"famous.provider.net");
-  [GetInteractionForPasswordDetailItem(federationCell)
-      assertWithMatcher:grey_layout(@[ Below() ], FederationHeader())];
-
-  [GetInteractionForPasswordDetailItem(DeleteButton())
-      assertWithMatcher:grey_layout(@[ Below() ], federationCell)];
-
-  // Check that the password is not present. Match directly to also catch the
-  // case where the password header would be present but not currently visible
-  // due to the scrolling state.
-  [[EarlGrey selectElementWithMatcher:PasswordHeader()]
+  [[EarlGrey selectElementWithMatcher:PasswordDetailWebsite()]
+      assertWithMatcher:grey_textFieldValue(@"https://example.com/")];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailUsername()]
+      assertWithMatcher:grey_textFieldValue(@"federated username")];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailPassword()]
       assertWithMatcher:grey_nil()];
 
+  [GetInteractionForPasswordDetailItem(PasswordDetailUsername())
+      assertWithMatcher:grey_layout(@[ Below() ], PasswordDetailWebsite())];
+
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
@@ -1110,15 +924,13 @@
 - (void)testStoredEntriesAlwaysShown {
   SaveExamplePasswordForm();
 
-  GREYAssert([PasswordSettingsAppInterface
-                 saveExampleBlockedOrigin:@"https://blocked.com"],
-             @"Stored form was not found in the PasswordStore results.");
-
   OpenPasswordSettings();
 
   // Toggle the "Save Passwords" control off and back on and check that stored
   // items are still present.
-  constexpr BOOL kExpectedState[] = {YES, NO};
+  BOOL isSwitchEnabled =
+      [PasswordSettingsAppInterface isCredentialsServiceEnabled];
+  BOOL kExpectedState[] = {isSwitchEnabled, !isSwitchEnabled};
   for (BOOL expected_state : kExpectedState) {
     // Toggle the switch. It is located near the top, so if not interactable,
     // try scrolling up.
@@ -1136,8 +948,6 @@
     // Check the stored items. Scroll down if needed.
     [GetInteractionForPasswordEntry(@"example.com, concrete username")
         assertWithMatcher:grey_notNil()];
-    [GetInteractionForPasswordEntry(@"blocked.com")
-        assertWithMatcher:grey_notNil()];
   }
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
@@ -1214,8 +1024,7 @@
   [PasswordSettingsAppInterface setUpMockReauthenticationModule];
   [PasswordSettingsAppInterface mockReauthenticationModuleCanAttempt:NO];
 
-  [GetInteractionForPasswordDetailItem(CopyPasswordButton())
-      performAction:grey_tap()];
+  CopyPasswordDetailWithID(IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
 
   NSString* title =
       l10n_util::GetNSString(IDS_IOS_SETTINGS_SET_UP_SCREENLOCK_TITLE);
@@ -1223,6 +1032,10 @@
       assertWithMatcher:grey_sufficientlyVisible()];
   [[EarlGrey selectElementWithMatcher:chrome_test_util::OKButton()]
       performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
 }
@@ -1303,12 +1116,12 @@
       assertWithMatcher:grey_notNil()];
 
   // Aim at an entry almost at the end of the list.
-  constexpr int kRemoteIndex = kPasswordsCount - 2;
+  constexpr int kRemoteIndex = kPasswordsCount - 4;
   // The scrolling in GetInteractionForPasswordEntry has too fine steps to
   // reach the desired part of the list quickly. The following gives it a head
-  // start of almost the desired position, counting 30 points per entry and
-  // aiming 3 entries before |kRemoteIndex|.
-  constexpr int kJump = (kRemoteIndex - 3) * 30;
+  // start of the desired position, counting 30 points per entry and
+  // aiming at |kRemoteIndex|.
+  constexpr int kJump = kRemoteIndex * 30 + 150;
   [[EarlGrey
       selectElementWithMatcher:grey_accessibilityID(kPasswordsTableViewId)]
       performAction:grey_scrollInDirection(kGREYDirectionDown, kJump)];
@@ -1317,10 +1130,10 @@
                        kRemoteIndex, kRemoteIndex]) performAction:grey_tap()];
 
   // Check that the detail view loaded correctly by verifying the site content.
-  id<GREYMatcher> siteCell = grey_accessibilityLabel([NSString
-      stringWithFormat:@"https://www%02d.example.com/", kRemoteIndex]);
-  [GetInteractionForPasswordDetailItem(siteCell)
-      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:PasswordDetailWebsite()]
+      assertWithMatcher:grey_textFieldValue([NSString
+                            stringWithFormat:@"https://www%02d.example.com/",
+                                             kRemoteIndex])];
 
   [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
       performAction:grey_tap()];
@@ -1469,15 +1282,17 @@
       assertWithMatcher:grey_nil()];
   [GetInteractionForPasswordEntry(@"exclude2.com")
       assertWithMatcher:grey_notNil()];
+  [[EarlGrey
+      selectElementWithMatcher:ButtonWithAccessibilityLabelId(IDS_CANCEL)]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsMenuBackButton()]
+      performAction:grey_tap()];
+  [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
+      performAction:grey_tap()];
 }
 
 // Test search and delete all passwords and blocked items.
 - (void)testSearchAndDeleteAllPasswords {
-  // TODO(crbug.com/1129441): This is failing regularly downstream on iOS14.
-  if (@available(iOS 14, *)) {
-    EARL_GREY_TEST_DISABLED(@"Test disabled on iOS14.");
-  }
-
   SaveExamplePasswordForms();
   SaveExampleBlockedForms();
 
@@ -1489,6 +1304,10 @@
   //  [[EarlGrey selectElementWithMatcher:SearchTextField()]
   //      performAction:grey_typeText(@"u\n")];
 
+  [[EarlGrey
+      selectElementWithMatcher:grey_accessibilityID(kPasswordsTableViewId)]
+      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];
+
   TapEdit();
 
   // Select all.
@@ -1496,6 +1315,7 @@
       performAction:grey_tap()];
   [GetInteractionForPasswordEntry(@"example12.com, user2")
       performAction:grey_tap()];
+
   [GetInteractionForPasswordEntry(@"exclude1.com") performAction:grey_tap()];
   [GetInteractionForPasswordEntry(@"exclude2.com") performAction:grey_tap()];
 
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index eb3edd97..1ae1ab0c 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -81,6 +81,7 @@
     "//ios/chrome/browser/ui/settings/password",
     "//ios/chrome/browser/ui/settings/password:password_ui",
     "//ios/chrome/browser/ui/settings/password:test_support",
+    "//ios/chrome/browser/ui/settings/password/password_details:password_details_ui",
     "//ios/chrome/browser/ui/tab_grid",
     "//ios/chrome/browser/ui/tabs",
     "//ios/chrome/browser/ui/util",
diff --git a/ios/chrome/test/app/password_test_util.mm b/ios/chrome/test/app/password_test_util.mm
index 56b6cfe9..f93ae119 100644
--- a/ios/chrome/test/app/password_test_util.mm
+++ b/ios/chrome/test/app/password_test_util.mm
@@ -6,6 +6,7 @@
 
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/settings/password/legacy_password_details_table_view_controller+testing.h"
+#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/password/passwords_table_view_controller.h"
 #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h"
 #import "ios/chrome/browser/ui/util/top_view_controller.h"
@@ -53,12 +54,11 @@
   SettingsNavigationController* settings_navigation_controller =
       base::mac::ObjCCastStrict<SettingsNavigationController>(
           top_view_controller::TopPresentedViewController());
-  LegacyPasswordDetailsTableViewController*
-      password_details_table_view_controller =
-          base::mac::ObjCCastStrict<LegacyPasswordDetailsTableViewController>(
-              settings_navigation_controller.topViewController);
-  [password_details_table_view_controller
-      setReauthenticationModule:mock_reauthentication_module];
+  PasswordDetailsTableViewController* password_details_table_view_controller =
+      base::mac::ObjCCastStrict<PasswordDetailsTableViewController>(
+          settings_navigation_controller.topViewController);
+  password_details_table_view_controller.reauthModule =
+      mock_reauthentication_module;
   return mock_reauthentication_module;
 }
 
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 5d1973ca..c66a0a9 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-d197a3a59d58f21d676c122cbf07014e26a5a2c5
\ No newline at end of file
+d691a7cfbbc42ed80b4fe79ed7e9ec6854fb5d63
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index cdddfb8..b031d7aa 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-3f46e3c72fd4a0b953fa39a301dd57f8d60e85e4
\ No newline at end of file
+b801dbbdc5d9a27894f9189b34259e161c04b448
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index d14a35f..61a25efd 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-ebcdef0f6b320efb61eff802c1eea98d9e07ce69
\ No newline at end of file
+ca9b30dd0c29bd05f0e95aca58b59bc678d2eaf6
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index 4726eabfb..8cf0c1c 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-f3e4eeb0c4b99fdd668a763002f995745e18ee78
\ No newline at end of file
+8d6cadf9f048470943b7750bca71609029309d45
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 61b1d99..89a803d4 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-968775bc0020cfc9d317d5391cfbf112edfcd540
\ No newline at end of file
+8059e77f0778e35612f9284ae31284a0aef3625e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index b965f22..f592089 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-4d87b3d11fc9be5f90248d01e22b369808e2a9ff
\ No newline at end of file
+e8426bac0180ccbc1eeb628214dc8c249c58cbe2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 09d0bf9..15157237 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-4de2901ca4eb1136131e2b88026537ed3aa11a4e
\ No newline at end of file
+19a534b52c943d122a6d78f1aeaf4e76b58fb5b2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 4fc6fa17..c04f921 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-63fea9288610814e72c1a9cc12ac760164f734be
\ No newline at end of file
+a5eaed1bb014e147e0e635c065a1af201092fa25
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index dfaade87..ad55020 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-dcc174d3ec0250665b01c8b71ae3451679026e56
\ No newline at end of file
+2503671c74bebfcf7a33c8fd39d26d7103d464e1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index c0821b1..938cbe25 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-62da03440236a10e7ca329ef88dbe623ed1c74ee
\ No newline at end of file
+9487a1b644d08b6191d11a7ece96a065a7a4c4c7
\ No newline at end of file
diff --git a/media/filters/video_renderer_algorithm.cc b/media/filters/video_renderer_algorithm.cc
index 08a95cb..0d33804 100644
--- a/media/filters/video_renderer_algorithm.cc
+++ b/media/filters/video_renderer_algorithm.cc
@@ -243,42 +243,32 @@
   // Even though we may not be able to remove anything due to having only one
   // frame, correct any estimates which may have been set during EnqueueFrame().
   UpdateFrameStatistics();
+  UpdateEffectiveFramesQueued();
 
   // We always leave at least one frame in the queue, so if there's only one
   // frame there's nothing we can expire.
-  if (frame_queue_.size() == 1) {
-    UpdateEffectiveFramesQueued();
+  if (frame_queue_.size() == 1)
     return 0;
-  }
 
   DCHECK_GT(average_frame_duration_, base::TimeDelta());
 
-  // Finds and removes all frames which are too old to be used; I.e., the end of
-  // their render interval is further than |max_acceptable_drift_| from the
-  // given |deadline|.  We also always expire anything inserted before the last
-  // rendered frame.
+  // Finds and removes all frames which are too old to be used.
   size_t frames_dropped_without_rendering = 0;
-  size_t frames_to_expire = 0;
-  const base::TimeTicks minimum_start_time =
-      deadline - max_acceptable_drift_ - average_frame_duration_;
-  for (; frames_to_expire < frame_queue_.size() - 1; ++frames_to_expire) {
-    const ReadyFrame& frame = frame_queue_[frames_to_expire];
-    if (frame.start_time >= minimum_start_time)
-      break;
+  size_t frames_to_expire = std::min(
+      frame_queue_.size() - 1, frame_queue_.size() - effective_frames_queued_);
+
+  if (!frames_to_expire)
+    return 0;
+
+  for (size_t i = 0; i < frames_to_expire; ++i) {
+    const ReadyFrame& frame = frame_queue_[i];
     if (frame.render_count == frame.drop_count)
       ++frames_dropped_without_rendering;
   }
 
-  if (!frames_to_expire) {
-    UpdateEffectiveFramesQueued();
-    return 0;
-  }
-
   cadence_frame_counter_ += frames_to_expire;
   frame_queue_.erase(frame_queue_.begin(),
                      frame_queue_.begin() + frames_to_expire);
-
-  UpdateEffectiveFramesQueued();
   return frames_dropped_without_rendering;
 }
 
diff --git a/media/filters/video_renderer_algorithm_unittest.cc b/media/filters/video_renderer_algorithm_unittest.cc
index 6707f4e..e343079 100644
--- a/media/filters/video_renderer_algorithm_unittest.cc
+++ b/media/filters/video_renderer_algorithm_unittest.cc
@@ -1245,12 +1245,12 @@
   tg.step(2);
   // Two frames are removed, one displayed frame (which should not be counted as
   // dropped) and one undisplayed one.
-  ASSERT_EQ(1u, algorithm_.RemoveExpiredFrames(tg.current()));
+  ASSERT_EQ(2u, algorithm_.RemoveExpiredFrames(tg.current()));
   // Since we just removed the last rendered frame, OnLastFrameDropped() should
   // be ignored.
   algorithm_.OnLastFrameDropped();
   frame = RenderAndStep(&tg, &frames_dropped);
-  EXPECT_EQ(1u, frames_dropped);
+  EXPECT_EQ(0u, frames_dropped);
   EXPECT_EQ(2u, frames_queued());
   EXPECT_EQ(1u, EffectiveFramesQueued());
   ASSERT_TRUE(frame);
diff --git a/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc b/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc
index 81f3b03..5e6546fa 100644
--- a/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc
+++ b/media/fuchsia/cdm/service/fuchsia_cdm_manager.cc
@@ -93,7 +93,7 @@
     }
 
     fidl::InterfaceHandle<fuchsia::io::Directory> data_directory =
-        base::fuchsia::OpenDirectory(storage_path);
+        base::OpenDirectoryHandle(storage_path);
     if (!data_directory.is_valid()) {
       DLOG(ERROR) << "Unable to OpenDirectory " << storage_path;
       return base::nullopt;
diff --git a/mojo/public/mojom/base/token.mojom b/mojo/public/mojom/base/token.mojom
index ac5f32c..62f85b3 100644
--- a/mojo/public/mojom/base/token.mojom
+++ b/mojo/public/mojom/base/token.mojom
@@ -5,6 +5,7 @@
 module mojo_base.mojom;
 
 // Corresponds to |base::Token| defined in base/token.h
+[Stable]
 struct Token {
   uint64 high;
   uint64 low;
diff --git a/net/third_party/nss/README.chromium b/net/third_party/nss/README.chromium
index 68395c7..87108826 100644
--- a/net/third_party/nss/README.chromium
+++ b/net/third_party/nss/README.chromium
@@ -4,6 +4,7 @@
 Security Critical: Yes
 License: MPL 2
 License File: LICENSE
+CPEPrefix: cpe:/a:mozilla:nss:3.23
 
 This directory includes a file derived from NSS's libssl, from the hg repo at:
   https://hg.mozilla.org/projects/nss
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index f905fbdc..e8032e4 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -26,6 +26,7 @@
 #include "base/threading/thread_id_name_manager.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
+#include "base/trace_event/task_execution_macros.h"
 #include "base/trace_event/thread_instruction_count.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_log.h"
@@ -1018,20 +1019,17 @@
 TEST_F(TraceEventDataSourceTest, TaskExecutionEvent) {
   CreateTraceEventDataSource();
 
-  INTERNAL_TRACE_EVENT_ADD(
-      TRACE_EVENT_PHASE_INSTANT, "toplevel", "ThreadControllerImpl::RunTask",
-      TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_TYPED_PROTO_ARGS, "src_file",
-      "my_file", "src_func", "my_func");
-  INTERNAL_TRACE_EVENT_ADD(
-      TRACE_EVENT_PHASE_INSTANT, "toplevel", "ThreadControllerImpl::RunTask",
-      TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_TYPED_PROTO_ARGS, "src_file",
-      "my_file", "src_func", "my_func");
+  base::PendingTask task;
+  task.posted_from =
+      base::Location("my_func", "my_file", 0, /*program_counter=*/&task);
+  { TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask1", task); }
+  { TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask1", task); }
 
   size_t packet_index = ExpectStandardPreamble();
 
   auto* e_packet = producer_client()->GetFinalizedPacket(packet_index++);
   ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
-                   TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
+                   TRACE_EVENT_PHASE_BEGIN);
 
   const auto& annotations = e_packet->track_event().debug_annotations();
   EXPECT_EQ(annotations.size(), 0);
@@ -1043,9 +1041,9 @@
   EXPECT_EQ(locations[0].function_name(), "my_func");
 
   // Second event should refer to the same interning entries.
-  auto* e_packet2 = producer_client()->GetFinalizedPacket(packet_index++);
+  auto* e_packet2 = producer_client()->GetFinalizedPacket(++packet_index);
   ExpectTraceEvent(e_packet2, /*category_iid=*/1u, /*name_iid=*/1u,
-                   TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
+                   TRACE_EVENT_PHASE_BEGIN);
 
   EXPECT_EQ(e_packet2->track_event().task_execution().posted_from_iid(), 1u);
   EXPECT_EQ(e_packet2->interned_data().source_locations().size(), 0);
@@ -1054,16 +1052,16 @@
 TEST_F(TraceEventDataSourceTest, TaskExecutionEventWithoutFunction) {
   CreateTraceEventDataSource();
 
-  INTERNAL_TRACE_EVENT_ADD(
-      TRACE_EVENT_PHASE_INSTANT, "toplevel", "ThreadControllerImpl::RunTask",
-      TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_TYPED_PROTO_ARGS, "src",
-      "my_file");
+  base::PendingTask task;
+  task.posted_from = base::Location(/*function_name=*/nullptr, "my_file", 0,
+                                    /*program_counter=*/&task);
+  { TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask", task); }
 
   size_t packet_index = ExpectStandardPreamble();
 
   auto* e_packet = producer_client()->GetFinalizedPacket(packet_index++);
   ExpectTraceEvent(e_packet, /*category_iid=*/1u, /*name_iid=*/1u,
-                   TRACE_EVENT_PHASE_INSTANT, TRACE_EVENT_SCOPE_THREAD);
+                   TRACE_EVENT_PHASE_BEGIN, TRACE_EVENT_SCOPE_THREAD);
 
   const auto& annotations = e_packet->track_event().debug_annotations();
   EXPECT_EQ(annotations.size(), 0);
diff --git a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
index 38ee903..75d5d52 100644
--- a/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
+++ b/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc
@@ -67,12 +67,6 @@
 constexpr uint64_t kThreadInstructionCountTrackUuidBit =
     static_cast<uint64_t>(1u) << 34;
 
-// Names of events that should be converted into a TaskExecution event.
-const char* kTaskExecutionEventCategory = "toplevel";
-const char* kTaskExecutionEventNames[3] = {"ThreadControllerImpl::RunTask",
-                                           "ThreadPool_RunTask",
-                                           "SimpleAlarmTimer::OnTimerFired"};
-
 void AddConvertableToTraceFormat(
     base::trace_event::ConvertableToTraceFormat* value,
     perfetto::protos::pbzero::DebugAnnotation* annotation) {
@@ -474,13 +468,6 @@
   const size_t kMaxSize = base::trace_event::TraceArguments::kMaxSize;
   InterningIndexEntry interned_annotation_names[kMaxSize] = {
       InterningIndexEntry{}};
-  InterningIndexEntry interned_source_location{};
-  InterningIndexEntry interned_log_message_body{};
-
-  const char* src_file = nullptr;
-  const char* src_func = nullptr;
-  const char* log_message_body = nullptr;
-  int line_number = 0;
 
   // No need to write the event name for end events (sync or nestable async).
   // Trace processor will match them without, provided event nesting is correct.
@@ -510,49 +497,8 @@
       }
     }
   } else {
-    // TODO(eseckler): Remove special handling of typed events here once we
-    // support them in TRACE_EVENT macros.
-
     if (flags & TRACE_EVENT_FLAG_TYPED_PROTO_ARGS) {
-      if (trace_event->arg_size() == 2u) {
-        DCHECK_EQ(strcmp(category_name, kTaskExecutionEventCategory), 0);
-        DCHECK(strcmp(trace_event->name(), kTaskExecutionEventNames[0]) == 0 ||
-               strcmp(trace_event->name(), kTaskExecutionEventNames[1]) == 0 ||
-               strcmp(trace_event->name(), kTaskExecutionEventNames[2]) == 0);
-        // Double argument task execution event (src_file, src_func).
-        DCHECK_EQ(trace_event->arg_type(0), TRACE_VALUE_TYPE_STRING);
-        DCHECK_EQ(trace_event->arg_type(1), TRACE_VALUE_TYPE_STRING);
-        src_file = trace_event->arg_value(0).as_string;
-        src_func = trace_event->arg_value(1).as_string;
-      } else {
-        // arg_size == 1 enforced by the maximum number of parameter == 2.
-        DCHECK_EQ(trace_event->arg_size(), 1u);
-
-        if (trace_event->arg_type(0) == TRACE_VALUE_TYPE_STRING) {
-          // Single argument task execution event (src_file).
-          DCHECK_EQ(strcmp(category_name, kTaskExecutionEventCategory), 0);
-          DCHECK(
-              strcmp(trace_event->name(), kTaskExecutionEventNames[0]) == 0 ||
-              strcmp(trace_event->name(), kTaskExecutionEventNames[1]) == 0 ||
-              strcmp(trace_event->name(), kTaskExecutionEventNames[2]) == 0);
-          src_file = trace_event->arg_value(0).as_string;
-        } else {
-          DCHECK_EQ(trace_event->arg_type(0), TRACE_VALUE_TYPE_CONVERTABLE);
-          DCHECK(strcmp(category_name, "log") == 0);
-          DCHECK(strcmp(trace_event->name(), "LogMessage") == 0);
-          const base::trace_event::LogMessage* value =
-              static_cast<base::trace_event::LogMessage*>(
-                  trace_event->arg_value(0).as_convertable);
-          src_file = value->file();
-          line_number = value->line_number();
-          log_message_body = value->message().c_str();
-
-          interned_log_message_body =
-              interned_log_message_bodies_.LookupOrAdd(value->message());
-        }  // else
-      }    // else
-      interned_source_location = interned_source_locations_.LookupOrAdd(
-          std::make_tuple(src_file, src_func, line_number));
+      NOTREACHED();
     } else if (!privacy_filtering_enabled_) {
       for (size_t i = 0;
            i < trace_event->arg_size() && trace_event->arg_name(i); ++i) {
@@ -626,14 +572,7 @@
     track_event->add_category_iids(interned_category.id);
   }
 
-  if (interned_log_message_body.id) {
-    auto* log_message = track_event->set_log_message();
-    log_message->set_source_location_iid(interned_source_location.id);
-    log_message->set_body_iid(interned_log_message_body.id);
-  } else if (interned_source_location.id) {
-    track_event->set_task_execution()->set_posted_from_iid(
-        interned_source_location.id);
-  } else if (!privacy_filtering_enabled_) {
+  if (!privacy_filtering_enabled_) {
     WriteDebugAnnotations(trace_event, track_event, interned_annotation_names);
   }
 
@@ -796,19 +735,7 @@
         std::make_tuple(IndexType::kName, IndexData{trace_event_name},
                         std::move(interned_name)));
   }
-  if (interned_log_message_body.id && !interned_log_message_body.was_emitted) {
-    pending_interning_updates_.push_back(
-        std::make_tuple(IndexType::kLogMessage, IndexData{log_message_body},
-                        std::move(interned_log_message_body)));
-  }
-  if (interned_source_location.id) {
-    if (!interned_source_location.was_emitted) {
-      pending_interning_updates_.push_back(std::make_tuple(
-          IndexType::kSourceLocation,
-          IndexData{std::make_tuple(src_file, src_func, line_number)},
-          std::move(interned_source_location)));
-    }
-  } else if (!privacy_filtering_enabled_) {
+  if (!privacy_filtering_enabled_) {
     for (size_t i = 0; i < trace_event->arg_size() && trace_event->arg_name(i);
          ++i) {
       DCHECK(interned_annotation_names[i].id);
diff --git a/skia/ext/test_fonts_fuchsia.cc b/skia/ext/test_fonts_fuchsia.cc
index 509cfb43..71454a9 100644
--- a/skia/ext/test_fonts_fuchsia.cc
+++ b/skia/ext/test_fonts_fuchsia.cc
@@ -38,7 +38,7 @@
   if (!base::PathService::Get(base::DIR_ASSETS, &assets_path))
     LOG(FATAL) << "Can't get DIR_ASSETS";
   launch_info.flat_namespace->directories.push_back(
-      base::fuchsia::OpenDirectory(assets_path.AppendASCII("test_fonts"))
+      base::OpenDirectoryHandle(assets_path.AppendASCII("test_fonts"))
           .TakeChannel());
 
   fidl::InterfaceHandle<fuchsia::io::Directory> font_provider_services_dir;
diff --git a/third_party/blink/public/common/privacy_budget/identifiable_surface.h b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
index 1bb0f368..4a4000ab 100644
--- a/third_party/blink/public/common/privacy_budget/identifiable_surface.h
+++ b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
@@ -184,6 +184,11 @@
     // will key this type on a digest of both the enums' values.
     kWebGLShaderPrecisionFormat = 16,
 
+    // A type for recording reads of the offsetWidth and offsetHeight properties
+    // when we believe it may be trying to detect the size of the scrollbar.
+    // The input for this surface should be a member of ScrollbarSurfaces.
+    kScrollbarSize = 17,
+
     // WebGL2RenderingContext.getInternal
     kWebGLInternalFormatParameter = 18,
 
@@ -208,6 +213,13 @@
     kMax = (1 << kTypeBits) - 1
   };
 
+  enum class ScrollbarSurface : uint64_t {
+    kBodyOffsetWidth = 0,
+    kBodyOffsetHeight = 1,
+    kElemScrollbarWidth = 2,
+    kElemScrollbarHeight = 3,
+  };
+
   // Default constructor is invalid.
   IdentifiableSurface() : IdentifiableSurface(kInvalidHash) {}
 
diff --git a/third_party/blink/public/mojom/memory_usage_monitor_linux.mojom b/third_party/blink/public/mojom/memory_usage_monitor_linux.mojom
index 4859cea..9407211 100644
--- a/third_party/blink/public/mojom/memory_usage_monitor_linux.mojom
+++ b/third_party/blink/public/mojom/memory_usage_monitor_linux.mojom
@@ -4,12 +4,13 @@
 
 module blink.mojom;
 
-import "mojo/public/mojom/base/file.mojom";
+import "mojo/public/mojom/base/read_only_file.mojom";
 
 // The interface to provide status and statm files to the renderer's memory
 // usage monitor. The interface is required only for Linux.
 interface MemoryUsageMonitorLinux {
   // The sandbox prevents the renderer from open()-ing any file, meaning
   // the file descriptors have to be provided by the browser process.
-  SetProcFiles(mojo_base.mojom.File statm_file, mojo_base.mojom.File status_file);
+  SetProcFiles(mojo_base.mojom.ReadOnlyFile statm_file,
+               mojo_base.mojom.ReadOnlyFile status_file);
 };
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 37754b14..4478cae59 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4087,83 +4087,95 @@
   if (parser_)
     parser_->StopParsing();
 
-  if (load_event_progress_ == kLoadEventNotRun)
+  if (load_event_progress_ == kLoadEventNotRun ||
+      load_event_progress_ > kUnloadEventInProgress) {
+    return;
+  }
+
+  Element* current_focused_element = FocusedElement();
+  if (auto* input = DynamicTo<HTMLInputElement>(current_focused_element))
+    input->EndEditing();
+
+  // If we've dispatched the pagehide event with 'persisted' set to true, it
+  // means we've dispatched the visibilitychange event before too. Also, we
+  // shouldn't dispatch the unload event because that event should only be
+  // fired when the pagehide event's 'persisted' bit is set to false.
+  bool dispatched_pagehide_persisted =
+      GetPage() && GetPage()->DispatchedPagehidePersistedAndStillHidden();
+
+  if (load_event_progress_ >= kPageHideInProgress ||
+      dispatched_pagehide_persisted) {
+    load_event_progress_ = kUnloadEventHandled;
+    return;
+  }
+
+  load_event_progress_ = kPageHideInProgress;
+  LocalDOMWindow* window = domWindow();
+  // We check for DispatchedPagehideAndStillHidden() here because it's possible
+  // to dispath pagehide with 'persisted' set to false before this and pass the
+  // |dispatched_pagehide_persisted| above, if we enable same-site
+  // ProactivelySwapBrowsingInstance but not BackForwardCache.
+  if (window && !GetPage()->DispatchedPagehideAndStillHidden()) {
+    const base::TimeTicks pagehide_event_start = base::TimeTicks::Now();
+    window->DispatchEvent(
+        *PageTransitionEvent::Create(event_type_names::kPagehide, false), this);
+    const base::TimeTicks pagehide_event_end = base::TimeTicks::Now();
+    DEFINE_STATIC_LOCAL(
+        CustomCountHistogram, pagehide_histogram,
+        ("DocumentEventTiming.PageHideDuration", 0, 10000000, 50));
+    pagehide_histogram.CountMicroseconds(pagehide_event_end -
+                                         pagehide_event_start);
+  }
+  if (!dom_window_)
     return;
 
-  if (load_event_progress_ <= kUnloadEventInProgress) {
-    Element* current_focused_element = FocusedElement();
-    if (auto* input = DynamicTo<HTMLInputElement>(current_focused_element))
-      input->EndEditing();
-    if (load_event_progress_ < kPageHideInProgress) {
-      load_event_progress_ = kPageHideInProgress;
-      LocalDOMWindow* window = domWindow();
-      if (window && !GetPage()->DispatchedPagehideAndStillHidden()) {
-        const base::TimeTicks pagehide_event_start = base::TimeTicks::Now();
-        window->DispatchEvent(
-            *PageTransitionEvent::Create(event_type_names::kPagehide, false),
-            this);
-        const base::TimeTicks pagehide_event_end = base::TimeTicks::Now();
-        DEFINE_STATIC_LOCAL(
-            CustomCountHistogram, pagehide_histogram,
-            ("DocumentEventTiming.PageHideDuration", 0, 10000000, 50));
-        pagehide_histogram.CountMicroseconds(pagehide_event_end -
-                                             pagehide_event_start);
-      }
-      if (!dom_window_)
-        return;
-
-      // This must be queried before |load_event_progress_| is changed to
-      // kUnloadVisibilityChangeInProgress because that would change the result.
-      bool page_visible = IsPageVisible();
-      load_event_progress_ = kUnloadVisibilityChangeInProgress;
-      if (page_visible) {
-        // Dispatch visibilitychange event, but don't bother doing
-        // other notifications as we're about to be unloaded.
-        const base::TimeTicks pagevisibility_hidden_event_start =
-            base::TimeTicks::Now();
-        DispatchEvent(
-            *Event::CreateBubble(event_type_names::kVisibilitychange));
-        const base::TimeTicks pagevisibility_hidden_event_end =
-            base::TimeTicks::Now();
-        DEFINE_STATIC_LOCAL(CustomCountHistogram, pagevisibility_histogram,
-                            ("DocumentEventTiming.PageVibilityHiddenDuration",
-                             0, 10000000, 50));
-        pagevisibility_histogram.CountMicroseconds(
-            pagevisibility_hidden_event_end -
-            pagevisibility_hidden_event_start);
-        DispatchEvent(
-            *Event::CreateBubble(event_type_names::kWebkitvisibilitychange));
-      }
-      if (!dom_window_)
-        return;
-
-      GetFrame()->Loader().SaveScrollAnchor();
-
-      load_event_progress_ = kUnloadEventInProgress;
-      Event& unload_event = *Event::Create(event_type_names::kUnload);
-      const base::TimeTicks unload_event_start = base::TimeTicks::Now();
-      dom_window_->DispatchEvent(unload_event, this);
-      const base::TimeTicks unload_event_end = base::TimeTicks::Now();
-
-      if (unload_timing) {
-        // Record unload event timing when navigating cross-document.
-        DEFINE_STATIC_LOCAL(
-            CustomCountHistogram, unload_histogram,
-            ("DocumentEventTiming.UnloadDuration", 0, 10000000, 50));
-        unload_histogram.CountMicroseconds(unload_event_end -
-                                           unload_event_start);
-
-        // Fill in the unload timing if the new document origin has access to
-        // them.
-        if (committing_origin->CanRequest(Url())) {
-          auto& timing = unload_timing->emplace();
-          timing.unload_event_start = unload_event_start;
-          timing.unload_event_end = unload_event_end;
-        }
-      }
-    }
-    load_event_progress_ = kUnloadEventHandled;
+  // This must be queried before |load_event_progress_| is changed to
+  // kUnloadVisibilityChangeInProgress because that would change the result.
+  bool page_visible = IsPageVisible();
+  load_event_progress_ = kUnloadVisibilityChangeInProgress;
+  if (page_visible) {
+    // Dispatch visibilitychange event, but don't bother doing
+    // other notifications as we're about to be unloaded.
+    const base::TimeTicks pagevisibility_hidden_event_start =
+        base::TimeTicks::Now();
+    DispatchEvent(*Event::CreateBubble(event_type_names::kVisibilitychange));
+    const base::TimeTicks pagevisibility_hidden_event_end =
+        base::TimeTicks::Now();
+    DEFINE_STATIC_LOCAL(
+        CustomCountHistogram, pagevisibility_histogram,
+        ("DocumentEventTiming.PageVibilityHiddenDuration", 0, 10000000, 50));
+    pagevisibility_histogram.CountMicroseconds(
+        pagevisibility_hidden_event_end - pagevisibility_hidden_event_start);
+    DispatchEvent(
+        *Event::CreateBubble(event_type_names::kWebkitvisibilitychange));
   }
+  if (!dom_window_)
+    return;
+
+  GetFrame()->Loader().SaveScrollAnchor();
+
+  load_event_progress_ = kUnloadEventInProgress;
+  Event& unload_event = *Event::Create(event_type_names::kUnload);
+  const base::TimeTicks unload_event_start = base::TimeTicks::Now();
+  dom_window_->DispatchEvent(unload_event, this);
+  const base::TimeTicks unload_event_end = base::TimeTicks::Now();
+
+  if (unload_timing) {
+    // Record unload event timing when navigating cross-document.
+    DEFINE_STATIC_LOCAL(
+        CustomCountHistogram, unload_histogram,
+        ("DocumentEventTiming.UnloadDuration", 0, 10000000, 50));
+    unload_histogram.CountMicroseconds(unload_event_end - unload_event_start);
+
+    // Fill in the unload timing if the new document origin has access to
+    // them.
+    if (committing_origin->CanRequest(Url())) {
+      auto& timing = unload_timing->emplace();
+      timing.unload_event_start = unload_event_start;
+      timing.unload_event_end = unload_event_end;
+    }
+  }
+  load_event_progress_ = kUnloadEventHandled;
 }
 
 void Document::DispatchFreezeEvent() {
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index c829b72..9550a35 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -91,7 +91,6 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/focus_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
@@ -649,20 +648,11 @@
   }
 
   wants_wheel_events_ = wants_wheel_events;
-  if (auto* page = element_->GetDocument().GetPage()) {
-    if (ScrollingCoordinator* scrolling_coordinator =
-            page->GetScrollingCoordinator()) {
-      // Only call scrolling_coordinator if attached.  SetWantsWheelEvents can
-      // be called from Plugin Initialization when it is not yet attached.
-      if (IsAttached()) {
-        LocalFrameView* frame_view = element_->GetDocument().GetFrame()->View();
-        scrolling_coordinator->NotifyGeometryChanged(frame_view);
 
-        // Scroll hit test data depend on wheel events. They are painted in the
-        // background phase.
-        GetLayoutEmbeddedContent()->SetBackgroundNeedsFullPaintInvalidation();
-      }
-    }
+  if (IsAttached()) {
+    // Scroll hit test data depend on wheel events. They are painted in the
+    // background phase.
+    GetLayoutEmbeddedContent()->SetBackgroundNeedsFullPaintInvalidation();
   }
 }
 
diff --git a/third_party/blink/renderer/core/frame/event_handler_registry.cc b/third_party/blink/renderer/core/frame/event_handler_registry.cc
index f33133d5..85c70ec0 100644
--- a/third_party/blink/renderer/core/frame/event_handler_registry.cc
+++ b/third_party/blink/renderer/core/frame/event_handler_registry.cc
@@ -103,32 +103,24 @@
   return targets_[handler_class].size();
 }
 
-bool EventHandlerRegistry::UpdateEventHandlerTargets(
+void EventHandlerRegistry::UpdateEventHandlerTargets(
     ChangeOperation op,
     EventHandlerClass handler_class,
     EventTarget* target) {
   EventTargetSet* targets = &targets_[handler_class];
-  if (op == kAdd) {
-    if (!targets->insert(target).is_new_entry) {
-      // Just incremented refcount, no real change.
-      return false;
-    }
-  } else {
-    DCHECK(op == kRemove || op == kRemoveAll);
-    DCHECK(op == kRemoveAll || targets->Contains(target));
-
-    if (op == kRemoveAll) {
-      if (!targets->Contains(target))
-        return false;
+  switch (op) {
+    case kAdd:
+      targets->insert(target);
+      return;
+    case kRemove:
+      DCHECK(targets->Contains(target));
+      targets->erase(target);
+      return;
+    case kRemoveAll:
       targets->RemoveAll(target);
-    } else {
-      if (!targets->erase(target)) {
-        // Just decremented refcount, no real update.
-        return false;
-      }
-    }
+      return;
   }
-  return true;
+  NOTREACHED();
 }
 
 bool EventHandlerRegistry::UpdateEventHandlerInternal(
@@ -136,21 +128,13 @@
     EventHandlerClass handler_class,
     EventTarget* target) {
   unsigned old_num_handlers = targets_[handler_class].size();
-  bool target_set_changed =
-      UpdateEventHandlerTargets(op, handler_class, target);
+  UpdateEventHandlerTargets(op, handler_class, target);
   unsigned new_num_handlers = targets_[handler_class].size();
 
   bool handlers_changed = old_num_handlers != new_num_handlers;
+  if (op != kRemoveAll && handlers_changed)
+    NotifyHandlersChanged(target, handler_class, new_num_handlers > 0);
 
-  if (op != kRemoveAll) {
-    if (handlers_changed)
-      NotifyHandlersChanged(target, handler_class, new_num_handlers > 0);
-
-    if (target_set_changed) {
-      NotifyDidAddOrRemoveEventHandlerTarget(GetLocalFrameForTarget(target),
-                                             handler_class);
-    }
-  }
   return handlers_changed;
 }
 
@@ -235,10 +219,6 @@
       bool has_handlers = targets_[handler_class].Contains(&target);
       NotifyHandlersChanged(&target, handler_class, has_handlers);
     }
-    if (target_set_changed[i]) {
-      NotifyDidAddOrRemoveEventHandlerTarget(GetLocalFrameForTarget(&target),
-                                             handler_class);
-    }
   }
 }
 
@@ -326,24 +306,6 @@
   }
 }
 
-void EventHandlerRegistry::NotifyDidAddOrRemoveEventHandlerTarget(
-    LocalFrame* frame,
-    EventHandlerClass handler_class) {
-  // TODO(keishi): Added for crbug.com/1090687. Change to CHECK once bug is
-  // fixed.
-  if (!GetPage())
-    return;
-  ScrollingCoordinator* scrolling_coordinator =
-      GetPage()->GetScrollingCoordinator();
-  if (scrolling_coordinator &&
-      (handler_class == kTouchAction ||
-       handler_class == kTouchStartOrMoveEventBlocking ||
-       handler_class == kTouchStartOrMoveEventBlockingLowLatency)) {
-    scrolling_coordinator->TouchEventTargetRectsDidChange(
-        &frame->LocalFrameRoot());
-  }
-}
-
 void EventHandlerRegistry::Trace(Visitor* visitor) const {
   visitor->Trace(frame_);
   visitor->template RegisterWeakCallbackMethod<
diff --git a/third_party/blink/renderer/core/frame/event_handler_registry.h b/third_party/blink/renderer/core/frame/event_handler_registry.h
index 6df2f6a6..0ac46004 100644
--- a/third_party/blink/renderer/core/frame/event_handler_registry.h
+++ b/third_party/blink/renderer/core/frame/event_handler_registry.h
@@ -96,7 +96,7 @@
 
   // Returns true if the operation actually added a new target or completely
   // removed an existing one.
-  bool UpdateEventHandlerTargets(ChangeOperation,
+  void UpdateEventHandlerTargets(ChangeOperation,
                                  EventHandlerClass,
                                  EventTarget*);
 
@@ -108,11 +108,6 @@
                              EventHandlerClass,
                              bool has_active_handlers);
 
-  // Called to notify clients whenever a single event handler target is
-  // registered or unregistered. If several handlers are registered for the
-  // same target, only the first registration will trigger this notification.
-  void NotifyDidAddOrRemoveEventHandlerTarget(LocalFrame*, EventHandlerClass);
-
   // Record a change operation to a given event handler class and notify any
   // parent registry and other clients accordingly.
   void UpdateEventHandlerOfType(ChangeOperation,
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 8dbb4fb..804247a 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -2007,12 +2007,6 @@
     return;
 
   ScheduleUpdatePluginsIfNecessary();
-
-  if (ScrollingCoordinator* scrolling_coordinator =
-          this->GetScrollingCoordinator()) {
-    scrolling_coordinator->NotifyGeometryChanged(this);
-  }
-
   SendResizeEventIfNeeded();
 }
 
@@ -2761,12 +2755,6 @@
   if (!is_capturing_layout)
     repainted = PaintTree(benchmark_mode);
 
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    if (GetLayoutView()->Compositor()->InCompositingMode()) {
-      GetScrollingCoordinator()->UpdateAfterPaint(this);
-    }
-  }
-
   if (benchmark_mode ==
           PaintBenchmarkMode::kForcePaintArtifactCompositorUpdate ||
       // TODO(paint-dev): Separate requirement for update for repaint and full
@@ -3011,10 +2999,6 @@
     if (GraphicsLayer* root_graphics_layer =
             layout_view->Compositor()->PaintRootGraphicsLayer()) {
       repainted = root_graphics_layer->PaintRecursively(benchmark_mode);
-      // If the painted result changed, the recorded hit test data may have
-      // changed which will affect the mapped hit test geometry.
-      if (repainted && GetScrollingCoordinator())
-        GetScrollingCoordinator()->NotifyGeometryChanged(this);
     } else {
       needs_clear_repaint_flags = true;
     }
@@ -3614,30 +3598,12 @@
     client->ScheduleAnimation(this, delay);
 }
 
-void LocalFrameView::ScrollableAreasDidChange() {
-  // Layout may update scrollable area bounding boxes. It also sets the same
-  // dirty flag making this one redundant (See
-  // |ScrollingCoordinator::notifyGeometryChanged|).
-  // So if layout is expected, ignore this call allowing scrolling coordinator
-  // to be notified post-layout to recompute gesture regions.
-  // TODO(wjmaclean): It would be nice to move the !NeedsLayout() check from
-  // here to SetScrollGestureRegionIsDirty(), but at present doing so breaks
-  // web tests. This suggests that there is something that wants to set the
-  // dirty bit when layout is needed, and won't re-try setting the bit after
-  // layout has completed - it would be nice to find that and fix it.
-  if (!NeedsLayout())
-    GetScrollingContext()->SetScrollGestureRegionIsDirty(true);
-}
-
 void LocalFrameView::AddScrollableArea(
     PaintLayerScrollableArea* scrollable_area) {
   DCHECK(scrollable_area);
   if (!scrollable_areas_)
     scrollable_areas_ = MakeGarbageCollected<ScrollableAreaSet>();
   scrollable_areas_->insert(scrollable_area);
-
-  if (GetScrollingCoordinator())
-    ScrollableAreasDidChange();
 }
 
 void LocalFrameView::RemoveScrollableArea(
@@ -3645,9 +3611,6 @@
   if (!scrollable_areas_)
     return;
   scrollable_areas_->erase(scrollable_area);
-
-  if (GetScrollingCoordinator())
-    ScrollableAreasDidChange();
 }
 
 void LocalFrameView::AddAnimatingScrollableArea(
@@ -4131,8 +4094,6 @@
 void LocalFrameView::Show() {
   if (!IsSelfVisible()) {
     SetSelfVisible(true);
-    if (GetScrollingCoordinator())
-      GetScrollingContext()->SetScrollGestureRegionIsDirty(true);
     SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
     if (IsParentVisible()) {
       ForAllChildViewsAndPlugins(
@@ -4152,8 +4113,6 @@
           });
     }
     SetSelfVisible(false);
-    if (GetScrollingCoordinator())
-      GetScrollingContext()->SetScrollGestureRegionIsDirty(true);
     SetNeedsCompositingUpdate(kCompositingUpdateRebuildTree);
   }
 }
@@ -4314,10 +4273,6 @@
 }
 
 void LocalFrameView::InvalidateForThrottlingChange() {
-  // ScrollingCoordinator needs to update according to the new throttling
-  // status.
-  if (ScrollingCoordinator* coordinator = this->GetScrollingCoordinator())
-    coordinator->NotifyGeometryChanged(this);
   // Start ticking animation frames again if necessary.
   if (GetPage())
     GetPage()->Animator().ScheduleVisualUpdate(frame_.Get());
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 0ea76e5..822bcdca 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -677,10 +677,6 @@
 
   const cc::Layer* RootCcLayer() const;
 
-  // Should be called whenever this LocalFrameView adds or removes a
-  // scrollable area, or gains/loses a composited layer.
-  void ScrollableAreasDidChange();
-
   ScrollingCoordinatorContext* GetScrollingContext() const;
   cc::AnimationHost* GetCompositorAnimationHost() const;
   CompositorAnimationTimeline* GetCompositorAnimationTimeline() const;
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.cc b/third_party/blink/renderer/core/html/canvas/image_data.cc
index 7c2f5aa..772f6ee 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.cc
+++ b/third_party/blink/renderer/core/html/canvas/image_data.cc
@@ -336,8 +336,6 @@
   if (!image_data)
     return nullptr;
 
-  // TODO(crbug.com/1115317): Verify if the color type uint16 needs to be
-  // considered separately.
   ImageDataArray data = image_data->data();
   SkColorType color_type = image_info.colorType();
   bool create_f32_image_data = (color_type == kRGBA_1010102_SkColorType ||
@@ -346,9 +344,15 @@
                                 color_type == kRGBA_F32_SkColorType);
 
   if (!create_f32_image_data) {
-    image_info = image_info.makeColorType(kRGBA_8888_SkColorType);
-    paint_image.readPixels(image_info, data.GetAsUint8ClampedArray()->Data(),
-                           image_info.minRowBytes(), 0, 0);
+    if (color_type == kR16G16B16A16_unorm_SkColorType) {
+      image_info = image_info.makeColorType(kR16G16B16A16_unorm_SkColorType);
+      paint_image.readPixels(image_info, data.GetAsUint16Array()->Data(),
+                             image_info.minRowBytes(), 0, 0);
+    } else {
+      image_info = image_info.makeColorType(kRGBA_8888_SkColorType);
+      paint_image.readPixels(image_info, data.GetAsUint8ClampedArray()->Data(),
+                             image_info.minRowBytes(), 0, 0);
+    }
   } else {
     image_info = image_info.makeColorType(kRGBA_F32_SkColorType);
     paint_image.readPixels(image_info, data.GetAsFloat32Array()->Data(),
@@ -620,13 +624,11 @@
       ScriptWrappable::AssociateWithWrapper(isolate, wrapper_type, wrapper);
 
   if (!wrapper.IsEmpty() && data_.IsUint8ClampedArray()) {
-    // Create a V8 Uint8ClampedArray object and set the "data" property
+    // Create a V8 object with |data_| and set the "data" property
     // of the ImageData object to the created v8 object, eliminating the
     // C++ callback when accessing the "data" property.
-    // TODO(crbug.com/1115317): |pixel_array| should be compatible with uint_8,
-    // float16 and float32.
-    v8::Local<v8::Value> pixel_array =
-        ToV8(data_.GetAsUint8ClampedArray().Get(), wrapper, isolate);
+
+    v8::Local<v8::Value> pixel_array = ToV8(data_, wrapper, isolate);
     bool defined_property;
     if (pixel_array.IsEmpty() ||
         !wrapper
@@ -793,6 +795,17 @@
       kNonOpaque);
 }
 
+SkImageInfo ImageData::GetSkImageInfo() {
+  SkColorType color_type = kN32_SkColorType;
+  if (data_u16_) {
+    color_type = kR16G16B16A16_unorm_SkColorType;
+  } else if (data_f32_) {
+    color_type = kRGBA_F32_SkColorType;
+  }
+  return SkImageInfo::Make(width(), height(), color_type,
+                           GetCanvasColorParams().GetSkAlphaType());
+}
+
 bool ImageData::ImageDataInCanvasColorSettings(
     CanvasColorSpace canvas_color_space,
     CanvasPixelFormat canvas_pixel_format,
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.h b/third_party/blink/renderer/core/html/canvas/image_data.h
index 6283c49..66d026c 100644
--- a/third_party/blink/renderer/core/html/canvas/image_data.h
+++ b/third_party/blink/renderer/core/html/canvas/image_data.h
@@ -149,6 +149,7 @@
 
   DOMArrayBufferBase* BufferBase() const;
   CanvasColorParams GetCanvasColorParams();
+  SkImageInfo GetSkImageInfo();
 
   // DataU8ColorType param specifies if the converted pixels in uint8 pixel
   // format should respect the "native" 32bit ARGB format of Skia's blitters.
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 1aaacce..4c5c62b 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -26,6 +26,8 @@
 #include "third_party/blink/renderer/core/html/html_element.h"
 
 #include "base/stl_util.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_treat_null_as_empty_string_or_trusted_script.h"
@@ -51,6 +53,7 @@
 #include "third_party/blink/renderer/core/events/keyboard_event.h"
 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
 #include "third_party/blink/renderer/core/frame/deprecation.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/custom/custom_element.h"
@@ -69,10 +72,12 @@
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
+#include "third_party/blink/renderer/core/layout/layout_box.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/mathml_names.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
 #include "third_party/blink/renderer/core/trustedtypes/trusted_script.h"
 #include "third_party/blink/renderer/core/xml_names.h"
@@ -1492,17 +1497,81 @@
   return 0;
 }
 
+void HTMLElement::RecordScrollbarSizeForStudy(int offset_measurement,
+                                              bool is_width) {
+  if (!IdentifiabilityStudySettings::Get()->IsTypeAllowed(
+          IdentifiableSurface::Type::kScrollbarSize))
+    return;
+
+  // Check for presence of a scrollbar.
+  PaintLayerScrollableArea* area;
+  if (this == GetDocument().ScrollingElementNoLayout()) {
+    auto* view = GetDocument().View();
+    if (!view)
+      return;
+    area = view->LayoutViewport();
+  } else {
+    auto* layout = GetLayoutBox();
+    if (!layout)
+      return;
+    area = layout->GetScrollableArea();
+  }
+  if (!area || area->HasOverlayOverflowControls())
+    return;
+
+  Scrollbar* scrollbar =
+      is_width ? area->VerticalScrollbar() : area->HorizontalScrollbar();
+  // We intentionally exclude platform overlay scrollbars since their size
+  // cannot be detected in JavaScript using the methods below.
+  if (!scrollbar)
+    return;
+
+  IdentifiableSurface::ScrollbarSurface surface;
+  int scrollbar_size;
+
+  // There are two common ways to detect the size of a scrollbar in a DOM
+  // window. They are:
+  // 1. Compute the difference of the window.inner[Width|Height] and the
+  //    corresponding document.scrollingElement.offset[Width|Height].
+  // 2. Any HTML element that insets the layout to fit a scrollbar, so it is
+  //    measurable by a JavaScript program on a site.
+  if (this == GetDocument().scrollingElement()) {
+    LocalDOMWindow* dom_window = GetDocument().domWindow();
+    scrollbar_size =
+        (is_width ? dom_window->innerWidth() : dom_window->innerHeight()) -
+        offset_measurement;
+    surface = is_width
+                  ? IdentifiableSurface::ScrollbarSurface::kBodyOffsetWidth
+                  : IdentifiableSurface::ScrollbarSurface::kBodyOffsetHeight;
+  } else {
+    scrollbar_size =
+        offset_measurement - (is_width ? clientWidth() : clientHeight());
+    surface = is_width
+                  ? IdentifiableSurface::ScrollbarSurface::kElemScrollbarWidth
+                  : IdentifiableSurface::ScrollbarSurface::kElemScrollbarHeight;
+  }
+
+  blink::IdentifiabilityMetricBuilder(GetDocument().UkmSourceID())
+      .Set(blink::IdentifiableSurface::FromTypeAndToken(
+               blink::IdentifiableSurface::Type::kScrollbarSize, surface),
+           scrollbar_size)
+      .Record(GetDocument().UkmRecorder());
+}
+
 int HTMLElement::offsetWidthForBinding() {
   GetDocument().EnsurePaintLocationDataValidForNode(
       this, DocumentUpdateReason::kJavaScript);
   Element* offset_parent = unclosedOffsetParent();
-  if (LayoutBoxModelObject* layout_object = GetLayoutBoxModelObject())
-    return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-               LayoutUnit(
-                   layout_object->PixelSnappedOffsetWidth(offset_parent)),
-               layout_object->StyleRef())
-        .Round();
-  return 0;
+  int result = 0;
+  if (LayoutBoxModelObject* layout_object = GetLayoutBoxModelObject()) {
+    result =
+        AdjustForAbsoluteZoom::AdjustLayoutUnit(
+            LayoutUnit(layout_object->PixelSnappedOffsetWidth(offset_parent)),
+            layout_object->StyleRef())
+            .Round();
+    RecordScrollbarSizeForStudy(result, /* isWidth= */ true);
+  }
+  return result;
 }
 
 DISABLE_CFI_PERF
@@ -1510,13 +1579,16 @@
   GetDocument().EnsurePaintLocationDataValidForNode(
       this, DocumentUpdateReason::kJavaScript);
   Element* offset_parent = unclosedOffsetParent();
-  if (LayoutBoxModelObject* layout_object = GetLayoutBoxModelObject())
-    return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-               LayoutUnit(
-                   layout_object->PixelSnappedOffsetHeight(offset_parent)),
-               layout_object->StyleRef())
-        .Round();
-  return 0;
+  int result = 0;
+  if (LayoutBoxModelObject* layout_object = GetLayoutBoxModelObject()) {
+    result =
+        AdjustForAbsoluteZoom::AdjustLayoutUnit(
+            LayoutUnit(layout_object->PixelSnappedOffsetHeight(offset_parent)),
+            layout_object->StyleRef())
+            .Round();
+    RecordScrollbarSizeForStudy(result, /* isWidth= */ false);
+  }
+  return result;
 }
 
 Element* HTMLElement::unclosedOffsetParent() {
diff --git a/third_party/blink/renderer/core/html/html_element.h b/third_party/blink/renderer/core/html/html_element.h
index 0f54c2fa..94cba571 100644
--- a/third_party/blink/renderer/core/html/html_element.h
+++ b/third_party/blink/renderer/core/html/html_element.h
@@ -212,6 +212,8 @@
 
   void HandleKeypressEvent(KeyboardEvent&);
 
+  void RecordScrollbarSizeForStudy(int, bool);
+
   static AttributeTriggers* TriggersForAttributeName(
       const QualifiedName& attr_name);
 
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index 778843e..4078a29 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -51,7 +51,6 @@
 #include "third_party/blink/renderer/core/loader/mixed_content_checker.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/plugin_data.h"
-#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
@@ -675,15 +674,6 @@
   // TODO(esprehn): WebPluginContainerImpl::SetCcLayer() also schedules a
   // compositing update, do we need both?
   SetNeedsCompositingUpdate();
-  // Make sure any input event handlers introduced by the plugin are taken into
-  // account.
-  if (Page* page = GetDocument().GetFrame()->GetPage()) {
-    if (ScrollingCoordinator* scrolling_coordinator =
-            page->GetScrollingCoordinator()) {
-      LocalFrameView* frame_view = GetDocument().GetFrame()->View();
-      scrolling_coordinator->NotifyGeometryChanged(frame_view);
-    }
-  }
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index ca8a5da..ff02f37 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -693,12 +693,10 @@
   LayoutUnit total_available_logical_width =
       BorderAndPaddingLogicalWidth() + AvailableLogicalWidth();
 
-  if (StyleRef().IsHorizontalWritingMode() &&
-      !StyleRef().IsLeftToRightDirection()) {
+  if (ShouldPlaceBlockDirectionScrollbarOnLogicalLeft())
     start_position -= LogicalLeftScrollbarWidth();
-  } else {
+  else
     start_position += LogicalLeftScrollbarWidth();
-  }
 
   LayoutUnit child_margin_start = MarginStartForChild(child);
   LayoutUnit new_position = start_position + child_margin_start;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 2b974623..9104f29f 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -89,7 +89,6 @@
 #include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
 #include "third_party/blink/renderer/core/page/autoscroll_controller.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
 #include "third_party/blink/renderer/core/paint/background_image_geometry.h"
 #include "third_party/blink/renderer/core/paint/box_paint_invalidator.h"
@@ -666,11 +665,6 @@
     }
   }
 
-  if (diff.TransformChanged()) {
-    if (auto* coordinator = GetFrame()->GetPage()->GetScrollingCoordinator())
-      coordinator->NotifyGeometryChanged(GetFrameView());
-  }
-
   // Update the script style map, from the new computed style.
   if (IsCustomItem())
     GetCustomLayoutChild()->styleMap()->UpdateStyle(GetDocument(), StyleRef());
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index a7b7a3ac..260f56a 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -49,7 +49,6 @@
 #include "third_party/blink/renderer/core/page/named_pages_mapper.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
-#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
@@ -936,13 +935,6 @@
 
   auto result = LayoutBlockFlow::RecalcLayoutOverflow();
   if (result.layout_overflow_changed) {
-    // Changing overflow should notify scrolling coordinator to ensures that it
-    // updates non-fast scroll rects even if there is no layout.
-    if (ScrollingCoordinator* scrolling_coordinator =
-            GetDocument().GetPage()->GetScrollingCoordinator()) {
-      GetFrameView()->GetScrollingContext()->SetScrollGestureRegionIsDirty(
-          true);
-    }
     if (NeedsLayout())
       return result;
     if (GetFrameView()->VisualViewportSuppliesScrollbars())
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
index b6e2afb..083cf427 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -269,32 +269,6 @@
   return !IsListMarker();
 }
 
-bool NGInlineCursorPosition::IsPartOfCulledInlineBox(
-    const LayoutInline& layout_inline) const {
-  DCHECK(!layout_inline.ShouldCreateBoxFragment());
-  DCHECK(*this);
-  const LayoutObject* const layout_object = GetLayoutObject();
-  // We use |IsInline()| to exclude floating and out-of-flow objects.
-  if (!layout_object || !layout_object->IsInline() ||
-      layout_object->IsAtomicInlineLevel())
-    return false;
-  DCHECK(!layout_object->IsFloatingOrOutOfFlowPositioned());
-  DCHECK(!BoxFragment() || !BoxFragment()->IsFormattingContextRoot());
-  for (const LayoutObject* parent = layout_object->Parent(); parent;
-       parent = parent->Parent()) {
-    // Children of culled inline should be included.
-    if (parent == &layout_inline)
-      return true;
-    // Grand children should be included only if children are also culled.
-    if (const auto* parent_layout_inline = ToLayoutInlineOrNull(parent)) {
-      if (!parent_layout_inline->ShouldCreateBoxFragment())
-        continue;
-    }
-    return false;
-  }
-  return false;
-}
-
 bool NGInlineCursor::IsLastLineInInlineBlock() const {
   DCHECK(Current().IsLineBox());
   if (!GetLayoutBlockFlow()->IsAtomicInlineLevel())
@@ -1700,23 +1674,15 @@
   return nullptr;
 }
 
-void NGInlineCursor::CulledInlineTraversal::SetUseFragmentTree(
-    const LayoutInline& layout_inline) {
-  layout_inline_ = &layout_inline;
-  use_fragment_tree_ = true;
-}
-
 const LayoutObject* NGInlineCursor::CulledInlineTraversal::MoveToFirstFor(
     const LayoutInline& layout_inline) {
   layout_inline_ = &layout_inline;
-  use_fragment_tree_ = false;
   current_object_ = Find(layout_inline.FirstChild());
   return current_object_;
 }
 
 const LayoutObject* NGInlineCursor::CulledInlineTraversal::MoveToNext() {
-  if (!current_object_)
-    return nullptr;
+  DCHECK(current_object_);
   current_object_ =
       Find(current_object_->NextInPreOrderAfterChildren(layout_inline_));
   return current_object_;
@@ -1724,19 +1690,6 @@
 
 void NGInlineCursor::MoveToFirstForCulledInline(
     const LayoutInline& layout_inline) {
-  // When |this| is a descendant cursor, |this| may be limited to a very small
-  // subset of the |LayoutObject| descendants, and that traversing
-  // |LayoutObject| descendants is much more expensive. Prefer checking every
-  // fragment in that case.
-  if (IsDescendantsCursor()) {
-    culled_inline_.SetUseFragmentTree(layout_inline);
-    DCHECK(!CanMoveAcrossFragmentainer());
-    MoveToFirst();
-    while (Current() && !Current().IsPartOfCulledInlineBox(layout_inline))
-      MoveToNext();
-    return;
-  }
-
   if (const LayoutObject* layout_object =
           culled_inline_.MoveToFirstFor(layout_inline)) {
     MoveTo(*layout_object);
@@ -1748,16 +1701,6 @@
 
 void NGInlineCursor::MoveToNextForCulledInline() {
   DCHECK(culled_inline_);
-  if (culled_inline_.UseFragmentTree()) {
-    const LayoutInline* layout_inline = culled_inline_.GetLayoutInline();
-    DCHECK(layout_inline);
-    DCHECK(!CanMoveAcrossFragmentainer());
-    do {
-      MoveToNext();
-    } while (Current() && !Current().IsPartOfCulledInlineBox(*layout_inline));
-    return;
-  }
-
   MoveToNextForSameLayoutObjectExceptCulledInline();
   // If we're at the end of fragments for the current |LayoutObject| that
   // contributes to the current culled inline, find the next |LayoutObject|.
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
index cb3f104e..8f69168 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -532,13 +532,8 @@
    public:
     CulledInlineTraversal() = default;
 
-    const LayoutInline* GetLayoutInline() const { return layout_inline_; }
-
-    explicit operator bool() const { return layout_inline_; }
-    void Reset() { layout_inline_ = nullptr; }
-
-    bool UseFragmentTree() const { return use_fragment_tree_; }
-    void SetUseFragmentTree(const LayoutInline& layout_inline);
+    explicit operator bool() const { return current_object_; }
+    void Reset() { current_object_ = nullptr; }
 
     // Returns first/next |LayoutObject| that contribute to |layout_inline|.
     const LayoutObject* MoveToFirstFor(const LayoutInline& layout_inline);
@@ -549,7 +544,6 @@
 
     const LayoutObject* current_object_ = nullptr;
     const LayoutInline* layout_inline_ = nullptr;
-    bool use_fragment_tree_ = false;
   };
 
   void MoveToFirstForCulledInline(const LayoutInline& layout_inline);
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index c87fcba..8ad0fb9 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -558,6 +558,11 @@
          mojom::blink::PagehideDispatch::kNotDispatched;
 }
 
+bool Page::DispatchedPagehidePersistedAndStillHidden() {
+  return lifecycle_state_->pagehide_dispatch ==
+         mojom::blink::PagehideDispatch::kDispatchedPersisted;
+}
+
 void Page::OnSetPageFrozen(bool frozen) {
   if (frozen_ == frozen)
     return;
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index 4e4e496..c184b1fd 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -374,6 +374,10 @@
   // still hidden (possibly preserved in the back-forward cache, or unloaded).
   bool DispatchedPagehideAndStillHidden();
 
+  // Similar to above, but will only return true if we've dispatched 'pagehide'
+  // with the 'persisted' property set to 'true'.
+  bool DispatchedPagehidePersistedAndStillHidden();
+
   static void PrepareForLeakDetection();
 
  private:
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
index 99d2ed6..4b0c31e 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.cc
@@ -71,11 +71,6 @@
   visitor->Trace(vertical_scrollbars_);
 }
 
-void ScrollingCoordinator::NotifyGeometryChanged(LocalFrameView* frame_view) {
-  frame_view->GetScrollingContext()->SetScrollGestureRegionIsDirty(true);
-  frame_view->GetScrollingContext()->SetTouchEventTargetRectsAreDirty(true);
-}
-
 ScrollableArea*
 ScrollingCoordinator::ScrollableAreaWithElementIdInAllLocalFrames(
     const CompositorElementId& id) {
@@ -129,78 +124,6 @@
   }
 }
 
-void ScrollingCoordinator::UpdateAfterPaint(LocalFrameView* frame_view) {
-  DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
-
-  LocalFrame* frame = &frame_view->GetFrame();
-  DCHECK(frame->IsLocalRoot());
-
-  bool scroll_gesture_region_dirty =
-      frame_view->GetScrollingContext()->ScrollGestureRegionIsDirty();
-  bool touch_event_rects_dirty =
-      frame_view->GetScrollingContext()->TouchEventTargetRectsAreDirty();
-
-  if (!scroll_gesture_region_dirty && !touch_event_rects_dirty)
-    return;
-
-  SCOPED_UMA_AND_UKM_TIMER(frame_view->EnsureUkmAggregator(),
-                           LocalFrameUkmAggregator::kScrollingCoordinator);
-  TRACE_EVENT0("input", "ScrollingCoordinator::UpdateAfterPaint");
-
-  if (scroll_gesture_region_dirty) {
-    UpdateNonFastScrollableRegions(frame);
-    frame_view->GetScrollingContext()->SetScrollGestureRegionIsDirty(false);
-  }
-
-  if (touch_event_rects_dirty) {
-    UpdateTouchEventTargetRectsIfNeeded(frame);
-    frame_view->GetScrollingContext()->SetTouchEventTargetRectsAreDirty(false);
-  }
-}
-
-// Set the non-fast scrollable regions on |layer|'s cc layer.
-static void UpdateLayerNonFastScrollableRegions(GraphicsLayer& layer) {
-  // CompositeAfterPaint does this update in PaintArtifactCompositor.
-  DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
-
-  DCHECK(layer.PaintsContentOrHitTest());
-
-  auto offset = layer.GetOffsetFromTransformNode();
-  gfx::Vector2dF layer_offset = gfx::Vector2dF(offset.X(), offset.Y());
-  PaintArtifactCompositor::UpdateNonFastScrollableRegions(
-      layer.CcLayer(), layer_offset, layer.GetPropertyTreeState().Unalias(),
-      PaintChunkSubset(layer.GetPaintController().GetPaintArtifactShared()));
-}
-
-// Compute the regions of the page where we can't handle scroll gestures on
-// the impl thread. This currently includes:
-// 1. All scrollable areas, such as subframes, overflow divs and list boxes,
-//    whose composited scrolling are not enabled. We need to do this even if
-//    the frame view whose layout was updated is not the main frame.
-// 2. Resize control areas, e.g. the small rect at the right bottom of
-//    div/textarea/iframe when CSS property "resize" is enabled.
-// 3. Plugin areas.
-void ScrollingCoordinator::UpdateNonFastScrollableRegions(LocalFrame* frame) {
-  auto* view_layer = frame->View()->GetLayoutView()->Layer();
-  if (auto* root = view_layer->Compositor()->PaintRootGraphicsLayer())
-    ForAllPaintingGraphicsLayers(*root, UpdateLayerNonFastScrollableRegions);
-}
-
-// Set the touch action rects on the cc layer from the touch action data stored
-// on the GraphicsLayer's paint chunks.
-static void UpdateLayerTouchActionRects(GraphicsLayer& layer) {
-  // CompositeAfterPaint does this update in PaintArtifactCompositor.
-  DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
-
-  DCHECK(layer.PaintsContentOrHitTest());
-
-  auto offset = layer.GetOffsetFromTransformNode();
-  gfx::Vector2dF layer_offset = gfx::Vector2dF(offset.X(), offset.Y());
-  PaintArtifactCompositor::UpdateTouchActionRects(
-      layer.CcLayer(), layer_offset, layer.GetPropertyTreeState().Unalias(),
-      PaintChunkSubset(layer.GetPaintController().GetPaintArtifactShared()));
-}
-
 void ScrollingCoordinator::WillDestroyScrollableArea(
     ScrollableArea* scrollable_area) {
   RemoveScrollbarLayer(scrollable_area, kHorizontalScrollbar);
@@ -370,52 +293,11 @@
       scrollable_area->GetCompositorAnimationTimeline());
 }
 
-void ScrollingCoordinator::UpdateTouchEventTargetRectsIfNeeded(
-    LocalFrame* frame) {
-  TRACE_EVENT0("input",
-               "ScrollingCoordinator::updateTouchEventTargetRectsIfNeeded");
-
-  DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
-
-  auto* view_layer = frame->View()->GetLayoutView()->Layer();
-  if (auto* root = view_layer->Compositor()->PaintRootGraphicsLayer())
-    ForAllPaintingGraphicsLayers(*root, UpdateLayerTouchActionRects);
-}
-
 void ScrollingCoordinator::Reset(LocalFrame* frame) {
   horizontal_scrollbars_.clear();
   vertical_scrollbars_.clear();
 }
 
-void ScrollingCoordinator::TouchEventTargetRectsDidChange(LocalFrame* frame) {
-  if (!frame)
-    return;
-
-  // If frame is not a local root, then the call to StaleInCompositingMode()
-  // below may unexpectedly fail.
-  DCHECK(frame->IsLocalRoot());
-  LocalFrameView* frame_view = frame->View();
-  if (!frame_view)
-    return;
-
-  // Wait until after layout to update.
-  // TODO(pdr): This check is wrong as we need to mark the rects as dirty
-  // regardless of whether the frame view needs layout. Remove this check.
-  if (frame_view->NeedsLayout())
-    return;
-
-  // FIXME: scheduleAnimation() is just a method of forcing the compositor to
-  // realize that it needs to commit here. We should expose a cleaner API for
-  // this.
-  auto* layout_view = frame->ContentLayoutObject();
-  if (layout_view && layout_view->Compositor() &&
-      layout_view->Compositor()->StaleInCompositingMode()) {
-    frame_view->ScheduleAnimation();
-  }
-
-  frame_view->GetScrollingContext()->SetTouchEventTargetRectsAreDirty(true);
-}
-
 void ScrollingCoordinator::AnimationHostInitialized(
     cc::AnimationHost& animation_host,
     LocalFrameView* view) {
@@ -456,17 +338,6 @@
   weak_ptr_factory_.InvalidateWeakPtrs();
 }
 
-bool ScrollingCoordinator::CoordinatesScrollingForFrameView(
-    LocalFrameView* frame_view) const {
-  DCHECK(IsMainThread());
-
-  // We currently only support composited mode.
-  auto* layout_view = frame_view->GetFrame().ContentLayoutObject();
-  if (!layout_view)
-    return false;
-  return layout_view->UsesCompositing();
-}
-
 bool ScrollingCoordinator::IsForMainFrame(
     ScrollableArea* scrollable_area) const {
   if (!IsA<LocalFrame>(page_->MainFrame()))
@@ -477,15 +348,4 @@
          page_->DeprecatedLocalMainFrame()->View()->LayoutViewport();
 }
 
-void ScrollingCoordinator::FrameViewRootLayerDidChange(
-    LocalFrameView* frame_view) {
-  DCHECK(IsMainThread());
-  DCHECK(page_);
-
-  if (!CoordinatesScrollingForFrameView(frame_view))
-    return;
-
-  NotifyGeometryChanged(frame_view);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
index 0590be5b..f8d8490d 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h
@@ -77,20 +77,6 @@
 
   void WillBeDestroyed();
 
-  // Return whether this scrolling coordinator handles scrolling for the given
-  // frame view.
-  bool CoordinatesScrollingForFrameView(LocalFrameView*) const;
-
-  // Called when any frame has done its layout or compositing has changed.
-  void NotifyGeometryChanged(LocalFrameView*);
-
-  // Update non-fast scrollable regions and touch event target rects.
-  // TODO(pdr): Refactor this out of ScrollingCoordinator.
-  void UpdateAfterPaint(LocalFrameView*);
-
-  // Should be called whenever the root layer for the given frame view changes.
-  void FrameViewRootLayerDidChange(LocalFrameView*);
-
   void WillDestroyScrollableArea(ScrollableArea*);
 
   // Updates scroll offset in cc scroll tree immediately. We don't wait for
@@ -113,11 +99,6 @@
   void ScrollableAreaScrollLayerDidChange(PaintLayerScrollableArea*);
   void ScrollableAreaScrollbarLayerDidChange(PaintLayerScrollableArea*,
                                              ScrollbarOrientation);
-  // LocalFrame* must be a local root if non-null.
-  void TouchEventTargetRectsDidChange(LocalFrame*);
-
-  void UpdateNonFastScrollableRegions(LocalFrame*);
-  void UpdateTouchEventTargetRectsIfNeeded(LocalFrame*);
 
   cc::AnimationHost* GetCompositorAnimationHost() { return animation_host_; }
   CompositorAnimationTimeline* GetCompositorAnimationTimeline() {
@@ -150,11 +131,6 @@
 
   Member<Page> page_;
 
-  // Dirty flags used to identify what really needs to be computed after
-  // compositing is updated.
-  bool touch_event_target_rects_are_dirty_;
-  bool should_scroll_on_main_thread_dirty_;
-
  private:
   void SetScrollbarLayer(ScrollableArea*,
                          ScrollbarOrientation,
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h
index 8a90cb03..a4b1b926f 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h
@@ -36,30 +36,10 @@
   }
   cc::AnimationHost* GetCompositorAnimationHost() { return animation_host_; }
 
-  // Non-fast scrollable regions need updating by ScrollingCoordinator.
-  bool ScrollGestureRegionIsDirty() const {
-    return scroll_gesture_region_is_dirty_;
-  }
-  // Touch event target rects need updating by ScrollingCoordinator.
-  bool TouchEventTargetRectsAreDirty() const {
-    return touch_event_target_rects_are_dirty_;
-  }
-
-  // Only ScrollingCoordinator should ever set |dirty| to |false|.
-  void SetScrollGestureRegionIsDirty(bool dirty) {
-    scroll_gesture_region_is_dirty_ = dirty;
-  }
-  void SetTouchEventTargetRectsAreDirty(bool dirty) {
-    touch_event_target_rects_are_dirty_ = dirty;
-  }
-
  private:
   std::unique_ptr<CompositorAnimationTimeline> animation_timeline_;
   cc::AnimationHost* animation_host_ = nullptr;
 
-  bool scroll_gesture_region_is_dirty_ = false;
-  bool touch_event_target_rects_are_dirty_ = false;
-
   DISALLOW_COPY_AND_ASSIGN(ScrollingCoordinatorContext);
 };
 
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
index d204f8d..e1d6107 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_test.cc
@@ -210,11 +210,6 @@
   LocalFrameView* frame_view = GetFrame()->View();
   Page* page = GetFrame()->GetPage();
   ASSERT_TRUE(page->GetScrollingCoordinator());
-  if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-    ASSERT_TRUE(
-        page->GetScrollingCoordinator()->CoordinatesScrollingForFrameView(
-            frame_view));
-  }
 
   // Fast scrolling should be enabled by default.
   const auto* outer_scroll_node =
@@ -1454,91 +1449,6 @@
             CurrentScrollOffset(inner_viewport_scroll_node));
 }
 
-TEST_P(ScrollingTest, UpdateUMAMetricUpdated) {
-  // The metrics are recorced in ScrollingCoordinator::UpdateAfterPaint() which
-  // is not called in CompositeAfterPaint.
-  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
-    return;
-
-  HistogramTester histogram_tester;
-  LoadHTML(R"HTML(
-    <div id='bg' style='background: blue;'></div>
-    <div id='scroller' style='overflow: scroll; width: 10px; height: 10px; background: blue'>
-      <div id='forcescroll' style='height: 1000px;'></div>
-    </div>
-  )HTML");
-
-  // The initial counts should be zero.
-  histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 0);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 0);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
-
-  // After an initial compositing update, we should have one scrolling update
-  // recorded as PreFCP.
-  GetWebView()->MainFrameViewWidget()->RecordStartOfFrameMetrics();
-  ForceFullCompositingUpdate();
-  GetWebView()->MainFrameViewWidget()->RecordEndOfFrameMetrics(
-      base::TimeTicks(), 0);
-  histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
-
-  // An update with no scrolling changes should not cause a scrolling update.
-  GetWebView()->MainFrameViewWidget()->RecordStartOfFrameMetrics();
-  ForceFullCompositingUpdate();
-  GetWebView()->MainFrameViewWidget()->RecordEndOfFrameMetrics(
-      base::TimeTicks(), 0);
-  histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 1);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 1);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 0);
-
-  // A change to background color does not need to cause a scrolling update but,
-  // because we record hit test data, we also cause a scrolling coordinator
-  // update when the background paints. Also render some text to get past FCP.
-  // Note that this frame is still considered pre-FCP.
-  auto* background = GetFrame()->GetDocument()->getElementById("bg");
-  background->removeAttribute(html_names::kStyleAttr);
-  background->setInnerHTML("Some Text");
-  GetWebView()->MainFrameViewWidget()->RecordStartOfFrameMetrics();
-  ForceFullCompositingUpdate();
-  GetWebView()->MainFrameViewWidget()->RecordEndOfFrameMetrics(
-      base::TimeTicks(), 0);
-  histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 2);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 2);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 0);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 1);
-
-  // Removing a scrollable area should cause a scrolling update.
-  auto* scroller = GetFrame()->GetDocument()->getElementById("scroller");
-  scroller->removeAttribute(html_names::kStyleAttr);
-  GetWebView()->MainFrameViewWidget()->RecordStartOfFrameMetrics();
-  ForceFullCompositingUpdate();
-  GetWebView()->MainFrameViewWidget()->RecordEndOfFrameMetrics(
-      base::TimeTicks(), 0);
-  histogram_tester.ExpectTotalCount("Blink.ScrollingCoordinator.UpdateTime", 3);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PreFCP", 2);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.PostFCP", 1);
-  histogram_tester.ExpectTotalCount(
-      "Blink.ScrollingCoordinator.UpdateTime.AggregatedPreFCP", 1);
-}
-
 TEST_P(ScrollingTest, NonCompositedNonFastScrollableRegion) {
   GetWebView()->GetPage()->GetSettings().SetPreferCompositingToLCDTextEnabled(
       false);
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 2af1e3c..27d4c5f 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
@@ -1258,9 +1258,6 @@
       if (scrolling_coordinator) {
         scrolling_coordinator->ScrollableAreaScrollLayerDidChange(
             scrollable_area);
-        const auto& object = GetLayoutObject();
-        if (auto* layout_view = DynamicTo<LayoutView>(object))
-          layout_view->GetFrameView()->ScrollableAreasDidChange();
       }
     }
   } else if (scrolling_contents_layer_) {
@@ -1269,9 +1266,6 @@
     if (scrolling_coordinator && scrollable_area) {
       scrolling_coordinator->ScrollableAreaScrollLayerDidChange(
           scrollable_area);
-      const auto& object = GetLayoutObject();
-      if (auto* layout_view = DynamicTo<LayoutView>(object))
-        layout_view->GetFrameView()->ScrollableAreasDidChange();
     }
   }
 
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
index 2c83054..4a2adfc 100644
--- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
+++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.cc
@@ -43,7 +43,6 @@
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
-#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
 #include "third_party/blink/renderer/core/paint/compositing/compositing_inputs_updater.h"
 #include "third_party/blink/renderer/core/paint/compositing/compositing_layer_assigner.h"
@@ -322,14 +321,8 @@
     CompositingLayerAssigner layer_assigner(this);
     layer_assigner.Assign(update_root, layers_needing_paint_invalidation);
 
-    if (layer_assigner.LayersChanged()) {
+    if (layer_assigner.LayersChanged())
       update_type = std::max(update_type, kCompositingUpdateRebuildTree);
-      if (ScrollingCoordinator* scrolling_coordinator =
-              GetScrollingCoordinator()) {
-        LocalFrameView* frame_view = layout_view_->GetFrameView();
-        scrolling_coordinator->NotifyGeometryChanged(frame_view);
-      }
-    }
   }
 
   GraphicsLayer* current_parent = nullptr;
@@ -417,16 +410,6 @@
       composited_layer_mapping_changed = true;
 
       RestartAnimationOnCompositor(layer->GetLayoutObject());
-
-      // At this time, the ScrollingCoordinator only supports the top-level
-      // frame.
-      if (layer->IsRootLayer() && layout_view_->GetFrame()->IsLocalRoot()) {
-        if (ScrollingCoordinator* scrolling_coordinator =
-                GetScrollingCoordinator()) {
-          scrolling_coordinator->FrameViewRootLayerDidChange(
-              layout_view_->GetFrameView());
-        }
-      }
       break;
     case kRemoveOwnCompositedLayerMapping:
     // PutInSquashingLayer means you might have to remove the composited layer
@@ -616,13 +599,6 @@
   }
 }
 
-ScrollingCoordinator* PaintLayerCompositor::GetScrollingCoordinator() const {
-  if (Page* page = GetPage())
-    return page->GetScrollingCoordinator();
-
-  return nullptr;
-}
-
 Page* PaintLayerCompositor::GetPage() const {
   return layout_view_->GetFrameView()->GetFrame().GetPage();
 }
diff --git a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h
index 7154b1a..2c76a1ba 100644
--- a/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h
+++ b/third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h
@@ -42,7 +42,6 @@
 class LayoutView;
 class Page;
 class Scrollbar;
-class ScrollingCoordinator;
 
 enum CompositingStateTransitionType {
   kNoCompositingStateChange,
@@ -170,8 +169,6 @@
 
   Page* GetPage() const;
 
-  ScrollingCoordinator* GetScrollingCoordinator() const;
-
   // Checks the given graphics layer against the compositor's horizontal and
   // vertical scrollbar graphics layers, returning the associated Scrollbar
   // instance if any, else nullptr.
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 89fee6b9..2515fb6 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -126,7 +126,6 @@
 #include "third_party/blink/renderer/core/page/print_context.h"
 #include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.h"
 #include "third_party/blink/renderer/core/page/scrolling/scroll_state.h"
-#include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_context.h"
 #include "third_party/blink/renderer/core/page/spatial_navigation_controller.h"
 #include "third_party/blink/renderer/core/page/validation_message_client.h"
 #include "third_party/blink/renderer/core/page/viewport_description.h"
@@ -2197,16 +2196,6 @@
   return document->GetFrame()->View()->MainThreadScrollingReasonsAsText();
 }
 
-void Internals::markGestureScrollRegionDirty(
-    Document* document,
-    ExceptionState& exception_state) const {
-  FrameView* frame_view = document->View();
-  if (!frame_view || !frame_view->IsLocalFrameView())
-    return;
-  LocalFrameView* lfv = static_cast<LocalFrameView*>(frame_view);
-  lfv->GetScrollingContext()->SetScrollGestureRegionIsDirty(true);
-}
-
 DOMRectList* Internals::nonFastScrollableRects(
     Document* document,
     ExceptionState& exception_state) const {
diff --git a/third_party/blink/renderer/core/testing/internals.h b/third_party/blink/renderer/core/testing/internals.h
index 4dc7737..9faef56 100644
--- a/third_party/blink/renderer/core/testing/internals.h
+++ b/third_party/blink/renderer/core/testing/internals.h
@@ -358,7 +358,6 @@
 
   String scrollingStateTreeAsText(Document*) const;
   String mainThreadScrollingReasons(Document*, ExceptionState&) const;
-  void markGestureScrollRegionDirty(Document*, ExceptionState&) const;
   DOMRectList* nonFastScrollableRects(Document*, ExceptionState&) const;
 
   void evictAllResources() const;
diff --git a/third_party/blink/renderer/core/testing/internals.idl b/third_party/blink/renderer/core/testing/internals.idl
index fd393d1b..edf43e7 100644
--- a/third_party/blink/renderer/core/testing/internals.idl
+++ b/third_party/blink/renderer/core/testing/internals.idl
@@ -206,7 +206,6 @@
     DOMString scrollingStateTreeAsText(Document document);
     [RaisesException] DOMString mainThreadScrollingReasons(Document document);
     [RaisesException] DOMRectList nonFastScrollableRects(Document document);
-    [RaisesException] void markGestureScrollRegionDirty(Document document);
 
     void evictAllResources();
 
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture.cc b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
index 6dca159..70eff50 100644
--- a/third_party/blink/renderer/modules/imagecapture/image_capture.cc
+++ b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
@@ -771,8 +771,7 @@
     media::mojom::blink::PhotoStatePtr photo_state) {
   UpdateMediaTrackCapabilities(base::DoNothing(), std::move(photo_state));
 
-  MediaStreamVideoTrack* video_track = MediaStreamVideoTrack::GetVideoTrack(
-      WebMediaStreamTrack(stream_track_->Component()));
+  auto* video_track = MediaStreamVideoTrack::From(stream_track_->Component());
   DCHECK(video_track);
 
   base::Optional<double> pan = video_track->pan();
diff --git a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
index 30a8c9d..38b6d77 100644
--- a/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/apply_constraints_processor.cc
@@ -286,8 +286,8 @@
 blink::MediaStreamVideoTrack*
 ApplyConstraintsProcessor::GetCurrentVideoTrack() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  MediaStreamVideoTrack* track = MediaStreamVideoTrack::GetVideoTrack(
-      WebMediaStreamTrack(current_request_->Track()));
+  MediaStreamVideoTrack* track =
+      MediaStreamVideoTrack::From(current_request_->Track());
   DCHECK(track);
   return track;
 }
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
index e51c6fa..4a267ac 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track.cc
@@ -181,8 +181,7 @@
   MediaStreamVideoSource* native_source =
       MediaStreamVideoSource::GetVideoSource(source);
   DCHECK(native_source);
-  MediaStreamVideoTrack* original_track =
-      MediaStreamVideoTrack::GetVideoTrack(WebMediaStreamTrack(original));
+  MediaStreamVideoTrack* original_track = MediaStreamVideoTrack::From(original);
   DCHECK(original_track);
   clone->SetPlatformTrack(std::make_unique<MediaStreamVideoTrack>(
       native_source, original_track->adapter_settings(),
@@ -250,7 +249,7 @@
   component_->SetMuted(ready_state_ == MediaStreamSource::kReadyStateMuted);
 
   MediaStreamVideoTrack* const video_track =
-      MediaStreamVideoTrack::GetVideoTrack(WebMediaStreamTrack(Component()));
+      MediaStreamVideoTrack::From(Component());
   if (video_track && component_->Source() &&
       component_->Source()->GetType() == MediaStreamSource::kTypeVideo) {
     bool pan_tilt_zoom_allowed =
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_sink.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_sink.cc
index 3eb247d..c8ed9f8 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_sink.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_sink.cc
@@ -30,8 +30,7 @@
     const EncodedVideoFrameCB& callback) {
   DCHECK(connected_encoded_track_.IsNull());
   connected_encoded_track_ = track;
-  MediaStreamVideoTrack* const video_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+  MediaStreamVideoTrack* const video_track = MediaStreamVideoTrack::From(track);
   DCHECK(video_track);
   video_track->AddEncodedSink(this, callback);
 }
@@ -43,7 +42,7 @@
 
 void MediaStreamVideoSink::DisconnectEncodedFromTrack() {
   MediaStreamVideoTrack* const video_track =
-      MediaStreamVideoTrack::GetVideoTrack(connected_encoded_track_);
+      MediaStreamVideoTrack::From(connected_encoded_track_);
   if (video_track) {
     video_track->RemoveEncodedSink(this);
   }
@@ -55,8 +54,7 @@
   if (connected_track_.IsNull())
     return;
 
-  if (auto* const video_track =
-          MediaStreamVideoTrack::GetVideoTrack(connected_track_))
+  if (auto* const video_track = MediaStreamVideoTrack::From(connected_track_))
     video_track->OnFrameDropped(reason);
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_source_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_source_test.cc
index e63e9ea..f47919e 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_source_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_source_test.cc
@@ -449,8 +449,7 @@
   EXPECT_EQ(track.Source().GetReadyState(),
             WebMediaStreamSource::kReadyStateLive);
 
-  MediaStreamVideoTrack* native_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+  MediaStreamVideoTrack* native_track = MediaStreamVideoTrack::From(track);
   MediaStreamTrackPlatform::Settings settings;
   native_track->GetSettings(settings);
   EXPECT_EQ(settings.width, 640);
@@ -480,8 +479,7 @@
   EXPECT_EQ(track.Source().GetReadyState(),
             WebMediaStreamSource::kReadyStateLive);
 
-  MediaStreamVideoTrack* native_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+  MediaStreamVideoTrack* native_track = MediaStreamVideoTrack::From(track);
   MediaStreamTrackPlatform::Settings settings;
   native_track->GetSettings(settings);
   EXPECT_EQ(settings.width, 640);
@@ -683,15 +681,13 @@
 
   // Simulate assigning |track1| to a sink, then removing it from the sink, and
   // then stopping it.
-  MediaStreamVideoTrack* track1 =
-      MediaStreamVideoTrack::GetVideoTrack(web_track1);
+  MediaStreamVideoTrack* track1 = MediaStreamVideoTrack::From(web_track1);
   mock_source()->UpdateHasConsumers(track1, true);
   mock_source()->UpdateHasConsumers(track1, false);
   track1->Stop();
 
   // Simulate assigning |track2| to a sink. The source should not be suspended.
-  MediaStreamVideoTrack* track2 =
-      MediaStreamVideoTrack::GetVideoTrack(web_track2);
+  MediaStreamVideoTrack* track2 = MediaStreamVideoTrack::From(web_track2);
   mock_source()->UpdateHasConsumers(track2, true);
   EXPECT_FALSE(mock_source()->is_suspended());
 }
@@ -702,8 +698,7 @@
   EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
   EXPECT_EQ(0, NumberOfFailedConstraintsCallbacks());
 
-  MediaStreamVideoTrack* track1 =
-      MediaStreamVideoTrack::GetVideoTrack(web_track1);
+  MediaStreamVideoTrack* track1 = MediaStreamVideoTrack::From(web_track1);
   EXPECT_CALL(*this, MockNotification());
   // This is equivalent to track.stop() in JavaScript.
   track1->StopAndNotify(WTF::Bind(&MediaStreamVideoSourceTest::MockNotification,
@@ -779,8 +774,8 @@
   InSequence s;
   EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
   WebMediaStreamTrack track = CreateTrack();
-  mock_source()->UpdateCapturingLinkSecure(
-      MediaStreamVideoTrack::GetVideoTrack(track), true);
+  mock_source()->UpdateCapturingLinkSecure(MediaStreamVideoTrack::From(track),
+                                           true);
 
   EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
   MockMediaStreamVideoSink sink;
@@ -788,8 +783,8 @@
   EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(true));
   sink.DisconnectEncodedFromTrack();
   EXPECT_CALL(*mock_source(), OnCapturingLinkSecured(false));
-  mock_source()->UpdateCapturingLinkSecure(
-      MediaStreamVideoTrack::GetVideoTrack(track), false);
+  mock_source()->UpdateCapturingLinkSecure(MediaStreamVideoTrack::From(track),
+                                           false);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
index 73d097c5..fa36dc3 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.cc
@@ -430,16 +430,14 @@
 }
 
 // static
-MediaStreamVideoTrack* MediaStreamVideoTrack::GetVideoTrack(
-    const WebMediaStreamTrack& track) {
-  if (track.IsNull())
+MediaStreamVideoTrack* MediaStreamVideoTrack::From(
+    const MediaStreamComponent* component) {
+  if (!component ||
+      component->Source()->GetType() != MediaStreamSource::kTypeVideo) {
     return nullptr;
+  }
 
-  MediaStreamComponent& component = *track;
-  if (component.Source()->GetType() != MediaStreamSource::kTypeVideo)
-    return nullptr;
-
-  return static_cast<MediaStreamVideoTrack*>(component.GetPlatformTrack());
+  return static_cast<MediaStreamVideoTrack*>(component->GetPlatformTrack());
 }
 
 MediaStreamVideoTrack::MediaStreamVideoTrack(
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.h b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.h
index ceed5e4..6475e37 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track.h
@@ -55,7 +55,7 @@
       MediaStreamVideoSource::ConstraintsOnceCallback callback,
       bool enabled);
 
-  static MediaStreamVideoTrack* GetVideoTrack(const WebMediaStreamTrack& track);
+  static MediaStreamVideoTrack* From(const MediaStreamComponent* track);
 
   // Constructors for video tracks.
   MediaStreamVideoTrack(
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_test.cc
index b8b7820..d1e9e2e 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_track_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_track_test.cc
@@ -238,8 +238,7 @@
   WebMediaStreamTrack track = CreateTrack();
   sink.ConnectToTrack(track);
 
-  MediaStreamVideoTrack* video_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+  MediaStreamVideoTrack* video_track = MediaStreamVideoTrack::From(track);
 
   DeliverDefaultSizeVideoFrameAndWaitForRenderer(&sink);
   EXPECT_EQ(1, sink.number_of_frames());
@@ -264,7 +263,7 @@
   InitializeSource();
   WebMediaStreamTrack track = CreateTrack();
   MockMediaStreamVideoSink sink;
-  auto* video_track = MediaStreamVideoTrack::GetVideoTrack(track);
+  auto* video_track = MediaStreamVideoTrack::From(track);
   video_track->StopAndNotify(base::BindOnce([] {}));
   sink.ConnectToTrack(track);
   sink.ConnectEncodedToTrack(track);
@@ -304,7 +303,7 @@
   EXPECT_EQ(WebMediaStreamSource::kReadyStateLive, sink2.state());
 
   MediaStreamVideoTrack* const native_track1 =
-      MediaStreamVideoTrack::GetVideoTrack(track1);
+      MediaStreamVideoTrack::From(track1);
   native_track1->Stop();
   EXPECT_EQ(WebMediaStreamSource::kReadyStateEnded, sink1.state());
   EXPECT_EQ(MediaStreamSource::kReadyStateLive,
@@ -312,7 +311,7 @@
   sink1.DisconnectFromTrack();
 
   MediaStreamVideoTrack* const native_track2 =
-      MediaStreamVideoTrack::GetVideoTrack(track2);
+      MediaStreamVideoTrack::From(track2);
   native_track2->Stop();
   EXPECT_EQ(WebMediaStreamSource::kReadyStateEnded, sink2.state());
   EXPECT_EQ(MediaStreamSource::kReadyStateEnded,
@@ -342,7 +341,7 @@
   InitializeSource();
   WebMediaStreamTrack track = CreateTrack();
   MediaStreamVideoTrack* const native_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+      MediaStreamVideoTrack::From(track);
   MediaStreamTrackPlatform::Settings settings;
   native_track->GetSettings(settings);
   // These values come straight from the mock video track implementation.
@@ -362,7 +361,7 @@
       kAdjustedFrameRate);
   WebMediaStreamTrack track = CreateTrackWithSettings(adapter_settings);
   MediaStreamVideoTrack* const native_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+      MediaStreamVideoTrack::From(track);
   MediaStreamTrackPlatform::Settings settings;
   native_track->GetSettings(settings);
   EXPECT_EQ(kAdjustedWidth, settings.width);
@@ -375,7 +374,7 @@
   InitializeSource();
   WebMediaStreamTrack track = CreateTrack();
   MediaStreamVideoTrack* const native_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+      MediaStreamVideoTrack::From(track);
   native_track->Stop();
   MediaStreamTrackPlatform::Settings settings;
   native_track->GetSettings(settings);
@@ -392,7 +391,7 @@
   WebMediaStreamTrack track = CreateTrack();
   sink.ConnectToTrack(track);
   MediaStreamVideoTrack* const native_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+      MediaStreamVideoTrack::From(track);
   MediaStreamTrackPlatform::Settings settings;
 
   auto frame1 = media::VideoFrame::CreateBlackFrame(gfx::Size(600, 400));
@@ -415,7 +414,7 @@
   MockMediaStreamVideoSink sink;
   WebMediaStreamTrack track = CreateTrack();
   sink.ConnectToTrack(track);
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetContentHint(GetParam());
+  MediaStreamVideoTrack::From(track)->SetContentHint(GetParam());
   EXPECT_EQ(sink.content_hint(), GetParam());
   sink.DisconnectFromTrack();
 }
@@ -500,7 +499,7 @@
   DeliverEncodedVideoFrameAndWait(key_frame, &sink);
 
   // Key frame when disabled -> shouldn't get dispatched
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetEnabled(false);
+  MediaStreamVideoTrack::From(track)->SetEnabled(false);
   EXPECT_FALSE(sink.enabled());
   {
     EXPECT_CALL(sink, OnEncodedVideoFrame).Times(0);
@@ -511,7 +510,7 @@
   // Delta frame when disabled -> shouldn't get dispatched until key frame
   // appears.
   EXPECT_CALL(*mock_source(), OnRequestRefreshFrame);
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetEnabled(true);
+  MediaStreamVideoTrack::From(track)->SetEnabled(true);
   EXPECT_TRUE(sink.enabled());
   {
     EXPECT_CALL(sink, OnEncodedVideoFrame).Times(0);
@@ -532,7 +531,7 @@
   MockMediaStreamVideoSink sink;
   WebMediaStreamTrack track = CreateTrack();
   sink.ConnectEncodedToTrack(track);
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetContentHint(GetParam());
+  MediaStreamVideoTrack::From(track)->SetContentHint(GetParam());
   EXPECT_EQ(sink.content_hint(), GetParam());
   sink.DisconnectEncodedFromTrack();
 }
@@ -575,7 +574,7 @@
   MockMediaStreamVideoSink sink;
   WebMediaStreamTrack track =
       CreateTrackWithSettings(VideoTrackAdapterSettings());
-  auto* video_track = MediaStreamVideoTrack::GetVideoTrack(track);
+  auto* video_track = MediaStreamVideoTrack::From(track);
   video_track->SetMinimumFrameRate(kMinFrameRate);
   video_track->SetIsScreencastForTesting(true);
 
@@ -596,7 +595,7 @@
   WebMediaStreamTrack track =
       CreateTrackWithSettings(VideoTrackAdapterSettings());
 
-  auto* video_track = MediaStreamVideoTrack::GetVideoTrack(track);
+  auto* video_track = MediaStreamVideoTrack::From(track);
   video_track->SetMinimumFrameRate(kMinFrameRate);
   // Refresh frame timer will not be run when |is_screencast_| is false.
   video_track->SetIsScreencastForTesting(false);
@@ -617,7 +616,7 @@
 
   WebMediaStreamTrack track =
       CreateTrackWithSettings(VideoTrackAdapterSettings());
-  auto* video_track = MediaStreamVideoTrack::GetVideoTrack(track);
+  auto* video_track = MediaStreamVideoTrack::From(track);
   video_track->SetIsScreencastForTesting(true);
 
   sink.ConnectToTrack(track);
@@ -634,7 +633,7 @@
   WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
       mock_source(), WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
       true);
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetIsScreencastForTesting(true);
+  MediaStreamVideoTrack::From(track)->SetIsScreencastForTesting(true);
 
   Persistent<MediaStreamComponent> media_stream_component = *track;
   blink::MediaStreamVideoWebRtcSink webrtc_sink(
@@ -655,7 +654,7 @@
   WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
       mock_source(), WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
       true);
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetIsScreencastForTesting(true);
+  MediaStreamVideoTrack::From(track)->SetIsScreencastForTesting(true);
 
   // First sink.
   MockMediaStreamVideoSink sink;
@@ -682,7 +681,7 @@
   WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
       mock_source(), WebPlatformMediaStreamSource::ConstraintsOnceCallback(),
       true);
-  MediaStreamVideoTrack::GetVideoTrack(track)->SetIsScreencastForTesting(true);
+  MediaStreamVideoTrack::From(track)->SetIsScreencastForTesting(true);
 
   // First sink.
   MockMediaStreamVideoSink sink;
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
index f53db188..af4af43b 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_client_test.cc
@@ -129,8 +129,7 @@
                    expected_source_frame_rate);
   EXPECT_EQ(component->Source()->GetReadyState(),
             MediaStreamSource::kReadyStateLive);
-  MediaStreamVideoTrack* track =
-      MediaStreamVideoTrack::GetVideoTrack(WebMediaStreamTrack(component));
+  MediaStreamVideoTrack* track = MediaStreamVideoTrack::From(component);
   EXPECT_EQ(track->source(), source);
 
   MediaStreamTrackPlatform::Settings settings;
@@ -1182,8 +1181,7 @@
 TEST_F(UserMediaClientTest, ApplyConstraintsVideoDeviceSingleTrack) {
   EXPECT_CALL(mock_dispatcher_host_, OnStreamStarted(_));
   MediaStreamComponent* component = RequestLocalVideoTrack();
-  MediaStreamVideoTrack* track =
-      MediaStreamVideoTrack::GetVideoTrack(WebMediaStreamTrack(component));
+  MediaStreamVideoTrack* track = MediaStreamVideoTrack::From(component);
   blink::MediaStreamVideoSource* source = track->source();
   CheckVideoSource(source, 0, 0, 0.0);
 
diff --git a/third_party/blink/renderer/modules/mediastream/web_media_stream_utils.cc b/third_party/blink/renderer/modules/mediastream/web_media_stream_utils.cc
index 648eab54..c88f2bba 100644
--- a/third_party/blink/renderer/modules/mediastream/web_media_stream_utils.cc
+++ b/third_party/blink/renderer/modules/mediastream/web_media_stream_utils.cc
@@ -21,16 +21,14 @@
                                WebMediaStreamSink* sink,
                                const VideoCaptureDeliverFrameCB& callback,
                                bool is_sink_secure) {
-  MediaStreamVideoTrack* const video_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+  MediaStreamVideoTrack* const video_track = MediaStreamVideoTrack::From(track);
   DCHECK(video_track);
   video_track->AddSink(sink, callback, is_sink_secure);
 }
 
 void RemoveSinkFromMediaStreamTrack(const WebMediaStreamTrack& track,
                                     WebMediaStreamSink* sink) {
-  MediaStreamVideoTrack* const video_track =
-      MediaStreamVideoTrack::GetVideoTrack(track);
+  MediaStreamVideoTrack* const video_track = MediaStreamVideoTrack::From(track);
   if (video_track)
     video_track->RemoveSink(sink);
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc
index 182e713..ed13308 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_video_webrtc_sink.cc
@@ -161,8 +161,7 @@
     MediaStreamComponent* component,
     PeerConnectionDependencyFactory* factory,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
-  MediaStreamVideoTrack* video_track =
-      MediaStreamVideoTrack::GetVideoTrack(WebMediaStreamTrack(component));
+  MediaStreamVideoTrack* video_track = MediaStreamVideoTrack::From(component);
   DCHECK(video_track);
 
   absl::optional<bool> needs_denoising =
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc
index eae548e..64ec47e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler_test.cc
@@ -395,11 +395,10 @@
 
   void StopAllTracks(MediaStreamDescriptor* descriptor) {
     for (auto component : descriptor->AudioComponents())
-      MediaStreamAudioTrack::From(WebMediaStreamTrack(component.Get()))->Stop();
+      MediaStreamAudioTrack::From(component.Get())->Stop();
 
     for (auto component : descriptor->VideoComponents()) {
-      MediaStreamVideoTrack::GetVideoTrack(WebMediaStreamTrack(component.Get()))
-          ->Stop();
+      MediaStreamVideoTrack::From(component.Get())->Stop();
     }
   }
 
diff --git a/third_party/blink/renderer/modules/shapedetection/shape_detector.cc b/third_party/blink/renderer/modules/shapedetection/shape_detector.cc
index cb8f225ea..28715ca 100644
--- a/third_party/blink/renderer/modules/shapedetection/shape_detector.cc
+++ b/third_party/blink/renderer/modules/shapedetection/shape_detector.cc
@@ -120,24 +120,17 @@
   }
 
   SkBitmap sk_bitmap;
-  if (!sk_bitmap.tryAllocPixels(
-          SkImageInfo::Make(image_data->width(), image_data->height(),
-                            kN32_SkColorType, kOpaque_SkAlphaType),
-          image_data->width() * 4 /* bytes per pixel */)) {
+  SkImageInfo sk_image_info = image_data->GetSkImageInfo();
+  if (!sk_bitmap.tryAllocPixels(sk_image_info, sk_image_info.minRowBytes())) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
         DOMExceptionCode::kInvalidStateError,
         "Failed to allocate pixels for current frame."));
     return promise;
   }
 
-  base::CheckedNumeric<int> allocation_size = image_data->Size().Area() * 4;
-  CHECK_EQ(allocation_size.ValueOrDefault(0), sk_bitmap.computeByteSize());
-
-  // TODO(crbug.com/1115317): Should be compatible with uint_8, float16 and
-  // float32.
-  memcpy(sk_bitmap.getPixels(),
-         image_data->data().GetAsUint8ClampedArray()->Data(),
-         sk_bitmap.computeByteSize());
+  size_t byte_size = sk_bitmap.computeByteSize();
+  CHECK_EQ(byte_size, image_data->BufferBase()->ByteLengthAsSizeT());
+  memcpy(sk_bitmap.getPixels(), image_data->BufferBase()->Data(), byte_size);
 
   return DoDetect(resolver, std::move(sk_bitmap));
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
index 74bfb7c..9bdfead2 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -131,8 +131,6 @@
       layer_state.Effect().HasBackdropEffect() ||
       !layer_state.Effect().Filter().IsEmpty());
 
-  PaintChunksToCcLayer::UpdateBackgroundColor(*cc_picture_layer_, paint_chunks);
-
   return cc_picture_layer_;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 85de5399..09313a5 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -118,13 +118,6 @@
   return layers_as_json.Finalize();
 }
 
-static void UpdateLayerProperties(const GraphicsLayer& graphics_layer) {
-  PaintChunksToCcLayer::UpdateBackgroundColor(
-      graphics_layer.CcLayer(),
-      PaintChunkSubset(
-          graphics_layer.GetPaintController().GetPaintArtifactShared()));
-}
-
 scoped_refptr<cc::Layer> PaintArtifactCompositor::WrappedCcLayerForPendingLayer(
     const PendingLayer& pending_layer) {
   if (pending_layer.compositing_type != PendingLayer::kForeignLayer &&
@@ -139,8 +132,6 @@
     layer = &pending_layer.graphics_layer->CcLayer();
     layer_offset =
         FloatPoint(pending_layer.graphics_layer->GetOffsetFromTransformNode());
-    if (pending_layer.graphics_layer->Repainted())
-      UpdateLayerProperties(*pending_layer.graphics_layer);
   } else {
     DCHECK_EQ(pending_layer.compositing_type, PendingLayer::kForeignLayer);
     // UpdateTouchActionRects() depends on the layer's offset, but when the
@@ -314,83 +305,6 @@
   return cc_layer;
 }
 
-void PaintArtifactCompositor::UpdateTouchActionRects(
-    cc::Layer& layer,
-    const gfx::Vector2dF& layer_offset,
-    const PropertyTreeState& layer_state,
-    const PaintChunkSubset& paint_chunks) {
-  cc::TouchActionRegion touch_action_in_layer_space;
-  for (const auto& chunk : paint_chunks) {
-    const auto* hit_test_data = chunk.hit_test_data.get();
-    if (!hit_test_data || hit_test_data->touch_action_rects.IsEmpty())
-      continue;
-
-    const auto& chunk_state = chunk.properties.GetPropertyTreeState().Unalias();
-    for (const auto& touch_action_rect : hit_test_data->touch_action_rects) {
-      auto rect = FloatClipRect(FloatRect(touch_action_rect.rect));
-      if (!GeometryMapper::LocalToAncestorVisualRect(chunk_state, layer_state,
-                                                     rect)) {
-        continue;
-      }
-      rect.MoveBy(FloatPoint(-layer_offset.x(), -layer_offset.y()));
-      touch_action_in_layer_space.Union(
-          touch_action_rect.allowed_touch_action,
-          (gfx::Rect)EnclosingIntRect(rect.Rect()));
-    }
-  }
-  layer.SetTouchActionRegion(std::move(touch_action_in_layer_space));
-}
-
-void PaintArtifactCompositor::UpdateNonFastScrollableRegions(
-    cc::Layer& layer,
-    const gfx::Vector2dF& layer_offset,
-    const PropertyTreeState& layer_state,
-    const PaintChunkSubset& paint_chunks,
-    PropertyTreeManager* property_tree_manager) {
-  cc::Region non_fast_scrollable_regions_in_layer_space;
-  for (const auto& chunk : paint_chunks) {
-    // Add any non-fast scrollable hit test data from the paint chunk.
-    const auto* hit_test_data = chunk.hit_test_data.get();
-    if (!hit_test_data || hit_test_data->scroll_hit_test_rect.IsEmpty())
-      continue;
-
-    // Skip the scroll hit test rect if it is for scrolling this cc::Layer.
-    // This is only needed for CompositeAfterPaint because
-    // pre-CompositeAfterPaint does not paint scroll hit test data for
-    // composited scrollers.
-    if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-      if (const auto* scroll_translation = hit_test_data->scroll_translation) {
-        const auto& scroll_node = *scroll_translation->ScrollNode();
-        auto scroll_element_id = scroll_node.GetCompositorElementId();
-        if (layer.element_id() == scroll_element_id)
-          continue;
-        // Ensure the cc scroll node to prepare for possible descendant nodes
-        // referenced by later composited layers. This can't be done by ensuring
-        // parent transform node in EnsureCompositorTransformNode() if the
-        // transform tree and the scroll tree have different topologies.
-        // This is not necessary with ScrollUnification which ensures the
-        // complete scroll tree.
-        if (!RuntimeEnabledFeatures::ScrollUnificationEnabled()) {
-          DCHECK(property_tree_manager);
-          property_tree_manager->EnsureCompositorScrollNode(
-              *scroll_translation);
-        }
-      }
-    }
-
-    FloatClipRect rect(FloatRect(chunk.hit_test_data->scroll_hit_test_rect));
-    const auto& chunk_state = chunk.properties.GetPropertyTreeState().Unalias();
-    if (!GeometryMapper::LocalToAncestorVisualRect(chunk_state, layer_state,
-                                                   rect)) {
-      continue;
-    }
-    rect.MoveBy(FloatPoint(-layer_offset.x(), -layer_offset.y()));
-    non_fast_scrollable_regions_in_layer_space.Union(
-        EnclosingIntRect(rect.Rect()));
-  }
-  layer.SetNonFastScrollableRegion(non_fast_scrollable_regions_in_layer_space);
-}
-
 bool PaintArtifactCompositor::HasComposited(
     CompositorElementId element_id) const {
   // |Update| creates PropertyTrees on the LayerTreeHost to represent the
@@ -1256,15 +1170,7 @@
         pending_layer, new_content_layer_clients, new_scroll_hit_test_layers,
         new_scrollbar_layers);
 
-    // In Pre-CompositeAfterPaint, touch action rects and non-fast scrollable
-    // regions are updated through ScrollingCoordinator.
-    if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
-      UpdateTouchActionRects(*layer, layer->offset_to_transform_parent(),
-                             property_state, pending_layer.chunks);
-      UpdateNonFastScrollableRegions(
-          *layer, layer->offset_to_transform_parent(), property_state,
-          pending_layer.chunks, &property_tree_manager);
-    }
+    UpdateLayerProperties(*layer, pending_layer, &property_tree_manager);
 
     layer->SetLayerTreeHost(root_layer_->layer_tree_host());
 
@@ -1347,6 +1253,24 @@
                   .Utf8();
 }
 
+void PaintArtifactCompositor::UpdateLayerProperties(
+    cc::Layer& layer,
+    const PendingLayer& pending_layer,
+    PropertyTreeManager* property_tree_manager) {
+  // Properties of foreign layers are managed by their owners.
+  if (pending_layer.compositing_type == PendingLayer::kForeignLayer)
+    return;
+
+  PaintChunkSubset chunks = pending_layer.chunks;
+  if (pending_layer.graphics_layer &&
+      pending_layer.graphics_layer->PaintsContentOrHitTest()) {
+    chunks = PaintChunkSubset(pending_layer.graphics_layer->GetPaintController()
+                                  .GetPaintArtifactShared());
+  }
+  PaintChunksToCcLayer::UpdateLayerProperties(
+      layer, pending_layer.property_tree_state, chunks, property_tree_manager);
+}
+
 void PaintArtifactCompositor::UpdateRepaintedLayerProperties() const {
   // TODO(paint-dev): Implement repaint-only update for CompositeAfterPaint.
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
@@ -1356,8 +1280,10 @@
     if (pending_layer.compositing_type != PendingLayer::kPreCompositedLayer)
       continue;
     DCHECK(pending_layer.graphics_layer);
-    if (pending_layer.graphics_layer->Repainted())
-      UpdateLayerProperties(*pending_layer.graphics_layer);
+    if (pending_layer.graphics_layer->Repainted()) {
+      UpdateLayerProperties(pending_layer.graphics_layer->CcLayer(),
+                            pending_layer);
+    }
   }
 
   UpdateDebugInfo();
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index 5f08ff30..8611f85 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -31,16 +31,11 @@
 class ScrollbarLayerBase;
 }
 
-namespace gfx {
-class Vector2dF;
-}
-
 namespace blink {
 
 class ContentLayerClientImpl;
 class GraphicsLayer;
 class JSONObject;
-class PropertyTreeManager;
 class SynthesizedClip;
 
 using CompositorScrollCallbacks = cc::ScrollCallbacks;
@@ -199,22 +194,6 @@
     return content_layer_clients_;
   }
 
-  // Update the cc::Layer's touch action region from the touch action rects of
-  // the paint chunks.
-  static void UpdateTouchActionRects(cc::Layer&,
-                                     const gfx::Vector2dF& layer_offset,
-                                     const PropertyTreeState& layer_state,
-                                     const PaintChunkSubset& paint_chunks);
-
-  // Update the cc::Layer's non-fast scrollable region from the non-fast regions
-  // in the paint chunks.
-  static void UpdateNonFastScrollableRegions(
-      cc::Layer&,
-      const gfx::Vector2dF& layer_offset,
-      const PropertyTreeState& layer_state,
-      const PaintChunkSubset& paint_chunks,
-      PropertyTreeManager* = nullptr);
-
   void SetNeedsUpdate() { needs_update_ = true; }
   bool NeedsUpdate() const { return needs_update_; }
   void ClearNeedsUpdateForTesting() { needs_update_ = false; }
@@ -298,8 +277,9 @@
     CompositingType compositing_type;
   };
 
-  void UpdateRepaintedLayerProperties(
-      const HashSet<const GraphicsLayer*>& repainted_layers) const;
+  static void UpdateLayerProperties(cc::Layer&,
+                                    const PendingLayer&,
+                                    PropertyTreeManager* = nullptr);
 
   void DecompositeTransforms();
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index 1dd13da3..20eded2f 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -10,6 +10,7 @@
 #include "cc/paint/paint_op_buffer.h"
 #include "cc/paint/render_surface_filters.h"
 #include "third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h"
+#include "third_party/blink/renderer/platform/graphics/compositing/property_tree_manager.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_list.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
@@ -809,9 +810,8 @@
 //   - The same color is used for the layer's safe opaque background color, but
 //     without the size requirement, as safe opaque background color should
 //     always get a value if possible.
-void PaintChunksToCcLayer::UpdateBackgroundColor(
-    cc::Layer& layer,
-    const PaintChunkSubset& paint_chunks) {
+static void UpdateBackgroundColor(cc::Layer& layer,
+                                  const PaintChunkSubset& paint_chunks) {
   SkColor color = SK_ColorTRANSPARENT;
   uint64_t area = 0;
   for (const auto& chunk : paint_chunks) {
@@ -831,4 +831,98 @@
   layer.SetBackgroundColor(color);
 }
 
+static void UpdateTouchActionRegion(
+    const HitTestData& hit_test_data,
+    const PropertyTreeState& layer_state,
+    const PropertyTreeState& chunk_state,
+    const FloatPoint& layer_offset,
+    cc::TouchActionRegion& touch_action_region) {
+  for (const auto& touch_action_rect : hit_test_data.touch_action_rects) {
+    auto rect = FloatClipRect(FloatRect(touch_action_rect.rect));
+    if (!GeometryMapper::LocalToAncestorVisualRect(chunk_state, layer_state,
+                                                   rect)) {
+      continue;
+    }
+    rect.MoveBy(-layer_offset);
+    touch_action_region.Union(touch_action_rect.allowed_touch_action,
+                              gfx::Rect(EnclosingIntRect(rect.Rect())));
+  }
+}
+
+static void UpdateNonFastScrollableRegion(
+    cc::Layer& layer,
+    const HitTestData& hit_test_data,
+    const PropertyTreeState& layer_state,
+    const PropertyTreeState& chunk_state,
+    const FloatPoint& layer_offset,
+    PropertyTreeManager* property_tree_manager,
+    cc::Region& non_fast_scrollable_region) {
+  if (hit_test_data.scroll_hit_test_rect.IsEmpty())
+    return;
+
+  // Skip the scroll hit test rect if it is for scrolling this cc::Layer.
+  // This is only needed for CompositeAfterPaint because
+  // pre-CompositeAfterPaint does not paint scroll hit test data for
+  // composited scrollers.
+  if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
+    if (const auto* scroll_translation = hit_test_data.scroll_translation) {
+      const auto& scroll_node = *scroll_translation->ScrollNode();
+      auto scroll_element_id = scroll_node.GetCompositorElementId();
+      if (layer.element_id() == scroll_element_id)
+        return;
+      // Ensure the cc scroll node to prepare for possible descendant nodes
+      // referenced by later composited layers. This can't be done by ensuring
+      // parent transform node in EnsureCompositorTransformNode() if the
+      // transform tree and the scroll tree have different topologies.
+      // This is not necessary with ScrollUnification which ensures the
+      // complete scroll tree.
+      if (!RuntimeEnabledFeatures::ScrollUnificationEnabled()) {
+        DCHECK(property_tree_manager);
+        property_tree_manager->EnsureCompositorScrollNode(*scroll_translation);
+      }
+    }
+  }
+
+  FloatClipRect rect(FloatRect(hit_test_data.scroll_hit_test_rect));
+  if (!GeometryMapper::LocalToAncestorVisualRect(chunk_state, layer_state,
+                                                 rect))
+    return;
+
+  rect.MoveBy(-layer_offset);
+  non_fast_scrollable_region.Union(EnclosingIntRect(rect.Rect()));
+}
+
+static void UpdateTouchActionAndNonFastScrollableRegions(
+    cc::Layer& layer,
+    const PropertyTreeState& layer_state,
+    const PaintChunkSubset& chunks,
+    PropertyTreeManager* property_tree_manager) {
+  gfx::Vector2dF cc_layer_offset = layer.offset_to_transform_parent();
+  FloatPoint layer_offset(cc_layer_offset.x(), cc_layer_offset.y());
+  cc::TouchActionRegion touch_action_region;
+  cc::Region non_fast_scrollable_region;
+  for (const auto& chunk : chunks) {
+    if (!chunk.hit_test_data)
+      continue;
+    auto chunk_state = chunk.properties.GetPropertyTreeState().Unalias();
+    UpdateTouchActionRegion(*chunk.hit_test_data, layer_state, chunk_state,
+                            layer_offset, touch_action_region);
+    UpdateNonFastScrollableRegion(
+        layer, *chunk.hit_test_data, layer_state, chunk_state, layer_offset,
+        property_tree_manager, non_fast_scrollable_region);
+  }
+  layer.SetTouchActionRegion(std::move(touch_action_region));
+  layer.SetNonFastScrollableRegion(std::move(non_fast_scrollable_region));
+}
+
+void PaintChunksToCcLayer::UpdateLayerProperties(
+    cc::Layer& layer,
+    const PropertyTreeState& layer_state,
+    const PaintChunkSubset& chunks,
+    PropertyTreeManager* property_tree_manager) {
+  UpdateBackgroundColor(layer, chunks);
+  UpdateTouchActionAndNonFastScrollableRegions(layer, layer_state, chunks,
+                                               property_tree_manager);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h
index c549b21..5b311bb 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h
@@ -25,6 +25,7 @@
 namespace blink {
 
 class PaintChunkSubset;
+class PropertyTreeManager;
 class PropertyTreeState;
 class RasterInvalidationTracking;
 
@@ -71,8 +72,10 @@
       cc::DisplayItemList::UsageHint,
       RasterUnderInvalidationCheckingParams* = nullptr);
 
-  static void UpdateBackgroundColor(cc::Layer& layer,
-                                    const PaintChunkSubset& paint_chunks);
+  static void UpdateLayerProperties(cc::Layer& layer,
+                                    const PropertyTreeState& layer_state,
+                                    const PaintChunkSubset&,
+                                    PropertyTreeManager* = nullptr);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index eda38c9..7312a356 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1668,7 +1668,7 @@
     },
     {
       name: "ScrollbarGutter",
-      status: "test",
+      status: "experimental",
     },
     {
       name: "ScrollCustomization",
diff --git a/third_party/blink/tools/OWNERS b/third_party/blink/tools/OWNERS
index 6e9e9c7b..aacb4c1 100644
--- a/third_party/blink/tools/OWNERS
+++ b/third_party/blink/tools/OWNERS
@@ -4,5 +4,8 @@
 tkent@chromium.org
 wangxianzhu@chromium.org
 
+# For Fuchsia related files
+per-file *fuchsia.py=file://build/fuchsia/OWNERS
+
 # TEAM: blink-infra@chromium.org
 # COMPONENT: Blink>Infra
diff --git a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
index 4b6aca6d..f11fea8e 100644
--- a/third_party/blink/web_tests/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
+++ b/third_party/blink/web_tests/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
@@ -121,7 +121,6 @@
       "name": "LayoutNGBlockFlow (positioned) DIV id='fixed2'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "backgroundColor": "#C0C0C0",
       "transform": 2
     },
     {
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 19155fb1..b9d4da1 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -242378,7 +242378,7 @@
        []
       ],
       "safari-technology-preview.rb": [
-       "0268ac4b4485d9f688636954fb5b3cc2ade01f3a",
+       "1a3620529c0403522a3ace06f9f1606d1633c5f8",
        []
       ],
       "system_info.yml": [
@@ -349804,7 +349804,7 @@
       ]
      ],
      "reporting-to-endpoint.https.html": [
-      "416732497bdd58f654cbfd883721352a8c7ecba7",
+      "7294bdeafab2c32512983954ed20d617a1418f29",
       [
        null,
        {
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
index 4167324..7294bdea 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/reporting-to-endpoint.https.html
@@ -26,53 +26,57 @@
   return new Promise(resolve => step_timeout(resolve, ms));
 }
 
-async function pollReports(endpoint, reports) {
-  while (true) {
-    await wait(200);
-    const res = await fetch(`resources/report.py?endpoint=${endpoint}`, {cache: 'no-store'});
-    if (res.status !== 200) {
-      continue;
-    }
-    for (const report of await res.json()) {
-      reports.push(report);
-    }
+async function fetchReports(endpoint) {
+  const res = await fetch(`resources/report.py?endpoint=${endpoint}`, {cache: 'no-store'});
+  if (res.status == 200) {
+    return await res.json();
   }
+  return [];
 }
 
-const reports = [];
-const reportsForReportOnly = [];
-pollReports('endpoint', reports);
-pollReports('report-only-endpoint', reportsForReportOnly);
-
-function checkCorpReportExistence(reports, blockedUrl, contextUrl, destination, disposition) {
+async function checkCorpReportExistence(endpoint, blockedUrl, contextUrl, destination, disposition) {
   blockedUrl = new URL(blockedUrl, location).href;
   contextUrl = new URL(contextUrl, location).href;
-  for (const report of reports) {
-    if (report.type !== 'coep' || report.url !== contextUrl ||
-        report.body.type !== 'corp') {
-      continue;
+
+  const timeout = 3000;
+  const retryDelay = 200;
+  for (let i = 0; i * retryDelay < timeout; i++) {
+    const reports = await fetchReports(endpoint);
+    for (const report of reports) {
+      if (report.type !== 'coep' || report.url !== contextUrl ||
+          report.body.type !== 'corp') {
+        continue;
+      }
+      if (report.body.blockedURL === blockedUrl &&
+          report.body.disposition === disposition) {
+        assert_equals(report.body.destination, destination);
+        return;
+      }
     }
-    if (report.body.blockedURL === blockedUrl &&
-        report.body.disposition === disposition) {
-      assert_equals(report.body.destination, destination);
-      return;
-    }
+    await wait(retryDelay);
   }
   assert_unreached(`A report whose blockedURL is ${blockedUrl} and url is ${contextUrl} is not found.`);
 }
 
-function checkNavigationReportExistence(reports, blockedUrl, contextUrl, disposition) {
+async function checkNavigationReportExistence(endpoint, blockedUrl, contextUrl, disposition) {
   blockedUrl = new URL(blockedUrl, location).href;
   contextUrl = new URL(contextUrl, location).href;
-  for (const report of reports) {
-    if (report.type !== 'coep' || report.url !== contextUrl ||
-        report.body.type !== 'navigation') {
-      continue;
+  const timeout = 3000;
+  const retryDelay = 200;
+  for (let i = 0; i * retryDelay < timeout; i++) {
+    const reports = await fetchReports(endpoint);
+
+    for (const report of reports) {
+      if (report.type !== 'coep' || report.url !== contextUrl ||
+          report.body.type !== 'navigation') {
+        continue;
+      }
+      if (report.body.blockedURL === blockedUrl &&
+          report.body.disposition === disposition) {
+        return;
+      }
     }
-    if (report.body.blockedURL === blockedUrl &&
-        report.body.disposition === disposition) {
-      return;
-    }
+    await wait(retryDelay);
   }
   assert_unreached(`A report whose blockedURL is ${blockedUrl} and url is ${contextUrl} is not found.`);
 }
@@ -93,12 +97,9 @@
   // header, so it is blocked.
   iframe.contentWindow.fetch(url, init).catch(() => {});
 
-  // Wait 3 seconds for reports to settle.
-  await wait(3000);
-
-  checkCorpReportExistence(reports, url, iframe.src, '', 'enforce');
-  checkCorpReportExistence(
-      reportsForReportOnly, url, iframe.src, '', 'reporting');
+  await checkCorpReportExistence('endpoint', url, iframe.src, '', 'enforce');
+  await checkCorpReportExistence(
+      'report-only-endpoint', url, iframe.src, '', 'reporting');
 }, 'subresource CORP');
 
 promise_test(async t => {
@@ -124,12 +125,10 @@
   // header, so it is blocked.
   attachFrame(url);
 
-  // Wait 3 seconds for reports to settle.
-  await wait(3000);
-
-  checkCorpReportExistence(reports, url, iframe.src, 'iframe', 'enforce');
-  checkCorpReportExistence(
-      reportsForReportOnly, url, iframe.src, 'iframe', 'reporting');
+  await checkCorpReportExistence(
+      'endpoint', url, iframe.src, 'iframe', 'enforce');
+  await checkCorpReportExistence(
+      'report-only-endpoint', url, iframe.src, 'iframe', 'reporting');
 }, 'navigation CORP');
 
 promise_test(async (t) => {
@@ -147,12 +146,10 @@
 
   document.body.appendChild(iframe);
 
-  // Wait 3 seconds for reports to settle.
-  await wait(3000);
-
-  checkNavigationReportExistence(reports, targetUrl, iframe.src, 'enforce');
-  checkNavigationReportExistence(
-    reportsForReportOnly, targetUrl, iframe.src, 'reporting');
+  await checkNavigationReportExistence(
+      'endpoint', targetUrl, iframe.src, 'enforce');
+  await checkNavigationReportExistence(
+    'report-only-endpoint', targetUrl, iframe.src, 'reporting');
 }, 'COEP violation on nested frame navigation');
 
-</script>$
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/azure/safari-technology-preview.rb b/third_party/blink/web_tests/external/wpt/tools/ci/azure/safari-technology-preview.rb
index 0268ac4b..1a36205 100644
--- a/third_party/blink/web_tests/external/wpt/tools/ci/azure/safari-technology-preview.rb
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/azure/safari-technology-preview.rb
@@ -1,10 +1,10 @@
 cask "safari-technology-preview" do
   if MacOS.version <= :catalina
-    version "113,001-46217-20200908-26bf578a-dcb0-4f70-b930-d9131bbf5d8a"
-    sha256 "09f86d0808e067a46584e9394767040012d7bbae453bb9842cabbf593d9185d9"
+    version "114,001-54431-20201007-6899e5c7-457d-484b-bf38-fce2fcc967da"
+    sha256 "1803c0affc0e7b28d945e122e99ed5741c19b9f2a949b473133adf916023db5b"
   else
-    version "113,001-43557-20200908-b620aaf9-e006-4855-8bdf-e7e76b5950bc"
-    sha256 "3dcf197b8c2c181861d0c3f3d00fbb65940d1c1aaf11511c76c94fb153d0a7f0"
+    version "114,001-53239-20201007-7bed156d-ab68-4db2-8ff0-3fa2e23fa2c0"
+    sha256 "c93ff3b5d485fce80b86f9656ffba3f5b967b189776cd20aea16a4c681419414"
   end
 
   url "https://secure-appldnld.apple.com/STP/#{version.after_comma}/SafariTechnologyPreview.dmg"
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
index 4b97748..8877f53 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
@@ -121,7 +121,6 @@
       "name": "LayoutBlockFlow (positioned) DIV id='fixed2'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "backgroundColor": "#C0C0C0",
       "transform": 2
     },
     {
diff --git a/third_party/blink/web_tests/flag-specific/force-device-scale-factor=1.5/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt b/third_party/blink/web_tests/flag-specific/force-device-scale-factor=1.5/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
index df409413..1cc7f8a 100644
--- a/third_party/blink/web_tests/flag-specific/force-device-scale-factor=1.5/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/force-device-scale-factor=1.5/compositing/layer-creation/fixed-position-change-out-of-view-in-view-expected.txt
@@ -122,7 +122,6 @@
       "name": "LayoutNGBlockFlow (positioned) DIV id='fixed2'",
       "contentsOpaque": true,
       "drawsContent": false,
-      "backgroundColor": "#C0C0C0",
       "transform": 2
     },
     {
diff --git a/third_party/closure_compiler/externs/accessibility_private.js b/third_party/closure_compiler/externs/accessibility_private.js
index b3f6beb..bba6f222 100644
--- a/third_party/closure_compiler/externs/accessibility_private.js
+++ b/third_party/closure_compiler/externs/accessibility_private.js
@@ -274,6 +274,13 @@
 chrome.accessibilityPrivate.updateSwitchAccessBubble = function(bubble, show, anchor, actions) {};
 
 /**
+ * Enable point scanning in Switch Access.
+ * @param {boolean} enabled True for start point scanning, false for end point
+ *     scanning.
+ */
+chrome.accessibilityPrivate.enablePointScan = function(enabled) {};
+
+/**
  * Sets current ARC app to use native ARC support.
  * @param {boolean} enabled True for ChromeVox (native), false for TalkBack.
  */
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4e4e308..4ea129a3 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -25041,6 +25041,8 @@
   <int value="1517" label="FILEMANAGERPRIVATEINTERNAL_GETPDFTHUMBNAIL"/>
   <int value="1518" label="AUTOTESTPRIVATE_REMOVEALLNOTIFICATIONS"/>
   <int value="1519" label="VIRTUALKEYBOARDPRIVATE_OPENSUGGESTIONSETTINGS"/>
+  <int value="1520" label="ACCESSIBILITY_PRIVATE_ENABLEPOINTSCAN"/>
+  <int value="1521" label="AUTOTESTPRIVATE_ACTIVATEADJACENTDESKSTOTARGETINDEX"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -54472,7 +54474,7 @@
   <int value="304" label="Security And Sign In"/>
   <int value="305" label="Fingerprint"/>
   <int value="306" label="Manage Other People"/>
-  <int value="307" label="Kerberos"/>
+  <int value="307" label="Kerberos Accounts"/>
   <int value="400" label="Pointers"/>
   <int value="401" label="Keyboard"/>
   <int value="402" label="Stylus"/>
diff --git a/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml b/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml
index cf53e4ab..f78e64c 100644
--- a/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/chromeos/histograms.xml
@@ -712,6 +712,18 @@
   </summary>
 </histogram>
 
+<histogram name="ChromeOS.Settings.People.AddAccountCount" units="accounts"
+    expires_after="2021-09-30">
+  <owner>khorimoto@chromium.org</owner>
+  <owner>hsuregan@chromium.org</owner>
+  <owner>cros-customization@google.com</owner>
+  <summary>
+    Records when users click the Add Account button on the People page. The
+    number of the account that would be added is saved, e.g. a sample of 2 means
+    the user entered the add account dialog for a 2nd account.
+  </summary>
+</histogram>
+
 <histogram name="ChromeOS.Settings.SearchLatency" units="ms"
     expires_after="2021-04-04">
   <owner>khorimoto@chromium.org</owner>
diff --git a/tools/perf/benchmarks/silk_flags.py b/tools/perf/benchmarks/silk_flags.py
deleted file mode 100644
index 478194a6..0000000
--- a/tools/perf/benchmarks/silk_flags.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-def CustomizeBrowserOptionsForSoftwareRasterization(options):
-  """Enables flags needed for forced software rasterization."""
-  options.AppendExtraBrowserArgs('--disable-gpu-rasterization')
-
-
-def CustomizeBrowserOptionsForGpuRasterization(options):
-  """Enables flags needed for forced GPU rasterization using Ganesh."""
-  options.AppendExtraBrowserArgs('--force-gpu-rasterization')
-
-
-def CustomizeBrowserOptionsForSyncScrolling(options):
-  """Enables flags needed for synchronous (main thread) scrolling."""
-  options.AppendExtraBrowserArgs('--disable-threaded-scrolling')
-
-def CustomizeBrowserOptionsForThreadTimes(options):
-  """Disables options known to cause noise in thread times"""
-  # Remove once crbug.com/621128 is fixed.
-  options.AppendExtraBrowserArgs('--disable-top-sites')
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 2f71dcdb..a4fd83024 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,12 +5,12 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/win/935a915963e1482109b102c82585d78c12112b31/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "002f2269c464fd57c15cac6a921fad003228af1e",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/815983783a765fa04e31bb514020fd1c55291a71/trace_processor_shell"
+            "hash": "f401bbb73573f5ce4747f8226200b560733980c0",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/109c355900d140e616c7ddc66c57e626678133ca/trace_processor_shell"
         },
         "linux": {
-            "hash": "5ee28a927ebcff327e0ef054a0fc33ea07dca9f7",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/815983783a765fa04e31bb514020fd1c55291a71/trace_processor_shell"
+            "hash": "04d376f2376bc6bd96b6a019256f61d92237d05a",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/109c355900d140e616c7ddc66c57e626678133ca/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/gfx/x/connection.cc b/ui/gfx/x/connection.cc
index d2b1d98e..a29aabc 100644
--- a/ui/gfx/x/connection.cc
+++ b/ui/gfx/x/connection.cc
@@ -28,6 +28,33 @@
 #include "ui/gfx/x/xproto_internal.h"
 #include "ui/gfx/x/xproto_types.h"
 
+extern "C" {
+typedef struct {
+  int type;
+  unsigned long serial;
+  Bool send_event;
+  Display* display;
+  Window window;
+  Window root;
+  Window subwindow;
+  Time time;
+  int x, y;
+  int x_root, y_root;
+  unsigned int state;
+  unsigned int keycode;
+  Bool same_screen;
+} XKeyEvent;
+
+// This is temporarily required to fix XKB key event processing (bugs 1125886,
+// 1136265, 1136248, 1136206).  It should be removed and replaced with an
+// XProto equivalent.
+int XLookupString(XKeyEvent* event_struct,
+                  char* buffer_return,
+                  int bytes_buffer,
+                  ::KeySym* keysym_return,
+                  void* status_in_out);
+}
+
 namespace x11 {
 
 namespace {
@@ -487,8 +514,16 @@
 
 KeySym Connection::KeycodeToKeysym(uint32_t keycode, unsigned int modifiers) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto sym = TranslateKey(keycode, modifiers);
-  return sym == static_cast<KeySym>(XK_VoidSymbol) ? kNoSymbol : sym;
+
+  XKeyEvent key_event{
+      .type = KeyEvent::Press,
+      .display = display_,
+      .state = modifiers,
+      .keycode = keycode,
+  };
+  ::KeySym keysym;
+  XLookupString(&key_event, nullptr, 0, &keysym, nullptr);
+  return static_cast<x11::KeySym>(keysym);
 }
 
 std::unique_ptr<Connection> Connection::Clone() const {
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
index cd82339..63569c6 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/TabTest.java
@@ -20,6 +20,8 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.weblayer.ActionModeCallback;
+import org.chromium.weblayer.ActionModeItemType;
 import org.chromium.weblayer.Browser;
 import org.chromium.weblayer.Tab;
 import org.chromium.weblayer.TabListCallback;
@@ -287,4 +289,21 @@
         // The WebContents should not have been hidden as a result of the rotation.
         Assert.assertFalse(mActivityTestRule.executeScriptAndExtractBoolean("gotHide", false));
     }
+
+    @Test
+    @SmallTest
+    @MinWebLayerVersion(88)
+    public void setFloatingActionModeOverride() throws Exception {
+        mActivity = mActivityTestRule.launchShellWithUrl("about:blank");
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mActivity.getBrowser().getActiveTab().setFloatingActionModeOverride(
+                    ActionModeItemType.SHARE, new ActionModeCallback() {
+                        @Override
+                        public void onActionItemClicked(
+                                @ActionModeItemType int item, String selectedText) {}
+                    });
+        });
+
+        // Smoke test. It's not possible to trigger an action mode click in a test.
+    }
 }
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 52cc7e5..a0101fe 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -340,6 +340,7 @@
 android_library("interfaces_java") {
   sources = [
     "org/chromium/weblayer_private/interfaces/APICallException.java",
+    "org/chromium/weblayer_private/interfaces/ActionModeItemType.java",
     "org/chromium/weblayer_private/interfaces/BrowserFragmentArgs.java",
     "org/chromium/weblayer_private/interfaces/BrowsingDataType.java",
     "org/chromium/weblayer_private/interfaces/CookieChangeCause.java",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java b/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java
index 6b1103e..4927ae6 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java
@@ -7,31 +7,68 @@
 import android.app.SearchManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.RemoteException;
 import android.view.ActionMode;
 import android.view.Menu;
 import android.view.MenuItem;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.base.PackageManagerUtils;
 import org.chromium.content_public.browser.ActionModeCallbackHelper;
 import org.chromium.content_public.browser.SelectionPopupController;
 import org.chromium.content_public.browser.WebContents;
+import org.chromium.weblayer_private.interfaces.APICallException;
+import org.chromium.weblayer_private.interfaces.ActionModeItemType;
+import org.chromium.weblayer_private.interfaces.ITabClient;
+import org.chromium.weblayer_private.interfaces.ObjectWrapper;
 
 /**
  * A class that handles selection action mode for WebLayer.
  */
 public final class ActionModeCallback implements ActionMode.Callback {
     private final ActionModeCallbackHelper mHelper;
+    // Can be null during init.
+    private @Nullable ITabClient mTabClient;
+
+    // Bitfield of @ActionModeItemType values.
+    private int mActionModeOverride;
+
+    // Convert from content ActionModeCallbackHelper.MENU_ITEM_* values to
+    // @ActionModeItemType values.
+    private static int contentToWebLayerType(int contentType) {
+        switch (contentType) {
+            case ActionModeCallbackHelper.MENU_ITEM_SHARE:
+                return ActionModeItemType.SHARE;
+            case ActionModeCallbackHelper.MENU_ITEM_WEB_SEARCH:
+                return ActionModeItemType.WEB_SEARCH;
+            case ActionModeCallbackHelper.MENU_ITEM_PROCESS_TEXT:
+            case 0:
+                return 0;
+            default:
+                assert false;
+                return 0;
+        }
+    }
 
     public ActionModeCallback(WebContents webContents) {
         mHelper =
                 SelectionPopupController.fromWebContents(webContents).getActionModeCallbackHelper();
     }
 
+    public void setTabClient(ITabClient tabClient) {
+        mTabClient = tabClient;
+    }
+
+    public void setOverride(int actionModeItemTypes) {
+        mActionModeOverride = actionModeItemTypes;
+    }
+
     @Override
     public final boolean onCreateActionMode(ActionMode mode, Menu menu) {
         int allowedActionModes = ActionModeCallbackHelper.MENU_ITEM_PROCESS_TEXT
                 | ActionModeCallbackHelper.MENU_ITEM_SHARE;
-        if (isWebSearchAvailable()) {
+        if ((mActionModeOverride & ActionModeItemType.WEB_SEARCH) != 0 || isWebSearchAvailable()) {
             allowedActionModes |= ActionModeCallbackHelper.MENU_ITEM_WEB_SEARCH;
         }
         mHelper.setAllowedMenuItems(allowedActionModes);
@@ -53,7 +90,19 @@
 
     @Override
     public final boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-        return mHelper.onActionItemClicked(mode, item);
+        int menuItemType = contentToWebLayerType(mHelper.getAllowedMenuItemIfAny(mode, item));
+        if ((menuItemType & mActionModeOverride) == 0) {
+            return mHelper.onActionItemClicked(mode, item);
+        }
+        assert WebLayerFactoryImpl.getClientMajorVersion() >= 88;
+        try {
+            mTabClient.onActionItemClicked(
+                    menuItemType, ObjectWrapper.wrap(mHelper.getSelectedText()));
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+        mode.finish();
+        return true;
     }
 
     @Override
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
index 5f862a7..8e04d59e 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java
@@ -140,6 +140,7 @@
     private DisplayCutoutController mDisplayCutoutController;
 
     private boolean mPostContainerViewInitDone;
+    private ActionModeCallback mActionModeCallback;
 
     private WebLayerAccessibilityUtil.Observer mAccessibilityObserver;
 
@@ -323,7 +324,8 @@
         mPostContainerViewInitDone = true;
         SelectionPopupController controller =
                 SelectionPopupController.fromWebContents(mWebContents);
-        controller.setActionModeCallback(new ActionModeCallback(mWebContents));
+        mActionModeCallback = new ActionModeCallback(mWebContents);
+        controller.setActionModeCallback(mActionModeCallback);
         controller.setSelectionClient(SelectionClient.createSmartSelectionClient(mWebContents));
     }
 
@@ -524,6 +526,7 @@
         StrictModeWorkaround.apply();
         mClient = client;
         mTabCallbackProxy = new TabCallbackProxy(mNativeTab, client);
+        mActionModeCallback.setTabClient(mClient);
     }
 
     @Override
@@ -591,11 +594,13 @@
 
     @Override
     public void setTranslateTargetLanguage(String targetLanguage) {
+        StrictModeWorkaround.apply();
         TabImplJni.get().setTranslateTargetLanguage(mNativeTab, targetLanguage);
     }
 
     @Override
     public void setScrollOffsetsEnabled(boolean enabled) {
+        StrictModeWorkaround.apply();
         if (enabled) {
             if (mGestureStateListenerWithScroll == null) {
                 mGestureStateListenerWithScroll = new GestureStateListenerWithScroll() {
@@ -619,6 +624,12 @@
         }
     }
 
+    @Override
+    public void setFloatingActionModeOverride(int actionModeItemTypes) {
+        StrictModeWorkaround.apply();
+        mActionModeCallback.setOverride(actionModeItemTypes);
+    }
+
     public void removeFaviconCallbackProxy(FaviconCallbackProxy proxy) {
         mFaviconCallbackProxies.remove(proxy);
     }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ActionModeItemType.java b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ActionModeItemType.java
new file mode 100644
index 0000000..bbce17545
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ActionModeItemType.java
@@ -0,0 +1,17 @@
+// 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.weblayer_private.interfaces;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({ActionModeItemType.SHARE, ActionModeItemType.WEB_SEARCH})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ActionModeItemType {
+    int SHARE = 1 << 0;
+    int WEB_SEARCH = 1 << 1;
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
index f2deacbb..3b41e1656 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl
@@ -75,4 +75,7 @@
 
   // Added in 87
   void setScrollOffsetsEnabled(in boolean enabled) = 26;
+
+  // Added in 88
+  void setFloatingActionModeOverride(in int actionModeItemTypes) = 27;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
index 94e02e8..02a0780 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl
@@ -46,4 +46,8 @@
 
   // Added in M87
   void onVerticalScrollOffsetChanged(in int offset) = 11;
+
+  // Added in M88
+  void onActionItemClicked(
+          in int actionModeItemType, in IObjectWrapper selectedString) = 12;
 }
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn
index c93fe494..a346309 100644
--- a/weblayer/public/java/BUILD.gn
+++ b/weblayer/public/java/BUILD.gn
@@ -35,6 +35,8 @@
 
 android_library("java") {
   sources = [
+    "org/chromium/weblayer/ActionModeCallback.java",
+    "org/chromium/weblayer/ActionModeItemType.java",
     "org/chromium/weblayer/BroadcastReceiver.java",
     "org/chromium/weblayer/Browser.java",
     "org/chromium/weblayer/BrowserControlsOffsetCallback.java",
diff --git a/weblayer/public/java/org/chromium/weblayer/ActionModeCallback.java b/weblayer/public/java/org/chromium/weblayer/ActionModeCallback.java
new file mode 100644
index 0000000..7014837
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/ActionModeCallback.java
@@ -0,0 +1,19 @@
+// 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.weblayer;
+
+/**
+ * Used to override floating some action mode menu items.
+ *
+ * @since 88
+ */
+public abstract class ActionModeCallback {
+    /**
+     * Called when an overridden item type is clicked. The action mode is closed after this returns.
+     * @param selectedText the raw selected text. Client is responsible for trimming it to fit into
+     *                     some use cases as the text can be very large.
+     */
+    public void onActionItemClicked(@ActionModeItemType int item, String selectedText) {}
+}
diff --git a/weblayer/public/java/org/chromium/weblayer/ActionModeItemType.java b/weblayer/public/java/org/chromium/weblayer/ActionModeItemType.java
new file mode 100644
index 0000000..b85fcd1
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/ActionModeItemType.java
@@ -0,0 +1,17 @@
+// 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.weblayer;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({ActionModeItemType.SHARE, ActionModeItemType.WEB_SEARCH})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ActionModeItemType {
+    int SHARE = org.chromium.weblayer_private.interfaces.ActionModeItemType.SHARE;
+    int WEB_SEARCH = org.chromium.weblayer_private.interfaces.ActionModeItemType.WEB_SEARCH;
+}
diff --git a/weblayer/public/java/org/chromium/weblayer/Tab.java b/weblayer/public/java/org/chromium/weblayer/Tab.java
index 9fd1304..646f2b9 100644
--- a/weblayer/public/java/org/chromium/weblayer/Tab.java
+++ b/weblayer/public/java/org/chromium/weblayer/Tab.java
@@ -56,6 +56,7 @@
     private FullscreenCallbackClientImpl mFullscreenCallbackClient;
     private NewTabCallback mNewTabCallback;
     private final ObserverList<ScrollOffsetCallback> mScrollOffsetCallbacks;
+    private @Nullable ActionModeCallback mActionModeCallback;
     // Id from the remote side.
     private final int mId;
 
@@ -740,6 +741,29 @@
         }
     }
 
+    /**
+     * Allow controlling and overriding custom items in the floating seleciton menu.
+     * Note floating action mode is available on M and up.
+     * @param actionModeItemTypes a bit field of values in ActionModeItemType.
+     * @param callback can be null if actionModeItemTypes is 0.
+     *
+     * @since 88
+     */
+    public void setFloatingActionModeOverride(
+            int actionModeItemTypes, @Nullable ActionModeCallback callback) {
+        ThreadCheck.ensureOnUiThread();
+        throwIfDestroyed();
+        if (WebLayer.getSupportedMajorVersionInternal() < 88) {
+            throw new UnsupportedOperationException();
+        }
+        mActionModeCallback = callback;
+        try {
+            mImpl.setFloatingActionModeOverride(actionModeItemTypes);
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
     // Called by Browser when removed.
     void onRemovedFromBrowser() {
         if (mDestroyOnRemove) {
@@ -904,6 +928,16 @@
                 callback.onVerticalScrollOffsetChanged(value);
             }
         }
+
+        @Override
+        public void onActionItemClicked(
+                int actionModeItemType, IObjectWrapper selectedStringWrapper) {
+            StrictModeWorkaround.apply();
+            String selectedString = ObjectWrapper.unwrap(selectedStringWrapper, String.class);
+            if (mActionModeCallback != null) {
+                mActionModeCallback.onActionItemClicked(actionModeItemType, selectedString);
+            }
+        }
     }
 
     private static final class ErrorPageCallbackClientImpl extends IErrorPageCallbackClient.Stub {