diff --git a/DEPS b/DEPS
index 79c53e8..0fa6172 100644
--- a/DEPS
+++ b/DEPS
@@ -209,11 +209,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '71863fa7876ef76c56e96a3e9b5dc5659e141674',
+  'skia_revision': '04e643e4625743139b6c75a33048c1d6e591bdfd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '8183a2825a4bec850a38cf0727b53e36cc5a8c96',
+  'v8_revision': '2f4fa28d3c7308bd059a10d1edb987201210abff',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -356,7 +356,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': 'dadd6794e46738b7a1db481f4f47e627fc448981',
+  'nearby_revision': '93eec2c07b588985da41af77a4589dd0de37c677',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -573,7 +573,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '2f66105eb32977817bcf2adc6d63feeb821bb9d1',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '377a73f6a8715d403fd4923612289fae6dbad665',
       'condition': 'checkout_ios',
   },
 
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9c34906c59161fbe6ee7e074f6d58df163311ecb',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e77e3c879a3f795161d3efa096c9caa33c7a0c2b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1416,7 +1416,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'SsyDlYQ2a6ql4a-FjopHtoILPoUql2y5XbeoyIMtnmsC'
+              'version': 'IFKk3HKRVi_NvWa_9abMZahaAGY7hGQ0MzuloLf6TkgC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1613,7 +1613,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@c1aa10e58dfd30057a32922eaaa77db3c8129336',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6781c008693620ac6c19a0328f84da9dbdd42de9',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/accessibility/magnifier/partial_magnification_controller.cc b/ash/accessibility/magnifier/partial_magnification_controller.cc
index 89f9ae3..76273f4c 100644
--- a/ash/accessibility/magnifier/partial_magnification_controller.cc
+++ b/ash/accessibility/magnifier/partial_magnification_controller.cc
@@ -46,9 +46,24 @@
   Shell::Get()->RemovePreTargetHandler(this);
 }
 
+void PartialMagnificationController::AddObserver(
+    PartialMagnificationController::Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void PartialMagnificationController::RemoveObserver(
+    PartialMagnificationController::Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
 void PartialMagnificationController::SetEnabled(bool enabled) {
+  if (is_enabled_ == enabled)
+    return;
+
   is_enabled_ = enabled;
   SetActive(false);
+  for (auto& observer : observers_)
+    observer.OnPartialMagnificationStateChanged(enabled);
 }
 
 void PartialMagnificationController::SwitchTargetRootWindowIfNeeded(
@@ -67,6 +82,10 @@
   OnLocatedEvent(event, event->pointer_details());
 }
 
+void PartialMagnificationController::OnMouseEvent(ui::MouseEvent* event) {
+  OnLocatedEvent(event, event->pointer_details());
+}
+
 void PartialMagnificationController::SetActive(bool active) {
   // Fail if we're trying to activate while disabled.
   DCHECK(is_enabled_ || !active);
@@ -88,6 +107,12 @@
   if (!is_enabled_)
     return;
 
+  if (allow_mouse_following_ &&
+      pointer_details.pointer_type == ui::EventPointerType::kMouse) {
+    SetActive(true);
+    return;
+  }
+
   if (pointer_details.pointer_type != ui::EventPointerType::kPen)
     return;
 
diff --git a/ash/accessibility/magnifier/partial_magnification_controller.h b/ash/accessibility/magnifier/partial_magnification_controller.h
index ca634193..a5cf97b5 100644
--- a/ash/accessibility/magnifier/partial_magnification_controller.h
+++ b/ash/accessibility/magnifier/partial_magnification_controller.h
@@ -9,6 +9,8 @@
 
 #include "ash/ash_export.h"
 #include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "ui/events/event_handler.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/size.h"
@@ -30,9 +32,18 @@
 // which is zoomed in.  The zoomed area follows the mouse cursor when enabled.
 class ASH_EXPORT PartialMagnificationController : public ui::EventHandler {
  public:
+  class Observer : public base::CheckedObserver {
+   public:
+    // Called when the partial magnification enabled state changes.
+    virtual void OnPartialMagnificationStateChanged(bool enabled) = 0;
+  };
+
   PartialMagnificationController();
   ~PartialMagnificationController() override;
 
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Turns the partial screen magnifier feature on or off. Turning the magnifier
   // on does not imply that it will be displayed; the magnifier is only
   // displayed when it is both enabled and active.
@@ -45,11 +56,18 @@
   //  - Switch the target window from current window to |new_root_window|.
   void SwitchTargetRootWindowIfNeeded(aura::Window* new_root_window);
 
+  void set_allow_mouse_following(bool enabled) {
+    allow_mouse_following_ = enabled;
+  }
+
+  bool is_enabled() const { return is_enabled_; }
+
  private:
   friend class PartialMagnificationControllerTestApi;
 
   // ui::EventHandler:
   void OnTouchEvent(ui::TouchEvent* event) override;
+  void OnMouseEvent(ui::MouseEvent* event) override;
 
   // Enables or disables the actual magnifier window.
   void SetActive(bool active);
@@ -61,8 +79,14 @@
   bool is_enabled_ = false;
   bool is_active_ = false;
 
+  // If enabled, allows the magnifier glass to follow the mouse without the need
+  // to pressing and holding the mouse.
+  bool allow_mouse_following_ = false;
+
   std::unique_ptr<MagnifierGlass> magnifier_glass_;
 
+  base::ObserverList<Observer> observers_;
+
   DISALLOW_COPY_AND_ASSIGN(PartialMagnificationController);
 };
 
diff --git a/ash/accessibility/magnifier/partial_magnification_controller_unittest.cc b/ash/accessibility/magnifier/partial_magnification_controller_unittest.cc
index e5bf21e..bdc1992 100644
--- a/ash/accessibility/magnifier/partial_magnification_controller_unittest.cc
+++ b/ash/accessibility/magnifier/partial_magnification_controller_unittest.cc
@@ -134,6 +134,15 @@
   EXPECT_FALSE(GetTestApi().is_active());
 }
 
+// The magnifier activates for mouse events.
+TEST_F(PartialMagnificationControllerTest, ActivatesForMouseEvents) {
+  GetController()->SetEnabled(true);
+  GetController()->set_allow_mouse_following(true);
+  ui::test::EventGenerator* event_generator = GetEventGenerator();
+  event_generator->MoveMouseBy(1, 1);
+  EXPECT_TRUE(GetTestApi().is_active());
+}
+
 // The magnifier is always located at pointer.
 TEST_F(PartialMagnificationControllerTest, MagnifierFollowsPointer) {
   ui::test::EventGenerator* event_generator = GetEventGenerator();
diff --git a/ash/app_list/model/search/search_result.h b/ash/app_list/model/search/search_result.h
index ff5779c..7c66ca83 100644
--- a/ash/app_list/model/search/search_result.h
+++ b/ash/app_list/model/search/search_result.h
@@ -42,7 +42,7 @@
   using Action = ash::SearchResultAction;
   using Actions = ash::SearchResultActions;
   using DisplayIndex = ash::SearchResultDisplayIndex;
-  using OmniboxType = ash::SearchResultOmniboxType;
+  using OmniboxType = ash::SearchResultOmniboxDisplayType;
 
   SearchResult();
   virtual ~SearchResult();
diff --git a/ash/app_list/views/expand_arrow_view.cc b/ash/app_list/views/expand_arrow_view.cc
index 855c2b1d..2010e0f 100644
--- a/ash/app_list/views/expand_arrow_view.cc
+++ b/ash/app_list/views/expand_arrow_view.cc
@@ -146,6 +146,17 @@
       this, std::make_unique<ExpandArrowHighlightPathGenerator>());
   views::InkDrop::UseInkDropWithoutAutoHighlight(this,
                                                  /*highlight_on_hover=*/false);
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+        const AppListColorProvider* color_provider =
+            AppListColorProvider::Get();
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetLocalBounds().InsetsFrom(GetCircleBounds()),
+            host->GetInkDropCenterBasedOnLastEvent(),
+            color_provider->GetRippleAttributesBaseColor(),
+            color_provider->GetRippleAttributesInkDropOpacity());
+      },
+      this));
 
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_APP_LIST_EXPAND_BUTTON));
 
@@ -273,16 +284,6 @@
   return "ExpandArrowView";
 }
 
-std::unique_ptr<views::InkDropRipple> ExpandArrowView::CreateInkDropRipple()
-    const {
-  const AppListColorProvider* color_provider = AppListColorProvider::Get();
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetLocalBounds().InsetsFrom(GetCircleBounds()),
-      GetInkDropCenterBasedOnLastEvent(),
-      color_provider->GetRippleAttributesBaseColor(),
-      color_provider->GetRippleAttributesInkDropOpacity());
-}
-
 void ExpandArrowView::AnimationProgressed(const gfx::Animation* animation) {
   // There are two cycles in one animation.
   constexpr auto kAnimationDuration = kCycleDuration * 2 + kCycleInterval;
diff --git a/ash/app_list/views/expand_arrow_view.h b/ash/app_list/views/expand_arrow_view.h
index 3e8e379..28a246f3 100644
--- a/ash/app_list/views/expand_arrow_view.h
+++ b/ash/app_list/views/expand_arrow_view.h
@@ -17,10 +17,6 @@
 class SlideAnimation;
 }  // namespace gfx
 
-namespace views {
-class InkDropRipple;
-}  // namespace views
-
 namespace ash {
 
 class AppListView;
@@ -43,9 +39,6 @@
   void OnBlur() override;
   const char* GetClassName() const override;
 
-  // views::InkDropHostView:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-
   // Calculates vertical offset between expand arrow circle's positions with app
   // list view drag progress |progress| and the current app list progress
   // (calculated without taking app list animation state into account).
diff --git a/ash/app_list/views/page_switcher.cc b/ash/app_list/views/page_switcher.cc
index 142c3d5ce..ebed5131 100644
--- a/ash/app_list/views/page_switcher.cc
+++ b/ash/app_list/views/page_switcher.cc
@@ -71,6 +71,26 @@
           return highlight;
         },
         this));
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](PageSwitcherButton* host) -> std::unique_ptr<views::InkDropRipple> {
+          const gfx::Point center = host->GetLocalBounds().CenterPoint();
+          const int max_radius =
+              host->is_root_app_grid_page_switcher_
+                  ? PageSwitcher::kMaxButtonRadiusForRootGrid
+                  : PageSwitcher::kMaxButtonRadiusForFolderGrid;
+          gfx::Rect bounds(center.x() - max_radius, center.y() - max_radius,
+                           2 * max_radius, 2 * max_radius);
+          const AppListColorProvider* const color_provider =
+              AppListColorProvider::Get();
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+              host->GetInkDropCenterBasedOnLastEvent(),
+              color_provider->GetRippleAttributesBaseColor(
+                  host->background_color_),
+              color_provider->GetRippleAttributesInkDropOpacity(
+                  host->background_color_));
+        },
+        this));
 
     views::InstallFixedSizeCircleHighlightPathGenerator(
         this, is_root_app_grid_page_switcher ? kInkDropRadiusForRootGrid
@@ -92,7 +112,7 @@
       NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
   }
 
-  // Overridden from views::View:
+  // views::Button:
   gfx::Size CalculatePreferredSize() const override {
     const int max_radius = is_root_app_grid_page_switcher_
                                ? PageSwitcher::kMaxButtonRadiusForRootGrid
@@ -105,21 +125,7 @@
   }
 
  protected:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
-    gfx::Point center = GetLocalBounds().CenterPoint();
-    const int max_radius = is_root_app_grid_page_switcher_
-                               ? PageSwitcher::kMaxButtonRadiusForRootGrid
-                               : PageSwitcher::kMaxButtonRadiusForFolderGrid;
-    gfx::Rect bounds(center.x() - max_radius, center.y() - max_radius,
-                     2 * max_radius, 2 * max_radius);
-    const AppListColorProvider* color_provider = AppListColorProvider::Get();
-    return std::make_unique<views::FloodFillInkDropRipple>(
-        size(), GetLocalBounds().InsetsFrom(bounds),
-        GetInkDropCenterBasedOnLastEvent(),
-        color_provider->GetRippleAttributesBaseColor(background_color_),
-        color_provider->GetRippleAttributesInkDropOpacity(background_color_));
-  }
-
+  // views::Button:
   void NotifyClick(const ui::Event& event) override {
     Button::NotifyClick(event);
     GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index 2e70afb..952c5219 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -772,7 +772,7 @@
   if (selected_result->result_type() == AppListSearchResultType::kOmnibox &&
       (!selected_result->is_omnibox_search() ||
        selected_result->omnibox_type() ==
-           SearchResultOmniboxType::kCalculatorAnswer) &&
+           SearchResultOmniboxDisplayType::kCalculatorAnswer) &&
       !selected_result->details().empty()) {
     // For url (non-search) results, use details to ensure that the url is
     // displayed. For calculator results, use details to ensure that the
diff --git a/ash/app_list/views/search_result_actions_view.cc b/ash/app_list/views/search_result_actions_view.cc
index e6794ec..b16dc9a 100644
--- a/ash/app_list/views/search_result_actions_view.cc
+++ b/ash/app_list/views/search_result_actions_view.cc
@@ -46,18 +46,15 @@
                           const SearchResult::Action& action);
   ~SearchResultImageButton() override {}
 
-  // ui::EventHandler:
+  // views::ImageButton:
   void OnGestureEvent(ui::GestureEvent* event) override;
 
-  // views::InkDropHostView:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-
   // Updates the button visibility upon state change of the button or the
   // search result view associated with it.
   void UpdateOnStateChanged();
 
  private:
-  // views::View overrides:
+  // views::ImageButton:
   void OnPaintBackground(gfx::Canvas* canvas) override;
 
   void SetButtonImage(const gfx::ImageSkia& source, int icon_dimension);
@@ -94,6 +91,23 @@
         return highlight;
       },
       this));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](SearchResultImageButton* host)
+          -> std::unique_ptr<views::InkDropRipple> {
+        const gfx::Point center = host->GetLocalBounds().CenterPoint();
+        const int ripple_radius = host->GetButtonRadius();
+        gfx::Rect bounds(center.x() - ripple_radius, center.y() - ripple_radius,
+                         2 * ripple_radius, 2 * ripple_radius);
+        const AppListColorProvider* const color_provider =
+            AppListColorProvider::Get();
+        const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+            host->GetInkDropCenterBasedOnLastEvent(),
+            color_provider->GetRippleAttributesBaseColor(bg_color),
+            color_provider->GetRippleAttributesInkDropOpacity(bg_color));
+      },
+      this));
 
   SetPreferredSize({kImageButtonSizeDip, kImageButtonSizeDip});
   SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
@@ -131,21 +145,6 @@
     Button::OnGestureEvent(event);
 }
 
-std::unique_ptr<views::InkDropRipple>
-SearchResultImageButton::CreateInkDropRipple() const {
-  const gfx::Point center = GetLocalBounds().CenterPoint();
-  const int ripple_radius = GetButtonRadius();
-  gfx::Rect bounds(center.x() - ripple_radius, center.y() - ripple_radius,
-                   2 * ripple_radius, 2 * ripple_radius);
-  const AppListColorProvider* color_provider = AppListColorProvider::Get();
-  const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetLocalBounds().InsetsFrom(bounds),
-      GetInkDropCenterBasedOnLastEvent(),
-      color_provider->GetRippleAttributesBaseColor(bg_color),
-      color_provider->GetRippleAttributesInkDropOpacity(bg_color));
-}
-
 void SearchResultImageButton::UpdateOnStateChanged() {
   // Show button if the associated result row is hovered or selected, or one
   // of the action buttons is selected.
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.cc b/ash/app_list/views/search_result_suggestion_chip_view.cc
index c2475bc..3411fbed 100644
--- a/ash/app_list/views/search_result_suggestion_chip_view.cc
+++ b/ash/app_list/views/search_result_suggestion_chip_view.cc
@@ -67,6 +67,23 @@
   views::InstallPillHighlightPathGenerator(this);
   views::InkDrop::UseInkDropWithoutAutoHighlight(this,
                                                  /*highlight_on_hover=*/false);
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+        const gfx::Point center = host->GetLocalBounds().CenterPoint();
+        const int ripple_radius = host->width() / 2;
+        const gfx::Rect bounds(center.x() - ripple_radius,
+                               center.y() - ripple_radius, 2 * ripple_radius,
+                               2 * ripple_radius);
+        const AppListColorProvider* const color_provider =
+            AppListColorProvider::Get();
+        const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+            host->GetInkDropCenterBasedOnLastEvent(),
+            color_provider->GetRippleAttributesBaseColor(bg_color),
+            color_provider->GetRippleAttributesInkDropOpacity(bg_color));
+      },
+      this));
 
   InitLayout();
 }
@@ -156,21 +173,6 @@
   SchedulePaint();
 }
 
-std::unique_ptr<views::InkDropRipple>
-SearchResultSuggestionChipView::CreateInkDropRipple() const {
-  const gfx::Point center = GetLocalBounds().CenterPoint();
-  const int ripple_radius = width() / 2;
-  gfx::Rect bounds(center.x() - ripple_radius, center.y() - ripple_radius,
-                   2 * ripple_radius, 2 * ripple_radius);
-  const AppListColorProvider* color_provider = AppListColorProvider::Get();
-  const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetLocalBounds().InsetsFrom(bounds),
-      GetInkDropCenterBasedOnLastEvent(),
-      color_provider->GetRippleAttributesBaseColor(bg_color),
-      color_provider->GetRippleAttributesInkDropOpacity(bg_color));
-}
-
 std::unique_ptr<ui::Layer> SearchResultSuggestionChipView::RecreateLayer() {
   std::unique_ptr<ui::Layer> old_layer = views::View::RecreateLayer();
   if (layer())
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.h b/ash/app_list/views/search_result_suggestion_chip_view.h
index 5161b492..741c698 100644
--- a/ash/app_list/views/search_result_suggestion_chip_view.h
+++ b/ash/app_list/views/search_result_suggestion_chip_view.h
@@ -14,7 +14,6 @@
 namespace views {
 class BoxLayout;
 class ImageView;
-class InkDropRipple;
 class Label;
 }  // namespace views
 
@@ -49,9 +48,6 @@
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   void OnThemeChanged() override;
 
-  // views::InkDropHost:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-
   // ui::LayerOwner:
   std::unique_ptr<ui::Layer> RecreateLayer() override;
 
diff --git a/ash/app_list/views/search_result_view.cc b/ash/app_list/views/search_result_view.cc
index d9a2e65..d91ee5e 100644
--- a/ash/app_list/views/search_result_view.cc
+++ b/ash/app_list/views/search_result_view.cc
@@ -406,6 +406,8 @@
   // clearing it out. It should work correctly as long as the SearchResult does
   // not forget to SetIcon when it's ready.
   if (result() && !result()->icon().isNull()) {
+    // TODO(crbug.com/1201151): These nested if/elses can be flattened into one
+    // switch statement pending decisions on rich entity icons.
     if (IsRichImage()) {
       gfx::ImageSkia image = result()->icon();
 
@@ -426,11 +428,19 @@
       icon_->SetVisible(false);
       image_icon_->SetVisible(true);
     } else {
-      const int dimension =
-          IsAnswer()
-              ? SharedAppListConfig::instance()
-                    .search_list_answer_icon_dimension()
-              : SharedAppListConfig::instance().search_list_icon_dimension();
+      int dimension;
+      if (IsAnswer()) {
+        dimension =
+            SharedAppListConfig::instance().search_list_answer_icon_dimension();
+      } else if (result()->omnibox_type() ==
+                 SearchResultOmniboxDisplayType::kFavicon) {
+        dimension =
+            SharedAppListConfig::instance().search_list_favicon_dimension();
+      } else {
+        dimension =
+            SharedAppListConfig::instance().search_list_icon_dimension();
+      }
+
       SetIconImage(result()->icon(), icon_, gfx::Size(dimension, dimension));
       icon_->SetVisible(true);
       image_icon_->SetVisible(false);
@@ -558,14 +568,14 @@
 
 bool SearchResultView::IsAnswer() const {
   return app_list_features::IsOmniboxRichEntitiesEnabled() && result() &&
-         (result()->omnibox_type() == SearchResultOmniboxType::kAnswer ||
+         (result()->omnibox_type() == SearchResultOmniboxDisplayType::kAnswer ||
           result()->omnibox_type() ==
-              SearchResultOmniboxType::kCalculatorAnswer);
+              SearchResultOmniboxDisplayType::kCalculatorAnswer);
 }
 
 bool SearchResultView::IsRichImage() const {
   return app_list_features::IsOmniboxRichEntitiesEnabled() && result() &&
-         result()->omnibox_type() == SearchResultOmniboxType::kRichImage;
+         result()->omnibox_type() == SearchResultOmniboxDisplayType::kRichImage;
 }
 
 }  // namespace ash
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index a25ae19..bbdcea9e 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -3512,26 +3512,26 @@
       </message>
 
       <!-- Shortcut deprecation notifications -->
-      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_DELETE" desc="Message telling user to use search/launcher + backspace instead of alt + backspace to emulate the delete key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_DELETE" desc="Message telling user to use search/launcher + backspace instead of alt + backspace to emulate the delete key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
         The Alt + Backspace keyboard shortcut has changed. To use the Delete key, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + backspace.
       </message>
-      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_END" desc="Message telling user to use search/launcher + right arrow instead of ctrl + alt + down arrow to emulate the end key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_END" desc="Message telling user to use search/launcher + right arrow instead of ctrl + alt + down arrow to emulate the end key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
         The Ctrl + Alt + Down Arrow keyboard shortcut has changed. To use the End key, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + Right Arrow.
       </message>
-      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_HOME" desc="Message telling user to use search/launcher + left arrow instead of ctrl + alt + up arrow to emulate the home key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_HOME" desc="Message telling user to use search/launcher + left arrow instead of ctrl + alt + up arrow to emulate the home key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
         The Ctrl + Alt + Up Arrow keyboard shortcut has changed. To use the Home key, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + Left Arrow.
       </message>
-      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_DOWN" desc="Message telling user to use search/launcher + down arrow instead of alt + down array to emulate the page down key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_DOWN" desc="Message telling user to use search/launcher + down arrow instead of alt + down array to emulate the page down key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
         The Alt + Down Arrow keyboard shortcut has changed. To use the Page Down key, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + Down Arrow.
       </message>
-      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_UP" desc="Message telling user to use search/launcher + up arrow instead of alt + up arrow to emulate the page up key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_UP" desc="Message telling user to use search/launcher + up arrow instead of alt + up arrow to emulate the page up key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
         The Alt + Up Arrow keyboard shortcut has changed. To use the Page Up key, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + Up Arrow.
       </message>
       <message name="IDS_ASH_SHORTCUT_DEPRECATION_ALT_CLICK" desc="Message telling user to use search/launcher click instead of alt click to emulate a right click. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
         The Alt + click keyboard shortcut has changed. To use your keyboard to right-click, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + click.
       </message>
-      <message name="IDS_ASH_SHORTCUT_DEPRECATION_FKEY" desc="Message telling user to use search/launcher + top row key instead of Search + a digit key to emulate a F-Key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard." translateable="false">
-        The Search + Digit keyboard shortcut has changed. To generate F-Keys, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + a key on the top row.
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_FKEY" desc="Message telling user to use search/launcher + top row key instead of Search + a digit key to emulate a F-Key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
+        The <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> + Number keyboard shortcut has changed. To use F-Keys, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + a key on the top row.
       </message>
 
 
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_DELETE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_DELETE.png.sha1
new file mode 100644
index 0000000..161ab18
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_DELETE.png.sha1
@@ -0,0 +1 @@
+df4fa8330d5e1facabe2ff61f36352f3cb927195
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_END.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_END.png.sha1
new file mode 100644
index 0000000..79b06d8
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_END.png.sha1
@@ -0,0 +1 @@
+193642cc92c18e3bfa2f686211e4194206980a7d
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_HOME.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_HOME.png.sha1
new file mode 100644
index 0000000..5f6bb27
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_HOME.png.sha1
@@ -0,0 +1 @@
+0d14b632557911dea4251dd98b94ea7664b96ec9
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_DOWN.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_DOWN.png.sha1
new file mode 100644
index 0000000..c0f69fdc
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_DOWN.png.sha1
@@ -0,0 +1 @@
+8699e2510748185681dee2ec3f3b38cf8a0d041f
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_UP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_UP.png.sha1
new file mode 100644
index 0000000..df836eeb
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_PAGE_UP.png.sha1
@@ -0,0 +1 @@
+24fa8ff5b665a7dceac162edd6dd4fa1b717e6f0
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_FKEY.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_FKEY.png.sha1
new file mode 100644
index 0000000..4bde087d
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_FKEY.png.sha1
@@ -0,0 +1 @@
+bfaf3221862d7f7675642f34eb15031142f93a6a
\ No newline at end of file
diff --git a/ash/assistant/ui/base/assistant_button.cc b/ash/assistant/ui/base/assistant_button.cc
index 854d51fd..de4063a2 100644
--- a/ash/assistant/ui/base/assistant_button.cc
+++ b/ash/assistant/ui/base/assistant_button.cc
@@ -67,6 +67,14 @@
         return highlight;
       },
       this));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), gfx::Insets(kInkDropInset),
+            host->GetInkDropCenterBasedOnLastEvent(),
+            host->GetInkDropBaseColor(), host->GetInkDropVisibleOpacity());
+      },
+      this));
 }
 
 AssistantButton::~AssistantButton() = default;
@@ -105,13 +113,6 @@
       width() / 2 - kInkDropInset, gfx::Insets(kInkDropInset)));
 }
 
-std::unique_ptr<views::InkDropRipple> AssistantButton::CreateInkDropRipple()
-    const {
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), gfx::Insets(kInkDropInset), GetInkDropCenterBasedOnLastEvent(),
-      GetInkDropBaseColor(), GetInkDropVisibleOpacity());
-}
-
 void AssistantButton::OnButtonPressed() {
   assistant::util::IncrementAssistantButtonClickCount(id_);
   listener_->OnButtonPressed(id_);
diff --git a/ash/assistant/ui/base/assistant_button.h b/ash/assistant/ui/base/assistant_button.h
index 59c4805..bd3a0cc6 100644
--- a/ash/assistant/ui/base/assistant_button.h
+++ b/ash/assistant/ui/base/assistant_button.h
@@ -73,7 +73,6 @@
 
   // views::ImageButton:
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
 
  private:
   void OnButtonPressed();
diff --git a/ash/capture_mode/stop_recording_button_tray.cc b/ash/capture_mode/stop_recording_button_tray.cc
index d12fb4d..49368696 100644
--- a/ash/capture_mode/stop_recording_button_tray.cc
+++ b/ash/capture_mode/stop_recording_button_tray.cc
@@ -31,8 +31,6 @@
   image_view->SetVerticalAlignment(views::ImageView::Alignment::kCenter);
   image_view->SetPreferredSize(gfx::Size(kTrayItemSize, kTrayItemSize));
   tray_container()->AddChildView(std::move(image_view));
-
-  set_use_bounce_in_animation(true);
 }
 
 StopRecordingButtonTray::~StopRecordingButtonTray() = default;
diff --git a/ash/capture_mode/view_with_ink_drop.h b/ash/capture_mode/view_with_ink_drop.h
index 38d51b1..5bcc142d 100644
--- a/ash/capture_mode/view_with_ink_drop.h
+++ b/ash/capture_mode/view_with_ink_drop.h
@@ -47,13 +47,13 @@
           return highlight;
         },
         this));
+    // TODO(pbos): See if this is a no-op when replaced with
+    // SetInkDropBaseColor(), i.e. that nothing sets it later.
+    T::SetInkDropBaseColorCallback(
+        base::BindRepeating([]() { return capture_mode::kInkDropBaseColor; }));
   }
 
   ~ViewWithInkDrop() override = default;
-
-  SkColor GetInkDropBaseColor() const override {
-    return capture_mode::kInkDropBaseColor;
-  }
 };
 
 }  // namespace ash
diff --git a/ash/components/audio/audio_devices_pref_handler.h b/ash/components/audio/audio_devices_pref_handler.h
index 4a98c229..f00ed8a 100644
--- a/ash/components/audio/audio_devices_pref_handler.h
+++ b/ash/components/audio/audio_devices_pref_handler.h
@@ -37,6 +37,11 @@
   // Sets the audio mute value to prefs for a device.
   virtual void SetMuteValue(const AudioDevice& device, bool mute_on) = 0;
 
+  // Reads whether input noise cancellation is on from profile prefs.
+  virtual bool GetNoiseCancellationState() = 0;
+  // Sets the input noise cancellation in profile prefs.
+  virtual void SetNoiseCancellationState(bool noise_cancellation_state) = 0;
+
   // Sets the device active state in prefs.
   // Note: |activate_by_user| indicates whether |device| is set to active
   // by user or by priority, and it only matters when |active| is true.
diff --git a/ash/components/audio/audio_devices_pref_handler_impl.cc b/ash/components/audio/audio_devices_pref_handler_impl.cc
index 5ea251c..0559e09e 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl.cc
+++ b/ash/components/audio/audio_devices_pref_handler_impl.cc
@@ -266,6 +266,16 @@
     return kDefaultOutputVolumePercent;
 }
 
+bool AudioDevicesPrefHandlerImpl::GetNoiseCancellationState() {
+  return local_state_->GetBoolean(prefs::kInputNoiseCancellationEnabled);
+}
+
+void AudioDevicesPrefHandlerImpl::SetNoiseCancellationState(
+    bool noise_cancellation_state) {
+  local_state_->SetBoolean(prefs::kInputNoiseCancellationEnabled,
+                           noise_cancellation_state);
+}
+
 AudioDevicesPrefHandlerImpl::AudioDevicesPrefHandlerImpl(
     PrefService* local_state)
     : device_mute_settings_(std::make_unique<base::DictionaryValue>()),
@@ -395,6 +405,7 @@
   registry->RegisterDictionaryPref(prefs::kAudioDevicesGainPercent);
   registry->RegisterDictionaryPref(prefs::kAudioDevicesMute);
   registry->RegisterDictionaryPref(prefs::kAudioDevicesState);
+  registry->RegisterBooleanPref(prefs::kInputNoiseCancellationEnabled, false);
 
   // Register the prefs backing the audio muting policies.
   // Policy for audio input is handled by kAudioCaptureAllowed in the Chrome
diff --git a/ash/components/audio/audio_devices_pref_handler_impl.h b/ash/components/audio/audio_devices_pref_handler_impl.h
index eddccfdea..1f51de3 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl.h
+++ b/ash/components/audio/audio_devices_pref_handler_impl.h
@@ -43,6 +43,9 @@
                        bool* active,
                        bool* activate_by_user) override;
 
+  bool GetNoiseCancellationState() override;
+  void SetNoiseCancellationState(bool noise_cancellation_state) override;
+
   bool GetAudioOutputAllowedValue() override;
 
   void AddAudioPrefObserver(AudioPrefObserver* observer) override;
diff --git a/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc b/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
index 39a976f..5893e59 100644
--- a/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
+++ b/ash/components/audio/audio_devices_pref_handler_impl_unittest.cc
@@ -449,4 +449,10 @@
   ExpectDeviceStateEquals(device_v2, false, false);
 }
 
+TEST_P(AudioDevicesPrefHandlerTest, InputNoiseCancellationPrefRegistered) {
+  EXPECT_FALSE(audio_pref_handler_->GetNoiseCancellationState());
+  audio_pref_handler_->SetNoiseCancellationState(true);
+  EXPECT_TRUE(audio_pref_handler_->GetNoiseCancellationState());
+}
+
 }  // namespace ash
diff --git a/ash/components/audio/audio_devices_pref_handler_stub.cc b/ash/components/audio/audio_devices_pref_handler_stub.cc
index 517ba92a..672ee8a 100644
--- a/ash/components/audio/audio_devices_pref_handler_stub.cc
+++ b/ash/components/audio/audio_devices_pref_handler_stub.cc
@@ -71,6 +71,12 @@
       audio_device_state_map_[device.stable_device_id].activate_by_user;
   return true;
 }
+bool AudioDevicesPrefHandlerStub::GetNoiseCancellationState() {
+  return false;
+}
+
+void AudioDevicesPrefHandlerStub::SetNoiseCancellationState(
+    bool noise_cancellation_state) {}
 
 bool AudioDevicesPrefHandlerStub::GetAudioOutputAllowedValue() {
   return true;
diff --git a/ash/components/audio/audio_devices_pref_handler_stub.h b/ash/components/audio/audio_devices_pref_handler_stub.h
index 22c07d37..d27e1c6 100644
--- a/ash/components/audio/audio_devices_pref_handler_stub.h
+++ b/ash/components/audio/audio_devices_pref_handler_stub.h
@@ -46,6 +46,9 @@
   void AddAudioPrefObserver(AudioPrefObserver* observer) override;
   void RemoveAudioPrefObserver(AudioPrefObserver* observer) override;
 
+  bool GetNoiseCancellationState() override;
+  void SetNoiseCancellationState(bool noise_cancellation_state) override;
+
  protected:
   ~AudioDevicesPrefHandlerStub() override;
 
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index af60e55..d428d42 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -102,6 +102,10 @@
 const char kDeviceWiFiFastTransitionEnabled[] =
     "net.device_wifi_fast_transition_enabled";
 
+// A boolean pref that controls whether input noise cancellation is enabled.
+const char kInputNoiseCancellationEnabled[] =
+    "ash.input_noise_cancellation_enabled";
+
 // A boolean pref to store if Secondary Google Account additions are allowed on
 // Chrome OS Account Manager. The default value is |true|, i.e. Secondary Google
 // Account additions are allowed by default.
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index c110b8d9..9a64dca 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -45,6 +45,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kDeviceWiFiFastTransitionEnabled[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const char kInputNoiseCancellationEnabled[];
+COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSecondaryGoogleAccountSigninAllowed[];
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSamlPasswordModifiedTime[];
diff --git a/ash/lock_screen_action/lock_screen_action_background_view.cc b/ash/lock_screen_action/lock_screen_action_background_view.cc
index c7b0f85..0a95a008 100644
--- a/ash/lock_screen_action/lock_screen_action_background_view.cc
+++ b/ash/lock_screen_action/lock_screen_action_background_view.cc
@@ -40,22 +40,30 @@
           return ink_drop;
         },
         this));
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+          const gfx::Point center = base::i18n::IsRTL()
+                                        ? host->GetLocalBounds().origin()
+                                        : host->GetLocalBounds().top_right();
+          auto ink_drop_ripple =
+              std::make_unique<views::FloodFillInkDropRipple>(
+                  host->size(), gfx::Insets(), center,
+                  host->GetInkDropBaseColor(), 1);
+          ink_drop_ripple->set_use_hide_transform_duration_for_hide_fade_out(
+              true);
+          ink_drop_ripple->set_duration_factor(1.5);
+          return ink_drop_ripple;
+        },
+        this));
+
+    // TODO(pbos): See if this is a no-op when replaced with
+    // SetInkDropBaseColor(), i.e. that nothing sets it later.
+    SetInkDropBaseColorCallback(
+        base::BindRepeating([]() { return SK_ColorBLACK; }));
   }
 
   ~NoteBackground() override = default;
 
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
-    gfx::Point center = base::i18n::IsRTL() ? GetLocalBounds().origin()
-                                            : GetLocalBounds().top_right();
-    auto ink_drop_ripple = std::make_unique<views::FloodFillInkDropRipple>(
-        size(), gfx::Insets(), center, GetInkDropBaseColor(), 1);
-    ink_drop_ripple->set_use_hide_transform_duration_for_hide_fade_out(true);
-    ink_drop_ripple->set_duration_factor(1.5);
-    return ink_drop_ripple;
-  }
-
-  SkColor GetInkDropBaseColor() const override { return SK_ColorBLACK; }
-
  private:
   views::InkDropObserver* observer_;
 
diff --git a/ash/login/ui/login_button.cc b/ash/login/ui/login_button.cc
index 91cb196..a1de38a7 100644
--- a/ash/login/ui/login_button.cc
+++ b/ash/login/ui/login_button.cc
@@ -36,6 +36,19 @@
             gfx::SizeF(host->size()), kInkDropHighlightColor);
       },
       this));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](LoginButton* host) -> std::unique_ptr<views::InkDropRipple> {
+        const gfx::Point center = host->GetLocalBounds().CenterPoint();
+        const int radius = host->GetInkDropRadius();
+        gfx::Rect bounds(center.x() - radius, center.y() - radius, radius * 2,
+                         radius * 2);
+
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+            host->GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor,
+            1.f /*visible_opacity*/);
+      },
+      this));
 
   SetInstallFocusRingOnFocus(true);
   login_views_utils::ConfigureRectFocusRingCircleInkDrop(this, focus_ring(),
@@ -44,18 +57,6 @@
 
 LoginButton::~LoginButton() = default;
 
-std::unique_ptr<views::InkDropRipple> LoginButton::CreateInkDropRipple() const {
-  gfx::Point center = GetLocalBounds().CenterPoint();
-  const int radius = GetInkDropRadius();
-  gfx::Rect bounds(center.x() - radius, center.y() - radius, radius * 2,
-                   radius * 2);
-
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetLocalBounds().InsetsFrom(bounds),
-      GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor,
-      1.f /*visible_opacity*/);
-}
-
 int LoginButton::GetInkDropRadius() const {
   return std::min(GetLocalBounds().width(), GetLocalBounds().height()) / 2;
 }
diff --git a/ash/login/ui/login_button.h b/ash/login/ui/login_button.h
index aab2acbb..58ea91f2 100644
--- a/ash/login/ui/login_button.h
+++ b/ash/login/ui/login_button.h
@@ -17,9 +17,6 @@
   explicit LoginButton(PressedCallback callback);
   ~LoginButton() override;
 
-  // views::InkDropHost:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-
  protected:
   virtual int GetInkDropRadius() const;
 
diff --git a/ash/login/ui/login_pin_view.cc b/ash/login/ui/login_pin_view.cc
index b15101e..9e7e2af2 100644
--- a/ash/login/ui/login_pin_view.cc
+++ b/ash/login/ui/login_pin_view.cc
@@ -116,6 +116,21 @@
           return highlight;
         },
         this));
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](BasePinButton* host) -> std::unique_ptr<views::InkDropRipple> {
+          const gfx::Point center = host->GetLocalBounds().CenterPoint();
+          const gfx::Rect bounds(center.x() - kInkDropCornerRadiusDp,
+                                 center.y() - kInkDropCornerRadiusDp,
+                                 kInkDropCornerRadiusDp * 2,
+                                 kInkDropCornerRadiusDp * 2);
+
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+              host->GetInkDropCenterBasedOnLastEvent(),
+              host->palette_.pin_ink_drop_ripple_color,
+              /*visible_opacity=*/1.f);
+        },
+        this));
 
     views::FocusRing* focus_ring = views::FocusRing::Install(this);
     login_views_utils::ConfigureRectFocusRingCircleInkDrop(
@@ -158,17 +173,7 @@
     node_data->SetName(accessible_name_);
     node_data->role = ax::mojom::Role::kButton;
   }
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
-    gfx::Point center = GetLocalBounds().CenterPoint();
-    gfx::Rect bounds(center.x() - kInkDropCornerRadiusDp,
-                     center.y() - kInkDropCornerRadiusDp,
-                     kInkDropCornerRadiusDp * 2, kInkDropCornerRadiusDp * 2);
 
-    return std::make_unique<views::FloodFillInkDropRipple>(
-        size(), GetLocalBounds().InsetsFrom(bounds),
-        GetInkDropCenterBasedOnLastEvent(), palette_.pin_ink_drop_ripple_color,
-        /*visible_opacity=*/1.f);
-  }
  protected:
   // Called when the button has been pressed.
   virtual void DispatchPress(ui::Event* event) {
diff --git a/ash/projector/projector_controller_impl.cc b/ash/projector/projector_controller_impl.cc
index c6d708cf..861f87f 100644
--- a/ash/projector/projector_controller_impl.cc
+++ b/ash/projector/projector_controller_impl.cc
@@ -134,6 +134,10 @@
   ui_controller_->OnSelfieCamPressed(enabled);
 }
 
+void ProjectorControllerImpl::OnMagnifierButtonPressed(bool enabled) {
+  ui_controller_->OnMagnifierButtonPressed(enabled);
+}
+
 void ProjectorControllerImpl::SetProjectorUiControllerForTest(
     std::unique_ptr<ProjectorUiController> ui_controller) {
   ui_controller_ = std::move(ui_controller);
diff --git a/ash/projector/projector_controller_impl.h b/ash/projector/projector_controller_impl.h
index caf82af..ddc50bce 100644
--- a/ash/projector/projector_controller_impl.h
+++ b/ash/projector/projector_controller_impl.h
@@ -73,6 +73,8 @@
   void OnClearAllMarkersPressed();
   // Invoked when selfie cam button is pressed.
   void OnSelfieCamPressed(bool enabled);
+  // Invoked when magnifier button is pressed.
+  void OnMagnifierButtonPressed(bool enabled);
 
   void SetProjectorUiControllerForTest(
       std::unique_ptr<ProjectorUiController> ui_controller);
diff --git a/ash/projector/projector_controller_unittest.cc b/ash/projector/projector_controller_unittest.cc
index 9eb11b5..e6b0ab5 100644
--- a/ash/projector/projector_controller_unittest.cc
+++ b/ash/projector/projector_controller_unittest.cc
@@ -211,4 +211,9 @@
   controller_->SetCaptionBubbleState(true);
 }
 
+TEST_F(ProjectorControllerTest, MagnifierButtonPressed) {
+  EXPECT_CALL(*mock_ui_controller_, OnMagnifierButtonPressed(true));
+  controller_->OnMagnifierButtonPressed(true);
+}
+
 }  // namespace ash
diff --git a/ash/projector/projector_ui_controller.cc b/ash/projector/projector_ui_controller.cc
index 8df51a8a..5f349a5c 100644
--- a/ash/projector/projector_ui_controller.cc
+++ b/ash/projector/projector_ui_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/projector/projector_ui_controller.h"
 
+#include "ash/accessibility/magnifier/partial_magnification_controller.h"
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/projector/ui/projector_bar_view.h"
 #include "ash/public/cpp/toast_data.h"
@@ -56,6 +57,13 @@
   MarkerController::Get()->SetEnabled(enabled);
 }
 
+void EnableMagnifier(bool enabled) {
+  auto* magnifier_controller = Shell::Get()->partial_magnification_controller();
+  DCHECK(magnifier_controller);
+  magnifier_controller->SetEnabled(enabled);
+  magnifier_controller->set_allow_mouse_following(enabled);
+}
+
 }  // namespace
 
 // This class controls the interaction with the caption bubble. It keeps track
@@ -137,6 +145,11 @@
   DCHECK(marker_controller);
   marker_controller_observation_.Observe(marker_controller);
 
+  auto* partial_magnification_controller =
+      Shell::Get()->partial_magnification_controller();
+  DCHECK(partial_magnification_controller);
+  partial_magnification_observation_.Observe(partial_magnification_controller);
+
   caption_bubble_ =
       std::make_unique<ProjectorUiController::CaptionBubbleController>(this);
 
@@ -229,6 +242,10 @@
     caption_bubble_->Close();
 }
 
+void ProjectorUiController::OnMagnifierButtonPressed(bool enabled) {
+  EnableMagnifier(enabled);
+}
+
 bool ProjectorUiController::IsToolbarVisible() const {
   return model_.bar_enabled();
 }
@@ -243,21 +260,27 @@
   EnableLaserPointer(false);
   // Reset marker.
   EnableMarker(false);
+  // Reset magnifier.
+  EnableMagnifier(false);
 }
 
 void ProjectorUiController::OnLaserPointerStateChanged(bool enabled) {
-  // Disable marker if laser pointer is enabled;
-  if (enabled)
-    MarkerController::Get()->SetEnabled(false);
+  // If laser pointer is enabled, disable marker and magnifier.
+  if (enabled) {
+    EnableMarker(false);
+    EnableMagnifier(false);
+  }
 
   if (projector_bar_view_)
     projector_bar_view_->OnLaserPointerStateChanged(enabled);
 }
 
 void ProjectorUiController::OnMarkerStateChanged(bool enabled) {
-  // Disable laser pointer since marker if enabled;
-  if (enabled)
-    Shell::Get()->laser_pointer_controller()->SetEnabled(false);
+  // If marker is enabled, disable laser pointer and magnifier.
+  if (enabled) {
+    EnableLaserPointer(false);
+    EnableMagnifier(false);
+  }
 
   if (projector_bar_view_)
     projector_bar_view_->OnMarkerStateChanged(enabled);
@@ -268,4 +291,15 @@
     MarkerController::Get()->Clear();
 }
 
+void ProjectorUiController::OnPartialMagnificationStateChanged(bool enabled) {
+  // If magnifier is enabled, disable laser pointer and marker.
+  if (enabled) {
+    EnableMarker(false);
+    EnableLaserPointer(false);
+  }
+
+  if (projector_bar_view_)
+    projector_bar_view_->OnMagnifierStateChanged(enabled);
+}
+
 }  // namespace ash
diff --git a/ash/projector/projector_ui_controller.h b/ash/projector/projector_ui_controller.h
index 6cd6a652e..d1ae621 100644
--- a/ash/projector/projector_ui_controller.h
+++ b/ash/projector/projector_ui_controller.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/accessibility/magnifier/partial_magnification_controller.h"
 #include "ash/ash_export.h"
 #include "ash/fast_ink/laser/laser_pointer_controller.h"
 #include "ash/marker/marker_controller.h"
@@ -23,9 +24,11 @@
 class ProjectorBarView;
 
 // The controller in charge of UI.
-class ASH_EXPORT ProjectorUiController : public LaserPointerObserver,
-                                         public MarkerObserver,
-                                         public ProjectorSessionObserver {
+class ASH_EXPORT ProjectorUiController
+    : public LaserPointerObserver,
+      public MarkerObserver,
+      public ProjectorSessionObserver,
+      public PartialMagnificationController::Observer {
  public:
   explicit ProjectorUiController(ProjectorControllerImpl* projector_controller);
   ProjectorUiController(const ProjectorUiController&) = delete;
@@ -50,11 +53,14 @@
   virtual void OnTranscription(const std::string& transcription, bool is_final);
   // Invoked when the selfie cam button is pressed. Virtual for testing.
   virtual void OnSelfieCamPressed(bool enabled);
-  // Called when the recording started or stopped. Virtual for testing.
+  // Invoked when the recording started or stopped. Virtual for testing.
   virtual void OnRecordingStateChanged(bool started);
   // Notifies the ProjectorControllerImpl and ProjectorBarView when the caption
   // bubble model's state changes.
   void OnCaptionBubbleModelStateChanged(bool visible);
+  // Invoked when  magnification is set to be enabled or not. Virtual for
+  // testing.
+  virtual void OnMagnifierButtonPressed(bool enabled);
 
   bool IsToolbarVisible() const;
 
@@ -80,6 +86,9 @@
   // ProjectorSessionObserver:
   void OnProjectorSessionActiveStateChanged(bool active) override;
 
+  // PartialMagnificationController::OnPartialMagnificationStateChanged:
+  void OnPartialMagnificationStateChanged(bool enabled) override;
+
   ProjectorUiModel model_;
   views::UniqueWidgetPtr projector_bar_widget_;
   ProjectorBarView* projector_bar_view_ = nullptr;
@@ -96,6 +105,10 @@
 
   base::ScopedObservation<ProjectorSession, ProjectorSessionObserver>
       projector_session_observation_{this};
+
+  base::ScopedObservation<PartialMagnificationController,
+                          PartialMagnificationController::Observer>
+      partial_magnification_observation_{this};
 };
 
 }  // namespace ash
diff --git a/ash/projector/projector_ui_controller_unittest.cc b/ash/projector/projector_ui_controller_unittest.cc
index e73056dd..c857625e 100644
--- a/ash/projector/projector_ui_controller_unittest.cc
+++ b/ash/projector/projector_ui_controller_unittest.cc
@@ -74,6 +74,16 @@
   controller_->OnLaserPointerPressed();
   EXPECT_FALSE(marker_controller_->is_enabled());
   EXPECT_TRUE(laser_pointer_controller_->is_enabled());
+
+  // Verify that toggling laser pointer disables magnifier when it was enabled.
+  auto* magnification_controller =
+      Shell::Get()->partial_magnification_controller();
+  controller_->OnMagnifierButtonPressed(true);
+  EXPECT_TRUE(magnification_controller->is_enabled());
+  EXPECT_FALSE(laser_pointer_controller_->is_enabled());
+  controller_->OnLaserPointerPressed();
+  EXPECT_TRUE(laser_pointer_controller_->is_enabled());
+  EXPECT_FALSE(magnification_controller->is_enabled());
 }
 
 // Verifies that toggling on the marker on Projector tools propagates to
@@ -116,6 +126,15 @@
   laser_pointer_controller_->SetEnabled(false);
   EXPECT_FALSE(marker_controller_->is_enabled());
   EXPECT_FALSE(laser_pointer_controller_->is_enabled());
+
+  // Verify that toggling marker disables magnifier when it was enabled.
+  auto* magnification_controller =
+      Shell::Get()->partial_magnification_controller();
+  controller_->OnMagnifierButtonPressed(true);
+  EXPECT_TRUE(magnification_controller->is_enabled());
+  controller_->OnMarkerPressed();
+  EXPECT_TRUE(marker_controller_->is_enabled());
+  EXPECT_FALSE(magnification_controller->is_enabled());
 }
 
 // Verifies that clicking the Clear All Markers button and disabling marker mode
@@ -184,4 +203,21 @@
   EXPECT_FALSE(controller_->IsCaptionBubbleModelOpen());
 }
 
+TEST_F(ProjectorUiControllerTest, EnablingDisablingMagnifierGlass) {
+  // Ensure that enabling magnifier disables marker if it was enabled.
+  controller_->ShowToolbar();
+  auto* marker_controller_ = MarkerController::Get();
+  marker_controller_->SetEnabled(true);
+  EXPECT_TRUE(marker_controller_->is_enabled());
+  controller_->OnMagnifierButtonPressed(true);
+  EXPECT_FALSE(marker_controller_->is_enabled());
+
+  // Ensures that enabling magnifier disables laser pointer if it was enabled.
+  auto* laser_pointer_controller_ = Shell::Get()->laser_pointer_controller();
+  laser_pointer_controller_->SetEnabled(true);
+  EXPECT_TRUE(laser_pointer_controller_->is_enabled());
+  controller_->OnMagnifierButtonPressed(true);
+  EXPECT_FALSE(laser_pointer_controller_->is_enabled());
+}
+
 }  // namespace ash
diff --git a/ash/projector/test/mock_projector_ui_controller.h b/ash/projector/test/mock_projector_ui_controller.h
index 0bcab62..5fc58d7 100644
--- a/ash/projector/test/mock_projector_ui_controller.h
+++ b/ash/projector/test/mock_projector_ui_controller.h
@@ -35,6 +35,7 @@
   MOCK_METHOD1(OnSelfieCamPressed, void(bool enabled));
   MOCK_METHOD1(OnRecordingStateChanged, void(bool started));
   MOCK_METHOD1(SetCaptionBubbleState, void(bool));
+  MOCK_METHOD1(OnMagnifierButtonPressed, void(bool));
 };
 
 }  // namespace ash
diff --git a/ash/projector/ui/projector_bar_view.cc b/ash/projector/ui/projector_bar_view.cc
index a3b45b5b..768ab79 100644
--- a/ash/projector/ui/projector_bar_view.cc
+++ b/ash/projector/ui/projector_bar_view.cc
@@ -131,6 +131,11 @@
     projector_controller_->OnClearAllMarkersPressed();
 }
 
+void ProjectorBarView::OnMagnifierStateChanged(bool enabled) {
+  magnifier_start_button_->SetVisible(!enabled);
+  magnifier_stop_button_->SetVisible(enabled);
+}
+
 void ProjectorBarView::OnThemeChanged() {
   views::View::OnThemeChanged();
 }
@@ -286,7 +291,7 @@
   magnifier_start_button_ =
       box_layout->AddChildView(std::make_unique<ProjectorImageButton>(
           base::BindRepeating(&ProjectorBarView::OnMagnifierButtonPressed,
-                              base::Unretained(this), /* enabled =*/false),
+                              base::Unretained(this), /* enabled =*/true),
           kZoomInIcon));
   magnifier_start_button_->SetVisible(true);
   magnifier_stop_button_ =
@@ -374,7 +379,7 @@
 }
 
 void ProjectorBarView::OnMagnifierButtonPressed(bool enabled) {
-  // TODO(crbug/1203444) Implement the magnifier button functionality.
+  projector_controller_->OnMagnifierButtonPressed(enabled);
 }
 
 void ProjectorBarView::OnChangeBarLocationButtonPressed() {
diff --git a/ash/projector/ui/projector_bar_view.h b/ash/projector/ui/projector_bar_view.h
index 01fc84c..3f902e2 100644
--- a/ash/projector/ui/projector_bar_view.h
+++ b/ash/projector/ui/projector_bar_view.h
@@ -44,6 +44,8 @@
   void OnLaserPointerStateChanged(bool enabled);
   // Invoke when marker activation state changed.
   void OnMarkerStateChanged(bool enabled);
+  // Invoked when the magnifier state changed.
+  void OnMagnifierStateChanged(bool enabled);
 
   // views::View:
   void OnThemeChanged() override;
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index a02fc4a1..bcd34e2 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -382,6 +382,7 @@
     "file_icon_util_unittest.cc",
     "holding_space/holding_space_image_unittest.cc",
     "holding_space/holding_space_item_unittest.cc",
+    "holding_space/holding_space_model_unittest.cc",
     "metrics_util_unittest.cc",
     "pagination/pagination_model_unittest.cc",
     "power_utils_unittest.cc",
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index 358f03cd..64e2076 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -92,6 +92,15 @@
                      search_list_image_icon_dimension_);
   }
 
+  int search_list_favicon_dimension() const {
+    return search_list_favicon_dimension_;
+  }
+
+  gfx::Size search_list_favicon_size() const {
+    return gfx::Size(search_list_favicon_dimension_,
+                     search_list_favicon_dimension_);
+  }
+
   int search_list_icon_vertical_bar_dimension() const {
     return search_list_icon_vertical_bar_dimension_;
   }
@@ -166,6 +175,9 @@
   // The dimension of image icons for list views in search result page view.
   const int search_list_image_icon_dimension_ = 32;
 
+  // The favicon dimension of list views in search result page view.
+  const int search_list_favicon_dimension_ = 18;
+
   // The vertical bar icon dimension of list views in search result page view.
   const int search_list_icon_vertical_bar_dimension_ = 48;
 
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index 538f36b8..ac5b818 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -186,11 +186,12 @@
 };
 
 // The rich entity subtype of Omnibox results.
-enum SearchResultOmniboxType {
+enum SearchResultOmniboxDisplayType {
   kDefault,
   kAnswer,
   kCalculatorAnswer,
   kRichImage,
+  kFavicon,
   kOmniboxTypeMax,  // Do not use.
 };
 
@@ -304,7 +305,8 @@
   SearchResultDisplayIndex display_index = SearchResultDisplayIndex::kUndefined;
 
   // The rich entity subtype of Omnibox results.
-  SearchResultOmniboxType omnibox_type = SearchResultOmniboxType::kDefault;
+  SearchResultOmniboxDisplayType omnibox_type =
+      SearchResultOmniboxDisplayType::kDefault;
 
   // A score to settle conflicts between two apps with the same requested
   // |display_index|.
diff --git a/ash/public/cpp/app_list/vector_icons/BUILD.gn b/ash/public/cpp/app_list/vector_icons/BUILD.gn
index c29fb5ac..c92cb549 100644
--- a/ash/public/cpp/app_list/vector_icons/BUILD.gn
+++ b/ash/public/cpp/app_list/vector_icons/BUILD.gn
@@ -13,12 +13,12 @@
     "badge_play.icon",
     "badge_rating.icon",
     "bookmark.icon",
-    "domain.icon",
     "equal.icon",
     "google_black.icon",
     "google_color.icon",
     "history.icon",
     "mic_black.icon",
+    "omnibox_generic.icon",
     "search.icon",
     "search_engine_not_google.icon",
     "search_result_append.icon",
diff --git a/ash/public/cpp/app_list/vector_icons/domain.icon b/ash/public/cpp/app_list/vector_icons/domain.icon
deleted file mode 100644
index a0694b7a..0000000
--- a/ash/public/cpp/app_list/vector_icons/domain.icon
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 20,
-MOVE_TO, 4, 3,
-R_H_LINE_TO, 7,
-R_LINE_TO, 5, 5,
-R_V_LINE_TO, 9,
-H_LINE_TO, 4,
-V_LINE_TO, 3,
-CLOSE,
-R_MOVE_TO, 5, 2,
-H_LINE_TO, 6,
-R_V_LINE_TO, 10,
-R_H_LINE_TO, 8,
-V_LINE_TO, 10,
-H_LINE_TO, 9,
-V_LINE_TO, 5,
-CLOSE,
-R_MOVE_TO, 5, 3,
-LINE_TO, 11, 5,
-R_V_LINE_TO, 3,
-R_H_LINE_TO, 3,
-CLOSE
diff --git a/ash/public/cpp/app_list/vector_icons/omnibox_generic.icon b/ash/public/cpp/app_list/vector_icons/omnibox_generic.icon
new file mode 100644
index 0000000..7f0ef652
--- /dev/null
+++ b/ash/public/cpp/app_list/vector_icons/omnibox_generic.icon
@@ -0,0 +1,32 @@
+CANVAS_DIMENSIONS, 20,
+NEW_PATH,
+MOVE_TO, 10, 2,
+CUBIC_TO, 5.58f, 2, 2, 5.58f, 2, 10,
+CUBIC_TO, 2, 14.42f, 5.58f, 18, 10, 18,
+CUBIC_TO, 14.42f, 18, 18, 14.42f, 18, 10,
+CUBIC_TO, 18, 5.58f, 14.42f, 2, 10, 2,
+CLOSE,
+MOVE_TO, 3.6f, 10,
+CUBIC_TO, 3.6f, 9.51f, 3.66f, 9.03f, 3.77f, 8.58f,
+LINE_TO, 7.59f, 12.4f,
+V_LINE_TO, 13.2f,
+CUBIC_TO, 7.59f, 14.08f, 8.31f, 14.8f, 9.19f, 14.8f,
+V_LINE_TO, 16.34f,
+CUBIC_TO, 6.05f, 15.94f, 3.6f, 13.26f, 3.6f, 10,
+CLOSE,
+MOVE_TO, 14.71f, 14.32f,
+CUBIC_TO, 14.5f, 13.67f, 13.91f, 13.2f, 13.19f, 13.2f,
+H_LINE_TO, 12.39f,
+V_LINE_TO, 10.8f,
+CUBIC_TO, 12.39f, 10.36f, 12.03f, 10, 11.59f, 10,
+H_LINE_TO, 6.79f,
+V_LINE_TO, 8.4f,
+H_LINE_TO, 8.39f,
+CUBIC_TO, 8.83f, 8.4f, 9.19f, 8.04f, 9.19f, 7.6f,
+V_LINE_TO, 6,
+H_LINE_TO, 10.79f,
+CUBIC_TO, 11.67f, 6, 12.39f, 5.28f, 12.39f, 4.4f,
+V_LINE_TO, 4.07f,
+CUBIC_TO, 14.74f, 5.02f, 16.4f, 7.32f, 16.4f, 10,
+CUBIC_TO, 16.4f, 11.66f, 15.75f, 13.18f, 14.71f, 14.32f,
+CLOSE
diff --git a/ash/public/cpp/holding_space/holding_space_item.cc b/ash/public/cpp/holding_space/holding_space_item.cc
index ec623862..2e1c8fb 100644
--- a/ash/public/cpp/holding_space/holding_space_item.cc
+++ b/ash/public/cpp/holding_space/holding_space_item.cc
@@ -37,7 +37,7 @@
 bool HoldingSpaceItem::operator==(const HoldingSpaceItem& rhs) const {
   return type_ == rhs.type_ && id_ == rhs.id_ && file_path_ == rhs.file_path_ &&
          file_system_url_ == rhs.file_system_url_ && text_ == rhs.text_ &&
-         *image_ == *rhs.image_;
+         *image_ == *rhs.image_ && progress_ == rhs.progress_;
 }
 
 // static
@@ -46,13 +46,24 @@
     const base::FilePath& file_path,
     const GURL& file_system_url,
     ImageResolver image_resolver) {
+  return CreateFileBackedItem(type, file_path, file_system_url,
+                              /*progress=*/1.f, std::move(image_resolver));
+}
+
+// static
+std::unique_ptr<HoldingSpaceItem> HoldingSpaceItem::CreateFileBackedItem(
+    Type type,
+    const base::FilePath& file_path,
+    const GURL& file_system_url,
+    const base::Optional<float>& progress,
+    ImageResolver image_resolver) {
   DCHECK(!file_system_url.is_empty());
 
   // Note: std::make_unique does not work with private constructors.
   return base::WrapUnique(new HoldingSpaceItem(
       type, /*id=*/base::UnguessableToken::Create().ToString(), file_path,
       file_system_url, file_path.BaseName().LossyDisplayName(),
-      std::move(image_resolver).Run(type, file_path)));
+      std::move(image_resolver).Run(type, file_path), progress));
 }
 
 // static
@@ -86,7 +97,7 @@
   return base::WrapUnique(new HoldingSpaceItem(
       type, DeserializeId(dict), file_path,
       /*file_system_url=*/GURL(), file_path.BaseName().LossyDisplayName(),
-      std::move(image_resolver).Run(type, file_path)));
+      std::move(image_resolver).Run(type, file_path), /*progress=*/1.f));
 }
 
 // static
@@ -145,12 +156,31 @@
   file_system_url_ = file_system_url;
 }
 
-void HoldingSpaceItem::UpdateBackingFile(const base::FilePath& file_path,
+bool HoldingSpaceItem::UpdateBackingFile(const base::FilePath& file_path,
                                          const GURL& file_system_url) {
+  if (file_path_ == file_path && file_system_url_ == file_system_url)
+    return false;
+
   file_path_ = file_path;
   file_system_url_ = file_system_url;
   text_ = file_path.BaseName().LossyDisplayName();
   image_->UpdateBackingFilePath(file_path);
+
+  return true;
+}
+
+bool HoldingSpaceItem::UpdateProgress(const base::Optional<float>& progress) {
+  // NOTE: Once set to `1.f`, `progress_` becomes read-only.
+  if (progress_ == progress || progress_ == 1.f)
+    return false;
+
+  if (progress.has_value()) {
+    DCHECK_GE(progress.value(), 0.f);
+    DCHECK_LE(progress.value(), 1.f);
+  }
+
+  progress_ = progress;
+  return true;
 }
 
 void HoldingSpaceItem::InvalidateImage() {
@@ -177,12 +207,19 @@
                                    const base::FilePath& file_path,
                                    const GURL& file_system_url,
                                    const std::u16string& text,
-                                   std::unique_ptr<HoldingSpaceImage> image)
+                                   std::unique_ptr<HoldingSpaceImage> image,
+                                   const base::Optional<float>& progress)
     : type_(type),
       id_(id),
       file_path_(file_path),
       file_system_url_(file_system_url),
       text_(text),
-      image_(std::move(image)) {}
+      image_(std::move(image)),
+      progress_(progress) {
+  if (progress_.has_value()) {
+    DCHECK_GE(progress_.value(), 0.f);
+    DCHECK_LE(progress_.value(), 1.f);
+  }
+}
 
 }  // namespace ash
diff --git a/ash/public/cpp/holding_space/holding_space_item.h b/ash/public/cpp/holding_space/holding_space_item.h
index a9e0139d..235abbb 100644
--- a/ash/public/cpp/holding_space/holding_space_item.h
+++ b/ash/public/cpp/holding_space/holding_space_item.h
@@ -12,6 +12,7 @@
 #include "base/callback_forward.h"
 #include "base/callback_list.h"
 #include "base/files/file_path.h"
+#include "base/optional.h"
 #include "url/gurl.h"
 
 namespace base {
@@ -22,8 +23,7 @@
 
 class HoldingSpaceImage;
 
-// Contains data needed to display a single item in the temporary holding space
-// UI.
+// Contains data needed to display a single item in the holding space UI.
 class ASH_PUBLIC_EXPORT HoldingSpaceItem {
  public:
   // Items types supported by the holding space.
@@ -59,6 +59,16 @@
       const GURL& file_system_url,
       ImageResolver image_resolver);
 
+  // Creates a HoldingSpaceItem that's backed by a file system URL.
+  // NOTE: `file_system_url` is expected to be non-empty.
+  // NOTE: If present, `progress` must be >= `0.f` and <= `1.f`.
+  static std::unique_ptr<HoldingSpaceItem> CreateFileBackedItem(
+      Type type,
+      const base::FilePath& file_path,
+      const GURL& file_system_url,
+      const base::Optional<float>& progress,
+      ImageResolver image_resolver);
+
   // Returns `true` if `type` is a download type, `false` otherwise.
   static bool IsDownload(HoldingSpaceItem::Type type);
 
@@ -91,10 +101,16 @@
   // `Deserialize()`.
   void Initialize(const GURL& file_system_url);
 
-  // Updates the file backing the item to `file_path` and `file_system_url`.
-  void UpdateBackingFile(const base::FilePath& file_path,
+  // Updates the file backing the item to `file_path` and `file_system_url`,
+  // returning `false` to indicate no-op.
+  bool UpdateBackingFile(const base::FilePath& file_path,
                          const GURL& file_system_url);
 
+  // Updates the `progress_` of the item, returning `false` to indicate no-op.
+  // NOTE: If present, `progress` must be >= `0.f` and <= `1.f`.
+  // NOTE: Once set to `1.f`, `progress_` becomes read-only.
+  bool UpdateProgress(const base::Optional<float>& progress);
+
   // Invalidates the current holding space image, so fresh image representations
   // are loaded when the image is next needed.
   void InvalidateImage();
@@ -114,6 +130,8 @@
 
   const GURL& file_system_url() const { return file_system_url_; }
 
+  const base::Optional<float>& progress() const { return progress_; }
+
   HoldingSpaceImage& image_for_testing() { return *image_; }
 
  private:
@@ -123,7 +141,8 @@
                    const base::FilePath& file_path,
                    const GURL& file_system_url,
                    const std::u16string& text,
-                   std::unique_ptr<HoldingSpaceImage> image);
+                   std::unique_ptr<HoldingSpaceImage> image,
+                   const base::Optional<float>& progress);
 
   const Type type_;
 
@@ -142,6 +161,11 @@
   // The image representation of the item.
   std::unique_ptr<HoldingSpaceImage> image_;
 
+  // The progress of the item.
+  // If present, the value is >= `0.f` and <= `1.f`.
+  // If absent, `progress_` is indeterminate.
+  base::Optional<float> progress_;
+
   // Mutable to allow const access from `AddDeletionCallback()`.
   mutable base::RepeatingClosureList deletion_callback_list_;
 };
diff --git a/ash/public/cpp/holding_space/holding_space_item_unittest.cc b/ash/public/cpp/holding_space/holding_space_item_unittest.cc
index c6be05e..cf5d90a 100644
--- a/ash/public/cpp/holding_space/holding_space_item_unittest.cc
+++ b/ash/public/cpp/holding_space/holding_space_item_unittest.cc
@@ -76,6 +76,51 @@
   EXPECT_EQ(deserialized_holding_space_id, holding_space_item->id());
 }
 
+// Tests progress for each holding space item type.
+TEST_P(HoldingSpaceItemTest, Progress) {
+  // Create a `holding_space_item` w/ explicitly specified progress.
+  auto holding_space_item = HoldingSpaceItem::CreateFileBackedItem(
+      /*type=*/GetParam(), base::FilePath("file_path"),
+      GURL("filesystem::file_system_url"), /*progress=*/0.5f,
+      /*image_resolver=*/base::BindOnce(&CreateFakeHoldingSpaceImage));
+
+  // Since explicitly specified during construction, progress should be `0.5`.
+  EXPECT_EQ(holding_space_item->progress(), 0.5f);
+
+  // It should be possible to update progress to a new value.
+  EXPECT_TRUE(holding_space_item->UpdateProgress(0.75f));
+  EXPECT_EQ(holding_space_item->progress(), 0.75f);
+
+  // It should no-op to try to update progress to its existing value.
+  EXPECT_FALSE(holding_space_item->UpdateProgress(0.75f));
+  EXPECT_EQ(holding_space_item->progress(), 0.75f);
+
+  // It should be possible to set indeterminate progress.
+  EXPECT_TRUE(holding_space_item->UpdateProgress(base::nullopt));
+  EXPECT_EQ(holding_space_item->progress(), base::nullopt);
+
+  // It should be possible to set progress complete.
+  EXPECT_TRUE(holding_space_item->UpdateProgress(1.f));
+  EXPECT_EQ(holding_space_item->progress(), 1.f);
+
+  // Once progress has been marked completed, it should become read-only.
+  EXPECT_FALSE(holding_space_item->UpdateProgress(0.75f));
+  EXPECT_EQ(holding_space_item->progress(), 1.f);
+
+  // Create a `holding_space_item` w/ default progress.
+  holding_space_item = HoldingSpaceItem::CreateFileBackedItem(
+      /*type=*/GetParam(), base::FilePath("file_path"),
+      GURL("filesystem::file_system_url"),
+      /*image_resolver=*/base::BindOnce(&CreateFakeHoldingSpaceImage));
+
+  // Since not specified during construction, progress should be `1.f`.
+  EXPECT_EQ(holding_space_item->progress(), 1.f);
+
+  // Since progress is marked completed, it should be read-only.
+  EXPECT_FALSE(holding_space_item->UpdateProgress(0.75f));
+  EXPECT_EQ(holding_space_item->progress(), 1.f);
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          HoldingSpaceItemTest,
                          testing::ValuesIn(GetHoldingSpaceItemTypes()));
diff --git a/ash/public/cpp/holding_space/holding_space_model.cc b/ash/public/cpp/holding_space/holding_space_model.cc
index 7bb22d5..a078bfb3 100644
--- a/ash/public/cpp/holding_space/holding_space_model.cc
+++ b/ash/public/cpp/holding_space/holding_space_model.cc
@@ -88,7 +88,28 @@
   HoldingSpaceItem* item = item_it->get();
   DCHECK(item->IsInitialized());
 
-  item->UpdateBackingFile(file_path, file_system_url);
+  if (!item->UpdateBackingFile(file_path, file_system_url))
+    return;
+
+  for (auto& observer : observers_)
+    observer.OnHoldingSpaceItemUpdated(item);
+}
+
+void HoldingSpaceModel::UpdateProgressForItem(
+    const std::string& id,
+    const base::Optional<float>& progress) {
+  auto item_it = std::find_if(
+      items_.begin(), items_.end(),
+      [&id](const std::unique_ptr<HoldingSpaceItem>& item) -> bool {
+        return item->id() == id;
+      });
+  DCHECK(item_it != items_.end());
+
+  HoldingSpaceItem* item = item_it->get();
+  DCHECK(item->IsInitialized());
+
+  if (!item->UpdateProgress(progress))
+    return;
 
   for (auto& observer : observers_)
     observer.OnHoldingSpaceItemUpdated(item);
diff --git a/ash/public/cpp/holding_space/holding_space_model.h b/ash/public/cpp/holding_space/holding_space_model.h
index 41066469..b7134d1f 100644
--- a/ash/public/cpp/holding_space/holding_space_model.h
+++ b/ash/public/cpp/holding_space/holding_space_model.h
@@ -15,6 +15,7 @@
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "base/callback.h"
 #include "base/observer_list.h"
+#include "base/optional.h"
 #include "url/gurl.h"
 
 namespace base {
@@ -65,6 +66,12 @@
                                 const base::FilePath& file_path,
                                 const GURL& file_system_url);
 
+  // Updates the progress for a single holding space item.
+  // NOTE: If present, `progress` must be >= `0.f` and <= `1.f`.
+  // NOTE: Once set to `1.f`, holding space item progress becomes read-only.
+  void UpdateProgressForItem(const std::string& id,
+                             const base::Optional<float>& progress);
+
   // Removes all holding space items from the model for which the specified
   // `predicate` returns true.
   using Predicate = base::RepeatingCallback<bool(const HoldingSpaceItem*)>;
diff --git a/ash/public/cpp/holding_space/holding_space_model_unittest.cc b/ash/public/cpp/holding_space/holding_space_model_unittest.cc
new file mode 100644
index 0000000..4944d99
--- /dev/null
+++ b/ash/public/cpp/holding_space/holding_space_model_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright 2021 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 "ash/public/cpp/holding_space/holding_space_model.h"
+
+#include <memory>
+#include <vector>
+
+#include "ash/public/cpp/holding_space/holding_space_image.h"
+#include "ash/public/cpp/holding_space/holding_space_item.h"
+#include "ash/public/cpp/holding_space/holding_space_model_observer.h"
+#include "base/scoped_observation.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+namespace {
+
+// Helpers ---------------------------------------------------------------------
+
+std::vector<HoldingSpaceItem::Type> GetHoldingSpaceItemTypes() {
+  std::vector<HoldingSpaceItem::Type> types;
+  for (int i = 0; i <= static_cast<int>(HoldingSpaceItem::Type::kMaxValue); ++i)
+    types.push_back(static_cast<HoldingSpaceItem::Type>(i));
+  return types;
+}
+
+std::unique_ptr<HoldingSpaceImage> CreateFakeHoldingSpaceImage(
+    HoldingSpaceItem::Type type,
+    const base::FilePath& file_path) {
+  return std::make_unique<HoldingSpaceImage>(
+      HoldingSpaceImage::GetMaxSizeForType(type), file_path,
+      /*async_bitmap_resolver=*/base::DoNothing());
+}
+
+// ScopedModelObservation ------------------------------------------------------
+
+// A class which observes a `HoldingSpaceModel` within its lifetime. Note that
+// this class must not outlive the `model` that it observes.
+class ScopedModelObservation : public HoldingSpaceModelObserver {
+ public:
+  explicit ScopedModelObservation(HoldingSpaceModel* model) {
+    observation_.Observe(model);
+  }
+
+  ScopedModelObservation(const ScopedModelObservation&) = delete;
+  ScopedModelObservation& operator=(const ScopedModelObservation&) = delete;
+  ~ScopedModelObservation() override = default;
+
+  // Returns the last `HoldingSpaceItem` for which `OnHoldingSpaceItemUpdated()`
+  // was called, clearing the cached value.
+  const HoldingSpaceItem* TakeLastUpdatedItem() {
+    const HoldingSpaceItem* result = last_updated_item_;
+    last_updated_item_ = nullptr;
+    return result;
+  }
+
+ private:
+  // HoldingSpaceModel::Observer:
+  void OnHoldingSpaceItemUpdated(const HoldingSpaceItem* item) override {
+    last_updated_item_ = item;
+  }
+
+  // The last `HoldingSpaceItem` for which `OnHoldingSpaceItemUpdated()` was
+  // called. May be `nullptr` prior to an update event or following a call to
+  // `TakeLastUpdatedItem()`.
+  const HoldingSpaceItem* last_updated_item_ = nullptr;
+
+  base::ScopedObservation<HoldingSpaceModel, HoldingSpaceModelObserver>
+      observation_{this};
+};
+
+}  // namespace
+
+// HoldingSpaceModelTest -------------------------------------------------------
+
+// Base class for `HoldingSpaceModel` tests, parameterized by the set of all
+// holding space item types.
+class HoldingSpaceModelTest
+    : public testing::TestWithParam<HoldingSpaceItem::Type> {
+ public:
+  // Returns the `HoldingSpaceModel` under test.
+  HoldingSpaceModel& model() { return model_; }
+
+ private:
+  HoldingSpaceModel model_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         HoldingSpaceModelTest,
+                         testing::ValuesIn(GetHoldingSpaceItemTypes()));
+
+// Tests -----------------------------------------------------------------------
+
+// Verifies that `HoldingSpaceModel::UpdateProgressForItem()` works as intended.
+TEST_P(HoldingSpaceModelTest, UpdateProgressForItem) {
+  ScopedModelObservation observation(&model());
+
+  // Verify the `model()` is initially empty.
+  EXPECT_EQ(model().items().size(), 0u);
+
+  // Create a holding space `item`.
+  auto item = HoldingSpaceItem::CreateFileBackedItem(
+      /*type=*/GetParam(), base::FilePath("file_path"),
+      GURL("filesystem::file_system_url"),
+      /*progress=*/base::nullopt,
+      /*image_resolver=*/base::BindOnce(&CreateFakeHoldingSpaceImage));
+  auto* item_ptr = item.get();
+
+  // Add `item` to the `model()`.
+  model().AddItem(std::move(item));
+  EXPECT_EQ(model().items().size(), 1u);
+  EXPECT_EQ(model().items()[0].get(), item_ptr);
+
+  // Verify progress is indeterminate.
+  EXPECT_EQ(item_ptr->progress(), base::nullopt);
+
+  // Update progress to `0.5f`.
+  model().UpdateProgressForItem(item_ptr->id(), 0.5f);
+  EXPECT_EQ(observation.TakeLastUpdatedItem(), item_ptr);
+  EXPECT_EQ(item_ptr->progress(), 0.5f);
+
+  // Update progress to `0.5f` again. This should no-op.
+  model().UpdateProgressForItem(item_ptr->id(), 0.5f);
+  EXPECT_FALSE(observation.TakeLastUpdatedItem());
+  EXPECT_EQ(item_ptr->progress(), 0.5f);
+
+  // Update progress to indeterminate.
+  model().UpdateProgressForItem(item_ptr->id(), base::nullopt);
+  EXPECT_EQ(observation.TakeLastUpdatedItem(), item_ptr);
+  EXPECT_EQ(item_ptr->progress(), base::nullopt);
+
+  // Update progress to complete.
+  model().UpdateProgressForItem(item_ptr->id(), 1.f);
+  EXPECT_EQ(observation.TakeLastUpdatedItem(), item_ptr);
+  EXPECT_EQ(item_ptr->progress(), 1.f);
+
+  // Update progress to `0.5f`. This should no-op as progress becomes read-only
+  // after being marked completed.
+  model().UpdateProgressForItem(item_ptr->id(), 0.5f);
+  EXPECT_FALSE(observation.TakeLastUpdatedItem());
+  EXPECT_EQ(item_ptr->progress(), 1.f);
+}
+
+}  // namespace ash
diff --git a/ash/search_box/search_box_view_base.cc b/ash/search_box/search_box_view_base.cc
index 6fbcd1b..5c7a14e 100644
--- a/ash/search_box/search_box_view_base.cc
+++ b/ash/search_box/search_box_view_base.cc
@@ -106,6 +106,22 @@
           return highlight;
         },
         this));
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](SearchBoxImageButton* host)
+            -> std::unique_ptr<views::InkDropRipple> {
+          const gfx::Point center = host->GetLocalBounds().CenterPoint();
+          const int ripple_radius = host->GetInkDropRadius();
+          gfx::Rect bounds(center.x() - ripple_radius,
+                           center.y() - ripple_radius, 2 * ripple_radius,
+                           2 * ripple_radius);
+          constexpr SkColor ripple_color =
+              SkColorSetA(gfx::kGoogleGrey900, 0x17);
+
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+              host->GetInkDropCenterBasedOnLastEvent(), ripple_color, 1.0f);
+        },
+        this));
 
     SetPreferredSize({kSearchBoxButtonSizeDip, kSearchBoxButtonSizeDip});
     SetImageHorizontalAlignment(ALIGN_CENTER);
@@ -137,19 +153,6 @@
     SchedulePaint();
   }
 
-  // views::InkDropHostView:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
-    const gfx::Point center = GetLocalBounds().CenterPoint();
-    const int ripple_radius = GetInkDropRadius();
-    gfx::Rect bounds(center.x() - ripple_radius, center.y() - ripple_radius,
-                     2 * ripple_radius, 2 * ripple_radius);
-    constexpr SkColor ripple_color = SkColorSetA(gfx::kGoogleGrey900, 0x17);
-
-    return std::make_unique<views::FloodFillInkDropRipple>(
-        size(), GetLocalBounds().InsetsFrom(bounds),
-        GetInkDropCenterBasedOnLastEvent(), ripple_color, 1.0f);
-  }
-
  private:
   int GetInkDropRadius() const { return width() / 2; }
 
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index 7a7d1ac..ad111982 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -286,6 +286,20 @@
   };
   icon_shadows_.assign(kShadows, kShadows + base::size(kShadows));
 
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](ShelfAppButton* host) -> std::unique_ptr<views::InkDropRipple> {
+        const gfx::Rect small_ripple_area = host->CalculateSmallRippleArea();
+        const int ripple_size = host->shelf_view_->GetShelfItemRippleSize();
+
+        return std::make_unique<views::SquareInkDropRipple>(
+            gfx::Size(ripple_size, ripple_size),
+            host->GetInkDropLargeCornerRadius(), small_ripple_area.size(),
+            host->GetInkDropSmallCornerRadius(),
+            small_ripple_area.CenterPoint(), host->GetInkDropBaseColor(),
+            host->GetInkDropVisibleOpacity());
+      },
+      this));
+
   // TODO: refactor the layers so each button doesn't require 3.
   // |icon_view_| needs its own layer so it can be scaled up independently of
   // the ink drop ripple.
@@ -786,18 +800,6 @@
     return Button::OnGestureEvent(event);
 }
 
-std::unique_ptr<views::InkDropRipple> ShelfAppButton::CreateInkDropRipple()
-    const {
-  const gfx::Rect small_ripple_area = CalculateSmallRippleArea();
-  const int ripple_size = shelf_view_->GetShelfItemRippleSize();
-
-  return std::make_unique<views::SquareInkDropRipple>(
-      gfx::Size(ripple_size, ripple_size), GetInkDropLargeCornerRadius(),
-      small_ripple_area.size(), GetInkDropSmallCornerRadius(),
-      small_ripple_area.CenterPoint(), GetInkDropBaseColor(),
-      GetInkDropVisibleOpacity());
-}
-
 bool ShelfAppButton::HandleAccessibleAction(
     const ui::AXActionData& action_data) {
   if (notification_indicator_ && notification_indicator_->GetVisible())
diff --git a/ash/shelf/shelf_app_button.h b/ash/shelf/shelf_app_button.h
index 4c253c2..bcbadfe 100644
--- a/ash/shelf/shelf_app_button.h
+++ b/ash/shelf/shelf_app_button.h
@@ -127,9 +127,6 @@
   // ui::EventHandler:
   void OnGestureEvent(ui::GestureEvent* event) override;
 
-  // views::Button:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-
   // ui::ImplicitAnimationObserver:
   void OnImplicitAnimationsCompleted() override;
 
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 9e619e7..30c46b95 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -525,6 +525,10 @@
   CloseBubble();
 }
 
+void HoldingSpaceTray::OnShouldShowAnimationChanged(bool should_animate) {
+  previews_tray_icon_->set_should_animate_updates(should_animate);
+}
+
 void HoldingSpaceTray::OnHoldingSpaceModelAttached(HoldingSpaceModel* model) {
   // When the `model` is attached the session is either being started/unlocked
   // or the active profile is being changed. It's also possible that the status
@@ -682,12 +686,6 @@
 
 void HoldingSpaceTray::OnSessionStateChanged(
     session_manager::SessionState state) {
-  // If the session is blocked the holding space tray should *not* bounce or
-  // animate previews when the session becomes unblocked. Note that the holding
-  // space tray is not visible if the session is blocked.
-  if (Shell::Get()->session_controller()->IsUserSessionBlocked())
-    SetShouldAnimate(false);
-
   UpdateVisibility();
 }
 
@@ -802,8 +800,14 @@
 }
 
 void HoldingSpaceTray::SetShouldAnimate(bool should_animate) {
-  set_use_bounce_in_animation(should_animate);
-  previews_tray_icon_->set_should_animate_updates(should_animate);
+  if (!should_animate) {
+    if (!animation_disabler_) {
+      animation_disabler_ =
+          std::make_unique<base::ScopedClosureRunner>(DisableShowAnimation());
+    }
+  } else if (animation_disabler_) {
+    animation_disabler_.reset();
+  }
 }
 
 BEGIN_METADATA(HoldingSpaceTray, TrayBackgroundView)
diff --git a/ash/system/holding_space/holding_space_tray.h b/ash/system/holding_space/holding_space_tray.h
index e08adc0..6007279 100644
--- a/ash/system/holding_space/holding_space_tray.h
+++ b/ash/system/holding_space/holding_space_tray.h
@@ -88,6 +88,7 @@
   void Layout() override;
   void VisibilityChanged(views::View* starting_from, bool is_visible) override;
   void OnThemeChanged() override;
+  void OnShouldShowAnimationChanged(bool should_animate) override;
 
   void set_use_zero_previews_update_delay_for_testing(bool zero_delay) {
     use_zero_previews_update_delay_ = zero_delay;
@@ -221,6 +222,9 @@
   base::ScopedObservation<views::Widget, views::WidgetObserver>
       widget_observer_{this};
 
+  // Animation will be disabled for the lifetime of this variable.
+  std::unique_ptr<base::ScopedClosureRunner> animation_disabler_;
+
   base::WeakPtrFactory<HoldingSpaceTray> weak_factory_{this};
 };
 
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index ea23dbc..cbb3ea6c 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -270,7 +270,8 @@
 
 class HoldingSpaceTrayTest : public AshTestBase {
  public:
-  HoldingSpaceTrayTest() = default;
+  HoldingSpaceTrayTest()
+      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
 
   // AshTestBase:
   void SetUp() override {
@@ -659,9 +660,9 @@
   GetTray()->FirePreviewsUpdateTimerIfRunningForTesting();
 
   EXPECT_TRUE(test_api()->IsShowingInShelf());
-    ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
-    EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
-              test_api()->GetPreviewsTrayIcon()->size());
+  ASSERT_TRUE(IsViewVisible(test_api()->GetPreviewsTrayIcon()));
+  EXPECT_EQ(gfx::Size(kHoldingSpaceTrayIconDefaultPreviewSize, kTrayItemSize),
+            test_api()->GetPreviewsTrayIcon()->size());
 
   TabletModeControllerTestApi().EnterTabletMode();
 
@@ -2412,7 +2413,7 @@
 // Verifies that the holding space tray animates in and out as expected.
 TEST_F(HoldingSpaceTrayTest, EnterAndExitAnimations) {
   ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
-      ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
 
   // Prior to session start, the tray should not be showing.
   EXPECT_FALSE(test_api()->IsShowingInShelf());
@@ -2424,16 +2425,21 @@
   ScopedTransformRecordingLayerDelegate transform_recorder(tray->layer());
 
   // Start the session. Because a holding space item was added in a previous
-  // session (according to prefs state), the tray should animate in.
+  // session (according to prefs state), the tray should show up without
+  // animation.
   StartSession();
   EXPECT_TRUE(test_api()->IsShowingInShelf());
 
-  // The entry animation should be the default entry animation in which the
-  // tray translates in from the right without scaling.
-  EXPECT_FALSE(transform_recorder.DidScale());
-  EXPECT_TRUE(transform_recorder.TranslatedFrom({0.f, 0.f}, {0.f, 0.f}));
-  EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {44.f, 0.f}));
-  transform_recorder.Reset();
+  // Gives it a small duration to let the session get changed. This duration is
+  // way smaller than the animation duration, so that the animation will not
+  // finish when this duration ends. The same for the other places below.
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+
+  EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
+
+  // Gives 3s duration to let the (if there is any) animation finish. The same
+  // for the other places below.
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
 
   // Pin a holding space item. Because the tray was already showing there
   // should be no change in tray visibility.
@@ -2449,6 +2455,7 @@
   // Remove all holding space items. Because a holding space item was
   // previously pinned, the tray should animate out.
   RemoveAllItems();
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
   EXPECT_FALSE(test_api()->IsShowingInShelf());
 
   // The exit animation should be the default exit animation in which the tray
@@ -2463,6 +2470,8 @@
   AddItem(HoldingSpaceItem::Type::kPinnedFile, base::FilePath("/tmp/fake2"));
   EXPECT_TRUE(test_api()->IsShowingInShelf());
 
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
+
   // The entry animation should be the bounce in animation in which the tray
   // translates in vertically with scaling (since it previously scaled out).
   EXPECT_TRUE(transform_recorder.ScaledFrom({0.5f, 0.5f}, {1.f, 1.f}));
@@ -2475,6 +2484,7 @@
   auto* session_controller =
       ash_test_helper()->test_session_controller_client();
   session_controller->LockScreen();
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
   EXPECT_FALSE(test_api()->IsShowingInShelf());
 
   // The exit animation should be the default exit animation in which the tray
@@ -2485,19 +2495,17 @@
   EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {11.f, 12.f}));
   transform_recorder.Reset();
 
-  // Unlock the screen. The tray should animate in.
+  // Unlock the screen. The tray should show up without animation.
   session_controller->UnlockScreen();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+
+  EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
+
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
   EXPECT_TRUE(test_api()->IsShowingInShelf());
 
-  // The entry animation should be the default entry animation in which the
-  // tray translates in from the right.
-  EXPECT_TRUE(transform_recorder.ScaledFrom({0.5f, 0.5f}, {1.f, 1.f}));
-  EXPECT_TRUE(transform_recorder.ScaledInRange({0.5f, 0.5f}, {1.f, 1.f}));
-  EXPECT_TRUE(transform_recorder.TranslatedFrom({11.f, 12.f}, {0.f, 0.f}));
-  EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {44.f, 12.f}));
-  transform_recorder.Reset();
-
-  // Switch to another user with a populated model. The tray should animate in.
+  // Switch to another user with a populated model. The tray should show up
+  // without animation.
   constexpr char kSecondaryUserId[] = "user@secondary";
   HoldingSpaceModel secondary_holding_space_model;
   AddItemToModel(&secondary_holding_space_model,
@@ -2505,15 +2513,13 @@
                  base::FilePath("/tmp/fake3"));
   SwitchToSecondaryUser(kSecondaryUserId, /*client=*/nullptr,
                         &secondary_holding_space_model);
-  EXPECT_TRUE(test_api()->IsShowingInShelf());
 
-  // The entry animation should be the default entry animation in which the
-  // tray translates in from the right.
-  EXPECT_TRUE(transform_recorder.ScaledFrom({1.f, 1.f}, {1.f, 1.f}));
-  EXPECT_TRUE(transform_recorder.ScaledInRange({0.5f, 0.5f}, {1.f, 1.f}));
-  EXPECT_TRUE(transform_recorder.TranslatedFrom({0.f, 0.f}, {0.f, 0.f}));
-  EXPECT_TRUE(transform_recorder.TranslatedInRange({0.f, 0.f}, {44.f, 12.f}));
-  transform_recorder.Reset();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+
+  EXPECT_FALSE(tray->layer()->GetAnimator()->is_animating());
+
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
+  EXPECT_TRUE(test_api()->IsShowingInShelf());
 
   // Clean up.
   UnregisterModelForUser(kSecondaryUserId);
diff --git a/ash/system/message_center/stacked_notification_bar.cc b/ash/system/message_center/stacked_notification_bar.cc
index 6216e3ab..254a363e 100644
--- a/ash/system/message_center/stacked_notification_bar.cc
+++ b/ash/system/message_center/stacked_notification_bar.cc
@@ -50,9 +50,10 @@
     TrayPopupUtils::ConfigureTrayPopupButton(
         this, TrayPopupInkDropStyle::FILL_BOUNDS, /*highlight_on_hover=*/true,
         /*highlight_on_focus=*/true);
-    // SetCreateInkDropHighlightCallback is explicitly called after
-    // ConfigureTrayPopupButton as the former configures the InkDrop and this
-    // overrides that behavior.
+    // SetCreateInkDropHighlightCallback and SetCreateInkDropRippleCallback are
+    // explicitly called after ConfigureTrayPopupButton as
+    // ConfigureTrayPopupButton configures the InkDrop and these callbacks
+    // override that behavior.
     SetCreateInkDropHighlightCallback(base::BindRepeating(
         [](InkDropHostView* host) {
           auto highlight = std::make_unique<views::InkDropHighlight>(
@@ -62,6 +63,14 @@
           return highlight;
         },
         this));
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+              message_center_style::kInkRippleColor,
+              message_center_style::kInkRippleOpacity);
+        },
+        this));
   }
 
   ~StackingBarLabelButton() override = default;
@@ -90,15 +99,6 @@
     views::LabelButton::PaintButtonContents(canvas);
   }
 
-  // TODO(crbug.com/1204653): Set these as callbacks in the constructor (after
-  // ConfigureTrayPopupButton, which does configure InkDrop callbacks).
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
-    return std::make_unique<views::FloodFillInkDropRipple>(
-        size(), GetInkDropCenterBasedOnLastEvent(),
-        message_center_style::kInkRippleColor,
-        message_center_style::kInkRippleOpacity);
-  }
-
  private:
   UnifiedMessageCenterView* message_center_view_;
   DISALLOW_COPY_AND_ASSIGN(StackingBarLabelButton);
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index 2cb9f88..a46f99f 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -52,6 +52,8 @@
   // horizontal shelf, no separator is required.
   set_separator_visibility(false);
 
+  set_use_bounce_in_animation(false);
+
   Shell::Get()->overview_controller()->AddObserver(this);
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
   Shell::Get()->shelf_config()->AddObserver(this);
diff --git a/ash/system/session/logout_button_tray.cc b/ash/system/session/logout_button_tray.cc
index 5865ec3..42a22a33 100644
--- a/ash/system/session/logout_button_tray.cc
+++ b/ash/system/session/logout_button_tray.cc
@@ -41,6 +41,7 @@
                               base::Unretained(this)),
           std::u16string(), CONTEXT_LAUNCHER_BUTTON));
   button_->SetProminent(true);
+  set_use_bounce_in_animation(false);
 }
 
 LogoutButtonTray::~LogoutButtonTray() {
diff --git a/ash/system/tray/actionable_view.cc b/ash/system/tray/actionable_view.cc
index 3afe0cf..c88a4444 100644
--- a/ash/system/tray/actionable_view.cc
+++ b/ash/system/tray/actionable_view.cc
@@ -39,6 +39,11 @@
       this));
   SetCreateInkDropHighlightCallback(base::BindRepeating(
       &TrayPopupUtils::CreateInkDropHighlight, base::Unretained(this)));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](ActionableView* host) {
+        return TrayPopupUtils::CreateInkDropRipple(host->ink_drop_style_, host);
+      },
+      this));
 }
 
 ActionableView::~ActionableView() {
@@ -71,11 +76,6 @@
 }
 
 
-std::unique_ptr<views::InkDropRipple> ActionableView::CreateInkDropRipple()
-    const {
-  return TrayPopupUtils::CreateInkDropRipple(ink_drop_style_, this);
-}
-
 void ActionableView::ButtonPressed(const ui::Event& event) {
   bool destroyed = false;
   destroyed_ = &destroyed;
diff --git a/ash/system/tray/actionable_view.h b/ash/system/tray/actionable_view.h
index 5d7e75b4..21079462 100644
--- a/ash/system/tray/actionable_view.h
+++ b/ash/system/tray/actionable_view.h
@@ -48,7 +48,6 @@
   const char* GetClassName() const override;
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
 
  private:
   void ButtonPressed(const ui::Event& event);
diff --git a/ash/system/tray/status_area_overflow_button_tray.cc b/ash/system/tray/status_area_overflow_button_tray.cc
index 86f4b8d..824d26c 100644
--- a/ash/system/tray/status_area_overflow_button_tray.cc
+++ b/ash/system/tray/status_area_overflow_button_tray.cc
@@ -93,6 +93,7 @@
 StatusAreaOverflowButtonTray::StatusAreaOverflowButtonTray(Shelf* shelf)
     : TrayBackgroundView(shelf), icon_(new IconView()) {
   tray_container()->AddChildView(icon_);
+  set_use_bounce_in_animation(false);
 }
 
 StatusAreaOverflowButtonTray::~StatusAreaOverflowButtonTray() {}
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index eb03adf..5e279ee1 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -10,8 +10,10 @@
 #include "ash/focus_cycler.h"
 #include "ash/login/ui/lock_screen.h"
 #include "ash/public/cpp/ash_constants.h"
+#include "ash/public/cpp/session/session_observer.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/session/session_controller_impl.h"
 #include "ash/shelf/login_shelf_view.h"
 #include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -160,6 +162,41 @@
   DISALLOW_COPY_AND_ASSIGN(TrayWidgetObserver);
 };
 
+// Handles `TrayBackgroundView`'s animation on session changed.
+class TrayBackgroundView::TrayBackgroundViewSessionChangeHandler
+    : public SessionObserver {
+ public:
+  explicit TrayBackgroundViewSessionChangeHandler(
+      TrayBackgroundView* tray_background_view)
+      : tray_(tray_background_view) {
+    DCHECK(tray_);
+  }
+  TrayBackgroundViewSessionChangeHandler(
+      const TrayBackgroundViewSessionChangeHandler&) = delete;
+  TrayBackgroundViewSessionChangeHandler& operator=(
+      const TrayBackgroundViewSessionChangeHandler&) = delete;
+  ~TrayBackgroundViewSessionChangeHandler() override = default;
+
+ private:  // SessionObserver:
+  void OnSessionStateChanged(session_manager::SessionState state) override {
+    DisableShowAnimationInSequence();
+  }
+  void OnActiveUserSessionChanged(const AccountId& account_id) override {
+    DisableShowAnimationInSequence();
+  }
+
+  // Disables the `TrayBackgroundView`'s show animation until all queued tasks
+  // in the current task sequence are run.
+  void DisableShowAnimationInSequence() {
+    base::ScopedClosureRunner callback = tray_->DisableShowAnimation();
+    base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                     callback.Release());
+  }
+
+  TrayBackgroundView* const tray_;
+  ScopedSessionObserver session_observer_{this};
+};
+
 ////////////////////////////////////////////////////////////////////////////////
 // TrayBackgroundView
 
@@ -173,7 +210,8 @@
       visible_preferred_(false),
       show_with_virtual_keyboard_(false),
       show_when_collapsed_(true),
-      widget_observer_(new TrayWidgetObserver(this)) {
+      widget_observer_(new TrayWidgetObserver(this)),
+      handler_(new TrayBackgroundViewSessionChangeHandler(this)) {
   DCHECK(shelf_);
   SetNotifyEnterExitOnChild(true);
 
@@ -203,6 +241,16 @@
         return highlight;
       },
       this));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](TrayBackgroundView* host) -> std::unique_ptr<views::InkDropRipple> {
+        const AshColorProvider::RippleAttributes ripple_attributes =
+            AshColorProvider::Get()->GetRippleAttributes();
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetBackgroundInsets(),
+            host->GetInkDropCenterBasedOnLastEvent(),
+            ripple_attributes.base_color, ripple_attributes.inkdrop_opacity);
+      },
+      this));
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
   SetInstallFocusRingOnFocus(true);
@@ -232,6 +280,7 @@
 TrayBackgroundView::~TrayBackgroundView() {
   Shell::Get()->system_tray_model()->virtual_keyboard()->RemoveObserver(this);
   widget_observer_.reset();
+  handler_.reset();
 }
 
 void TrayBackgroundView::Initialize() {
@@ -280,18 +329,43 @@
     // layer->SetVisible(true) immediately interrupts the animation of this
     // property, and keeps the layer visible.
     layer()->SetVisible(true);
-  }
 
-  if (visible) {
-    if (use_bounce_in_animation_)
-      BounceInAnimation();
-    else
-      FadeInAnimation();
+    // We only show visible animation when `IsShowAnimationEnabled()`.
+    if (IsShowAnimationEnabled()) {
+      if (use_bounce_in_animation_)
+        BounceInAnimation();
+      else
+        FadeInAnimation();
+    } else {
+      // The opacity and scale of the `layer()` may have been manipulated, so
+      // reset it before it is shown.
+      layer()->SetOpacity(1.0f);
+      layer()->SetTransform(gfx::Transform());
+    }
   } else {
     HideAnimation();
   }
 }
 
+base::ScopedClosureRunner TrayBackgroundView::DisableShowAnimation() {
+  if (layer()->GetAnimator()->is_animating())
+    layer()->GetAnimator()->StopAnimating();
+
+  ++disable_show_animation_count_;
+  if (disable_show_animation_count_ == 1u)
+    OnShouldShowAnimationChanged(false);
+
+  return base::ScopedClosureRunner(base::BindOnce(
+      [](const base::WeakPtr<TrayBackgroundView>& ptr) {
+        if (ptr) {
+          --ptr->disable_show_animation_count_;
+          if (ptr->IsShowAnimationEnabled())
+            ptr->OnShouldShowAnimationChanged(true);
+        }
+      },
+      weak_factory_.GetWeakPtr()));
+}
+
 void TrayBackgroundView::UpdateStatusArea(bool should_log_visible_pod_count) {
   auto* status_area_widget = shelf_->GetStatusAreaWidget();
   if (status_area_widget) {
@@ -348,15 +422,6 @@
   return views::View::RecreateLayer();
 }
 
-std::unique_ptr<views::InkDropRipple> TrayBackgroundView::CreateInkDropRipple()
-    const {
-  const AshColorProvider::RippleAttributes ripple_attributes =
-      AshColorProvider::Get()->GetRippleAttributes();
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetBackgroundInsets(), GetInkDropCenterBasedOnLastEvent(),
-      ripple_attributes.base_color, ripple_attributes.inkdrop_opacity);
-}
-
 void TrayBackgroundView::OnThemeChanged() {
   ActionableView::OnThemeChanged();
   UpdateBackground();
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index 049f3205..17eecb9 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -14,6 +14,7 @@
 #include "ash/system/tray/tray_bubble_view.h"
 #include "ash/system/user/login_status.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/gfx/geometry/insets.h"
 
@@ -46,7 +47,6 @@
   static void InitializeBubbleAnimations(views::Widget* bubble_widget);
 
   // ActionableView:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
   void OnThemeChanged() override;
 
   // VirtualKeyboardModel::Observer:
@@ -145,6 +145,10 @@
   virtual void SetVisiblePreferred(bool visible_preferred);
   bool visible_preferred() const { return visible_preferred_; }
 
+  // Disables bounce in and fade in animation. The animation will remain
+  // disabled until the returned scoped closure runner is run.
+  base::ScopedClosureRunner DisableShowAnimation();
+
  protected:
   // ActionableView:
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
@@ -153,6 +157,8 @@
                                  const ui::Event& event) override;
   views::PaintInfo::ScaleType GetPaintScaleType() const override;
 
+  virtual void OnShouldShowAnimationChanged(bool should_animate) {}
+
   void set_show_with_virtual_keyboard(bool show_with_virtual_keyboard) {
     show_with_virtual_keyboard_ = show_with_virtual_keyboard;
   }
@@ -163,6 +169,7 @@
 
  private:
   class TrayWidgetObserver;
+  class TrayBackgroundViewSessionChangeHandler;
 
   void StartVisibilityAnimation(bool visible);
 
@@ -206,6 +213,11 @@
   // state.
   bool GetEffectiveVisibility();
 
+  // Checks if we should show bounce in or fade in animation.
+  bool IsShowAnimationEnabled() const {
+    return disable_show_animation_count_ == 0u;
+  }
+
   // The shelf containing the system tray for this view.
   Shelf* shelf_;
 
@@ -231,11 +243,17 @@
   // If true, the view is visible when the status area is collapsed.
   bool show_when_collapsed_;
 
-  bool use_bounce_in_animation_ = false;
+  bool use_bounce_in_animation_ = true;
   bool is_starting_animation_ = false;
 
+  // Number of active requests to disable the bounce-in and fade-in animation.
+  size_t disable_show_animation_count_ = 0;
+
   std::unique_ptr<TrayWidgetObserver> widget_observer_;
   std::unique_ptr<TrayEventFilter> tray_event_filter_;
+  std::unique_ptr<TrayBackgroundViewSessionChangeHandler> handler_;
+
+  base::WeakPtrFactory<TrayBackgroundView> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/system/tray/tray_background_view_unittest.cc b/ash/system/tray/tray_background_view_unittest.cc
index 65ac1c0..6b864d0 100644
--- a/ash/system/tray/tray_background_view_unittest.cc
+++ b/ash/system/tray/tray_background_view_unittest.cc
@@ -4,10 +4,13 @@
 
 #include "ash/system/tray/tray_background_view.h"
 
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
 #include "ash/system/status_area_widget_delegate.h"
 #include "ash/system/status_area_widget_test_helper.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/task_environment.h"
+#include "components/user_manager/user_manager.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 
@@ -94,4 +97,67 @@
   EXPECT_FALSE(test_view->layer()->GetAnimator()->is_animating());
 }
 
+TEST_F(TrayBackgroundViewTest, HandleSessionChange) {
+  ui::ScopedAnimationDurationScaleMode test_duration_mode(
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+  // Not showing animation after logging in.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
+  // Gives it a small duration to let the session get changed. This duration is
+  // way smaller than the animation duration, so that the animation will not
+  // finish when this duration ends. The same for the other places below.
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+
+  test_view->SetVisiblePreferred(false);
+  test_view->SetVisiblePreferred(true);
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  EXPECT_TRUE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::ACTIVE);
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  EXPECT_FALSE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+
+  // Enable the animation after session state get changed.
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  test_view->SetVisiblePreferred(false);
+  test_view->SetVisiblePreferred(true);
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  EXPECT_TRUE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+
+  // Not showing animation after unlocking screen.
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOCKED);
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+
+  test_view->SetVisiblePreferred(false);
+  test_view->SetVisiblePreferred(true);
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  EXPECT_TRUE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::ACTIVE);
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  EXPECT_FALSE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+
+  // Not showing animation when switching users.
+  GetSessionControllerClient()->AddUserSession("a");
+  test_view->SetVisiblePreferred(false);
+  test_view->SetVisiblePreferred(true);
+  EXPECT_TRUE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+
+  // Simulates user switching by changing the order of session_ids.
+  Shell::Get()->session_controller()->SetUserSessionOrder({2u, 1u});
+  task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(20));
+  EXPECT_FALSE(test_view->layer()->GetAnimator()->is_animating());
+  EXPECT_TRUE(test_view->GetVisible());
+}
+
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/system/unified/page_indicator_view.cc b/ash/system/unified/page_indicator_view.cc
index a3e8f364..391fcb0 100644
--- a/ash/system/unified/page_indicator_view.cc
+++ b/ash/system/unified/page_indicator_view.cc
@@ -68,6 +68,18 @@
           return highlight;
         },
         this));
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](PageIndicatorButton* host) -> std::unique_ptr<views::InkDropRipple> {
+          gfx::Point center = host->GetLocalBounds().CenterPoint();
+          gfx::Rect bounds(center.x() - kInkDropRadius,
+                           center.y() - kInkDropRadius, 2 * kInkDropRadius,
+                           2 * kInkDropRadius);
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetLocalBounds().InsetsFrom(bounds),
+              host->GetInkDropCenterBasedOnLastEvent(),
+              host->ripple_base_color_, host->inkdrop_opacity_);
+        },
+        this));
   }
 
   ~PageIndicatorButton() override {}
@@ -121,16 +133,6 @@
 
  protected:
   // views::Button:
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override {
-    gfx::Point center = GetLocalBounds().CenterPoint();
-    gfx::Rect bounds(center.x() - kInkDropRadius, center.y() - kInkDropRadius,
-                     2 * kInkDropRadius, 2 * kInkDropRadius);
-    return std::make_unique<views::FloodFillInkDropRipple>(
-        size(), GetLocalBounds().InsetsFrom(bounds),
-        GetInkDropCenterBasedOnLastEvent(), ripple_base_color_,
-        inkdrop_opacity_);
-  }
-
   void NotifyClick(const ui::Event& event) override {
     Button::NotifyClick(event);
     GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 400552a..27d1c9b 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -168,6 +168,7 @@
   AddTrayItemToContainer(time_view_);
 
   set_separator_visibility(false);
+  set_use_bounce_in_animation(false);
 
   ShelfConfig::Get()->AddObserver(this);
 }
diff --git a/ash/wm/desks/close_desk_button.cc b/ash/wm/desks/close_desk_button.cc
index a4c6adcc..2858ba2b 100644
--- a/ash/wm/desks/close_desk_button.cc
+++ b/ash/wm/desks/close_desk_button.cc
@@ -39,6 +39,8 @@
         return highlight;
       },
       this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](CloseDeskButton* host) { return host->inkdrop_base_color_; }, this));
 
   SetFocusPainter(nullptr);
   SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
@@ -54,10 +56,6 @@
   return "CloseDeskButton";
 }
 
-SkColor CloseDeskButton::GetInkDropBaseColor() const {
-  return inkdrop_base_color_;
-}
-
 void CloseDeskButton::OnThemeChanged() {
   views::ImageButton::OnThemeChanged();
   AshColorProvider* color_provider = AshColorProvider::Get();
diff --git a/ash/wm/desks/close_desk_button.h b/ash/wm/desks/close_desk_button.h
index e30bae2..aa0cfaa 100644
--- a/ash/wm/desks/close_desk_button.h
+++ b/ash/wm/desks/close_desk_button.h
@@ -28,7 +28,6 @@
 
   // views::ImageButton:
   const char* GetClassName() const override;
-  SkColor GetInkDropBaseColor() const override;
   void OnThemeChanged() override;
 
   // views::ViewTargeterDelegate:
diff --git a/ash/wm/desks/desks_bar_view.cc b/ash/wm/desks/desks_bar_view.cc
index 2365764..968ac19 100644
--- a/ash/wm/desks/desks_bar_view.cc
+++ b/ash/wm/desks/desks_bar_view.cc
@@ -366,10 +366,6 @@
   return false;
 }
 
-float DesksBarView::GetOnHoverWindowSizeScaleFactor() const {
-  return float{height()} / overview_grid_->root_window()->bounds().height();
-}
-
 int DesksBarView::GetMiniViewIndex(const DeskMiniView* mini_view) const {
   auto begin_iter = mini_views_.cbegin();
   auto end_iter = mini_views_.cend();
@@ -680,7 +676,7 @@
     PerformExpandedStateToZeroStateMiniViewAnimation(this, removed_mini_views);
     return;
   }
-  overview_grid_->OnDesksChanged();
+  Layout();
   PerformRemoveDeskMiniViewAnimation(
       removed_mini_view,
       std::vector<DeskMiniView*>(mini_views_.begin(), partition_iter),
@@ -695,7 +691,7 @@
   auto* reordered_view = mini_views_[new_index];
   reordered_view->parent()->ReorderChildView(reordered_view, new_index);
 
-  overview_grid_->OnDesksChanged();
+  Layout();
 
   // Call the animation function after reorder the mini views.
   PerformReorderDeskMiniViewAnimation(old_index, new_index, mini_views_);
@@ -772,7 +768,7 @@
       highlight_controller->MoveHighlightToView(newly_added_name_view);
   }
 
-  overview_grid_->OnDesksChanged();
+  Layout();
 
   if (expanding_bar_view) {
     UpdateDeskButtonsVisibility();
diff --git a/ash/wm/desks/desks_bar_view.h b/ash/wm/desks/desks_bar_view.h
index 2ed060e..966e2c4 100644
--- a/ash/wm/desks/desks_bar_view.h
+++ b/ash/wm/desks/desks_bar_view.h
@@ -81,10 +81,6 @@
   // DeskNameView on this bar.
   bool IsDeskNameBeingModified() const;
 
-  // Returns the scale factor by which a window's size will be scaled down when
-  // it is dragged and hovered on this desks bar.
-  float GetOnHoverWindowSizeScaleFactor() const;
-
   // Get the index of a desk mini view in the |mini_views|.
   int GetMiniViewIndex(const DeskMiniView* mini_view) const;
 
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc
index 000e886a..535e1ef 100644
--- a/ash/wm/desks/desks_controller.cc
+++ b/ash/wm/desks/desks_controller.cc
@@ -560,8 +560,17 @@
     return false;
 
   // Try replacing an ongoing desk animation of the same source.
-  if (animation_ && animation_->Replace(going_left, source)) {
-    return true;
+  if (animation_) {
+    if (animation_->Replace(going_left, source))
+      return true;
+
+    // We arrive here if `DeskActivationAnimation::Replace()` fails
+    // due to trying to replace an animation before the original animation has
+    // finished taking their screenshots. We can continue with creating a new
+    // animation in `ActivateDesk()`, but we need to clean up some desk state.
+    ActivateDeskInternal(desks()[animation_->ending_desk_index()].get(),
+                         /*update_window_activation=*/false);
+    animation_.reset();
   }
 
   const Desk* desk_to_activate = going_left ? GetPreviousDesk() : GetNextDesk();
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index df25da1..0813c5b 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -5367,6 +5367,61 @@
   EXPECT_EQ(desks_bar_view->GetDragDeskMiniViewForTesting(), mini_view_1);
 }
 
+// Tests that the right desk containers are visible when switching between desks
+// really fast. Regression test for https://crbug.com/1194757.
+TEST_F(DesksTest, FastDeskSwitches) {
+  // Add 3 more desks and add a couple windows on each one.
+  CreateTestWindow();
+  CreateTestWindow();
+
+  for (int i = 0; i < 3; ++i) {
+    NewDesk();
+    CreateTestWindow();
+    CreateTestWindow();
+  }
+
+  // Start at the rightmost, 4th desk.
+  auto* desks_controller = DesksController::Get();
+  ASSERT_EQ(4u, desks_controller->desks().size());
+  desks_controller->ActivateDesk(desks_controller->desks()[3].get(),
+                                 DesksSwitchSource::kUserSwitch);
+
+  ui::ScopedAnimationDurationScaleMode normal_anim(
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+  // Activate the first 2 desks very quickly, without waiting for screenshots to
+  // be taken.
+  desks_controller->ActivateAdjacentDesk(
+      /*going_left=*/true, DesksSwitchSource::kDeskSwitchShortcut);
+  desks_controller->ActivateAdjacentDesk(
+      /*going_left=*/true, DesksSwitchSource::kDeskSwitchShortcut);
+
+  // Let the last desk animation to complete.
+  desks_controller->ActivateAdjacentDesk(
+      /*going_left=*/true, DesksSwitchSource::kDeskSwitchShortcut);
+  // Note that `DeskController::ActivateAdjacentDesk()` will trigger two
+  // `OnDeskSwitchAnimationFinished()` calls. The first is from the previous
+  // animation which we destroy since its screenshots have not been taken yet.
+  // The second is the animation to the first desk that we want to wait to take
+  // the screenshots and animate.
+  DeskSwitchAnimationWaiter waiter;
+  waiter.Wait();
+
+  // Check the desk containers. Test that only the first desk container is
+  // visible, but they should all be opaque.
+  std::vector<aura::Window*> desk_containers =
+      desks_util::GetDesksContainers(Shell::GetPrimaryRootWindow());
+  ASSERT_EQ(8u, desk_containers.size());
+  EXPECT_TRUE(desk_containers[0]->IsVisible());
+  EXPECT_EQ(1.f, desk_containers[0]->layer()->opacity());
+
+  for (size_t i = 1; i < desk_containers.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Desk #%lu", i + 1));
+    EXPECT_FALSE(desk_containers[i]->IsVisible());
+    EXPECT_EQ(1.f, desk_containers[i]->layer()->opacity());
+  }
+}
+
 // A test class that uses a mock time test environment.
 class DesksMockTimeTest : public DesksTest {
  public:
diff --git a/ash/wm/desks/zero_state_button.cc b/ash/wm/desks/zero_state_button.cc
index 7782b4a..b75734c 100644
--- a/ash/wm/desks/zero_state_button.cc
+++ b/ash/wm/desks/zero_state_button.cc
@@ -70,6 +70,13 @@
         return highlight;
       },
       this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](DeskButtonBase* host) {
+        return AshColorProvider::Get()
+            ->GetRippleAttributes(host->background_color_)
+            .base_color;
+      },
+      this));
 
   SetInkDropMode(InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
@@ -107,12 +114,6 @@
   }
 }
 
-SkColor DeskButtonBase::GetInkDropBaseColor() const {
-  return AshColorProvider::Get()
-      ->GetRippleAttributes(background_color_)
-      .base_color;
-}
-
 void DeskButtonBase::OnThemeChanged() {
   LabelButton::OnThemeChanged();
   background_color_ = AshColorProvider::Get()->GetControlsLayerColor(
diff --git a/ash/wm/desks/zero_state_button.h b/ash/wm/desks/zero_state_button.h
index dbbd54d..36443f5 100644
--- a/ash/wm/desks/zero_state_button.h
+++ b/ash/wm/desks/zero_state_button.h
@@ -28,7 +28,6 @@
   // LabelButton:
   const char* GetClassName() const override;
   void OnPaintBackground(gfx::Canvas* canvas) override;
-  SkColor GetInkDropBaseColor() const override;
   void OnThemeChanged() override;
 
   // OverviewHighlightController::OverviewHighlightableView:
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 9a4d3bc..728018e 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1324,11 +1324,10 @@
   DCHECK(desks_util::ShouldDesksBarBeCreated());
 
   // The desk bar view is not active if there is only a single desk when
-  // overview is started. Once there are more than one desk, it should stay
-  // active even if the 2nd to last desk is deleted in classic desks. Zero state
-  // desks bar should not be treated as active.
+  // overview is started. Or when the desks bar view has been created and in
+  // zero state.
   return DesksController::Get()->desks().size() > 1 ||
-         (desks_bar_view_ && !desks_bar_view_->mini_views().empty());
+         (desks_bar_view_ && !desks_bar_view_->IsZeroState());
 }
 
 gfx::Rect OverviewGrid::GetGridEffectiveBounds() const {
@@ -1585,13 +1584,6 @@
   return width;
 }
 
-void OverviewGrid::OnDesksChanged() {
-  if (MaybeUpdateDesksWidgetBounds())
-    PositionWindows(/*animate=*/false, /*ignored_items=*/{});
-  else
-    desks_bar_view_->Layout();
-}
-
 bool OverviewGrid::IsDeskNameBeingModified() const {
   return desks_bar_view_ && desks_bar_view_->IsDeskNameBeingModified();
 }
@@ -1960,4 +1952,5 @@
   Shell::Get()->frame_throttling_controller()->StartThrottling(
       windows_to_throttle);
 }
+
 }  // namespace ash
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 30963e3..3167ca86 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -315,10 +315,6 @@
   // splitview or entering splitview.
   int CalculateWidthAndMaybeSetUnclippedBounds(OverviewItem* item, int height);
 
-  // Called when a desk is added or removed to update the bounds of the desks
-  // widget as it may need to switch between default and compact layouts.
-  void OnDesksChanged();
-
   // Returns true if any desk name is being modified in its mini view on this
   // grid.
   bool IsDeskNameBeingModified() const;
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc
index 3c3d8bb..a810d0e 100644
--- a/ash/wm/overview/overview_window_drag_controller.cc
+++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -98,7 +98,8 @@
   const DesksBarView* desks_bar_view = overview_grid->desks_bar_view();
   DCHECK(desks_bar_view);
 
-  const float scale_factor = desks_bar_view->GetOnHoverWindowSizeScaleFactor();
+  const float scale_factor = float{desks_bar_view->height()} /
+                             overview_grid->root_window()->bounds().height();
   gfx::SizeF scaled_size = gfx::ScaleSize(window_original_size, scale_factor);
   // Add the margins overview mode adds around the window's contents.
   scaled_size.Enlarge(2 * kWindowMargin, 2 * kWindowMargin + kHeaderHeightDp);
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
index 2aeb747..a2e0520e 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -455,6 +455,7 @@
 
 #if PA_ALLOW_PCSCAN
 void EnablePCScan() {
+  internal::PCScan::Initialize();
   internal::PCScan::RegisterScannableRoot(Allocator());
   if (Allocator() != AlignedAllocator())
     internal::PCScan::RegisterScannableRoot(AlignedAllocator());
diff --git a/base/allocator/partition_allocator/starscan/pcscan.cc b/base/allocator/partition_allocator/starscan/pcscan.cc
index e0390e5c..fc640c6 100644
--- a/base/allocator/partition_allocator/starscan/pcscan.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan.cc
@@ -533,6 +533,9 @@
   PCScanInternal(const PCScanInternal&) = delete;
   PCScanInternal& operator=(const PCScanInternal&) = delete;
 
+  void Initialize();
+  bool is_initialized() const { return is_initialized_; }
+
   TaskHandle CurrentPCScanTask() const {
     std::lock_guard<std::mutex> lock(current_task_mutex_);
     return current_task_;
@@ -596,6 +599,8 @@
 
   const char* process_name_ = nullptr;
   const SimdSupport simd_support_;
+
+  bool is_initialized_ = false;
 };
 
 void PCScanInternal::Roots::Add(Root* root) {
@@ -638,11 +643,16 @@
   }
 }
 
-PCScanInternal::PCScanInternal() : simd_support_(DetectSimdSupport()) {
+PCScanInternal::PCScanInternal() : simd_support_(DetectSimdSupport()) {}
+
+void PCScanInternal::Initialize() {
+  PA_DCHECK(!is_initialized_);
   CommitCardTable();
+  is_initialized_ = true;
 }
 
 void PCScanInternal::RegisterScannableRoot(Root* root) {
+  PA_DCHECK(is_initialized());
   PA_DCHECK(root);
   PA_CHECK(root->IsQuarantineAllowed());
   typename Root::ScopedGuard guard(root->lock_);
@@ -656,6 +666,7 @@
 }
 
 void PCScanInternal::RegisterNonScannableRoot(Root* root) {
+  PA_DCHECK(is_initialized());
   PA_DCHECK(root);
   PA_CHECK(root->IsQuarantineAllowed());
   typename Root::ScopedGuard guard(root->lock_);
@@ -667,12 +678,14 @@
 }
 
 void PCScanInternal::SetProcessName(const char* process_name) {
+  PA_DCHECK(is_initialized());
   PA_DCHECK(process_name);
   PA_DCHECK(!process_name_);
   process_name_ = process_name;
 }
 
 size_t PCScanInternal::CalculateTotalHeapSize() const {
+  PA_DCHECK(is_initialized());
   const auto acc = [](size_t size, Root* root) {
     return size + root->get_total_size_of_committed_pages();
   };
@@ -729,7 +742,8 @@
 }
 
 void PCScanInternal::ReinitForTesting() {
-  CommitCardTable();
+  is_initialized_ = false;
+  Initialize();
 }
 
 class PCScanSnapshot final {
@@ -1632,6 +1646,7 @@
   const auto& internal = PCScanInternal::Instance();
   const auto& scannable_roots = internal.scannable_roots();
   const auto& nonscannable_roots = internal.nonscannable_roots();
+  PA_DCHECK(internal.is_initialized());
   PA_DCHECK(scannable_roots.size() > 0);
   PA_DCHECK(std::all_of(scannable_roots.begin(), scannable_roots.end(),
                         [](Root* root) { return root->IsScanEnabled(); }));
@@ -1702,6 +1717,10 @@
   current_task->RunFromScanner();
 }
 
+void PCScan::Initialize() {
+  PCScanInternal::Instance().Initialize();
+}
+
 void PCScan::RegisterScannableRoot(Root* root) {
   PCScanInternal::Instance().RegisterScannableRoot(root);
 }
diff --git a/base/allocator/partition_allocator/starscan/pcscan.h b/base/allocator/partition_allocator/starscan/pcscan.h
index 52ec083..271f3aa 100644
--- a/base/allocator/partition_allocator/starscan/pcscan.h
+++ b/base/allocator/partition_allocator/starscan/pcscan.h
@@ -58,6 +58,9 @@
   PCScan(const PCScan&) = delete;
   PCScan& operator=(const PCScan&) = delete;
 
+  // Initializes PCScan and prepares internal data structures.
+  static void Initialize();
+
   // Registers a root for scanning.
   static void RegisterScannableRoot(Root* root);
   // Registers a root that doesn't need to be scanned but still contains
@@ -85,7 +88,8 @@
   static void DisableStackScanning();
   static bool IsStackScanningEnabled();
 
-  // Notify PCScan that a new thread was created/destroyed.
+  // Notify PCScan that a new thread was created/destroyed. Can be called for
+  // uninitialized PCScan (before Initialize()).
   static void NotifyThreadCreated(void* stack_top);
   static void NotifyThreadDestroyed();
 
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 3960226..03d4beb 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/auto_reset.h"
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/debug/activity_tracker.h"
@@ -17,6 +18,7 @@
 #include "base/process/process_handle.h"
 #include "base/process/process_info.h"
 #include "base/rand_util.h"
+#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -440,9 +442,7 @@
 
 FieldTrialList::FieldTrialList(
     std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider)
-    : entropy_provider_(std::move(entropy_provider)),
-      observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
-          ObserverListPolicy::EXISTING_ONLY)) {
+    : entropy_provider_(std::move(entropy_provider)) {
   DCHECK(!global_);
   DCHECK(!used_without_global_);
   global_ = this;
@@ -917,7 +917,8 @@
 bool FieldTrialList::AddObserver(Observer* observer) {
   if (!global_)
     return false;
-  global_->observer_list_->AddObserver(observer);
+  AutoLock auto_lock(global_->lock_);
+  global_->observers_.push_back(observer);
   return true;
 }
 
@@ -925,19 +926,10 @@
 void FieldTrialList::RemoveObserver(Observer* observer) {
   if (!global_)
     return;
-  global_->observer_list_->RemoveObserver(observer);
-}
-
-// static
-void FieldTrialList::SetSynchronousObserver(Observer* observer) {
-  DCHECK(!global_->synchronous_observer_);
-  global_->synchronous_observer_ = observer;
-}
-
-// static
-void FieldTrialList::RemoveSynchronousObserver(Observer* observer) {
-  DCHECK_EQ(global_->synchronous_observer_, observer);
-  global_->synchronous_observer_ = nullptr;
+  AutoLock auto_lock(global_->lock_);
+  Erase(global_->observers_, observer);
+  DCHECK_EQ(global_->num_ongoing_notify_field_trial_group_selection_calls_, 0)
+      << "Cannot call RemoveObserver while accessing FieldTrial::group().";
 }
 
 // static
@@ -959,6 +951,8 @@
   if (!global_)
     return;
 
+  std::vector<Observer*> local_observers;
+
   {
     AutoLock auto_lock(global_->lock_);
     if (field_trial->group_reported_)
@@ -968,17 +962,24 @@
     if (!field_trial->enable_field_trial_)
       return;
 
+    ++global_->num_ongoing_notify_field_trial_group_selection_calls_;
+
     ActivateFieldTrialEntryWhileLocked(field_trial);
+
+    // Copy observers to a local variable to access outside the scope of the
+    // lock. Since removing observers concurrently with this method is
+    // disallowed, pointers should remain valid while observers are notified.
+    local_observers = global_->observers_;
   }
 
-  if (global_->synchronous_observer_) {
-    global_->synchronous_observer_->OnFieldTrialGroupFinalized(
-        field_trial->trial_name(), field_trial->group_name_internal());
+  for (Observer* observer : local_observers) {
+    observer->OnFieldTrialGroupFinalized(field_trial->trial_name(),
+                                         field_trial->group_name_internal());
   }
 
-  global_->observer_list_->NotifySynchronously(
-      FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
-      field_trial->trial_name(), field_trial->group_name_internal());
+  int previous_num_ongoing_notify_field_trial_group_selection_calls =
+      global_->num_ongoing_notify_field_trial_group_selection_calls_--;
+  DCHECK_GT(previous_num_ongoing_notify_field_trial_group_selection_calls, 0);
 }
 
 // static
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 9b61c1e..1c2460c 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -57,6 +57,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <atomic>
 #include <map>
 #include <memory>
 #include <string>
@@ -268,6 +269,7 @@
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault);
+  FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ObserveReentrancy);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
@@ -608,26 +610,15 @@
   // Add an observer to be notified when a field trial is irrevocably committed
   // to being part of some specific field_group (and hence the group_name is
   // also finalized for that field_trial). Returns false and does nothing if
-  // there is no FieldTrialList singleton.
+  // there is no FieldTrialList singleton. The observer can be notified on any
+  // sequence; it must be thread-safe.
   static bool AddObserver(Observer* observer);
 
-  // Remove an observer.
+  // Remove an observer. This cannot be invoked concurrently with
+  // FieldTrial::group() (typically, this means that no other thread should be
+  // running when this is invoked).
   static void RemoveObserver(Observer* observer);
 
-  // Similar to AddObserver(), but the passed observer will be notified
-  // synchronously when a field trial is activated and its group selected. It
-  // will be notified synchronously on the same thread where the activation and
-  // group selection happened. It is the responsibility of the observer to make
-  // sure that this is a safe operation and the operation must be fast, as this
-  // work is done synchronously as part of group() or related APIs. Only a
-  // single such observer is supported, exposed specifically for crash
-  // reporting. Must be called on the main thread before any other threads
-  // have been started.
-  static void SetSynchronousObserver(Observer* observer);
-
-  // Removes the single synchronous observer.
-  static void RemoveSynchronousObserver(Observer* observer);
-
   // Grabs the lock if necessary and adds the field trial to the allocator. This
   // should only be called from FinalizeGroupChoice().
   static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial);
@@ -750,7 +741,7 @@
   typedef std::map<std::string, FieldTrial*, std::less<>> RegistrationMap;
 
   // Helper function should be called only while holding lock_.
-  FieldTrial* PreLockedFind(StringPiece name);
+  FieldTrial* PreLockedFind(StringPiece name) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Register() stores a pointer to the given trial in a global map.
   // This method also AddRef's the indicated trial.
@@ -768,19 +759,22 @@
   // FieldTrialList is created after that.
   static bool used_without_global_;
 
-  // Lock for access to registered_ and field_trial_allocator_.
+  // Lock for access to |registered_|, |observers_| and
+  // |field_trial_allocator_|.
   Lock lock_;
-  RegistrationMap registered_;
+  RegistrationMap registered_ GUARDED_BY(lock_);
 
   // Entropy provider to be used for one-time randomized field trials. If NULL,
   // one-time randomization is not supported.
   std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider_;
 
   // List of observers to be notified when a group is selected for a FieldTrial.
-  scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
+  std::vector<Observer*> observers_ GUARDED_BY(lock_);
 
-  // Single synchronous observer to be notified when a trial group is chosen.
-  Observer* synchronous_observer_ = nullptr;
+  // Counts the ongoing calls to
+  // FieldTrialList::NotifyFieldTrialGroupSelection(). Used to ensure that
+  // RemoveObserver() isn't called while notifying observers.
+  std::atomic_int num_ongoing_notify_field_trial_group_selection_calls_{0};
 
   // Allocator in shared memory containing field trial data. Used in both
   // browser and child processes, but readonly in the child.
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 07fbdd2..6e70648c 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -55,29 +55,15 @@
       FieldTrial::SESSION_RANDOMIZED, default_group_number);
 }
 
-// FieldTrialList::Observer implementation for testing.
+// A FieldTrialList::Observer implementation which stores the trial name and
+// group name received via OnFieldTrialGroupFinalized() for later inspection.
 class TestFieldTrialObserver : public FieldTrialList::Observer {
  public:
-  enum Type {
-    ASYNCHRONOUS,
-    SYNCHRONOUS,
-  };
-
-  explicit TestFieldTrialObserver(Type type) : type_(type) {
-    if (type == SYNCHRONOUS)
-      FieldTrialList::SetSynchronousObserver(this);
-    else
-      FieldTrialList::AddObserver(this);
-  }
+  TestFieldTrialObserver() { FieldTrialList::AddObserver(this); }
   TestFieldTrialObserver(const TestFieldTrialObserver&) = delete;
   TestFieldTrialObserver& operator=(const TestFieldTrialObserver&) = delete;
 
-  ~TestFieldTrialObserver() override {
-    if (type_ == SYNCHRONOUS)
-      FieldTrialList::RemoveSynchronousObserver(this);
-    else
-      FieldTrialList::RemoveObserver(this);
-  }
+  ~TestFieldTrialObserver() override { FieldTrialList::RemoveObserver(this); }
 
   void OnFieldTrialGroupFinalized(const std::string& trial,
                                   const std::string& group) override {
@@ -89,11 +75,39 @@
   const std::string& group_name() const { return group_name_; }
 
  private:
-  const Type type_;
   std::string trial_name_;
   std::string group_name_;
 };
 
+// A FieldTrialList::Observer implementation which accesses the group of a
+// FieldTrial from OnFieldTrialGroupFinalized(). Used to test reentrancy.
+class FieldTrialObserverAccessingGroup : public FieldTrialList::Observer {
+ public:
+  // |trial_to_access| is the FieldTrial on which to invoke group() when
+  // receiving an OnFieldTrialGroupFinalized() notification.
+  explicit FieldTrialObserverAccessingGroup(
+      scoped_refptr<FieldTrial> trial_to_access)
+      : trial_to_access_(trial_to_access) {
+    FieldTrialList::AddObserver(this);
+  }
+  FieldTrialObserverAccessingGroup(const FieldTrialObserverAccessingGroup&) =
+      delete;
+  FieldTrialObserverAccessingGroup& operator=(
+      const FieldTrialObserverAccessingGroup&) = delete;
+
+  ~FieldTrialObserverAccessingGroup() override {
+    FieldTrialList::RemoveObserver(this);
+  }
+
+  void OnFieldTrialGroupFinalized(const std::string& trial,
+                                  const std::string& group) override {
+    trial_to_access_->group();
+  }
+
+ private:
+  scoped_refptr<FieldTrial> trial_to_access_;
+};
+
 std::string MockEscapeQueryParamValue(const std::string& input) {
   return input;
 }
@@ -614,7 +628,7 @@
 TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) {
   ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
 
-  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
+  TestFieldTrialObserver observer;
   ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/"));
   RunLoop().RunUntilIdle();
   // Observer shouldn't be notified.
@@ -623,7 +637,6 @@
   // Check that the values still get returned and querying them activates them.
   EXPECT_EQ("def", FieldTrialList::FindFullName("Abc"));
 
-  RunLoop().RunUntilIdle();
   EXPECT_EQ("Abc", observer.trial_name());
   EXPECT_EQ("def", observer.group_name());
 }
@@ -888,26 +901,7 @@
   const char kTrialName[] = "TrialToObserve1";
   const char kSecondaryGroupName[] = "SecondaryGroup";
 
-  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
-  int default_group = -1;
-  scoped_refptr<FieldTrial> trial =
-      CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
-  const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50);
-  const int chosen_group = trial->group();
-  EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group);
-
-  EXPECT_EQ(kTrialName, observer.trial_name());
-  if (chosen_group == default_group)
-    EXPECT_EQ(kDefaultGroupName, observer.group_name());
-  else
-    EXPECT_EQ(kSecondaryGroupName, observer.group_name());
-}
-
-TEST_F(FieldTrialTest, SynchronousObserver) {
-  const char kTrialName[] = "TrialToObserve1";
-  const char kSecondaryGroupName[] = "SecondaryGroup";
-
-  TestFieldTrialObserver observer(TestFieldTrialObserver::SYNCHRONOUS);
+  TestFieldTrialObserver observer;
   int default_group = -1;
   scoped_refptr<FieldTrial> trial =
       CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
@@ -923,10 +917,39 @@
     EXPECT_EQ(kSecondaryGroupName, observer.group_name());
 }
 
+// Verify that no hang occurs when a FieldTrial group is selected from a
+// FieldTrialList::Observer::OnFieldTrialGroupFinalized() notification. If the
+// FieldTrialList's lock is held when observers are notified, this test will
+// hang due to reentrant lock acquisition when selecting the FieldTrial group.
+TEST_F(FieldTrialTest, ObserveReentrancy) {
+  const char kTrialName1[] = "TrialToObserve1";
+  const char kTrialName2[] = "TrialToObserve2";
+
+  int default_group_1 = -1;
+  scoped_refptr<FieldTrial> trial_1 =
+      CreateFieldTrial(kTrialName1, 100, kDefaultGroupName, &default_group_1);
+
+  FieldTrialObserverAccessingGroup observer(trial_1);
+
+  int default_group_2 = -1;
+  scoped_refptr<FieldTrial> trial_2 =
+      CreateFieldTrial(kTrialName2, 100, kDefaultGroupName, &default_group_2);
+
+  // No group should be selected for |trial_1| yet.
+  EXPECT_EQ(FieldTrial::kNotFinalized, trial_1->group_);
+
+  // Force selection of a group for |trial_2|. This will notify |observer| which
+  // will force the selection of a group for |trial_1|. This should not hang.
+  trial_2->group();
+
+  // The above call should have selected a group for |trial_1|.
+  EXPECT_NE(FieldTrial::kNotFinalized, trial_1->group_);
+}
+
 TEST_F(FieldTrialTest, ObserveDisabled) {
   const char kTrialName[] = "TrialToObserve2";
 
-  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
+  TestFieldTrialObserver observer;
   int default_group = -1;
   scoped_refptr<FieldTrial> trial =
       CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
@@ -950,7 +973,7 @@
 TEST_F(FieldTrialTest, ObserveForcedDisabled) {
   const char kTrialName[] = "TrialToObserve3";
 
-  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
+  TestFieldTrialObserver observer;
   int default_group = -1;
   scoped_refptr<FieldTrial> trial =
       CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
@@ -1046,14 +1069,13 @@
     { 0.95, kDefaultGroupName },
   };
 
-  for (size_t i = 0; i < base::size(test_cases); ++i) {
-    TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
-    scoped_refptr<FieldTrial> trial(
-       FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, kDefaultGroupName,
-                                             test_cases[i].entropy_value));
+  for (auto& test_case : test_cases) {
+    TestFieldTrialObserver observer;
+    scoped_refptr<FieldTrial> trial(FieldTrial::CreateSimulatedFieldTrial(
+        kTrialName, 100, kDefaultGroupName, test_case.entropy_value));
     trial->AppendGroup("A", 80);
     trial->AppendGroup("B", 10);
-    EXPECT_EQ(test_cases[i].expected_group, trial->group_name());
+    EXPECT_EQ(test_case.expected_group, trial->group_name());
 
     // Field trial shouldn't have been registered with the list.
     EXPECT_FALSE(FieldTrialList::TrialExists(kTrialName));
diff --git a/base/time/time.h b/base/time/time.h
index db6504cd..631ba05 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -191,6 +191,14 @@
   // minimum time delta to a time or another time delta has an undefined result.
   static constexpr TimeDelta Min();
 
+  // Returns the maximum time delta which is not equivalent to infinity. Only
+  // subtracting a finite time delta from this time delta has a defined result.
+  static constexpr TimeDelta FiniteMax();
+
+  // Returns the minimum time delta which is not equivalent to -infinity. Only
+  // adding a finite time delta to this time delta has a defined result.
+  static constexpr TimeDelta FiniteMin();
+
   // Returns the internal numeric value of the TimeDelta object. Please don't
   // use this and do arithmetic on it, as it is more error prone than using the
   // provided operators.
@@ -969,6 +977,16 @@
   return TimeDelta(std::numeric_limits<int64_t>::min());
 }
 
+// static
+constexpr TimeDelta TimeDelta::FiniteMax() {
+  return TimeDelta(std::numeric_limits<int64_t>::max() - 1);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FiniteMin() {
+  return TimeDelta(std::numeric_limits<int64_t>::min() + 1);
+}
+
 // For logging use only.
 BASE_EXPORT std::ostream& operator<<(std::ostream& os, Time time);
 
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc
index 34cc347..cd4bc1c 100644
--- a/base/time/time_unittest.cc
+++ b/base/time/time_unittest.cc
@@ -1936,6 +1936,17 @@
             std::numeric_limits<int64_t>::min());
 }
 
+TEST(TimeDelta, FiniteMaxMin) {
+  constexpr TimeDelta kFiniteMax = TimeDelta::FiniteMax();
+  constexpr TimeDelta kUnit = TimeDelta::FromMicroseconds(1);
+  static_assert(kFiniteMax + kUnit == TimeDelta::Max(), "");
+  static_assert(kFiniteMax - kUnit < kFiniteMax, "");
+
+  constexpr TimeDelta kFiniteMin = TimeDelta::FiniteMin();
+  static_assert(kFiniteMin - kUnit == TimeDelta::Min(), "");
+  static_assert(kFiniteMin + kUnit > kFiniteMin, "");
+}
+
 TEST(TimeDelta, NumericOperators) {
   constexpr double d = 0.5;
   EXPECT_EQ(TimeDelta::FromMilliseconds(500),
diff --git a/build/fuchsia/run_test_package.py b/build/fuchsia/run_test_package.py
index 090bd07..e072e1f5 100644
--- a/build/fuchsia/run_test_package.py
+++ b/build/fuchsia/run_test_package.py
@@ -86,7 +86,7 @@
 
     read_pipe, write_pipe = os.pipe()
 
-    self._output_stream = os.fdopen(write_pipe, 'w', 1)
+    self._output_stream = os.fdopen(write_pipe, 'wb', 1)
     self._thread = threading.Thread(target=self._Run)
     self._thread.start()
 
@@ -117,9 +117,7 @@
           primary_fd = None
 
       for fileno in rlist:
-        # TODO(chonggu): Encode streams with 'utf-8' instead of decoding each
-        # line read once we drop Python 2 support.
-        line = streams_by_fd[fileno].readline().decode('utf-8')
+        line = streams_by_fd[fileno].readline()
         if line:
           self._output_stream.write(line)
         else:
@@ -137,9 +135,7 @@
         break
 
       for fileno in rlist:
-        # TODO(chonggu): Switch to encoding='utf-8' once we drop Python 2
-        # support.
-        line = streams_by_fd[fileno].readline().decode('utf-8')
+        line = streams_by_fd[fileno].readline()
         if line:
           self._output_stream.write(line)
         else:
diff --git a/cc/test/layer_tree_json_parser.cc b/cc/test/layer_tree_json_parser.cc
index 5f7e41d2..768e6a5 100644
--- a/cc/test/layer_tree_json_parser.cc
+++ b/cc/test/layer_tree_json_parser.cc
@@ -6,6 +6,9 @@
 
 #include <stddef.h>
 
+#include <memory>
+#include <utility>
+
 #include "base/test/values_test_util.h"
 #include "base/values.h"
 #include "cc/layers/layer.h"
@@ -128,7 +131,7 @@
   new_layer->SetTransform(layer_transform);
 
   success &= dict->GetList("Children", &list);
-  for (const auto& value : *list) {
+  for (const auto& value : list->GetList()) {
     new_layer->AddChild(ParseTreeFromValue(value, content_client));
   }
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 4eb848f4..97b0d75f 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2191,8 +2191,6 @@
   if (active_tree_->has_presentation_callbacks()) {
     presentation_time_callbacks_.RegisterMainThreadPresentationCallbacks(
         metadata.frame_token, active_tree_->TakePresentationCallbacks());
-    presentation_time_callbacks_.RegisterFrameTime(
-        metadata.frame_token, CurrentBeginFrameArgs().frame_time);
   }
 
   if (GetDrawMode() == DRAW_MODE_RESOURCELESS_SOFTWARE) {
diff --git a/cc/trees/presentation_time_callback_buffer.cc b/cc/trees/presentation_time_callback_buffer.cc
index 636f36ac..c6de964c 100644
--- a/cc/trees/presentation_time_callback_buffer.cc
+++ b/cc/trees/presentation_time_callback_buffer.cc
@@ -61,20 +61,6 @@
   DCHECK_LE(frame_token_infos_.size(), 25u);
 }
 
-void PresentationTimeCallbackBuffer::RegisterFrameTime(
-    uint32_t frame_token,
-    base::TimeTicks frame_time) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  FrameTokenInfo& frame_info = GetOrMakeRegistration(frame_token);
-
-  // Protect against clobbering previously registered frame times.
-  DCHECK(frame_info.frame_time.is_null() ||
-         frame_info.frame_time == frame_time);
-  frame_info.frame_time = frame_time;
-
-  DCHECK_LE(frame_token_infos_.size(), 25u);
-}
-
 PresentationTimeCallbackBuffer::PendingCallbacks::PendingCallbacks() = default;
 PresentationTimeCallbackBuffer::PendingCallbacks::PendingCallbacks(
     PendingCallbacks&&) = default;
@@ -94,11 +80,6 @@
     if (viz::FrameTokenGT(info->token, frame_token))
       break;
 
-    // Forward compositor frame timings on exact frame token matches. These
-    // timings correspond to frames that caused on-screen damage.
-    if (info->token == frame_token && !info->frame_time.is_null())
-      result.frame_time = info->frame_time;
-
     // Collect the main-thread callbacks. It's the caller's job to post them to
     // the main thread.
     std::move(info->main_thread_callbacks.begin(),
diff --git a/cc/trees/presentation_time_callback_buffer.h b/cc/trees/presentation_time_callback_buffer.h
index d5ccdc5..820b8fc3 100644
--- a/cc/trees/presentation_time_callback_buffer.h
+++ b/cc/trees/presentation_time_callback_buffer.h
@@ -60,12 +60,6 @@
       uint32_t frame_token,
       std::vector<CallbackType> callbacks);
 
-  // The given |frame_time| is associated with the given |frame_token| and will
-  // be exposed through |PopPendingCallbacks| if there is an exact frame token
-  // match. Note that it is an error to register distinct |frame_time|s against
-  // the same |frame_token|.
-  void RegisterFrameTime(uint32_t frame_token, base::TimeTicks frame_time);
-
   // Structured return value for |PopPendingCallbacks|. CC_EXPORT is only
   // needed for testing.
   struct CC_EXPORT PendingCallbacks {
@@ -86,21 +80,12 @@
     // Holds callbacks registered through
     // |RegisterCompositorPresentationCallbacks|.
     std::vector<CallbackType> compositor_thread_callbacks;
-
-    // Note: calling code needs to test against frame_time.is_null() because
-    // frame_time is not always defined. See |PopPendingCallbacks|.
-    base::TimeTicks frame_time;
   };
 
   // Call this once the presentation for the given |frame_token| has completed.
   // Yields any pending callbacks that were registered against a frame token
   // that was less than or equal to the given |frame_token|. It is the caller's
-  // responsibility to run the callbacks on the right threads/sequences. When
-  // the given |frame_token| is an exact match to a registered entry,
-  // |frame_time| will be set to the frame time supplied through
-  // |RegisterFrameTime|. Otherwise, |frame_time| will be default constructed
-  // and should not be used. Calling code can assume |frame_time| is meaningful
-  // iff frame_time.is_null() returns false.
+  // responsibility to run the callbacks on the right threads/sequences.
   PendingCallbacks PopPendingCallbacks(uint32_t frame_token);
 
  private:
@@ -118,11 +103,6 @@
     // presentation feedback with the relevant compositor frame.
     uint32_t token;
 
-    // A copy of the |frame_time| from the |BeginFrameArgs| associated with
-    // frame. Useful for tracking latency between frame requests and frame
-    // presentations.
-    base::TimeTicks frame_time;
-
     // The callbacks to send back to the main thread.
     std::vector<CallbackType> main_thread_callbacks;
 
diff --git a/cc/trees/presentation_time_callback_buffer_unittest.cc b/cc/trees/presentation_time_callback_buffer_unittest.cc
index f080d6d4..6b8aa1e0 100644
--- a/cc/trees/presentation_time_callback_buffer_unittest.cc
+++ b/cc/trees/presentation_time_callback_buffer_unittest.cc
@@ -15,7 +15,7 @@
   while (num_callbacks-- > 0) {
     // PresentationTimeCallbackBuffer isn't supposed to invoke any callbacks.
     // We can check for that by passing callbacks which cause test failure.
-    result.emplace_back(base::BindOnce([](const gfx::PresentationFeedback&) {
+    result.push_back(base::BindOnce([](const gfx::PresentationFeedback&) {
       FAIL() << "Callbacks should not be directly invoked by "
                 "PresentationTimeCallbackBuffer";
     }));
@@ -24,10 +24,6 @@
   return result;
 }
 
-base::TimeTicks MakeTicks(uint64_t us) {
-  return base::TimeTicks() + base::TimeDelta::FromMicroseconds(us);
-}
-
 constexpr uint32_t kFrameToken1 = 234;
 constexpr uint32_t kFrameToken2 = 345;
 constexpr uint32_t kFrameToken3 = 456;
@@ -44,7 +40,6 @@
 
   EXPECT_TRUE(result.main_thread_callbacks.empty());
   EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-  EXPECT_TRUE(result.frame_time.is_null());
 }
 
 TEST(PresentationTimeCallbackBufferTest, TestOneMainThreadCallback) {
@@ -59,14 +54,12 @@
     auto result = buffer.PopPendingCallbacks(kFrameToken1);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 
   {
     auto result = buffer.PopPendingCallbacks(kFrameToken2);
     EXPECT_EQ(result.main_thread_callbacks.size(), 1ull);
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 
   // Make sure that the buffer has removed the registration since the "pop".
@@ -74,7 +67,6 @@
     auto result = buffer.PopPendingCallbacks(kFrameToken2);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 }
 
@@ -90,14 +82,12 @@
     auto result = buffer.PopPendingCallbacks(kFrameToken1);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 
   {
     auto result = buffer.PopPendingCallbacks(kFrameToken2);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_EQ(result.compositor_thread_callbacks.size(), 1ull);
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 
   // Make sure that the buffer has removed the registration since the "pop".
@@ -105,51 +95,16 @@
     auto result = buffer.PopPendingCallbacks(kFrameToken2);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
-  }
-}
-
-TEST(PresentationTimeCallbackBufferTest, TestFrameTimeRegistration) {
-  PresentationTimeCallbackBuffer buffer;
-
-  base::TimeTicks frame_time = MakeTicks(234);
-  buffer.RegisterFrameTime(kFrameToken2, frame_time);
-
-  // Make sure that popping early frame tokens doesn't return irrelevant
-  // entries.
-  {
-    auto result = buffer.PopPendingCallbacks(kFrameToken1);
-    EXPECT_TRUE(result.main_thread_callbacks.empty());
-    EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
-  }
-
-  {
-    auto result = buffer.PopPendingCallbacks(kFrameToken2);
-    EXPECT_TRUE(result.main_thread_callbacks.empty());
-    EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_FALSE(result.frame_time.is_null());
-    EXPECT_EQ(result.frame_time, frame_time);
-  }
-
-  // Make sure that the buffer has removed the registration since the "pop".
-  {
-    auto result = buffer.PopPendingCallbacks(kFrameToken2);
-    EXPECT_TRUE(result.main_thread_callbacks.empty());
-    EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 }
 
 TEST(PresentationTimeCallbackBufferTest, TestMixedCallbacks) {
   PresentationTimeCallbackBuffer buffer;
 
-  base::TimeTicks frame_time = MakeTicks(123);
   buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2,
                                                  GenerateCallbacks(1));
   buffer.RegisterCompositorPresentationCallbacks(kFrameToken2,
                                                  GenerateCallbacks(1));
-  buffer.RegisterFrameTime(kFrameToken2, frame_time);
 
   // Make sure that popping early frame tokens doesn't return irrelevant
   // entries.
@@ -157,15 +112,12 @@
     auto result = buffer.PopPendingCallbacks(kFrameToken1);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 
   {
     auto result = buffer.PopPendingCallbacks(kFrameToken2);
     EXPECT_EQ(result.main_thread_callbacks.size(), 1ull);
     EXPECT_EQ(result.compositor_thread_callbacks.size(), 1ull);
-    EXPECT_FALSE(result.frame_time.is_null());
-    EXPECT_EQ(result.frame_time, frame_time);
   }
 
   // Make sure that the buffer has removed the registrations since the "pop".
@@ -173,62 +125,26 @@
     auto result = buffer.PopPendingCallbacks(kFrameToken2);
     EXPECT_TRUE(result.main_thread_callbacks.empty());
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
   }
 }
 
-TEST(PresentationTimeCallbackBufferTest, TestCallbackBatchingNoFrameTime) {
+TEST(PresentationTimeCallbackBufferTest, TestCallbackBatching) {
   PresentationTimeCallbackBuffer buffer;
 
-  base::TimeTicks frame_time1 = MakeTicks(123);
-  base::TimeTicks frame_time2 = MakeTicks(234);
-  base::TimeTicks frame_time4 = MakeTicks(456);
-
-  // Register one callback for frame1, two for frame2 and two for frame4.
-  buffer.RegisterMainThreadPresentationCallbacks(kFrameToken1,
-                                                 GenerateCallbacks(1));
-  buffer.RegisterFrameTime(kFrameToken1, frame_time1);
-  buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2,
-                                                 GenerateCallbacks(2));
-  buffer.RegisterFrameTime(kFrameToken2, frame_time2);
-  buffer.RegisterMainThreadPresentationCallbacks(kFrameToken4,
-                                                 GenerateCallbacks(2));
-  buffer.RegisterFrameTime(kFrameToken4, frame_time4);
-
-  // Pop callbacks up to and including frame3. Should be three in total; one
-  // from frame1 and two from frame2. There shouldn't be a frame time because
-  // no frame time was registered against exactly kFrameToken3.
-  {
-    auto result = buffer.PopPendingCallbacks(kFrameToken3);
-    EXPECT_EQ(result.main_thread_callbacks.size(), 3ull);
-    EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_TRUE(result.frame_time.is_null());
-  }
-}
-
-TEST(PresentationTimeCallbackBufferTest, TestCallbackBatchingWithFrameTime) {
-  PresentationTimeCallbackBuffer buffer;
-
-  base::TimeTicks frame_time3 = MakeTicks(345);
-
   // Register one callback for frame1, two for frame2 and two for frame4.
   buffer.RegisterMainThreadPresentationCallbacks(kFrameToken1,
                                                  GenerateCallbacks(1));
   buffer.RegisterMainThreadPresentationCallbacks(kFrameToken2,
                                                  GenerateCallbacks(2));
-  buffer.RegisterFrameTime(kFrameToken3, frame_time3);
   buffer.RegisterMainThreadPresentationCallbacks(kFrameToken4,
                                                  GenerateCallbacks(2));
 
   // Pop callbacks up to and including frame3. Should be three in total; one
-  // from frame1 and two from frame2. There should be a frame time because
-  // a frame time was registered against exactly kFrameToken3.
+  // from frame1 and two from frame2.
   {
     auto result = buffer.PopPendingCallbacks(kFrameToken3);
     EXPECT_EQ(result.main_thread_callbacks.size(), 3ull);
     EXPECT_TRUE(result.compositor_thread_callbacks.empty());
-    EXPECT_FALSE(result.frame_time.is_null());
-    EXPECT_EQ(result.frame_time, frame_time3);
   }
 }
 
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java
index 3ec0369..931f2691 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutPerfTest.java
@@ -323,6 +323,9 @@
     @Test
     @EnormousTest
     @CommandLineFlags.Add({BASE_PARAMS})
+    @DisableIf.Build(message = "Flaky on Android P, see https://crbug.com/1184787",
+            supported_abis_includes = "x86", sdk_is_greater_than = VERSION_CODES.O_MR1,
+            sdk_is_less_than = VERSION_CODES.Q)
     public void testGridToTabToCurrentNTP() throws InterruptedException {
         prepareTabs(1, NTP_URL);
         reportGridToTabPerf(false, false, "Grid-to-Tab to current NTP");
diff --git a/chrome/app/protocol_handler_intent_picker_strings.grdp b/chrome/app/protocol_handler_intent_picker_strings.grdp
index ef02354e..4109e1182 100644
--- a/chrome/app/protocol_handler_intent_picker_strings.grdp
+++ b/chrome/app/protocol_handler_intent_picker_strings.grdp
@@ -1,26 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Protocol-Handler-Intent-Picker strings (included from generated_resources.grd). -->
 <grit-part>
-  <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_TITLE" desc="Title for the protocol handler intent picker when there is more than one web app choice">
-    Which application do you want to use?
-  </message>
   <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE" desc="Title for the protocol handler intent picker when there is one web app choice">
     Do you want to use this application?
   </message>
-  <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_REMEMBER_SELECTION" desc="Label for the checkbox in the protocol handler intent picker to save the current selection so that the protocol handler intent picker will not be shown again.">
-    Remember my choice
-  </message>
-  <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_OK_BUTTON_TEXT" desc="Label for the button in the protocol handler intent picker dialog that dismisses the dialog and launches an application when there is more than one web app choice">
-    Open
-  </message>
   <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT" desc="Label for the button in the protocol handler intent picker dialog that dismisses the dialog and launches an application when there is one web app choice">
     Allow
   </message>
-  <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_CANCEL_BUTTON_TEXT" desc="Label for the button in the protocol handler intent picker dialog that dismisses the dialog and does not launch an application">
-    Cancel
-  </message>
   <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT" desc="Label for the button in the protocol handler intent picker dialog that dismisses the dialog and does not launch an application when there is one web app choice">
-    Block
+    Cancel
   </message>
   <message name="IDS_PROTOCOL_HANDLER_INTENT_PICKER_APP_ORIGIN_LABEL" desc="Label for the url origin of each item in the web app list in the protocol handler intent picker dialog">
     Publisher: <ph name="APP_ORIGIN">$1<ex>google.com</ex></ph>
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_APP_ORIGIN_LABEL.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_APP_ORIGIN_LABEL.png.sha1
index b950885c..2b02458 100644
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_APP_ORIGIN_LABEL.png.sha1
+++ b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_APP_ORIGIN_LABEL.png.sha1
@@ -1 +1 @@
-04fafe5e2e86111e0b4532e2a28d64a6b08cb860
\ No newline at end of file
+af4a036812ba61536eea9af5e7ed7b36f69f7859
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_CANCEL_BUTTON_TEXT.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_CANCEL_BUTTON_TEXT.png.sha1
deleted file mode 100644
index c5c7792b..0000000
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_CANCEL_BUTTON_TEXT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b9cd310fe11259ebbdb1f401ca9ac3d0464c5831
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_OK_BUTTON_TEXT.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_OK_BUTTON_TEXT.png.sha1
deleted file mode 100644
index de6a078..0000000
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_OK_BUTTON_TEXT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1acb8a087948aa212cf79d79b5e675d7cbedba12
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_TITLE.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_TITLE.png.sha1
deleted file mode 100644
index 6f5a892..0000000
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-1c1b3b120459cce3d89af5531725abaa45cd3166
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_REMEMBER_SELECTION.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_REMEMBER_SELECTION.png.sha1
deleted file mode 100644
index 92298f1..0000000
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_REMEMBER_SELECTION.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fb507788954325e9c8662fd1dae20d8332fcdf48
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT.png.sha1
index cba38eb..b8462d23 100644
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT.png.sha1
+++ b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT.png.sha1
@@ -1 +1 @@
-1e63215b07c3a7c383ef7169e5e680f0c6de5533
\ No newline at end of file
+f2820a81df5f31fd9bb2647de326e7eb235baa95
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT.png.sha1
index 2a32386c..2b43597e 100644
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT.png.sha1
+++ b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT.png.sha1
@@ -1 +1 @@
-2a909dbb29ddad95f32f3cad3a88040f3a6159e2
\ No newline at end of file
+74205678aff0b85ef2da2184cb2df1704a599490
\ No newline at end of file
diff --git a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE.png.sha1 b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE.png.sha1
index 1a3dd22..f292392 100644
--- a/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE.png.sha1
+++ b/chrome/app/protocol_handler_intent_picker_strings_grdp/IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE.png.sha1
@@ -1 +1 @@
-11c24a134a4eb2c84b9829c6504c5bee2d2f35cf
\ No newline at end of file
+c93fc64068b1a5f8c068749b8ef87754c3c764d3
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 78d0f22..5ae5f0d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1193,23 +1193,6 @@
         nullptr,
     },
 };
-
-const FeatureEntry::FeatureVariation
-    kOmniboxKeywordSpaceTriggeringVariations[] = {
-        {
-            "Single Space",
-            (FeatureEntry::FeatureParam[]){},
-            0,
-            nullptr,
-        },
-        {
-            "Double Space",
-            (FeatureEntry::FeatureParam[]){
-                {"KeywordSpaceTriggeringDoubleSpace", "true"}},
-            1,
-            nullptr,
-        }};
-
 #endif  // defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_MAC) ||
         // defined(OS_WIN)
 
@@ -3798,9 +3781,6 @@
     {"offlining-recent-pages", flag_descriptions::kOffliningRecentPagesName,
      flag_descriptions::kOffliningRecentPagesDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(offline_pages::kOffliningRecentPagesFeature)},
-    {"offline-pages-ct", flag_descriptions::kOfflinePagesCtName,
-     flag_descriptions::kOfflinePagesCtDescription, kOsAndroid,
-     FEATURE_VALUE_TYPE(offline_pages::kOfflinePagesCTFeature)},
     {"offline-pages-ct-v2", flag_descriptions::kOfflinePagesCtV2Name,
      flag_descriptions::kOfflinePagesCtV2Description, kOsAndroid,
      FEATURE_VALUE_TYPE(offline_pages::kOfflinePagesCTV2Feature)},
@@ -4336,12 +4316,6 @@
      flag_descriptions::kOmniboxDisableCGIParamMatchingName,
      flag_descriptions::kOmniboxDisableCGIParamMatchingDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(omnibox::kDisableCGIParamMatching)},
-    {"omnibox-keyword-space-triggering",
-     flag_descriptions::kOmniboxKeywordSpaceTriggeringName,
-     flag_descriptions::kOmniboxKeywordSpaceTriggeringDescription, kOsDesktop,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(omnibox::kKeywordSpaceTriggering,
-                                    kOmniboxKeywordSpaceTriggeringVariations,
-                                    "OmniboxBundledExperimentV1")},
     {"omnibox-keyword-space-triggering-setting",
      flag_descriptions::kOmniboxKeywordSpaceTriggeringSettingName,
      flag_descriptions::kOmniboxKeywordSpaceTriggeringSettingDescription,
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index c9da7df..a58c0e8 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -110,6 +110,7 @@
 #include "ppapi/buildflags/buildflags.h"
 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
 #include "services/network/public/cpp/features.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/compositor_observer.h"
@@ -1786,6 +1787,45 @@
   GetGuestViewManager()->WaitForAllGuestsDeleted();
 }
 
+// Creates a guest in a unattached state, then confirms that calling
+// |RenderFrameHost::ForEachFrame| on the embedder will include the guest's
+// frame.
+IN_PROC_BROWSER_TEST_F(WebViewNewWindowTest,
+                       NewWindow_UnattachedVisitedByForEachFrame) {
+  TestHelper("testNewWindowDeferredAttachmentIndefinitely",
+             "web_view/newwindow", NEEDS_TEST_SERVER);
+  // The test creates two guests, one of which is created but left in an
+  // unattached state.
+  GetGuestViewManager()->WaitForNumGuestsCreated(2);
+
+  content::WebContents* embedder = GetEmbedderWebContents();
+  auto* unattached_guest = extensions::WebViewGuest::FromWebContents(
+      GetGuestViewManager()->GetLastGuestCreated());
+  ASSERT_TRUE(unattached_guest);
+  ASSERT_EQ(embedder, unattached_guest->owner_web_contents());
+  ASSERT_FALSE(unattached_guest->attached());
+  ASSERT_FALSE(unattached_guest->embedder_web_contents());
+
+  std::vector<content::WebContents*> guest_contents_list;
+  GetGuestViewManager()->GetGuestWebContentsList(&guest_contents_list);
+  ASSERT_EQ(2u, guest_contents_list.size());
+  content::WebContents* other_guest =
+      (guest_contents_list[0] == unattached_guest->web_contents())
+          ? guest_contents_list[1]
+          : guest_contents_list[0];
+
+  content::RenderFrameHost* embedder_main_frame = embedder->GetMainFrame();
+  content::RenderFrameHost* unattached_guest_main_frame =
+      unattached_guest->web_contents()->GetMainFrame();
+  content::RenderFrameHost* other_guest_main_frame =
+      other_guest->GetMainFrame();
+
+  EXPECT_THAT(
+      content::CollectAllFrames(embedder_main_frame),
+      testing::UnorderedElementsAre(embedder_main_frame, other_guest_main_frame,
+                                    unattached_guest_main_frame));
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestContentLoadEvent) {
   TestHelper("testContentLoadEvent", "web_view/shim", NO_TEST_SERVER);
 }
diff --git a/chrome/browser/ash/arc/usb/arc_usb_host_bridge_delegate.cc b/chrome/browser/ash/arc/usb/arc_usb_host_bridge_delegate.cc
index 249b39fb..79b6f41d 100644
--- a/chrome/browser/ash/arc/usb/arc_usb_host_bridge_delegate.cc
+++ b/chrome/browser/ash/arc/usb/arc_usb_host_bridge_delegate.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ash/arc/usb/arc_usb_host_bridge_delegate.h"
 
-#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
+#include "chrome/browser/ash/usb/cros_usb_detector.h"
 #include "components/arc/arc_util.h"
 
 namespace arc {
diff --git a/chrome/browser/ash/settings/cros_settings.cc b/chrome/browser/ash/settings/cros_settings.cc
index bcaf3e5..f1ebb303 100644
--- a/chrome/browser/ash/settings/cros_settings.cc
+++ b/chrome/browser/ash/settings/cros_settings.cc
@@ -238,16 +238,13 @@
     *wildcard_match = false;
 
   bool found_wildcard_match = false;
-  for (base::ListValue::const_iterator entry(list->begin());
-       entry != list->end();
-       ++entry) {
-    std::string entry_string;
-    if (!entry->GetAsString(&entry_string)) {
+  for (const auto& entry : list->GetList()) {
+    if (!entry.is_string()) {
       NOTREACHED();
       continue;
     }
     std::string canonicalized_entry(
-        gaia::CanonicalizeEmail(gaia::SanitizeEmail(entry_string)));
+        gaia::CanonicalizeEmail(gaia::SanitizeEmail(entry.GetString())));
 
     if (canonicalized_entry != wildcard_email &&
         canonicalized_entry == canonicalized_email) {
diff --git a/chrome/browser/chromeos/usb/DIR_METADATA b/chrome/browser/ash/usb/DIR_METADATA
similarity index 100%
rename from chrome/browser/chromeos/usb/DIR_METADATA
rename to chrome/browser/ash/usb/DIR_METADATA
diff --git a/chrome/browser/chromeos/usb/OWNERS b/chrome/browser/ash/usb/OWNERS
similarity index 100%
rename from chrome/browser/chromeos/usb/OWNERS
rename to chrome/browser/ash/usb/OWNERS
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.cc b/chrome/browser/ash/usb/cros_usb_detector.cc
similarity index 99%
rename from chrome/browser/chromeos/usb/cros_usb_detector.cc
rename to chrome/browser/ash/usb/cros_usb_detector.cc
index 307b075..e832ccf6 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.cc
+++ b/chrome/browser/ash/usb/cros_usb_detector.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
+#include "chrome/browser/ash/usb/cros_usb_detector.h"
 
 #include <fcntl.h>
 
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.h b/chrome/browser/ash/usb/cros_usb_detector.h
similarity index 98%
rename from chrome/browser/chromeos/usb/cros_usb_detector.h
rename to chrome/browser/ash/usb/cros_usb_detector.h
index ab616c8..4aa77b7 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector.h
+++ b/chrome/browser/ash/usb/cros_usb_detector.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_USB_CROS_USB_DETECTOR_H_
-#define CHROME_BROWSER_CHROMEOS_USB_CROS_USB_DETECTOR_H_
+#ifndef CHROME_BROWSER_ASH_USB_CROS_USB_DETECTOR_H_
+#define CHROME_BROWSER_ASH_USB_CROS_USB_DETECTOR_H_
 
 #include <map>
 #include <memory>
@@ -276,4 +276,4 @@
 
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_USB_CROS_USB_DETECTOR_H_
+#endif  // CHROME_BROWSER_ASH_USB_CROS_USB_DETECTOR_H_
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
rename to chrome/browser/ash/usb/cros_usb_detector_unittest.cc
index a945481..40af7ff2 100644
--- a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
+++ b/chrome/browser/ash/usb/cros_usb_detector_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
+#include "chrome/browser/ash/usb/cros_usb_detector.h"
 
 #include <algorithm>
 #include <memory>
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
index 4004837..fa138f7 100644
--- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -457,8 +457,8 @@
   EXPECT_EQ(true, ExecuteScript(web_ui, kDomExceptionScript));
   auto report = endpoint.WaitForReport();
   EXPECT_NE(std::string::npos,
-            report.query.find("error_message=Unhandled%20rejection%3A%20Error%"
-                              "3A%20NotAFile%3A%20Not%20a%20file."))
+            report.query.find("error_message=Unhandled%20rejection%3A"
+                              "%20%5BNotAFile%5D%20Not%20a%20file."))
       << report.query;
   EXPECT_NE(std::string::npos, report.query.find("prod=ChromeOS_MediaApp"));
 }
@@ -491,8 +491,8 @@
   EXPECT_EQ(true, ExecuteScript(web_ui, kUnhandledRejectionScript));
   auto report = endpoint.WaitForReport();
   EXPECT_NE(std::string::npos,
-            report.query.find("error_message=Unhandled%20rejection%3A%20Error%"
-                              "3A%20FakeErrorName%3A%20fake_throw"))
+            report.query.find("error_message=Unhandled%20rejection%3A%20%5B"
+                              "FakeErrorName%5D%20fake_throw"))
       << report.query;
   EXPECT_NE(std::string::npos, report.query.find("prod=ChromeOS_MediaApp"));
 }
@@ -525,7 +525,7 @@
   auto report = endpoint.WaitForReport();
   EXPECT_NE(std::string::npos,
             report.query.find(
-                "error_message=Error%3A%20ErrorEvent%3A%20Uncaught%20TypeError%"
+                "error_message=ErrorEvent%3A%20%5B%5D%20Uncaught%20TypeError%"
                 "3A%20event.notAFunction%20is%20not%20a%20function"))
       << report.query;
   EXPECT_NE(std::string::npos, report.query.find("prod=ChromeOS_MediaApp"));
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 80dbf390..2af5c0c 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1828,6 +1828,8 @@
     "../ash/system_logs/touch_log_source.h",
     "../ash/system_logs/ui_hierarchy_log_source.cc",
     "../ash/system_logs/ui_hierarchy_log_source.h",
+    "../ash/usb/cros_usb_detector.cc",
+    "../ash/usb/cros_usb_detector.h",
     "../ash/web_applications/camera_system_web_app_info.cc",
     "../ash/web_applications/camera_system_web_app_info.h",
     "../ash/web_applications/chrome_camera_app_ui_constants.h",
@@ -3122,8 +3124,6 @@
     "tpm_firmware_update_notification.h",
     "u2f_notification.cc",
     "u2f_notification.h",
-    "usb/cros_usb_detector.cc",
-    "usb/cros_usb_detector.h",
     "vm_shutdown_observer.h",
     "vm_starting_observer.h",
     "window_throttle_observer_base.cc",
@@ -3868,6 +3868,7 @@
     "../ash/system_logs/shill_log_source_unittest.cc",
     "../ash/system_logs/single_debug_daemon_log_source_unittest.cc",
     "../ash/system_logs/single_log_file_log_source_unittest.cc",
+    "../ash/usb/cros_usb_detector_unittest.cc",
     "../ash/web_applications/help_app/help_app_discover_tab_notification_unittest.cc",
     "../ash/web_applications/help_app/help_app_notification_controller_unittest.cc",
     "../ash/wilco_dtc_supportd/testing_wilco_dtc_supportd_bridge_wrapper.cc",
@@ -4273,7 +4274,6 @@
     "throttle_observer_unittest.cc",
     "throttle_service_unittest.cc",
     "tpm_firmware_update_unittest.cc",
-    "usb/cros_usb_detector_unittest.cc",
 
     # TODO(zturner): Enable this on Windows. See
     # BrowserWithTestWindowTest::SetUp() for a comment explaining why this is
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 3a74d5f0..55ac618 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -75,6 +75,7 @@
 #include "chrome/browser/ash/system/breakpad_consent_watcher.h"
 #include "chrome/browser/ash/system/input_device_settings.h"
 #include "chrome/browser/ash/system/user_removal_manager.h"
+#include "chrome/browser/ash/usb/cros_usb_detector.h"
 #include "chrome/browser/ash/wilco_dtc_supportd/wilco_dtc_supportd_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
@@ -134,7 +135,6 @@
 #include "chrome/browser/chromeos/scheduler_configuration_manager.h"
 #include "chrome/browser/chromeos/startup_settings_cache.h"
 #include "chrome/browser/chromeos/system_token_cert_db_initializer.h"
-#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
 #include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
diff --git a/chrome/browser/chromeos/chromebox_for_meetings/logger/cfm_logger_service.cc b/chrome/browser/chromeos/chromebox_for_meetings/logger/cfm_logger_service.cc
index 90560bd..9012d52 100644
--- a/chrome/browser/chromeos/chromebox_for_meetings/logger/cfm_logger_service.cc
+++ b/chrome/browser/chromeos/chromebox_for_meetings/logger/cfm_logger_service.cc
@@ -101,12 +101,13 @@
 }
 
 void CfmLoggerService::OnAdaptorConnect(bool success) {
-  if (success) {
-    VLOG(3) << "Adaptor is connected.";
-    delegate_->Init();
-  } else {
+  if (!success) {
     LOG(ERROR) << "Adaptor connection failed.";
+    return;
   }
+
+  VLOG(3) << "Adaptor is connected.";
+  delegate_->Init();
 }
 
 void CfmLoggerService::OnAdaptorDisconnect() {
diff --git a/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.cc b/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.cc
index feaa063..c5dc47d 100644
--- a/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.cc
+++ b/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.cc
@@ -36,6 +36,11 @@
                                   "Meet logger service not yet initialised.");
 }
 
+::reporting::Status LogPolicyDisabled() {
+  return ::reporting::Status(reporting::error::UNAUTHENTICATED,
+                             "System log upload policy not enabled");
+}
+
 constexpr auto kHandlerDestination =
     ::reporting::Destination::MEET_DEVICE_TELEMETRY;
 
@@ -117,7 +122,8 @@
 
   auto config_result = reporting::ReportQueueConfiguration::Create(
       dm_token_, kHandlerDestination,
-      base::BindRepeating([]() { return ::reporting::Status::StatusOK(); }));
+      base::BindRepeating(&ReportingPipeline::CheckPolicy,
+                          base::Unretained(this)));
 
   if (!config_result.ok()) {
     LOG(ERROR) << "Report Client Configuration failed with error message: "
@@ -145,6 +151,21 @@
           std::move(config_result).ValueOrDie(), std::move(queue_callback)));
 }
 
+::reporting::Status ReportingPipeline::CheckPolicy() const {
+  auto* chrome_device_settings =
+      ash::DeviceSettingsService::Get()->device_settings();
+  bool policy_enabled = false;
+
+  if (chrome_device_settings &&
+      chrome_device_settings->has_device_log_upload_settings()) {
+    auto device_upload_settings =
+        chrome_device_settings->device_log_upload_settings();
+    policy_enabled = device_upload_settings.system_log_upload_enabled();
+  }
+
+  return policy_enabled ? ::reporting::Status::StatusOK() : LogPolicyDisabled();
+}
+
 void ReportingPipeline::OnReportQueueUpdated(
     reporting::ReportQueueProvider::CreateReportQueueResponse
         report_queue_result) {
diff --git a/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.h b/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.h
index 079e3073..cd2cce4e 100644
--- a/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.h
+++ b/chrome/browser/chromeos/chromebox_for_meetings/logger/reporting_pipeline.h
@@ -44,6 +44,7 @@
 
  private:
   void UpdateToken(std::string request_token);
+  ::reporting::Status CheckPolicy() const;
   void OnReportQueueUpdated(
       reporting::ReportQueueProvider::CreateReportQueueResponse
           report_queue_result);
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index c8e643a0f..28997a7 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -30,6 +30,7 @@
 #include "chrome/browser/ash/crostini/throttle/crostini_throttle.h"
 #include "chrome/browser/ash/guest_os/guest_os_share_path.h"
 #include "chrome/browser/ash/guest_os/guest_os_stability_monitor.h"
+#include "chrome/browser/ash/usb/cros_usb_detector.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/crostini/crostini_engagement_metrics_service.h"
@@ -46,7 +47,6 @@
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
 #include "chrome/browser/chromeos/policy/powerwash_requirements_checker.h"
 #include "chrome/browser/chromeos/scheduler_configuration_manager.h"
-#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/extensions/api/chrome_extensions_api_client.cc b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
index 99a1061..7e22c3c 100644
--- a/chrome/browser/extensions/api/chrome_extensions_api_client.cc
+++ b/chrome/browser/extensions/api/chrome_extensions_api_client.cc
@@ -330,7 +330,7 @@
   const base::ListValue* policy_list;
   if (ash::CrosSettings::Get()->GetList(chromeos::kUsbDetachableAllowlist,
                                         &policy_list)) {
-    for (const auto& entry : *policy_list) {
+    for (const auto& entry : policy_list->GetList()) {
       if (entry.FindIntKey(chromeos::kUsbDetachableAllowlistKeyVid) == vid &&
           entry.FindIntKey(chromeos::kUsbDetachableAllowlistKeyPid) == pid) {
         return true;
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_store.cc b/chrome/browser/extensions/api/content_settings/content_settings_store.cc
index 899972b4..3f6164d 100644
--- a/chrome/browser/extensions/api/content_settings/content_settings_store.cc
+++ b/chrome/browser/extensions/api/content_settings/content_settings_store.cc
@@ -310,7 +310,7 @@
     const std::string& extension_id,
     const base::ListValue* list,
     ExtensionPrefsScope scope) {
-  for (const auto& value : *list) {
+  for (const auto& value : list->GetList()) {
     const base::DictionaryValue* dict = nullptr;
     if (!value.GetAsDictionary(&dict)) {
       NOTREACHED();
diff --git a/chrome/browser/extensions/api/declarative_content/content_action.cc b/chrome/browser/extensions/api/declarative_content/content_action.cc
index b05def4..db763a3b 100644
--- a/chrome/browser/extensions/api/declarative_content/content_action.cc
+++ b/chrome/browser/extensions/api/declarative_content/content_action.cc
@@ -175,9 +175,9 @@
 // Helper for getting JS collections into C++.
 static bool AppendJSStringsToCPPStrings(const base::ListValue& append_strings,
                                         std::vector<std::string>* append_to) {
-  for (auto it = append_strings.begin(); it != append_strings.end(); ++it) {
+  for (const auto& entry : append_strings.GetList()) {
     std::string value;
-    if (it->GetAsString(&value)) {
+    if (entry.GetAsString(&value)) {
       append_to->push_back(value);
     } else {
       return false;
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
index f895d8c..3f98266 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc
@@ -327,7 +327,7 @@
 
     std::u16string error;
     std::vector<std::string> actual_ids;
-    for (const auto& val : ids_value)
+    for (const auto& val : ids_value.GetList())
       actual_ids.push_back(val.GetString());
 
     EXPECT_THAT(expected_ids, UnorderedElementsAreArray(actual_ids));
diff --git a/chrome/browser/extensions/api/terminal/terminal_private_api.cc b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
index 92bdc338..682b4a21 100644
--- a/chrome/browser/extensions/api/terminal/terminal_private_api.cc
+++ b/chrome/browser/extensions/api/terminal/terminal_private_api.cc
@@ -160,7 +160,8 @@
                        extensions::events::HistogramValue histogram,
                        const char* eventName) {
   auto args = std::make_unique<base::ListValue>();
-  args->Append(profile->GetPrefs()->Get(pref_name)->CreateDeepCopy());
+  args->Append(base::Value::ToUniquePtrValue(
+      profile->GetPrefs()->Get(pref_name)->Clone()));
   extensions::EventRouter* event_router = extensions::EventRouter::Get(profile);
   if (event_router) {
     auto event = std::make_unique<extensions::Event>(histogram, eventName,
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index 1424f2b..9f582d8 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -328,21 +328,20 @@
   ASSERT_TRUE(RunExtensionTest("webnavigation/backForwardCache")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, IFrame) {
-  ASSERT_TRUE(RunExtensionTest("webnavigation/iframe")) << message_;
+IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, IFrame) {
+  ASSERT_TRUE(RunTest("webnavigation/iframe")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, SrcDoc) {
   ASSERT_TRUE(RunExtensionTest("webnavigation/srcdoc")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, OpenTab) {
-  ASSERT_TRUE(RunExtensionTest("webnavigation/openTab")) << message_;
+IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, OpenTab) {
+  ASSERT_TRUE(RunTest("webnavigation/openTab")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ReferenceFragment) {
-  ASSERT_TRUE(RunExtensionTest("webnavigation/referenceFragment"))
-      << message_;
+IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, ReferenceFragment) {
+  ASSERT_TRUE(RunTest("webnavigation/referenceFragment")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, SimpleLoad) {
@@ -569,10 +568,10 @@
   ASSERT_TRUE(RunTest("webnavigation/crossProcessIframe")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, PendingDeletion) {
+IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, PendingDeletion) {
   content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
   ASSERT_TRUE(StartEmbeddedTestServer());
-  ASSERT_TRUE(RunExtensionTest("webnavigation/pendingDeletion")) << message_;
+  ASSERT_TRUE(RunTest("webnavigation/pendingDeletion")) << message_;
 }
 
 IN_PROC_BROWSER_TEST_P(WebNavigationApiTestWithContextType, Crash) {
diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc
index d9a652b..949060ac 100644
--- a/chrome/browser/extensions/extension_management.cc
+++ b/chrome/browser/extensions/extension_management.cc
@@ -441,8 +441,9 @@
   if ((denied_list_pref &&
        // TODO(crbug.com/1187106): Use base::Contains once |denied_list_pref| is
        // not a ListValue.
-       std::find(denied_list_pref->begin(), denied_list_pref->end(),
-                 wildcard) != denied_list_pref->end()) ||
+       std::find(denied_list_pref->GetList().begin(),
+                 denied_list_pref->GetList().end(),
+                 wildcard) != denied_list_pref->GetList().end()) ||
       (extension_request_pref && extension_request_pref->GetBool())) {
     default_settings_->installation_mode = INSTALLATION_BLOCKED;
   }
@@ -468,17 +469,15 @@
   ExtensionId id;
 
   if (allowed_list_pref) {
-    for (auto it = allowed_list_pref->begin(); it != allowed_list_pref->end();
-         ++it) {
-      if (it->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
+    for (const auto& entry : allowed_list_pref->GetList()) {
+      if (entry.GetAsString(&id) && crx_file::id_util::IdIsValid(id))
         AccessById(id)->installation_mode = INSTALLATION_ALLOWED;
     }
   }
 
   if (denied_list_pref) {
-    for (auto it = denied_list_pref->begin(); it != denied_list_pref->end();
-         ++it) {
-      if (it->GetAsString(&id) && crx_file::id_util::IdIsValid(id))
+    for (const auto& entry : denied_list_pref->GetList()) {
+      if (entry.GetAsString(&id) && crx_file::id_util::IdIsValid(id))
         AccessById(id)->installation_mode = INSTALLATION_BLOCKED;
     }
   }
@@ -487,10 +486,9 @@
 
   if (install_sources_pref) {
     global_settings_->has_restricted_install_sources = true;
-    for (auto it = install_sources_pref->begin();
-         it != install_sources_pref->end(); ++it) {
+    for (const auto& entry : install_sources_pref->GetList()) {
       std::string url_pattern;
-      if (it->GetAsString(&url_pattern)) {
+      if (entry.GetAsString(&url_pattern)) {
         URLPattern entry(URLPattern::SCHEME_ALL);
         if (entry.Parse(url_pattern) == URLPattern::ParseResult::kSuccess) {
           global_settings_->install_sources.AddPattern(entry);
@@ -505,15 +503,14 @@
 
   if (allowed_types_pref) {
     global_settings_->has_restricted_allowed_types = true;
-    for (auto it = allowed_types_pref->begin(); it != allowed_types_pref->end();
-         ++it) {
-      if (it->is_int() && it->GetInt() >= 0 &&
-          it->GetInt() < Manifest::Type::NUM_LOAD_TYPES) {
+    for (const auto& entry : allowed_types_pref->GetList()) {
+      if (entry.is_int() && entry.GetInt() >= 0 &&
+          entry.GetInt() < Manifest::Type::NUM_LOAD_TYPES) {
         global_settings_->allowed_types.push_back(
-            static_cast<Manifest::Type>(it->GetInt()));
-      } else if (it->is_string()) {
+            static_cast<Manifest::Type>(entry.GetInt()));
+      } else if (entry.is_string()) {
         Manifest::Type manifest_type =
-            schema_constants::GetManifestType(it->GetString());
+            schema_constants::GetManifestType(entry.GetString());
         if (manifest_type != Manifest::TYPE_UNKNOWN)
           global_settings_->allowed_types.push_back(manifest_type);
       }
diff --git a/chrome/browser/extensions/extension_override_apitest.cc b/chrome/browser/extensions/extension_override_apitest.cc
index 8d5a177..81284be 100644
--- a/chrome/browser/extensions/extension_override_apitest.cc
+++ b/chrome/browser/extensions/extension_override_apitest.cc
@@ -56,7 +56,7 @@
       return false;
 
     std::set<std::string> seen_overrides;
-    for (const auto& val : *values) {
+    for (const auto& val : values->GetList()) {
       const base::DictionaryValue* dict = nullptr;
       std::string entry;
       if (!val.GetAsDictionary(&dict) || !dict->GetString("entry", &entry) ||
diff --git a/chrome/browser/extensions/extension_web_ui.cc b/chrome/browser/extensions/extension_web_ui.cc
index 3e47a2d..287b306 100644
--- a/chrome/browser/extensions/extension_web_ui.cc
+++ b/chrome/browser/extensions/extension_web_ui.cc
@@ -71,7 +71,7 @@
 void InitializeOverridesList(base::ListValue* list) {
   base::ListValue migrated;
   std::set<std::string> seen_entries;
-  for (auto& val : *list) {
+  for (auto& val : list->GetList()) {
     base::Value new_dict(base::Value::Type::DICTIONARY);
     std::string entry_name;
     if (val.is_dict()) {
@@ -104,7 +104,7 @@
 // marks it as active.
 void AddOverridesToList(base::ListValue* list, const GURL& override_url) {
   const std::string& spec = override_url.spec();
-  for (auto& val : *list) {
+  for (auto& val : list->GetList()) {
     std::string* entry = nullptr;
     if (val.is_dict()) {
       entry = val.FindStringKey(kEntry);
@@ -142,7 +142,7 @@
                            base::ListValue* list) {
   base::ListValue migrated;
   std::set<std::string> seen_hosts;
-  for (auto& val : *list) {
+  for (auto& val : list->GetList()) {
     std::string* entry = nullptr;
     if (val.is_dict()) {
       entry = val.FindStringKey(kEntry);
@@ -201,15 +201,15 @@
 bool UpdateOverridesList(base::ListValue* overrides_list,
                          const std::string& override_url,
                          UpdateBehavior behavior) {
-  auto iter = std::find_if(overrides_list->begin(), overrides_list->end(),
-                           [&override_url](const base::Value& value) {
-                             if (!value.is_dict())
-                               return false;
-                             const std::string* entry =
-                                 value.FindStringKey(kEntry);
-                             return entry && *entry == override_url;
-                           });
-  if (iter != overrides_list->end()) {
+  auto iter = std::find_if(
+      overrides_list->GetList().begin(), overrides_list->GetList().end(),
+      [&override_url](const base::Value& value) {
+        if (!value.is_dict())
+          return false;
+        const std::string* entry = value.FindStringKey(kEntry);
+        return entry && *entry == override_url;
+      });
+  if (iter != overrides_list->GetList().end()) {
     switch (behavior) {
       case UPDATE_DEACTIVATE: {
         // See comment about CHECK(success) in ForEachOverrideList.
@@ -372,7 +372,7 @@
   std::vector<GURL> component_overrides;
 
   // Iterate over the URL list looking for suitable overrides.
-  for (const auto& value : *url_list) {
+  for (const auto& value : url_list->GetList()) {
     GURL override_url;
     const Extension* extension = nullptr;
     if (!ValidateOverrideURL(&value, url, extensions, &override_url,
@@ -465,7 +465,7 @@
     if (!dict_iter.value().GetAsList(&url_list))
       continue;
 
-    for (const auto& list_iter : *url_list) {
+    for (const auto& list_iter : url_list->GetList()) {
       const std::string* override = nullptr;
       if (list_iter.is_dict())
         override = list_iter.FindStringKey(kEntry);
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc
index 055ac84..be45a54 100644
--- a/chrome/browser/extensions/install_signer.cc
+++ b/chrome/browser/extensions/install_signer.cc
@@ -141,12 +141,12 @@
 bool GetExtensionIdSet(const base::DictionaryValue& dictionary,
                        const char* key,
                        ExtensionIdSet* ids) {
-  const base::ListValue* id_list = NULL;
+  const base::ListValue* id_list = nullptr;
   if (!dictionary.GetList(key, &id_list))
     return false;
-  for (auto i = id_list->begin(); i != id_list->end(); ++i) {
+  for (const auto& entry : id_list->GetList()) {
     std::string id;
-    if (!i->GetAsString(&id)) {
+    if (!entry.GetAsString(&id)) {
       return false;
     }
     ids->insert(id);
diff --git a/chrome/browser/extensions/policy_handlers.cc b/chrome/browser/extensions/policy_handlers.cc
index 16bbac069..353152d 100644
--- a/chrome/browser/extensions/policy_handlers.cc
+++ b/chrome/browser/extensions/policy_handlers.cc
@@ -114,13 +114,13 @@
     return false;
   }
 
-  for (auto entry(policy_list_value->begin());
-       entry != policy_list_value->end(); ++entry) {
+  int index = -1;
+  for (const auto& entry : policy_list_value->GetList()) {
+    ++index;
     std::string entry_string;
-    if (!entry->GetAsString(&entry_string)) {
+    if (!entry.GetAsString(&entry_string)) {
       if (errors) {
-        errors->AddError(policy_name(), entry - policy_list_value->begin(),
-                         IDS_POLICY_TYPE_ERROR,
+        errors->AddError(policy_name(), index, IDS_POLICY_TYPE_ERROR,
                          base::Value::GetTypeName(base::Value::Type::STRING));
       }
       continue;
@@ -144,9 +144,7 @@
     if (!crx_file::id_util::IdIsValid(extension_id) ||
         !GURL(update_url).is_valid()) {
       if (errors) {
-        errors->AddError(policy_name(),
-                         entry - policy_list_value->begin(),
-                         IDS_POLICY_VALUE_FORMAT_ERROR);
+        errors->AddError(policy_name(), index, IDS_POLICY_VALUE_FORMAT_ERROR);
       }
       continue;
     }
@@ -187,11 +185,11 @@
   }
 
   // Check that the list contains valid URLPattern strings only.
-  for (auto entry(list_value->begin()); entry != list_value->end(); ++entry) {
+  int index = 0;
+  for (const auto& entry : list_value->GetList()) {
     std::string url_pattern_string;
-    if (!entry->GetAsString(&url_pattern_string)) {
-      errors->AddError(policy_name(), entry - list_value->begin(),
-                       IDS_POLICY_TYPE_ERROR,
+    if (!entry.GetAsString(&url_pattern_string)) {
+      errors->AddError(policy_name(), index, IDS_POLICY_TYPE_ERROR,
                        base::Value::GetTypeName(base::Value::Type::STRING));
       return false;
     }
@@ -199,11 +197,10 @@
     URLPattern pattern(URLPattern::SCHEME_ALL);
     if (pattern.Parse(url_pattern_string) !=
         URLPattern::ParseResult::kSuccess) {
-      errors->AddError(policy_name(),
-                       entry - list_value->begin(),
-                       IDS_POLICY_VALUE_FORMAT_ERROR);
+      errors->AddError(policy_name(), index, IDS_POLICY_VALUE_FORMAT_ERROR);
       return false;
     }
+    ++index;
   }
 
   return true;
diff --git a/chrome/browser/extensions/printer_provider_apitest.cc b/chrome/browser/extensions/printer_provider_apitest.cc
index 68796fc..b825019d 100644
--- a/chrome/browser/extensions/printer_provider_apitest.cc
+++ b/chrome/browser/extensions/printer_provider_apitest.cc
@@ -267,7 +267,7 @@
       const std::vector<std::unique_ptr<base::Value>>& expected_printers) {
     ASSERT_EQ(expected_printers.size(), printers.GetSize());
     for (const auto& printer_value : expected_printers) {
-      EXPECT_TRUE(printers.Find(*printer_value) != printers.end())
+      EXPECT_TRUE(printers.Find(*printer_value) != printers.GetList().end())
           << "Unable to find " << *printer_value << " in " << printers;
     }
   }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index b3351c91..d6b55ad 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3811,11 +3811,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "offline-pages-ct",
-    "owners": [ "sclittle", "srsudar", "offline-dev" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "offline-pages-ct-suppress-completed-notification",
     "owners": [ "sclittle", "srsudar", "offline-dev" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4e5506e..aa89c3bd 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1718,12 +1718,6 @@
     "Enables an experimental scoring mode for suggestions when Google is the "
     "default search engine.";
 
-const char kOmniboxKeywordSpaceTriggeringName[] =
-    "Omnibox Keyword Space Triggering";
-const char kOmniboxKeywordSpaceTriggeringDescription[] =
-    "Controls whether keyword mode can be triggered by space, double space, or "
-    "neither.";
-
 const char kOmniboxKeywordSpaceTriggeringSettingName[] =
     "Omnibox Keyword Space Triggering Setting";
 const char kOmniboxKeywordSpaceTriggeringSettingDescription[] =
@@ -3147,9 +3141,6 @@
 const char kOfflineIndicatorV2Description[] =
     "Show a persistent offline indicator when offline.";
 
-const char kOfflinePagesCtName[] = "Enable Offline Pages CT features.";
-const char kOfflinePagesCtDescription[] = "Enable Offline Pages CT features.";
-
 const char kOfflinePagesCtV2Name[] = "Enable Offline Pages CT V2 features.";
 const char kOfflinePagesCtV2Description[] =
     "V2 features include attributing pages to the app that initiated the "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8f870bb6..5ee4bc4d 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -995,9 +995,6 @@
 extern const char kOmniboxDefaultTypedNavigationsToHttpsName[];
 extern const char kOmniboxDefaultTypedNavigationsToHttpsDescription[];
 
-extern const char kOmniboxKeywordSpaceTriggeringName[];
-extern const char kOmniboxKeywordSpaceTriggeringDescription[];
-
 extern const char kOmniboxKeywordSpaceTriggeringSettingName[];
 extern const char kOmniboxKeywordSpaceTriggeringSettingDescription[];
 
@@ -1829,9 +1826,6 @@
 extern const char kOfflineIndicatorV2Name[];
 extern const char kOfflineIndicatorV2Description[];
 
-extern const char kOfflinePagesCtName[];
-extern const char kOfflinePagesCtDescription[];
-
 extern const char kOfflinePagesCtV2Name[];
 extern const char kOfflinePagesCtV2Description[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 0f41f117..3cf84e1e 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -278,7 +278,6 @@
     &language::kTranslateIntent,
     &messages::kMessagesForAndroidInfrastructure,
     &offline_pages::kOfflineIndicatorFeature,
-    &offline_pages::kOfflinePagesCTFeature,    // See crbug.com/620421.
     &offline_pages::kOfflinePagesCTV2Feature,  // See crbug.com/734753.
     &offline_pages::kOfflinePagesDescriptiveFailStatusFeature,
     &offline_pages::kOfflinePagesDescriptivePendingStatusFeature,
diff --git a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
index b903036..e56ea69 100644
--- a/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
+++ b/chrome/browser/media/router/presentation/presentation_service_delegate_impl_unittest.cc
@@ -5,6 +5,7 @@
 #include "components/media_router/browser/presentation/presentation_service_delegate_impl.h"
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
 #include "base/test/mock_callback.h"
 #include "build/build_config.h"
 #include "chrome/browser/media/router/presentation/chrome_local_presentation_manager_factory.h"
@@ -831,8 +832,8 @@
   // profile.
   const base::ListValue* non_off_the_record_origins =
       profile()->GetPrefs()->GetList(prefs::kMediaRouterTabMirroringSources);
-  EXPECT_EQ(non_off_the_record_origins->Find(base::Value(origin)),
-            non_off_the_record_origins->end());
+  EXPECT_FALSE(base::Contains(non_off_the_record_origins->GetList(),
+                              base::Value(origin)));
 
   // Auto-join requests should be rejected.
   EXPECT_CALL(mock_create_connection_callbacks, OnCreateConnectionError(_));
diff --git a/chrome/browser/media/router/providers/cast/cast_media_controller.cc b/chrome/browser/media/router/providers/cast/cast_media_controller.cc
index 0dc0fd5..25e5e1cc 100644
--- a/chrome/browser/media/router/providers/cast/cast_media_controller.cc
+++ b/chrome/browser/media/router/providers/cast/cast_media_controller.cc
@@ -233,14 +233,14 @@
         base::Value::AsListValue(*commands_value);
     // |can_set_volume| and |can_mute| are not used, because the receiver volume
     // info obtained in SetSession() is used instead.
-    media_status_.can_play_pause =
-        base::Contains(commands_list, base::Value(kMediaCommandPause));
+    media_status_.can_play_pause = base::Contains(
+        commands_list.GetList(), base::Value(kMediaCommandPause));
     media_status_.can_seek =
-        base::Contains(commands_list, base::Value(kMediaCommandSeek));
-    media_status_.can_skip_to_next_track =
-        base::Contains(commands_list, base::Value(kMediaCommandQueueNext));
-    media_status_.can_skip_to_previous_track =
-        base::Contains(commands_list, base::Value(kMediaCommandQueuePrev));
+        base::Contains(commands_list.GetList(), base::Value(kMediaCommandSeek));
+    media_status_.can_skip_to_next_track = base::Contains(
+        commands_list.GetList(), base::Value(kMediaCommandQueueNext));
+    media_status_.can_skip_to_previous_track = base::Contains(
+        commands_list.GetList(), base::Value(kMediaCommandQueuePrev));
   }
 
   const base::Value* player_state = status_value.FindKey("playerState");
diff --git a/chrome/browser/metrics/plugin_metrics_provider.cc b/chrome/browser/metrics/plugin_metrics_provider.cc
index 13edcd8..42af3d5 100644
--- a/chrome/browser/metrics/plugin_metrics_provider.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider.cc
@@ -141,7 +141,7 @@
 
   metrics::SystemProfileProto::Stability* stability =
       system_profile_proto->mutable_stability();
-  for (const auto& value : *plugin_stats_list) {
+  for (const auto& value : plugin_stats_list->GetList()) {
     const base::DictionaryValue* plugin_dict;
     if (!value.GetAsDictionary(&plugin_dict)) {
       NOTREACHED();
@@ -207,7 +207,7 @@
   base::ListValue* plugins = update.Get();
   DCHECK(plugins);
 
-  for (auto& value : *plugins) {
+  for (auto& value : plugins->GetList()) {
     base::DictionaryValue* plugin_dict;
     if (!value.GetAsDictionary(&plugin_dict)) {
       NOTREACHED();
diff --git a/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc b/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc
index 3e12b0e..6f0ef36 100644
--- a/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc
+++ b/chrome/browser/printing/cloud_print/cloud_print_printer_list.cc
@@ -31,7 +31,7 @@
   }
 
   DeviceList devices;
-  for (const auto& printer : *printers) {
+  for (const auto& printer : printers->GetList()) {
     const base::DictionaryValue* printer_dict;
     if (!printer.GetAsDictionary(&printer_dict))
       continue;
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index 4743a6e..cbe686c 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -270,7 +270,7 @@
   return document()->settings();
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
 void PrintJob::SetSource(PrintJob::Source source,
                          const std::string& source_id) {
   source_ = source;
@@ -284,7 +284,7 @@
 const std::string& PrintJob::source_id() const {
   return source_id_;
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // defined(OS_CHROMEOS)
 
 #if defined(OS_WIN)
 class PrintJob::PdfConversionState {
diff --git a/chrome/browser/printing/print_job_unittest.cc b/chrome/browser/printing/print_job_unittest.cc
index be165b66..d2d2907 100644
--- a/chrome/browser/printing/print_job_unittest.cc
+++ b/chrome/browser/printing/print_job_unittest.cc
@@ -100,9 +100,9 @@
   volatile bool check = false;
   scoped_refptr<PrintJob> job(new TestPrintJob(&check));
   job->Initialize(std::make_unique<TestQuery>(), std::u16string(), 1);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
   job->SetSource(PrintJob::Source::PRINT_PREVIEW, /*source_id=*/"");
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // defined(OS_CHROMEOS)
   job->Stop();
   while (job->document()) {
     base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc
index 91edb6e..6c70ed5 100644
--- a/chrome/browser/printing/print_job_worker.cc
+++ b/chrome/browser/printing/print_job_worker.cc
@@ -194,7 +194,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void PrintJobWorker::SetSettingsFromPOD(
-    std::unique_ptr<printing::PrintSettings> new_settings,
+    std::unique_ptr<PrintSettings> new_settings,
     SettingsCallback callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
@@ -252,7 +252,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 void PrintJobWorker::UpdatePrintSettingsFromPOD(
-    std::unique_ptr<printing::PrintSettings> new_settings,
+    std::unique_ptr<PrintSettings> new_settings,
     SettingsCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   PrintingContext::Result result =
@@ -390,7 +390,7 @@
 #if defined(OS_WIN)
   const bool source_is_pdf =
       !print_job_->document()->settings().is_modifiable();
-  if (!printing::features::ShouldPrintUsingXps(source_is_pdf)) {
+  if (!features::ShouldPrintUsingXps(source_is_pdf)) {
     // Using the Windows GDI print API.
     if (!OnNewPageHelperGdi())
       return;
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index 9aa99b7..f60e42b 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -67,6 +67,16 @@
 #include "chrome/browser/win/conflicts/module_database.h"
 #endif
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "base/callback_helpers.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/grit/generated_resources.h"
+#include "chromeos/lacros/lacros_chrome_service_impl.h"
+#include "printing/print_settings.h"
+#include "printing/printing_utils.h"
+#include "ui/base/l10n/l10n_util.h"
+#endif
+
 namespace printing {
 
 namespace {
@@ -74,6 +84,31 @@
 using PrintSettingsCallback =
     base::OnceCallback<void(std::unique_ptr<PrinterQuery>)>;
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+crosapi::mojom::PrintJobPtr PrintJobToMojom(
+    const JobEventDetails& event_details,
+    PrintJob::Source source,
+    const std::string& source_id) {
+  PrintedDocument* document = event_details.document();
+  std::u16string title = SimplifyDocumentTitle(document->name());
+  if (title.empty()) {
+    title = SimplifyDocumentTitle(
+        l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
+  }
+  const PrintSettings& settings = document->settings();
+  int duplex = static_cast<int>(settings.duplex_mode());
+  DCHECK(duplex >= 0);
+  DCHECK(duplex < 3);
+  return crosapi::mojom::PrintJob::New(
+      base::UTF16ToUTF8(settings.device_name()), base::UTF16ToUTF8(title),
+      event_details.job_id(), document->page_count(), source, source_id,
+      settings.color(),
+      static_cast<crosapi::mojom::PrintJob::DuplexMode>(duplex),
+      settings.requested_media().size_microns,
+      settings.requested_media().vendor_id, settings.copies());
+}
+#endif
+
 void ShowWarningMessageBox(const std::u16string& message) {
   // Runs always on the UI thread.
   static bool is_dialog_shown = false;
@@ -764,7 +799,18 @@
       break;
     }
     case JobEventDetails::DOC_DONE: {
-      // Don't care about the actual printing process, except on Android.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+      chromeos::LacrosChromeServiceImpl* service =
+          chromeos::LacrosChromeServiceImpl::Get();
+      if (!service->IsAvailable<crosapi::mojom::LocalPrinter>()) {
+        LOG(ERROR) << "Could not report print job queued";
+      } else {
+        service->GetRemote<crosapi::mojom::LocalPrinter>()->CreatePrintJob(
+            PrintJobToMojom(event_details, print_job_->source(),
+                            print_job_->source_id()),
+            base::DoNothing());
+      }
+#endif
 #if defined(OS_ANDROID)
       DCHECK_LE(number_pages_, kMaxPageCount);
       PdfWritingDone(base::checked_cast<int>(number_pages_));
@@ -861,7 +907,7 @@
   DCHECK(!print_job_);
   print_job_ = base::MakeRefCounted<PrintJob>();
   print_job_->Initialize(std::move(query), RenderSourceName(), number_pages_);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
   print_job_->SetSource(web_contents()->GetBrowserContext()->IsOffTheRecord()
                             ? PrintJob::Source::PRINT_PREVIEW_INCOGNITO
                             : PrintJob::Source::PRINT_PREVIEW,
diff --git a/chrome/browser/printing/print_view_manager_unittest.cc b/chrome/browser/printing/print_view_manager_unittest.cc
index 20b2e3b..d1f3955 100644
--- a/chrome/browser/printing/print_view_manager_unittest.cc
+++ b/chrome/browser/printing/print_view_manager_unittest.cc
@@ -87,9 +87,9 @@
   bool CreateNewPrintJob(std::unique_ptr<PrinterQuery> query) override {
     print_job_ = base::MakeRefCounted<TestPrintJob>();
     print_job_->Initialize(std::move(query), RenderSourceName(), number_pages_);
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
     print_job_->SetSource(PrintJob::Source::PRINT_PREVIEW, /*source_id=*/"");
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // defined(OS_CHROMEOS)
     return true;
   }
 
diff --git a/chrome/browser/profiles/profile_keep_alive_types.cc b/chrome/browser/profiles/profile_keep_alive_types.cc
index 9fd823c..2263b03 100644
--- a/chrome/browser/profiles/profile_keep_alive_types.cc
+++ b/chrome/browser/profiles/profile_keep_alive_types.cc
@@ -39,6 +39,8 @@
       return out << "kChromeViewsDelegate";
     case ProfileKeepAliveOrigin::kDevToolsWindow:
       return out << "kDevToolsWindow";
+    case ProfileKeepAliveOrigin::kWebAppPermissionDialogWindow:
+      return out << "kWebAppPermissionDialogWindow";
   }
   NOTREACHED();
   return out << static_cast<int>(origin);
diff --git a/chrome/browser/profiles/profile_keep_alive_types.h b/chrome/browser/profiles/profile_keep_alive_types.h
index 9c9193a4..3ad9d7f 100644
--- a/chrome/browser/profiles/profile_keep_alive_types.h
+++ b/chrome/browser/profiles/profile_keep_alive_types.h
@@ -71,7 +71,10 @@
   // A DevTools window is open.
   kDevToolsWindow = 14,
 
-  kMaxValue = kDevToolsWindow,
+  // A web app permission dialog window is open.
+  kWebAppPermissionDialogWindow = 15,
+
+  kMaxValue = kWebAppPermissionDialogWindow,
 };
 
 std::ostream& operator<<(std::ostream& out,
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
index fe57890..a877cb5 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
@@ -20,7 +20,7 @@
     align-items: center;
     background-color: var(--google-grey-100);
     border: 2px solid transparent;
-    border-radius: 41px;
+    border-radius: 40px;
     height: 40px;
     line-height: 40px;
     margin-bottom: 8px;
@@ -32,17 +32,21 @@
 
   .result {
     border: 2px solid transparent;
+    border-radius: 40px;
     display: flex;
   }
   .result:focus,
   .result:active {
     border-color: var(--cr-focus-outline-color);
-    border-radius: 4px;
     border-style: solid;
     border-width: 2px;
     outline-style: none;
   }
 
+  .result:hover {
+    background-color: var(--cr-hover-background-color);
+  }
+
   emoji-button {
     margin-inline-end: 12px;
   }
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
index 19e5c34..4b4f4160 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/internet_page/BUILD.gn
@@ -205,6 +205,7 @@
   deps = [
     ":cellular_networks_list.m",
     ":internet_page_browser_proxy.m",
+    ":network_always_on_vpn.m",
     "//chrome/browser/resources/settings:router",
     "//chrome/browser/resources/settings/chromeos:deep_linking_behavior.m",
     "//chrome/browser/resources/settings/chromeos:metrics_recorder.m",
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html
index 4e6ecd72c..aee6113 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html
@@ -9,6 +9,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
+<link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/assert.html">
@@ -25,6 +26,7 @@
 <link rel="import" href="./cellular_networks_list.html">
 <link rel="import" href="../route_origin_behavior.html">
 <link rel="import" href="internet_page_browser_proxy.html">
+<link rel="import" href="network_always_on_vpn.html">
 
 <dom-module id="settings-internet-subpage">
   <template>
@@ -75,6 +77,10 @@
         flex: 1;
       }
 
+      network-always-on-vpn {
+        width: 100%;
+      }
+
       #gmscore-notifications-container {
         border-top: var(--cr-separator-line);
         margin: 10px 0;
@@ -238,6 +244,21 @@
       </div>
 
       <template is="dom-if"
+          if="[[shouldShowVpnPreferences_(isManaged_, deviceState)]]">
+        <div class="settings-box first">
+          <h2>$i18n{networkVpnPreferences}</h2>
+        </div>
+        <div class="settings-box first">
+          <network-always-on-vpn id="alwaysOnVpnSelector"
+              networks="[[getAlwaysOnVpnNetworks_(deviceState,
+                networkStateList_, thirdPartyVpns_)]]"
+              mode="{{alwaysOnVpnMode_}}"
+              service="{{alwaysOnVpnService_}}">
+          </network-always-on-vpn>
+        </div>
+      </template>
+
+      <template is="dom-if"
           if="[[tetherToggleIsVisible_(deviceState, tetherDeviceState)]]">
         <div class="settings-box two-line" actionable
             on-click="onTetherEnabledChange_">
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
index b614cdd..d7c1d2d 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
@@ -88,6 +88,30 @@
     },
 
     /**
+     * Whether the browser/ChromeOS is managed by their organization
+     * through enterprise policies.
+     * @private
+     */
+    isManaged_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('isManaged');
+      },
+    },
+
+    /**
+     * Always-on VPN operating mode.
+     * @private {!chromeos.networkConfig.mojom.AlwaysOnVpnMode|undefined}
+     */
+    alwaysOnVpnMode_: Number,
+
+    /**
+     * Always-on VPN service automatically started on login.
+     * @private {!string|undefined}
+     */
+    alwaysOnVpnService_: String,
+
+    /**
      * List of potential Tether hosts whose "Google Play Services" notifications
      * are disabled (these notifications are required to use Instant Tethering).
      * @private {!Array<string>}
@@ -163,7 +187,10 @@
   /** settings.RouteOriginBehavior override */
   route_: settings.routes.INTERNET_NETWORKS,
 
-  observers: ['deviceStateChanged_(deviceState)'],
+  observers: [
+    'deviceStateChanged_(deviceState)',
+    'onAlwaysOnVpnChanged_(alwaysOnVpnMode_, alwaysOnVpnService_)',
+  ],
 
   /** @private {number|null} */
   scanIntervalId_: null,
@@ -274,6 +301,9 @@
     // Request the list of networks and start scanning if necessary.
     this.getNetworkStateList_();
     this.updateScanning_();
+
+    // Get always-on VPN configuration.
+    this.updateAlwaysOnVpnPreferences_();
   },
 
   /**
@@ -287,6 +317,7 @@
   /** NetworkListenerBehavior override */
   onNetworkStateListChanged() {
     this.getNetworkStateList_();
+    this.updateAlwaysOnVpnPreferences_();
   },
 
   /** NetworkListenerBehavior override */
@@ -955,5 +986,80 @@
 
     return this.i18n('gmscoreNotificationsManyDevicesSubtitle');
   },
+
+  /**
+   * Tells when VPN preferences section should be displayed. It is
+   * displayed when the preferences are applicable to the current device.
+   * @return {boolean}
+   * @private
+   */
+  shouldShowVpnPreferences_() {
+    if (!this.deviceState) {
+      return false;
+    }
+    // For now the section only contain always-on VPN settings. It should not be
+    // displayed on managed devices while the legacy always-on VPN based on ARC
+    // is not replaced/extended by the new implementation.
+    return !this.isManaged_ && this.matchesType_('VPN', this.deviceState);
+  },
+
+  /**
+   * Generates the list of VPN services available for always-on. It keeps from
+   * the network list only the supported technologies.
+   * @return {!Array<!OncMojo.NetworkStateProperties>}
+   * @private
+   */
+  getAlwaysOnVpnNetworks_() {
+    if (!this.deviceState || this.deviceState.type !== mojom.NetworkType.kVPN) {
+      return [];
+    }
+
+    /** @type {!Array<!OncMojo.NetworkStateProperties>} */
+    const alwaysOnVpnList = this.networkStateList_.slice();
+    for (const vpnList of Object.values(this.thirdPartyVpns_)) {
+      assert(vpnList.length > 0);
+      // ARC VPNs are excluded from always-on VPN for now.
+      if (vpnList[0].typeState.vpn.type === mojom.VpnType.kArc) {
+        continue;
+      }
+      alwaysOnVpnList.push(...vpnList);
+    }
+
+    return alwaysOnVpnList;
+  },
+
+  /**
+   * Fetches the always-on VPN configuration from network config.
+   * @private
+   */
+  updateAlwaysOnVpnPreferences_() {
+    if (!this.deviceState || this.deviceState.type !== mojom.NetworkType.kVPN) {
+      return;
+    }
+
+    this.networkConfig_.getAlwaysOnVpn().then(result => {
+      this.alwaysOnVpnMode_ = result.properties.mode;
+      this.alwaysOnVpnService_ = result.properties.serviceGuid;
+    });
+  },
+
+  /**
+   * Handles a change in |alwaysOnVpnMode_| or |alwaysOnVpnService_|
+   * triggered via the observer.
+   * @private
+   */
+  onAlwaysOnVpnChanged_() {
+    if (this.alwaysOnVpnMode_ === undefined ||
+        this.alwaysOnVpnService_ === undefined) {
+      return;
+    }
+
+    /** @type {!chromeos.networkConfig.mojom.AlwaysOnVpnProperties} */
+    const properties = {
+      mode: this.alwaysOnVpnMode_,
+      serviceGuid: this.alwaysOnVpnService_,
+    };
+    this.networkConfig_.setAlwaysOnVpn(properties);
+  },
 });
 })();
diff --git a/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts b/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
index 460d091..ad8c179 100644
--- a/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
+++ b/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
@@ -13,13 +13,13 @@
 import '../shared/step_indicator.js';
 import '../strings.m.js';
 
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {isRTL} from 'chrome://resources/js/util.m.js';
 import {IronA11yAnnouncer} from 'chrome://resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
 import {afterNextRender, html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {navigateToNextStep, NavigationBehavior} from '../navigation_behavior.js';
+import {navigateToNextStep, NavigationBehavior, NavigationBehaviorInterface} from '../navigation_behavior.js';
 import {BookmarkBarManager, BookmarkProxy, BookmarkProxyImpl} from '../shared/bookmark_proxy.js';
 import {ModuleMetricsManager} from '../shared/module_metrics_proxy.js';
 import {stepIndicatorModel} from '../shared/nux_types.js';
@@ -44,7 +44,10 @@
 const KEYBOARD_FOCUSED = 'keyboard-focused';
 
 const NuxGoogleAppsElementBase =
-    mixinBehaviors([I18nBehavior, NavigationBehavior], PolymerElement);
+    mixinBehaviors([I18nBehavior, NavigationBehavior], PolymerElement) as {
+      new ():
+          PolymerElement & NavigationBehaviorInterface & I18nBehaviorInterface
+    };
 
 /** @polymer */
 export class NuxGoogleAppsElement extends NuxGoogleAppsElementBase {
@@ -288,4 +291,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(NuxGoogleAppsElement.is, NuxGoogleAppsElement as any);
+customElements.define(NuxGoogleAppsElement.is, NuxGoogleAppsElement);
diff --git a/chrome/browser/resources/welcome/landing_view.ts b/chrome/browser/resources/welcome/landing_view.ts
index 0ac5f2e..bfa29f7 100644
--- a/chrome/browser/resources/welcome/landing_view.ts
+++ b/chrome/browser/resources/welcome/landing_view.ts
@@ -13,12 +13,19 @@
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {LandingViewProxy, LandingViewProxyImpl} from './landing_view_proxy.js';
-import {navigateTo, NavigationBehavior, Routes} from './navigation_behavior.js';
+import {navigateTo, NavigationBehavior, NavigationBehaviorInterface, Routes} from './navigation_behavior.js';
 import {OnboardingBackgroundElement} from './shared/onboarding_background.js';
 import {WelcomeBrowserProxyImpl} from './welcome_browser_proxy.js';
 
+export interface LandingViewElement {
+  $: {
+    background: OnboardingBackgroundElement,
+  };
+}
+
 const LandingViewElementBase =
-    mixinBehaviors([NavigationBehavior], PolymerElement);
+    mixinBehaviors([NavigationBehavior], PolymerElement) as
+    {new (): PolymerElement & NavigationBehaviorInterface};
 
 /** @polymer */
 export class LandingViewElement extends LandingViewElementBase {
@@ -37,6 +44,7 @@
 
   private landingViewProxy_: LandingViewProxy|null = null;
   private finalized_: boolean = false;
+  private signinAllowed_: boolean;
 
   ready() {
     super.ready();
@@ -46,11 +54,11 @@
   onRouteEnter() {
     this.finalized_ = false;
     this.landingViewProxy_.recordPageShown();
-    (this.$.background as OnboardingBackgroundElement).play();
+    this.$.background.play();
   }
 
   onRouteExit() {
-    (this.$.background as OnboardingBackgroundElement).pause();
+    this.$.background.pause();
   }
 
   onRouteUnload() {
@@ -83,4 +91,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(LandingViewElement.is, LandingViewElement as any);
+customElements.define(LandingViewElement.is, LandingViewElement);
diff --git a/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts b/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts
index c7e6738..6e562ab 100644
--- a/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts
+++ b/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts
@@ -12,12 +12,12 @@
 import '../shared/step_indicator.js';
 import '../strings.m.js';
 
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {isRTL} from 'chrome://resources/js/util.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {navigateToNextStep, NavigationBehavior} from '../navigation_behavior.js';
+import {navigateToNextStep, NavigationBehavior, NavigationBehaviorInterface} from '../navigation_behavior.js';
 import {ModuleMetricsManager} from '../shared/module_metrics_proxy.js';
 import {stepIndicatorModel} from '../shared/nux_types.js';
 
@@ -26,8 +26,17 @@
 
 const KEYBOARD_FOCUSED_CLASS = 'keyboard-focused';
 
+export interface NuxNtpBackgroundElement {
+  $: {
+    backgroundPreview: HTMLElement,
+  };
+}
+
 const NuxNtpBackgroundElementBase =
-    mixinBehaviors([I18nBehavior, NavigationBehavior], PolymerElement);
+    mixinBehaviors([I18nBehavior, NavigationBehavior], PolymerElement) as {
+      new ():
+          PolymerElement & NavigationBehaviorInterface & I18nBehaviorInterface
+    };
 
 /** @polymer */
 export class NuxNtpBackgroundElement extends NuxNtpBackgroundElementBase {
@@ -182,7 +191,7 @@
 
     // Reverse direction if RTL.
     const buttons =
-        this.shadowRoot.querySelectorAll('.option') as Array<HTMLButtonElement>;
+        this.shadowRoot.querySelectorAll<HTMLButtonElement>('.option');
     const targetIndex = Array.prototype.indexOf.call(buttons, element);
 
     const oldFocus = buttons[targetIndex];
@@ -230,5 +239,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(
-    NuxNtpBackgroundElement.is, NuxNtpBackgroundElement as any);
+customElements.define(NuxNtpBackgroundElement.is, NuxNtpBackgroundElement);
diff --git a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts
index d03ee107..8edd5054 100644
--- a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts
+++ b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts
@@ -13,16 +13,20 @@
 import '../strings.m.js';
 
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
-import {WebUIListenerBehavior} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
+import {WebUIListenerBehavior, WebUIListenerBehaviorInterface} from 'chrome://resources/js/web_ui_listener_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {navigateToNextStep, NavigationBehavior} from '../navigation_behavior.js';
+import {navigateToNextStep, NavigationBehavior, NavigationBehaviorInterface} from '../navigation_behavior.js';
 import {DefaultBrowserInfo, stepIndicatorModel} from '../shared/nux_types.js';
 
 import {NuxSetAsDefaultProxy, NuxSetAsDefaultProxyImpl} from './nux_set_as_default_proxy.js';
 
 const NuxSetAsDefaultElementBase =
-    mixinBehaviors([WebUIListenerBehavior, NavigationBehavior], PolymerElement);
+    mixinBehaviors(
+        [WebUIListenerBehavior, NavigationBehavior], PolymerElement) as {
+      new (): PolymerElement & WebUIListenerBehaviorInterface &
+      NavigationBehaviorInterface
+    };
 
 /** @polymer */
 export class NuxSetAsDefaultElement extends NuxSetAsDefaultElementBase {
@@ -139,4 +143,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(NuxSetAsDefaultElement.is, NuxSetAsDefaultElement as any);
+customElements.define(NuxSetAsDefaultElement.is, NuxSetAsDefaultElement);
diff --git a/chrome/browser/resources/welcome/shared/step_indicator.ts b/chrome/browser/resources/welcome/shared/step_indicator.ts
index 81151aa..7524c1c 100644
--- a/chrome/browser/resources/welcome/shared/step_indicator.ts
+++ b/chrome/browser/resources/welcome/shared/step_indicator.ts
@@ -9,12 +9,14 @@
 import 'chrome://resources/cr_elements/shared_vars_css.m.js';
 import './navi_colors_css.js';
 
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {I18nBehavior, I18nBehaviorInterface} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {stepIndicatorModel} from './nux_types.js';
 
-const StepIndicatorElementBase = mixinBehaviors([I18nBehavior], PolymerElement);
+const StepIndicatorElementBase =
+    mixinBehaviors([I18nBehavior], PolymerElement) as
+    {new (): PolymerElement & I18nBehaviorInterface};
 
 /** @polymer */
 export class StepIndicatorElement extends StepIndicatorElementBase {
@@ -53,4 +55,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(StepIndicatorElement.is, StepIndicatorElement as any);
+customElements.define(StepIndicatorElement.is, StepIndicatorElement);
diff --git a/chrome/browser/resources/welcome/signin_view.ts b/chrome/browser/resources/welcome/signin_view.ts
index 5599b49..2054335 100644
--- a/chrome/browser/resources/welcome/signin_view.ts
+++ b/chrome/browser/resources/welcome/signin_view.ts
@@ -12,13 +12,20 @@
 
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {NavigationBehavior} from './navigation_behavior.js';
+import {NavigationBehavior, NavigationBehaviorInterface} from './navigation_behavior.js';
 import {OnboardingBackgroundElement} from './shared/onboarding_background.js';
 import {SigninViewProxy, SigninViewProxyImpl} from './signin_view_proxy.js';
 import {WelcomeBrowserProxy, WelcomeBrowserProxyImpl} from './welcome_browser_proxy.js';
 
+export interface SigninViewElement {
+  $: {
+    background: OnboardingBackgroundElement,
+  };
+}
+
 const SigninViewElementBase =
-    mixinBehaviors([NavigationBehavior], PolymerElement);
+    mixinBehaviors([NavigationBehavior], PolymerElement) as
+    {new (): PolymerElement & NavigationBehaviorInterface};
 
 /** @polymer */
 export class SigninViewElement extends SigninViewElementBase {
@@ -43,7 +50,7 @@
   onRouteEnter() {
     this.finalized_ = false;
     this.signinViewProxy_.recordPageShown();
-    (this.$.background as OnboardingBackgroundElement).play();
+    this.$.background.play();
   }
 
   onRouteExit() {
@@ -52,7 +59,7 @@
     }
     this.finalized_ = true;
     this.signinViewProxy_.recordNavigatedAwayThroughBrowserHistory();
-    (this.$.background as OnboardingBackgroundElement).pause();
+    this.$.background.pause();
   }
 
   onRouteUnload() {
@@ -81,4 +88,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(SigninViewElement.is, SigninViewElement as any);
+customElements.define(SigninViewElement.is, SigninViewElement);
diff --git a/chrome/browser/resources/welcome/tsconfig_base.json b/chrome/browser/resources/welcome/tsconfig_base.json
index cdaa6b32e..bc09bd3 100644
--- a/chrome/browser/resources/welcome/tsconfig_base.json
+++ b/chrome/browser/resources/welcome/tsconfig_base.json
@@ -1,9 +1,7 @@
 {
   "extends": "../../../../tools/typescript/tsconfig_base.json",
   "compilerOptions": {
-    "noPropertyAccessFromIndexSignature": false,
     "noUnusedLocals": false,
-    "noUnusedParameters": false,
     "strict": false
   }
 }
diff --git a/chrome/browser/resources/welcome/welcome_app.ts b/chrome/browser/resources/welcome/welcome_app.ts
index 3bffe5a..db0c9a4 100644
--- a/chrome/browser/resources/welcome/welcome_app.ts
+++ b/chrome/browser/resources/welcome/welcome_app.ts
@@ -12,11 +12,12 @@
 import './signin_view.js';
 import '../strings.m.js';
 
+import {CrViewManagerElement} from 'chrome://resources/cr_elements/cr_view_manager/cr_view_manager.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {NavigationBehavior, Routes} from './navigation_behavior.js';
+import {NavigationBehavior, NavigationBehaviorInterface, Routes} from './navigation_behavior.js';
 import {NuxSetAsDefaultProxyImpl} from './set_as_default/nux_set_as_default_proxy.js';
 import {BookmarkBarManager} from './shared/bookmark_proxy.js';
 import {WelcomeBrowserProxyImpl} from './welcome_browser_proxy.js';
@@ -43,8 +44,16 @@
  */
 const MODULES_NEEDING_INDICATOR: Set<string> =
     new Set(['nux-google-apps', 'nux-ntp-background', 'nux-set-as-default']);
+
+export interface WelcomeAppElement {
+  $: {
+    viewManager: CrViewManagerElement,
+  };
+}
+
 const WelcomeAppElementBase =
-    mixinBehaviors([NavigationBehavior], PolymerElement);
+    mixinBehaviors([NavigationBehavior], PolymerElement) as
+    {new (): PolymerElement & NavigationBehaviorInterface};
 
 /** @polymer */
 export class WelcomeAppElement extends WelcomeAppElementBase {
@@ -84,7 +93,7 @@
     this.shadowRoot.querySelector('cr-toast').show();
   }
 
-  private onRouteChange(route: Routes, step: number) {
+  onRouteChange(route: Routes, step: number) {
     const setStep = () => {
       // If the specified step doesn't exist, that means there are no more
       // steps. In that case, replace this page with NTP.
@@ -182,4 +191,4 @@
     return html`{__html_template__}`;
   }
 }
-customElements.define(WelcomeAppElement.is, WelcomeAppElement as any);
+customElements.define(WelcomeAppElement.is, WelcomeAppElement);
diff --git a/chrome/browser/search/task_module/task_module_service.cc b/chrome/browser/search/task_module/task_module_service.cc
index c087193c..fc94de2 100644
--- a/chrome/browser/search/task_module/task_module_service.cc
+++ b/chrome/browser/search/task_module/task_module_service.cc
@@ -331,5 +331,5 @@
       GetDismissedTasksPrefName(task_module_type));
   DCHECK(dismissed_tasks);
   return dismissed_tasks->Find(base::Value(task_name)) !=
-         dismissed_tasks->end();
+         dismissed_tasks->GetList().end();
 }
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
index 2aa2d4d..eaf39c2e 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -43,7 +43,7 @@
   using Action = ash::SearchResultAction;
   using Actions = ash::SearchResultActions;
   using DisplayIndex = ash::SearchResultDisplayIndex;
-  using OmniboxType = ash::SearchResultOmniboxType;
+  using OmniboxType = ash::SearchResultOmniboxDisplayType;
 
   ChromeSearchResult();
   virtual ~ChromeSearchResult();
diff --git a/chrome/browser/ui/app_list/search/omnibox_result.cc b/chrome/browser/ui/app_list/search/omnibox_result.cc
index 1074abc..3d63c32f 100644
--- a/chrome/browser/ui/app_list/search/omnibox_result.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_result.cc
@@ -72,10 +72,10 @@
 
 // Types of generic icon to show with a result.
 enum class IconType {
-  kDomainIcon,
-  kSearchIcon,
-  kHistoryIcon,
-  kEqualIcon,
+  kDomain,
+  kSearch,
+  kHistory,
+  kCalculator,
 };
 
 const IconType MatchTypeToIconType(AutocompleteMatchType::Type type) {
@@ -94,7 +94,7 @@
     case AutocompleteMatchType::TAB_SEARCH_DEPRECATED:
     case AutocompleteMatchType::DOCUMENT_SUGGESTION:
     case AutocompleteMatchType::PEDAL:
-      return IconType::kDomainIcon;
+      return IconType::kDomain;
 
     case AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED:
     case AutocompleteMatchType::SEARCH_SUGGEST:
@@ -106,34 +106,34 @@
     case AutocompleteMatchType::VOICE_SUGGEST:
     case AutocompleteMatchType::CLIPBOARD_TEXT:
     case AutocompleteMatchType::CLIPBOARD_IMAGE:
-      return IconType::kSearchIcon;
+      return IconType::kSearch;
 
     case AutocompleteMatchType::SEARCH_HISTORY:
     case AutocompleteMatchType::SEARCH_SUGGEST_PERSONALIZED:
-      return IconType::kHistoryIcon;
+      return IconType::kHistory;
 
     case AutocompleteMatchType::CALCULATOR:
-      return IconType::kEqualIcon;
+      return IconType::kCalculator;
 
     case AutocompleteMatchType::EXTENSION_APP_DEPRECATED:
     case AutocompleteMatchType::TILE_SUGGESTION:
     case AutocompleteMatchType::TILE_NAVSUGGEST:
     case AutocompleteMatchType::NUM_TYPES:
       NOTREACHED();
-      return IconType::kDomainIcon;
+      return IconType::kDomain;
   }
 }
 
 // AutocompleteMatchType::Type to vector icon, used for app list.
 const gfx::VectorIcon& TypeToVectorIcon(AutocompleteMatchType::Type type) {
   switch (MatchTypeToIconType(type)) {
-    case IconType::kDomainIcon:
-      return ash::kDomainIcon;
-    case IconType::kSearchIcon:
+    case IconType::kDomain:
+      return ash::kOmniboxGenericIcon;
+    case IconType::kSearch:
       return ash::kSearchIcon;
-    case IconType::kHistoryIcon:
+    case IconType::kHistory:
       return ash::kHistoryIcon;
-    case IconType::kEqualIcon:
+    case IconType::kCalculator:
       return ash::kEqualIcon;
   }
 }
@@ -350,8 +350,7 @@
 }
 
 void OmniboxResult::UpdateIcon() {
-  // TODO(crbug.com/1201151): Refactor this method once we've decided whether or
-  // not to use favicons for bookmarks.
+  // TODO(crbug.com/1201151): Refactor this method.
 
   if (app_list_features::IsOmniboxRichEntitiesEnabled() &&
       IsRichEntityResult()) {
@@ -372,30 +371,35 @@
       // All remaining rich entity icons will have their image downloaded.
       FetchRichEntityImage(match_.image_url);
     }
-  } else {
-    if (favicon_cache_ &&
-        MatchTypeToIconType(match_.type) == IconType::kDomainIcon) {
-      // If we have a favicon available for this URL, use it. Otherwise fall
-      // back on using a generic icon.
-      const auto icon = favicon_cache_->GetFaviconForPageUrl(
-          match_.destination_url,
-          base::BindOnce(&OmniboxResult::OnFaviconFetched,
-                         weak_factory_.GetWeakPtr()));
-      if (!icon.IsEmpty()) {
-        SetIcon(icon.AsImageSkia());
-        return;
-      }
+    return;
+  }
+
+  // Use a favicon if eligible. If the result should have a favicon but there
+  // isn't one in the cache, fall through to using a generic icon instead.
+  if (favicon_cache_ && MatchTypeToIconType(match_.type) == IconType::kDomain) {
+    const auto icon = favicon_cache_->GetFaviconForPageUrl(
+        match_.destination_url, base::BindOnce(&OmniboxResult::OnFaviconFetched,
+                                               weak_factory_.GetWeakPtr()));
+    if (!icon.IsEmpty()) {
+      SetOmniboxType(OmniboxType::kFavicon);
+      SetIcon(icon.AsImageSkia());
+      return;
     }
+  }
 
-    BookmarkModel* bookmark_model =
-        BookmarkModelFactory::GetForBrowserContext(profile_);
-    bool is_bookmarked =
-        bookmark_model && bookmark_model->IsBookmarked(match_.destination_url);
-
-    const gfx::VectorIcon& icon =
-        is_bookmarked ? omnibox::kBookmarkIcon : TypeToVectorIcon(match_.type);
+  // If this is neither a rich entity nor eligible for a favicon, use either the
+  // generic bookmark or another generic icon as appropriate.
+  BookmarkModel* bookmark_model =
+      BookmarkModelFactory::GetForBrowserContext(profile_);
+  if (bookmark_model && bookmark_model->IsBookmarked(match_.destination_url)) {
     SetIcon(gfx::CreateVectorIcon(
-        icon, ash::SharedAppListConfig::instance().search_list_icon_dimension(),
+        omnibox::kBookmarkIcon,
+        ash::SharedAppListConfig::instance().search_list_icon_dimension(),
+        kListIconColor));
+  } else {
+    SetIcon(gfx::CreateVectorIcon(
+        TypeToVectorIcon(match_.type),
+        ash::SharedAppListConfig::instance().search_list_icon_dimension(),
         kListIconColor));
   }
 }
@@ -480,6 +484,7 @@
 void OmniboxResult::OnFaviconFetched(const gfx::Image& icon) {
   // By contract, this is never called with an empty |icon|.
   DCHECK(!icon.IsEmpty());
+  SetOmniboxType(OmniboxType::kFavicon);
   SetIcon(icon.AsImageSkia());
 }
 
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 55b68cd5..c7f99efa 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
@@ -22,11 +22,9 @@
 // HoldingSpaceDownloadsDelegate -----------------------------------------------
 
 HoldingSpaceDownloadsDelegate::HoldingSpaceDownloadsDelegate(
-    Profile* profile,
-    HoldingSpaceModel* model,
-    ItemDownloadedCallback item_downloaded_callback)
-    : HoldingSpaceKeyedServiceDelegate(profile, model),
-      item_downloaded_callback_(item_downloaded_callback) {}
+    HoldingSpaceKeyedService* service,
+    HoldingSpaceModel* model)
+    : HoldingSpaceKeyedServiceDelegate(service, model) {}
 
 HoldingSpaceDownloadsDelegate::~HoldingSpaceDownloadsDelegate() = default;
 
@@ -121,7 +119,8 @@
 
 void HoldingSpaceDownloadsDelegate::ManagerGoingDown(
     content::DownloadManager* manager) {
-  RemoveObservers();
+  download_manager_observation_.Reset();
+  download_item_observations_.RemoveAllObservations();
 }
 
 void HoldingSpaceDownloadsDelegate::OnDownloadCreated(
@@ -150,17 +149,13 @@
   }
 }
 
+// TODO(crbug.com/1184438): Support in-progress downloads.
 void HoldingSpaceDownloadsDelegate::OnDownloadCompleted(
     HoldingSpaceItem::Type type,
     const base::FilePath& file_path) {
   DCHECK(HoldingSpaceItem::IsDownload(type));
   if (!is_restoring_persistence())
-    item_downloaded_callback_.Run(type, file_path);
-}
-
-void HoldingSpaceDownloadsDelegate::RemoveObservers() {
-  download_manager_observation_.Reset();
-  download_item_observations_.RemoveAllObservations();
+    service()->AddDownload(type, file_path, /*progress=*/1.f);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
index 3c30d28a..adfc7b3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h
@@ -21,23 +21,14 @@
 namespace ash {
 
 // A delegate of `HoldingSpaceKeyedService` tasked with monitoring the status of
-// of downloads and notifying a callback on download completion.
+// of downloads on its behalf.
 class HoldingSpaceDownloadsDelegate : public HoldingSpaceKeyedServiceDelegate,
                                       public arc::ArcIntentHelperObserver,
                                       public content::DownloadManager::Observer,
                                       public download::DownloadItem::Observer {
  public:
-  // Callback to be invoked when a download is completed. Note that the
-  // specified type must be a download type. Also note that this callback will
-  // only be invoked after holding space persistence is restored.
-  using ItemDownloadedCallback =
-      base::RepeatingCallback<void(HoldingSpaceItem::Type,
-                                   const base::FilePath&)>;
-
-  HoldingSpaceDownloadsDelegate(
-      Profile* profile,
-      HoldingSpaceModel* model,
-      ItemDownloadedCallback item_downloaded_callback);
+  HoldingSpaceDownloadsDelegate(HoldingSpaceKeyedService* service,
+                                HoldingSpaceModel* model);
   HoldingSpaceDownloadsDelegate(const HoldingSpaceDownloadsDelegate&) = delete;
   HoldingSpaceDownloadsDelegate& operator=(
       const HoldingSpaceDownloadsDelegate&) = delete;
@@ -72,12 +63,6 @@
   void OnDownloadCompleted(HoldingSpaceItem::Type type,
                            const base::FilePath& file_path);
 
-  // Removes all observers.
-  void RemoveObservers();
-
-  // Callback to invoke when a download is completed.
-  ItemDownloadedCallback item_downloaded_callback_;
-
   base::ScopedObservation<arc::ArcIntentHelperBridge,
                           arc::ArcIntentHelperObserver>
       arc_intent_helper_observation_{this};
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
index 45d1d92..b0742df7 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.cc
@@ -137,9 +137,9 @@
 // HoldingSpaceFileSystemDelegate ----------------------------------------------
 
 HoldingSpaceFileSystemDelegate::HoldingSpaceFileSystemDelegate(
-    Profile* profile,
+    HoldingSpaceKeyedService* service,
     HoldingSpaceModel* model)
-    : HoldingSpaceKeyedServiceDelegate(profile, model),
+    : HoldingSpaceKeyedServiceDelegate(service, model),
       file_system_watcher_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h
index e828bfe3..47e1a81 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h
@@ -41,7 +41,8 @@
       public drivefs::DriveFsHostObserver,
       public file_manager::VolumeManagerObserver {
  public:
-  HoldingSpaceFileSystemDelegate(Profile* profile, HoldingSpaceModel* model);
+  HoldingSpaceFileSystemDelegate(HoldingSpaceKeyedService* service,
+                                 HoldingSpaceModel* model);
   HoldingSpaceFileSystemDelegate(const HoldingSpaceFileSystemDelegate&) =
       delete;
   HoldingSpaceFileSystemDelegate& operator=(
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
index f34e36a..83b64b7d 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_file_system_delegate.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_util.h"
 #include "components/account_id/account_id.h"
@@ -216,9 +217,10 @@
 
 void HoldingSpaceKeyedService::AddDownload(
     HoldingSpaceItem::Type type,
-    const base::FilePath& download_file) {
+    const base::FilePath& download_file,
+    const base::Optional<float>& progress) {
   DCHECK(HoldingSpaceItem::IsDownload(type));
-  AddItemOfType(type, download_file);
+  AddItemOfType(type, download_file, progress);
 }
 
 void HoldingSpaceKeyedService::AddNearbyShare(
@@ -267,15 +269,17 @@
   holding_space_model_.AddItems(std::move(items));
 }
 
-void HoldingSpaceKeyedService::AddItemOfType(HoldingSpaceItem::Type type,
-                                             const base::FilePath& file_path) {
+void HoldingSpaceKeyedService::AddItemOfType(
+    HoldingSpaceItem::Type type,
+    const base::FilePath& file_path,
+    const base::Optional<float>& progress) {
   const GURL file_system_url =
       holding_space_util::ResolveFileSystemUrl(profile_, file_path);
   if (file_system_url.is_empty())
     return;
 
   AddItem(HoldingSpaceItem::CreateFileBackedItem(
-      type, file_path, file_system_url,
+      type, file_path, file_system_url, progress,
       base::BindOnce(&holding_space_util::ResolveImage, &thumbnail_loader_)));
 }
 
@@ -331,21 +335,15 @@
 
   // The `HoldingSpaceDownloadsDelegate` monitors the status of downloads.
   delegates_.push_back(std::make_unique<HoldingSpaceDownloadsDelegate>(
-      profile_, &holding_space_model_,
-      /*item_downloaded_callback=*/
-      base::BindRepeating(&HoldingSpaceKeyedService::AddDownload,
-                          weak_factory_.GetWeakPtr())));
+      this, &holding_space_model_));
 
   // The `HoldingSpaceFileSystemDelegate` monitors the file system for changes.
   delegates_.push_back(std::make_unique<HoldingSpaceFileSystemDelegate>(
-      profile_, &holding_space_model_));
+      this, &holding_space_model_));
 
   // The `HoldingSpacePersistenceDelegate` manages holding space persistence.
   delegates_.push_back(std::make_unique<HoldingSpacePersistenceDelegate>(
-      profile_, &holding_space_model_, &thumbnail_loader_,
-      /*item_restored_callback=*/
-      base::BindRepeating(&HoldingSpaceKeyedService::AddItem,
-                          weak_factory_.GetWeakPtr()),
+      this, &holding_space_model_, &thumbnail_loader_,
       /*persistence_restored_callback=*/
       base::BindOnce(&HoldingSpaceKeyedService::OnPersistenceRestored,
                      weak_factory_.GetWeakPtr())));
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
index 9f93985..de042d47 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
@@ -14,7 +14,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_manager_observer.h"
 #include "chrome/browser/ui/ash/holding_space/holding_space_client_impl.h"
-#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h"
 #include "chrome/browser/ui/ash/thumbnail_loader.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "components/account_id/account_id.h"
@@ -37,6 +36,8 @@
 
 namespace ash {
 
+class HoldingSpaceKeyedServiceDelegate;
+
 // Browser context keyed service that:
 // *   Manages the temporary holding space per-profile data model.
 // *   Serves as an entry point to add holding space items from Chrome.
@@ -72,9 +73,12 @@
   std::vector<GURL> GetPinnedFiles() const;
 
   // Adds a download item of the specified `type` backed by the provided
-  // absolute file path. Note that `type` must refer to a download type.
+  // absolute file path.
+  // NOTE: `type` must refer to a download type.
+  // NOTE: If present, `progress` must be >= `0.f` and <= `1.f`.
   void AddDownload(HoldingSpaceItem::Type type,
-                   const base::FilePath& download_path);
+                   const base::FilePath& download_path,
+                   const base::Optional<float>& progress = 1.f);
 
   // Adds a nearby share item backed by the provided absolute file path.
   void AddNearbyShare(const base::FilePath& nearby_share_path);
@@ -96,8 +100,13 @@
 
   // Adds an item of the specified `type` backed by the provided absolute
   // `file_path` to the holding space model.
+  // NOTE: If present, `progress` must be >= `0.f` and <= `1.f`.
   void AddItemOfType(HoldingSpaceItem::Type type,
-                     const base::FilePath& file_path);
+                     const base::FilePath& file_path,
+                     const base::Optional<float>& progress = base::nullopt);
+
+  // Returns the `profile_` associated with this service.
+  Profile* profile() { return profile_; }
 
   const HoldingSpaceClient* client_for_testing() const {
     return &holding_space_client_;
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
index 627dc21..9799eb3 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.cc
@@ -26,11 +26,11 @@
 }
 
 HoldingSpaceKeyedServiceDelegate::HoldingSpaceKeyedServiceDelegate(
-    Profile* profile,
+    HoldingSpaceKeyedService* service,
     HoldingSpaceModel* model)
-    : profile_(profile), model_(model) {
-  // It is expected that `profile` already be ready prior to delegate creation.
-  DCHECK(GetProfileManager()->IsValidProfile(profile));
+    : service_(service), model_(model) {
+  // It's expected that `profile()` already be ready prior to delegate creation.
+  DCHECK(GetProfileManager()->IsValidProfile(profile()));
   holding_space_model_observation_.Observe(model);
 }
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
index 9fce7457..f82e089 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_delegate.h
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "ash/public/cpp/holding_space/holding_space_model_observer.h"
 #include "base/scoped_observation.h"
+#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h"
 
 class Profile;
 
@@ -32,12 +33,16 @@
   void NotifyPersistenceRestored();
 
  protected:
-  HoldingSpaceKeyedServiceDelegate(Profile* profile, HoldingSpaceModel* model);
+  HoldingSpaceKeyedServiceDelegate(HoldingSpaceKeyedService* service,
+                                   HoldingSpaceModel* model);
 
-  // Returns the `profile_` associated with the `HoldingSpaceKeyedService`.
-  Profile* profile() { return profile_; }
+  // Returns the `profile_` associated with the `service_`.
+  Profile* profile() { return service_->profile(); }
 
-  // Returns the holding space model owned by `HoldingSpaceKeyedService`.
+  // Returns the `service` which owns this delegate.
+  HoldingSpaceKeyedService* service() { return service_; }
+
+  // Returns the holding space model owned by `service_`.
   HoldingSpaceModel* model() { return model_; }
 
   // Returns if persistence is being restored.
@@ -54,7 +59,7 @@
   // Invoked when holding space persistence has been restored.
   virtual void OnPersistenceRestored();
 
-  Profile* const profile_;
+  HoldingSpaceKeyedService* const service_;
   HoldingSpaceModel* const model_;
 
   // If persistence is being restored.
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
index 5d47e21..8960421 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.cc
@@ -33,14 +33,12 @@
 constexpr char HoldingSpacePersistenceDelegate::kPersistencePath[];
 
 HoldingSpacePersistenceDelegate::HoldingSpacePersistenceDelegate(
-    Profile* profile,
+    HoldingSpaceKeyedService* service,
     HoldingSpaceModel* model,
     ThumbnailLoader* thumbnail_loader,
-    ItemRestoredCallback item_restored_callback,
     PersistenceRestoredCallback persistence_restored_callback)
-    : HoldingSpaceKeyedServiceDelegate(profile, model),
+    : HoldingSpaceKeyedServiceDelegate(service, model),
       thumbnail_loader_(thumbnail_loader),
-      item_restored_callback_(item_restored_callback),
       persistence_restored_callback_(std::move(persistence_restored_callback)) {
 }
 
@@ -59,6 +57,7 @@
   RestoreModelFromPersistence();
 }
 
+// TODO(crbug.com/1184438): Do not persist in-progress `items`.
 void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemsAdded(
     const std::vector<const HoldingSpaceItem*>& items) {
   if (is_restoring_persistence())
@@ -87,6 +86,7 @@
   });
 }
 
+// TODO(crbug.com/1184438): Persist `items` no longer in-progress.
 void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemUpdated(
     const HoldingSpaceItem* item) {
   if (is_restoring_persistence())
@@ -125,10 +125,11 @@
             base::Value::AsDictionaryValue(persisted_holding_space_item),
             base::BindOnce(&holding_space_util::ResolveImage,
                            base::Unretained(thumbnail_loader_)));
-    if (ShouldIgnoreItem(profile(), holding_space_item.get()))
-      continue;
-    item_restored_callback_.Run(std::move(holding_space_item));
+
+    if (!ShouldIgnoreItem(profile(), holding_space_item.get()))
+      service()->AddItem(std::move(holding_space_item));
   }
+
   // Notify completion of persistence restoration.
   std::move(persistence_restored_callback_).Run();
 }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h
index 701bf23..561c3046 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h
+++ b/chrome/browser/ui/ash/holding_space/holding_space_persistence_delegate.h
@@ -34,19 +34,13 @@
   // NOTE: Any changes to persistence must be backwards compatible.
   static constexpr char kPersistencePath[] = "ash.holding_space.items";
 
-  // Callback to invoke when the specified holding space item has been restored
-  // from persistence.
-  using ItemRestoredCallback =
-      base::RepeatingCallback<void(HoldingSpaceItemPtr)>;
-
   // Callback to invoke when holding space persistence has been restored.
   using PersistenceRestoredCallback = base::OnceClosure;
 
   HoldingSpacePersistenceDelegate(
-      Profile* profile,
+      HoldingSpaceKeyedService* service,
       HoldingSpaceModel* model,
       ThumbnailLoader* thumbnail_loader,
-      ItemRestoredCallback item_restored_callback,
       PersistenceRestoredCallback persistence_restored_callback);
   HoldingSpacePersistenceDelegate(const HoldingSpacePersistenceDelegate&) =
       delete;
@@ -72,9 +66,6 @@
   // Owned by `HoldingSpaceKeyedService`.
   ThumbnailLoader* const thumbnail_loader_;
 
-  // Callback to invoke when an item has been restored from persistence.
-  ItemRestoredCallback item_restored_callback_;
-
   // Callback to invoke when holding space persistence has been restored.
   PersistenceRestoredCallback persistence_restored_callback_;
 
diff --git a/chrome/browser/ui/ash/login_screen_client_impl.cc b/chrome/browser/ui/ash/login_screen_client_impl.cc
index c7950def..2d44c58 100644
--- a/chrome/browser/ui/ash/login_screen_client_impl.cc
+++ b/chrome/browser/ui/ash/login_screen_client_impl.cc
@@ -303,7 +303,7 @@
     std::unique_ptr<base::ListValue> keyboard_layouts) {
   std::vector<ash::InputMethodItem> result;
 
-  for (const auto& i : *keyboard_layouts) {
+  for (const auto& i : keyboard_layouts->GetList()) {
     const base::DictionaryValue* dictionary;
     if (!i.GetAsDictionary(&dictionary))
       continue;
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
index 6655f3b5..1fe573f 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
@@ -117,7 +117,7 @@
   if (!policy_apps)
     return AppListControllerDelegate::PIN_EDITABLE;
 
-  for (const base::Value& policy_dict_entry : *policy_apps) {
+  for (const base::Value& policy_dict_entry : policy_apps->GetList()) {
     if (!policy_dict_entry.is_dict())
       return AppListControllerDelegate::PIN_EDITABLE;
 
diff --git a/chrome/browser/ui/browser_dialogs.h b/chrome/browser/ui/browser_dialogs.h
index 951641e..12979c2 100644
--- a/chrome/browser/ui/browser_dialogs.h
+++ b/chrome/browser/ui/browser_dialogs.h
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
 #include "chrome/common/buildflags.h"
 #include "content/public/browser/content_browser_client.h"
 #include "extensions/buildflags/buildflags.h"
@@ -33,7 +34,6 @@
 #endif
 
 namespace base {
-class CommandLine;
 class FilePath;
 }
 
@@ -122,13 +122,19 @@
                              AppInstallationAcceptanceCallback callback);
 
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+// Callback used to indicate whether a user has accepted the launch of a
+// web app. The boolean parameter is true when the user accepts the dialog.
+using WebAppProtocolHandlerAcceptanceCallback =
+    base::OnceCallback<void(bool accepted)>;
+
 // Shows the Web App Protocol Handler Intent Picker view.
-// |close_callback| may be null.
+// |profile| is kept alive throughout the processing and running of
+// |close_callback|. |close_callback| may be null.
 void ShowWebAppProtocolHandlerIntentPicker(
     const GURL& url,
     Profile* profile,
-    const base::CommandLine& command_line,
-    base::OnceCallback<void(bool accepted)> close_callback);
+    const web_app::AppId& app_id,
+    WebAppProtocolHandlerAcceptanceCallback close_callback);
 #endif
 
 // Sets whether |ShowWebAppDialog| should accept immediately without any
diff --git a/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc b/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc
index 75fe9f30..0bf1d542 100644
--- a/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc
+++ b/chrome/browser/ui/global_media_controls/media_session_notification_producer.cc
@@ -101,6 +101,13 @@
   SetController(std::move(controller));
   if (presentation_manager_)
     presentation_manager_->AddObserver(this);
+
+  bool has_presentation_request =
+      presentation_manager_ &&
+      presentation_manager_->HasDefaultPresentationRequest();
+  base::UmaHistogramBoolean(
+      "Media.GlobalMediaControls.HasDefaultPresentationRequest",
+      has_presentation_request);
 }
 
 MediaSessionNotificationProducer::Session::~Session() {
diff --git a/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc b/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc
index 851de9b..69202d7f5 100644
--- a/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_pedals_unittest.cc
@@ -23,7 +23,9 @@
   std::unique_ptr<base::Environment> env = base::Environment::Create();
   MockAutocompleteProviderClient client;
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(omnibox::kOmniboxPedalsBatch2);
+  feature_list.InitWithFeatures(
+      {omnibox::kOmniboxPedalsBatch2, omnibox::kOmniboxPedalsBatch2NonEnglish},
+      {});
 
   struct TestCase {
     std::string locale;
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index f5d9caf..047567d 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -108,6 +108,7 @@
 #endif
 
 #if defined(OS_WIN)
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/metrics/jumplist_metrics_win.h"
 #include "chrome/browser/notifications/notification_platform_bridge_win.h"
@@ -124,6 +125,12 @@
 #include "ui/base/ui_base_features.h"
 #endif
 
+#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h"
+#endif
+
 #if defined(OS_WIN) || defined(OS_MAC) || \
     (defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS))
 #include "chrome/browser/web_applications/components/url_handler_launch_params.h"
@@ -390,54 +397,92 @@
   StartupBrowserCreatorImpl::MaybeToggleFullscreen(browser);
 }
 
-// Tries to get the protocol url from the command line.
-// If the protocol app url switch doesnt exist, checks if the passed in url
-// is a potential protocol url, if it is, check the protocol handler registry
-// for an entry. Return the protocol url if there are handlers for this scheme.
+#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+// Tries to launch the web app from a protocol handler url. Checks if the passed
+// in url is a potential protocol url, if it is, check the protocol handler
+// registry for an entry. If there is an entry, then check if we need approval
+// from the user to launch this web app. If we didn't have approval from a
+// previous session, launch the permission dialog to ask for approval. If we did
+// get permission in the past, directly launch the web app with the translated
+// url. Returns true if the command line references a valid app and also
+// contains a protocol url that is specifically registered by the app.
+// `launch_mode_recorder` is used if and only if this function returns true.
 bool MaybeLaunchProtocolHandlerWebApp(
     const base::CommandLine& command_line,
     const base::FilePath& cur_dir,
     Profile* profile,
     std::unique_ptr<LaunchModeRecorder> launch_mode_recorder) {
-  // Maybe the URL passed in is a protocol URL.
+  std::string app_id = command_line.GetSwitchValueASCII(switches::kAppId);
+  // We must have a kAppId switch arg in the command line to launch.
+  if (app_id.empty())
+    return false;
+
   GURL protocol_url;
   base::CommandLine::StringVector args = command_line.GetArgs();
   for (const auto& arg : args) {
 #if defined(OS_WIN)
-    GURL potential_protocol(base::WideToUTF16(arg));
+    GURL potential_protocol(base::AsStringPiece16(arg));
 #else
     GURL potential_protocol(arg);
 #endif  // defined(OS_WIN)
-    if (potential_protocol.is_valid() && !potential_protocol.IsStandard()) {
-      protocol_url = potential_protocol;
+    // protocol_url is checked for validity later on with GetHandlersFor() where
+    // we consult the ProtocolHandlerRegistry and find entries that match this
+    // protocol.
+    if (potential_protocol.is_valid()) {
+      protocol_url = std::move(potential_protocol);
       break;
     }
   }
   if (protocol_url.is_empty())
     return false;
 
+  // Check the ProtocolHandlerRegistry for an entry that matches the appId
+  // and the protocol_url provided in the command line. We want to make sure
+  // that we only handle protocol handlers that we've registered for when
+  // installing a web app. This check also filters out file handler url
+  // activation and other unregistered protocol urls that will be handled
+  // by other startup code paths.
+  // TODO::(crbug/1105257) Adjust this once we no longer use
+  // the ProtocolHandlerRegistry to store protocol handler
+  // registration info for web apps.
   ProtocolHandlerRegistry* handler_registry =
       ProtocolHandlerRegistryFactory::GetForBrowserContext(profile);
   const std::vector<ProtocolHandler> handlers =
       handler_registry->GetHandlersFor(protocol_url.scheme());
 
-  // Check that there is at least one handler with web_app_id.
-  // TODO(crbug/1019239): Display intent picker if there are multiple handlers.
-  for (const auto& handler : handlers) {
-    if (handler.web_app_id().has_value()) {
-      apps::AppServiceProxyFactory::GetForProfile(profile)
-          ->BrowserAppLauncher()
-          ->LaunchAppWithCallback(
-              handler.web_app_id().value(), command_line, cur_dir,
-              /*url_handler_launch_url=*/base::nullopt, protocol_url,
-              base::BindOnce(&FinalizeWebAppLaunch,
-                             std::move(launch_mode_recorder)));
-      return true;
-    }
+  // Nothing to do if there are no handlers with a web_app_id.
+  if (!base::Contains(handlers, true, [](const auto& handler) {
+        return handler.web_app_id().has_value();
+      })) {
+    return false;
   }
 
-  return false;
+  auto launch_callback = base::BindOnce(
+      [](const base::CommandLine& command_line, const base::FilePath& cur_dir,
+         Profile* profile, const GURL& protocol_url,
+         const web_app::AppId& app_id,
+         std::unique_ptr<LaunchModeRecorder> launch_mode_recorder,
+         bool accepted) {
+        if (accepted) {
+          apps::AppServiceProxyFactory::GetForProfile(profile)
+              ->BrowserAppLauncher()
+              ->LaunchAppWithCallback(
+                  app_id, command_line, cur_dir,
+                  /*url_handler_launch_url=*/base::nullopt, protocol_url,
+                  base::BindOnce(&FinalizeWebAppLaunch,
+                                 std::move(launch_mode_recorder)));
+        }  // else allow the process to exit without opening a browser.
+      },
+      command_line, cur_dir, profile, protocol_url, app_id,
+      std::move(launch_mode_recorder));
+  // ShowWebAppProtocolHandlerIntentPicker keeps the `profile` alive through
+  // running of `launch_callback`.
+  chrome::ShowWebAppProtocolHandlerIntentPicker(std::move(protocol_url),
+                                                profile, std::move(app_id),
+                                                std::move(launch_callback));
+  return true;
 }
+#endif  // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
 
 // If the process was launched with the web application command line flags,
 // e.g. --app=http://www.google.com/ or --app_id=... return true.
@@ -1042,12 +1087,14 @@
     }
   }
 
+#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
   // Web app Protocol handling.
   if (MaybeLaunchProtocolHandlerWebApp(
           command_line, cur_dir, privacy_safe_profile,
           std::make_unique<LaunchModeRecorder>())) {
     return true;
   }
+#endif
 
   // If we're being run as an application window or application tab, don't
   // restore tabs or open initial URLs as the user has directly launched an app
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index e061439a..dc0d71e 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -102,6 +102,11 @@
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_types.h"
 
+#if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
+#include "ui/views/widget/any_widget_observer.h"
+#include "ui/views/widget/widget.h"
+#endif
+
 #if defined(OS_WIN) || defined(OS_MAC) || \
     (defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS))
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
@@ -1604,9 +1609,11 @@
     return app_id;
   }
 
-  void SetUpCommandlineAndStart(const std::string& url) {
+  void SetUpCommandlineAndStart(const std::string& url,
+                                const web_app::AppId& app_id) {
     base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     command_line.AppendArg(url);
+    command_line.AppendSwitchASCII(switches::kAppId, app_id);
 
     std::vector<Profile*> last_opened_profiles;
     StartupBrowserCreator browser_creator;
@@ -1618,8 +1625,12 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(StartupBrowserWebAppProtocolHandlingTest,
-                       WebAppLaunch_WebAppIsLaunchedWithProtocolUrl) {
+IN_PROC_BROWSER_TEST_F(
+    StartupBrowserWebAppProtocolHandlingTest,
+    WebAppLaunch_WebAppIsNotLaunchedWithProtocolUrlAndDialogCancel) {
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "WebAppProtocolHandlerIntentPickerView");
+
   // Register web app as a protocol handler that should handle the launch.
   blink::Manifest::ProtocolHandler protocol_handler;
   const std::string handler_url = std::string(kStartUrl) + "/testing=%s";
@@ -1628,7 +1639,35 @@
   web_app::AppId app_id = InstallWebAppWithProtocolHandlers({protocol_handler});
 
   // Launch the browser via a command line with a handled protocol URL param.
-  SetUpCommandlineAndStart("web+test://parameterString");
+  SetUpCommandlineAndStart("web+test://parameterString", app_id);
+
+  // The waiter will get the dialog when it shows up and close it.
+  waiter.WaitIfNeededAndGet()->CloseWithReason(
+      views::Widget::ClosedReason::kEscKeyPressed);
+
+  // Check that no extra window is launched.
+  ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    StartupBrowserWebAppProtocolHandlingTest,
+    WebAppLaunch_WebAppIsLaunchedWithProtocolUrlAndDialogAccept) {
+  views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
+                                       "WebAppProtocolHandlerIntentPickerView");
+
+  // Register web app as a protocol handler that should handle the launch.
+  blink::Manifest::ProtocolHandler protocol_handler;
+  const std::string handler_url = std::string(kStartUrl) + "/testing=%s";
+  protocol_handler.url = GURL(handler_url);
+  protocol_handler.protocol = u"web+test";
+  web_app::AppId app_id = InstallWebAppWithProtocolHandlers({protocol_handler});
+
+  // Launch the browser via a command line with a handled protocol URL param.
+  SetUpCommandlineAndStart("web+test://parameterString", app_id);
+
+  // The waiter will get the dialog when it shows up and accepts it.
+  waiter.WaitIfNeededAndGet()->CloseWithReason(
+      views::Widget::ClosedReason::kAcceptButtonClicked);
 
   // Wait for app launch task to complete.
   content::RunAllTasksUntilIdle();
@@ -1650,7 +1689,7 @@
 
 IN_PROC_BROWSER_TEST_F(
     StartupBrowserWebAppProtocolHandlingTest,
-    WebAppLaunch_WebAppIsNotLaunchedWithUnhandledProtocolUrl) {
+    WebAppLaunch_WebAppIsNotTranslatedWithUnhandledProtocolUrl) {
   // Register web app as a protocol handler that should *not* handle the launch.
   blink::Manifest::ProtocolHandler protocol_handler;
   const std::string handler_url = std::string(kStartUrl) + "/testing=%s";
@@ -1659,26 +1698,26 @@
   web_app::AppId app_id = InstallWebAppWithProtocolHandlers({protocol_handler});
 
   // Launch the browser via a command line with an unhandled protocol URL param.
-  SetUpCommandlineAndStart("web+unhandled://parameterString");
+  SetUpCommandlineAndStart("web+unhandled://parameterString", app_id);
 
   // Wait for app launch task to complete.
   content::RunAllTasksUntilIdle();
 
-  // Check an app window is not launched.
+  // Check an app window is launched.
   ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
   Browser* app_browser;
   app_browser = FindOneOtherBrowser(browser());
   ASSERT_TRUE(app_browser);
-  EXPECT_FALSE(web_app::AppBrowserController::IsWebApp(app_browser));
+  EXPECT_TRUE(web_app::AppBrowserController::IsForWebApp(app_browser, app_id));
 
-  // Check the browser launches to a blank new tab page.
+  // Check the app is launched to the home page and not the translated URL.
   TabStripModel* tab_strip = app_browser->tab_strip_model();
   ASSERT_EQ(1, tab_strip->count());
   content::WebContents* web_contents = tab_strip->GetWebContentsAt(0);
-  EXPECT_EQ(chrome::kChromeUINewTabURL, web_contents->GetVisibleURL());
+  EXPECT_EQ(GURL(kStartUrl), web_contents->GetVisibleURL());
 }
 
-#endif  // defined(OS_WIN)
+#endif  // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX)
 
 class StartupBrowserCreatorExtensionsCheckupExperimentTest
     : public extensions::ExtensionBrowserTest {
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model.cc b/chrome/browser/ui/toolbar/toolbar_actions_model.cc
index 3b619d1..30dd44a8 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model.cc
@@ -99,35 +99,31 @@
   // We don't want to add the same extension twice. It may have already been
   // added by EXTENSION_BROWSER_ACTION_VISIBILITY_CHANGED below, if the user
   // hides the browser action and then disables and enables the extension.
-  if (!HasAction(extension->id()))
-    AddExtension(extension);
+  if (!HasAction(extension->id()) && ShouldAddExtension(extension))
+    AddAction(extension->id());
 }
 
 void ToolbarActionsModel::OnExtensionUnloaded(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
     extensions::UnloadedExtensionReason reason) {
-  RemoveExtension(extension);
+  RemoveAction(extension->id());
 }
 
 void ToolbarActionsModel::OnExtensionUninstalled(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension,
     extensions::UninstallReason reason) {
+  if (profile_->IsOffTheRecord()) {
+    // The on-the-record version will update the prefs; incognito is read-only.
+    return;
+  }
+
   // Remove the extension id from the ordered list, if it exists (the extension
   // might not be represented in the list because it might not have an icon).
   RemovePref(extension->id());
 }
 
-void ToolbarActionsModel::OnLoadFailure(
-    content::BrowserContext* browser_context,
-    const base::FilePath& extension_path,
-    const std::string& error) {
-  for (ToolbarActionsModel::Observer& observer : observers_) {
-    observer.OnToolbarActionLoadFailed();
-  }
-}
-
 void ToolbarActionsModel::OnExtensionManagementSettingsChanged() {
   UpdatePinnedActionIds();
 }
@@ -148,9 +144,6 @@
 void ToolbarActionsModel::OnReady() {
   InitializeActionList();
 
-  load_error_reporter_observation_.Observe(
-      extensions::LoadErrorReporter::GetInstance());
-
   // Wait until the extension system is ready before observing any further
   // changes so that the toolbar buttons can be shown in their stable ordering
   // taken from prefs.
@@ -178,13 +171,6 @@
   return extension_action_manager_->GetExtensionAction(*extension) != nullptr;
 }
 
-void ToolbarActionsModel::AddExtension(const extensions::Extension* extension) {
-  if (!ShouldAddExtension(extension))
-    return;
-
-  AddAction(extension->id());
-}
-
 void ToolbarActionsModel::AddAction(const ActionId& action_id) {
   // We only use AddAction() once the system is initialized.
   CHECK(actions_initialized_);
@@ -236,6 +222,8 @@
   // out-of-bounds access, size_t wraps, etc). Keep this a hard CHECK (not a
   // DCHECK).
   CHECK(!pinned_action_ids_.empty());
+  DCHECK(!profile_->IsOffTheRecord())
+      << "Changing action position is disallowed in incognito.";
 
   auto current_position_on_toolbar = std::find(
       pinned_action_ids_.begin(), pinned_action_ids_.end(), action_id);
@@ -299,11 +287,6 @@
   DCHECK(pinned_action_ids_ == GetFilteredPinnedActionIds());
 }
 
-void ToolbarActionsModel::RemoveExtension(
-    const extensions::Extension* extension) {
-  RemoveAction(extension->id());
-}
-
 // Combine the currently enabled extensions that have browser actions (which
 // we get from the ExtensionRegistry) with the ordering we get from the pref
 // service. For robustness we use a somewhat inefficient process:
@@ -383,16 +366,21 @@
                                               bool is_now_visible) {
   DCHECK_NE(is_now_visible, IsActionPinned(action_id));
   DCHECK(!IsActionForcePinned(action_id));
-  auto new_pinned_action_ids = pinned_action_ids_;
+  DCHECK(!profile_->IsOffTheRecord())
+      << "Changing action pin state is disallowed in incognito.";
+
+  auto stored_pinned_action_ids = extension_prefs_->GetPinnedExtensions();
+  DCHECK_NE(is_now_visible,
+            base::Contains(stored_pinned_action_ids, action_id));
   if (is_now_visible) {
-    new_pinned_action_ids.push_back(action_id);
+    stored_pinned_action_ids.push_back(action_id);
   } else {
-    base::Erase(new_pinned_action_ids, action_id);
+    base::Erase(stored_pinned_action_ids, action_id);
   }
-  extension_prefs_->SetPinnedExtensions(new_pinned_action_ids);
+  extension_prefs_->SetPinnedExtensions(stored_pinned_action_ids);
   // The |pinned_action_ids_| should be updated as a result of updating the
   // preference.
-  DCHECK(pinned_action_ids_ == new_pinned_action_ids);
+  DCHECK(pinned_action_ids_ == GetFilteredPinnedActionIds());
 }
 
 const extensions::Extension* ToolbarActionsModel::GetExtensionById(
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model.h b/chrome/browser/ui/toolbar/toolbar_actions_model.h
index 87a273a7..56d4606 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model.h
@@ -15,7 +15,6 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
 #include "chrome/browser/extensions/extension_management.h"
-#include "chrome/browser/extensions/load_error_reporter.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "extensions/browser/extension_action.h"
@@ -42,7 +41,6 @@
 // actions in a particular window should check that window's instance of
 // ExtensionsContainer, which is responsible for the per-window layout.
 class ToolbarActionsModel : public extensions::ExtensionActionAPI::Observer,
-                            public extensions::LoadErrorReporter::Observer,
                             public extensions::ExtensionRegistryObserver,
                             public extensions::ExtensionManagement::Observer,
                             public KeyedService {
@@ -67,17 +65,9 @@
     // toolbar.
     virtual void OnToolbarActionRemoved(const ActionId& id) = 0;
 
-    // Signals that the extension, corresponding to the toolbar action, has
-    // failed to load.
-    virtual void OnToolbarActionLoadFailed() = 0;
-
     // Signals that the browser action with |id| has been updated.
     virtual void OnToolbarActionUpdated(const ActionId& id) = 0;
 
-    // Signals when the container needs to be redrawn because of a size change,
-    // and when the model has finished loading.
-    virtual void OnToolbarVisibleCountChanged() = 0;
-
     // Signals that the toolbar model has been initialized, so that if any
     // observers were postponing animation during the initialization stage, they
     // can catch up.
@@ -147,11 +137,6 @@
       content::WebContents* web_contents,
       content::BrowserContext* browser_context) override;
 
-  // extensions::LoadErrorReporter::Observer:
-  void OnLoadFailure(content::BrowserContext* browser_context,
-                     const base::FilePath& extension_path,
-                     const std::string& error) override;
-
   // extensions::ExtensionManagement::Observer:
   void OnExtensionManagementSettingsChanged() override;
 
@@ -169,10 +154,6 @@
   // Returns true if the given |extension| should be added to the toolbar.
   bool ShouldAddExtension(const extensions::Extension* extension);
 
-  // Adds or removes the given |extension| from the toolbar model.
-  void AddExtension(const extensions::Extension* extension);
-  void RemoveExtension(const extensions::Extension* extension);
-
   // Returns true if |action_id| is in the toolbar model.
   bool HasAction(const ActionId& action_id) const;
 
@@ -240,10 +221,6 @@
   // For observing pinned extensions changing.
   PrefChangeRegistrar pref_change_registrar_;
 
-  base::ScopedObservation<extensions::LoadErrorReporter,
-                          extensions::LoadErrorReporter::Observer>
-      load_error_reporter_observation_{this};
-
   base::ScopedObservation<extensions::ExtensionManagement,
                           extensions::ExtensionManagement::Observer>
       extension_management_observation_{this};
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
index d22fafc..e9cc512 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
@@ -83,13 +83,9 @@
     ++removed_count_;
   }
 
-  void OnToolbarActionLoadFailed() override {}
-
   void OnToolbarActionUpdated(
       const ToolbarActionsModel::ActionId& id) override {}
 
-  void OnToolbarVisibleCountChanged() override {}
-
   void OnToolbarModelInitialized() override { ++initialized_count_; }
 
   void OnToolbarPinnedActionsChanged() override {
@@ -490,8 +486,6 @@
 
   // Pinning from the original profile transfers to the incognito profile, so
   // pinning B results in a change.
-  // TODO(devlin): That seems questionable. It's not a leak (since it's not
-  // incognito -> on-the-record), but it still seems uncharacteristic.
   toolbar_model()->SetActionVisibility(browser_action_b()->id(), true);
   EXPECT_THAT(incognito_model->pinned_action_ids(),
               ::testing::ElementsAre(browser_action_c()->id(),
@@ -536,8 +530,6 @@
                              browser_action_c()->id()));
 
   // Moving extension C to index 0 affects both profiles.
-  // As above, this is questionable.
-  // TODO(https://crbug.com/1203833): Rationalize this.
   toolbar_model()->MovePinnedAction(browser_action_c()->id(), 0);
   EXPECT_THAT(
       toolbar_model()->pinned_action_ids(),
@@ -1085,28 +1077,28 @@
       ::testing::ElementsAre(browser_action_a()->id(), browser_action_b()->id(),
                              browser_action_c()->id()));
 
-  // Repeat the unload, reload flow, but move a pinned action between the
-  // unload and the reload.
+  // Repeat the unload, reload flow, but move a pinned action
+  // (https://crbug.com/1203899) and unpin an action
+  // (https://crbug.com/1205561) between the unload and the reload.
   service()->DisableExtension(browser_action_a()->id(),
                               extensions::disable_reason::DISABLE_USER_ACTION);
   toolbar_model()->MovePinnedAction(browser_action_b()->id(), 1u);
+  toolbar_model()->SetActionVisibility(browser_action_b()->id(), false);
 
-  // Interim: state should be C, B.
+  // Interim: state should include both B and C, but only C should be pinned.
   EXPECT_THAT(toolbar_model()->action_ids(),
               ::testing::UnorderedElementsAre(browser_action_b()->id(),
                                               browser_action_c()->id()));
   EXPECT_THAT(toolbar_model()->pinned_action_ids(),
-              ::testing::ElementsAre(browser_action_c()->id(),
-                                     browser_action_b()->id()));
+              ::testing::ElementsAre(browser_action_c()->id()));
 
-  // Reload - state should be A, C, B.
+  // Reload - state should include all of A, B, C, with pinned order of A, C.
   service()->EnableExtension(browser_action_a()->id());
   EXPECT_THAT(toolbar_model()->action_ids(),
               ::testing::UnorderedElementsAre(browser_action_a()->id(),
                                               browser_action_b()->id(),
                                               browser_action_c()->id()));
-  EXPECT_THAT(
-      toolbar_model()->pinned_action_ids(),
-      ::testing::ElementsAre(browser_action_a()->id(), browser_action_c()->id(),
-                             browser_action_b()->id()));
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(browser_action_a()->id(),
+                                     browser_action_c()->id()));
 }
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index b62168f..465db44 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -151,20 +151,22 @@
     views::InstallRectHighlightPathGenerator(this);
     SetInkDropMode(InkDropMode::ON);
     set_context_menu_controller(parent);
+    // Button subclasses need to provide this because the default color is
+    // kPlaceholderColor. In theory we could statically compute it in the
+    // constructor but then it won't be correct after dark mode changes, and to
+    // deal with that this class would have to observe NativeTheme and so on.
+    SetInkDropBaseColorCallback(base::BindRepeating(
+        [](views::View* host) {
+          // This button will be used like a LabelButton, so use the same
+          // foreground base color as a label button.
+          return color_utils::DeriveDefaultIconColor(
+              views::style::GetColor(*host, views::style::CONTEXT_BUTTON,
+                                     views::style::STYLE_PRIMARY));
+        },
+        this));
   }
   ~TransparentButton() override = default;
 
-  // Button subclasses need to provide this because the default color is
-  // kPlaceholderColor. In theory we could statically compute it in the
-  // constructor but then it won't be correct after dark mode changes, and to
-  // deal with that this class would have to observe NativeTheme and so on.
-  SkColor GetInkDropBaseColor() const override {
-    // This button will be used like a LabelButton, so use the same foreground
-    // base color as a label button.
-    return color_utils::DeriveDefaultIconColor(views::style::GetColor(
-        *this, views::style::CONTEXT_BUTTON, views::style::STYLE_PRIMARY));
-  }
-
   // Forward dragging and capture loss events, since this class doesn't have
   // enough context to handle them. Let the button class manage visual
   // transitions.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_button.cc b/chrome/browser/ui/views/extensions/extensions_menu_button.cc
index c0f2656..589a5854 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_button.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/extensions/extensions_menu_button.h"
 
+#include "base/bind.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
@@ -30,14 +31,16 @@
       controller_(controller),
       allow_pinning_(allow_pinning) {
   controller_->SetDelegate(this);
+  // TODO(pbos): This currently inherits HoverButton, is this not a no-op?
+  // Also see call in OnThemeChanged() to SetInkDropBaseColor which tries to do
+  // the same thing.
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](views::View* host) { return HoverButton::GetInkDropColor(host); },
+      this));
 }
 
 ExtensionsMenuButton::~ExtensionsMenuButton() = default;
 
-SkColor ExtensionsMenuButton::GetInkDropBaseColor() const {
-  return HoverButton::GetInkDropColor(this);
-}
-
 bool ExtensionsMenuButton::CanShowIconInToolbar() const {
   return allow_pinning_;
 }
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_button.h b/chrome/browser/ui/views/extensions/extensions_menu_button.h
index 73b64c1..3f6675e 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_button.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_button.h
@@ -34,7 +34,6 @@
   ~ExtensionsMenuButton() override;
 
   // HoverButton:
-  SkColor GetInkDropBaseColor() const override;
   bool CanShowIconInToolbar() const override;
   void AddedToWidget() override;
   void OnThemeChanged() override;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
index 2ebe71d0..f469f07 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -461,20 +461,11 @@
   SanityCheck();
 }
 
-void ExtensionsMenuView::OnToolbarActionLoadFailed() {
-  // Ignore. We don't handle the load / unload dance specially here for
-  // reloading extensions.
-}
-
 void ExtensionsMenuView::OnToolbarActionUpdated(
     const ToolbarActionsModel::ActionId& action_id) {
   UpdateActionStates();
 }
 
-void ExtensionsMenuView::OnToolbarVisibleCountChanged() {
-  // Ignore. The ExtensionsMenuView always shows all extensions.
-}
-
 void ExtensionsMenuView::OnToolbarModelInitialized() {
   DCHECK(extensions_menu_items_.empty());
   Populate();
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.h b/chrome/browser/ui/views/extensions/extensions_menu_view.h
index e6e5280..2dc9bc2 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.h
@@ -83,10 +83,8 @@
   void OnToolbarActionAdded(const ToolbarActionsModel::ActionId& item) override;
   void OnToolbarActionRemoved(
       const ToolbarActionsModel::ActionId& action_id) override;
-  void OnToolbarActionLoadFailed() override;
   void OnToolbarActionUpdated(
       const ToolbarActionsModel::ActionId& action_id) override;
-  void OnToolbarVisibleCountChanged() override;
   void OnToolbarModelInitialized() override;
   void OnToolbarPinnedActionsChanged() override;
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index af7b9e7..534a577 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -447,8 +447,6 @@
   UpdateContainerVisibilityAfterAnimation();
 }
 
-void ExtensionsToolbarContainer::OnToolbarActionLoadFailed() {}
-
 void ExtensionsToolbarContainer::OnToolbarActionUpdated(
     const ToolbarActionsModel::ActionId& action_id) {
   ToolbarActionViewController* action = GetActionForId(action_id);
@@ -456,8 +454,6 @@
     action->UpdateState();
 }
 
-void ExtensionsToolbarContainer::OnToolbarVisibleCountChanged() {}
-
 void ExtensionsToolbarContainer::OnToolbarModelInitialized() {
   CreateActions();
 }
@@ -555,13 +551,17 @@
 
 int ExtensionsToolbarContainer::GetDragOperationsForView(View* sender,
                                                          const gfx::Point& p) {
-  return ui::DragDropTypes::DRAG_MOVE;
+  return browser_->profile()->IsOffTheRecord() ? ui::DragDropTypes::DRAG_NONE
+                                               : ui::DragDropTypes::DRAG_MOVE;
 }
 
 bool ExtensionsToolbarContainer::CanStartDragForView(View* sender,
                                                      const gfx::Point& press_pt,
                                                      const gfx::Point& p) {
-  if (!CanShowIconInToolbar())
+  // We don't allow dragging if the container isn't in the toolbar, or if
+  // the profile is incognito (to avoid changing state from an incognito
+  // window).
+  if (!CanShowIconInToolbar() || browser_->profile()->IsOffTheRecord())
     return false;
 
   // Only pinned extensions should be draggable.
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
index f2a4a6d6..c721669 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
@@ -224,10 +224,8 @@
       const ToolbarActionsModel::ActionId& action_id) override;
   void OnToolbarActionRemoved(
       const ToolbarActionsModel::ActionId& action_id) override;
-  void OnToolbarActionLoadFailed() override;
   void OnToolbarActionUpdated(
       const ToolbarActionsModel::ActionId& action_id) override;
-  void OnToolbarVisibleCountChanged() override;
   void OnToolbarModelInitialized() override;
   void OnToolbarPinnedActionsChanged() override;
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container_browsertest.cc
index f359a6c..08cac1f 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container_browsertest.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/extensions/extension_action_runner.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/scripting_permissions_modifier.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -24,13 +25,16 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "extensions/browser/disable_reason.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
 #include "extensions/browser/process_manager.h"
+#include "extensions/browser/test_extension_registry_observer.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "extensions/common/extension.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/views/test/widget_test.h"
 
 namespace {
@@ -388,6 +392,56 @@
   destroyed_waiter.Wait();
 }
 
+// Verifies that dragging extension icons is disabled in incognito windows.
+// https://crbug.com/1203833.
+IN_PROC_BROWSER_TEST_F(ExtensionsToolbarContainerBrowserTest,
+                       IncognitoDraggingIsDisabled) {
+  // Load an extension, pin it, and enable it in incognito.
+  scoped_refptr<const extensions::Extension> extension =
+      LoadTestExtension("extensions/simple_with_popup");
+  ASSERT_TRUE(extension);
+
+  ToolbarActionsModel* const toolbar_model =
+      ToolbarActionsModel::Get(profile());
+  toolbar_model->SetActionVisibility(extension->id(), true);
+
+  {
+    extensions::TestExtensionRegistryObserver observer(
+        extensions::ExtensionRegistry::Get(profile()), extension->id());
+    extensions::util::SetIsIncognitoEnabled(extension->id(), profile(), true);
+    ASSERT_TRUE(observer.WaitForExtensionLoaded());
+  }
+
+  Browser* incognito_browser = CreateIncognitoBrowser();
+
+  // Verify the extension has a (visible) action for both the incognito and
+  // on-the-record browser.
+  std::vector<ToolbarActionView*> on_the_record_views = GetToolbarActionViews();
+  ASSERT_EQ(1u, on_the_record_views.size());
+  ToolbarActionView* on_the_record_view = on_the_record_views[0];
+  EXPECT_EQ(extension->id(), on_the_record_view->view_controller()->GetId());
+  EXPECT_TRUE(on_the_record_view->GetVisible());
+
+  std::vector<ToolbarActionView*> incognito_views =
+      GetToolbarActionViewsForBrowser(incognito_browser);
+  ASSERT_EQ(1u, incognito_views.size());
+  ToolbarActionView* incognito_view = incognito_views[0];
+  EXPECT_EQ(extension->id(), incognito_view->view_controller()->GetId());
+  EXPECT_TRUE(incognito_view->GetVisible());
+
+  // Dragging should be enabled for the on-the-record view, but not the
+  // incognito view.
+  EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE,
+            on_the_record_view->GetDragOperationsForTest(gfx::Point()));
+  EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
+            incognito_view->GetDragOperationsForTest(gfx::Point()));
+
+  // The two views should have the same notifiable event. This is important to
+  // test, since it can be dependent on draggability.
+  EXPECT_EQ(on_the_record_view->button_controller()->notify_action(),
+            incognito_view->button_controller()->notify_action());
+}
+
 namespace {
 
 class IncognitoExtensionsToolbarContainerBrowserTest
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc
index aad6238..b1299e3 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.h"
 
+#include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -78,6 +79,8 @@
   SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   SetInkDropMode(Button::InkDropMode::ON);
   SetInkDropBaseColor(foreground_color);
+  // Bypass color-callback setup in HoverButton.
+  SetInkDropBaseColorCallback({});
   SetHasInkDropActionOnClick(true);
   SetPreferredSize(kDeviceEntryViewSize);
 }
@@ -115,10 +118,6 @@
   SetHighlighted(is_highlighted_);
 }
 
-SkColor AudioDeviceEntryView::GetInkDropBaseColor() const {
-  return views::Button::GetInkDropBaseColor();
-}
-
 DeviceEntryUIType AudioDeviceEntryView::GetType() const {
   return DeviceEntryUIType::kAudio;
 }
@@ -140,6 +139,8 @@
   SetInkDropMode(Button::InkDropMode::ON);
   SetInkDropBaseColor(foreground_color);
   SetHasInkDropActionOnClick(true);
+  // Bypass color-callback setup in HoverButton.
+  SetInkDropBaseColorCallback({});
   SetPreferredSize(kDeviceEntryViewSize);
 }
 
@@ -153,10 +154,6 @@
   return DeviceEntryUIType::kCast;
 }
 
-SkColor CastDeviceEntryView::GetInkDropBaseColor() const {
-  return views::Button::GetInkDropBaseColor();
-}
-
 void CastDeviceEntryView::OnFocus() {
   // CastDialogSinkButton::OnFocus() changes the button's status text to "Stop
   // Casting" if the sink is connected. This status text may cause confusion to
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.h b/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.h
index 9f1056b..4e26b7c49 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.h
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.h
@@ -56,9 +56,6 @@
                        SkColor background_color) override;
   DeviceEntryUIType GetType() const override;
 
-  // HoverButton
-  SkColor GetInkDropBaseColor() const override;
-
   void SetHighlighted(bool highlighted);
   bool GetHighlighted() const;
 };
@@ -79,9 +76,6 @@
                        SkColor background_color) override;
   DeviceEntryUIType GetType() const override;
 
-  // HoverButton
-  SkColor GetInkDropBaseColor() const override;
-
   // media_router::CastDialogSinkButton
   void OnFocus() override;
 
diff --git a/chrome/browser/ui/views/hover_button.cc b/chrome/browser/ui/views/hover_button.cc
index 9ab6cbd..1dedade5 100644
--- a/chrome/browser/ui/views/hover_button.cc
+++ b/chrome/browser/ui/views/hover_button.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/bind.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -97,6 +98,8 @@
 
   SetInkDropMode(InkDropMode::ON);
   views::InkDrop::UseInkDropForFloodFillRipple(this);
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](views::View* host) { return GetInkDropColor(host); }, this));
 
   SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
                            ui::EF_RIGHT_MOUSE_BUTTON);
@@ -272,10 +275,6 @@
   }
 }
 
-SkColor HoverButton::GetInkDropBaseColor() const {
-  return GetInkDropColor(this);
-}
-
 views::View* HoverButton::GetTooltipHandlerForPoint(const gfx::Point& point) {
   if (!HitTestPoint(point))
     return nullptr;
diff --git a/chrome/browser/ui/views/hover_button.h b/chrome/browser/ui/views/hover_button.h
index 8d8b36e..97e7be7 100644
--- a/chrome/browser/ui/views/hover_button.h
+++ b/chrome/browser/ui/views/hover_button.h
@@ -87,7 +87,6 @@
   // views::MenuButton:
   KeyClickAction GetKeyClickActionForEvent(const ui::KeyEvent& event) override;
   void StateChanged(ButtonState old_state) override;
-  SkColor GetInkDropBaseColor() const override;
   views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override;
 
   views::StyledLabel* title() const { return title_; }
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index c8992474..6f4ecf4 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -158,6 +158,11 @@
         return ink_drop;
       },
       this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](IconLabelBubbleView* host) {
+        return host->delegate_->GetIconLabelBubbleInkDropColor();
+      },
+      this));
 
   views::HighlightPathGenerator::Install(
       this, std::make_unique<HighlightPathGenerator>());
@@ -333,10 +338,6 @@
   UpdateLabelColors();
 }
 
-SkColor IconLabelBubbleView::GetInkDropBaseColor() const {
-  return delegate_->GetIconLabelBubbleInkDropColor();
-}
-
 bool IconLabelBubbleView::IsTriggerableEvent(const ui::Event& event) {
   if (event.IsMouseEvent())
     return !IsBubbleShowing() && !suppress_button_release_;
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index e921f46..aa9adb2 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -148,7 +148,6 @@
   void Layout() override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void OnThemeChanged() override;
-  SkColor GetInkDropBaseColor() const override;
   bool IsTriggerableEvent(const ui::Event& event) override;
   bool ShouldUpdateInkDropOnClickCanceled() const override;
   void NotifyClick(const ui::Event& event) override;
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index 8e9527d..719fc00 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -82,6 +82,7 @@
 StarView::~StarView() {}
 
 void StarView::AfterPropertyChange(const void* key, int64_t old_value) {
+  View::AfterPropertyChange(key, old_value);
   if (key == kHasInProductHelpPromoKey) {
     views::InkDropState next_state;
     if (GetProperty(kHasInProductHelpPromoKey) || GetVisible()) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
index a211a2a..d55cbb3 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
@@ -57,6 +57,13 @@
 
     SetInkDropHighlightOpacity(
         GetOmniboxStateOpacity(OmniboxPartState::HOVERED));
+    SetInkDropBaseColorCallback(base::BindRepeating(
+        [](OmniboxSuggestionRowButton* host) {
+          return color_utils::GetColorWithMaxContrast(
+              host->omnibox_bg_color_.value());
+        },
+        this));
+
     focus_ring()->SetHasFocusPredicate([=](View* view) {
       return view->GetVisible() &&
              popup_contents_view_->model()->selection() == selection_;
@@ -69,10 +76,6 @@
 
   ~OmniboxSuggestionRowButton() override = default;
 
-  SkColor GetInkDropBaseColor() const override {
-    return color_utils::GetColorWithMaxContrast(omnibox_bg_color_.value());
-  }
-
   void OnOmniboxBackgroundChange(SkColor omnibox_bg_color) {
     focus_ring()->SchedulePaint();
     omnibox_bg_color_ = omnibox_bg_color;
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
index d41fdf2..769e26a 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -47,6 +47,8 @@
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(BrowserAppMenuButton, kIdentifier);
+
 // static
 bool BrowserAppMenuButton::g_open_app_immediately_for_testing = false;
 
@@ -55,6 +57,7 @@
                                         base::Unretained(this))),
       toolbar_view_(toolbar_view) {
   SetHorizontalAlignment(gfx::ALIGN_RIGHT);
+  SetProperty(views::kElementIdentifierKey, kIdentifier);
 }
 
 BrowserAppMenuButton::~BrowserAppMenuButton() {}
diff --git a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
index 24744a33..3624c20 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/toolbar/app_menu_icon_controller.h"
 #include "chrome/browser/ui/user_education/feature_promo_controller.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
@@ -25,6 +26,8 @@
 class BrowserAppMenuButton : public AppMenuButton {
  public:
   METADATA_HEADER(BrowserAppMenuButton);
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(BrowserAppMenuButton, kIdentifier);
+
   explicit BrowserAppMenuButton(ToolbarView* toolbar_view);
   BrowserAppMenuButton(const BrowserAppMenuButton&) = delete;
   BrowserAppMenuButton& operator=(const BrowserAppMenuButton&) = delete;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index e04eb1d..2d2bc57 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
 #include "components/sessions/content/session_tab_helper.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/notification_source.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -66,6 +67,14 @@
   view_controller_->SetDelegate(this);
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
   set_drag_controller(delegate_);
+  // Normally, the notify action is determined by whether a view is draggable
+  // (and is set to press for non-draggable and release for draggable views).
+  // However, ToolbarActionViews may be draggable or non-draggable depending on
+  // whether they are shown in an incognito window. We want to preserve the same
+  // trigger event to keep the UX (more) consistent. Set all ToolbarActionViews
+  // to trigger on mouse release.
+  button_controller()->set_notify_action(
+      views::ButtonController::NotifyAction::kOnRelease);
 
   context_menu_controller_ =
       std::make_unique<ExtensionContextMenuController>(view_controller);
@@ -148,6 +157,10 @@
   return GetImage(views::Button::STATE_NORMAL);
 }
 
+int ToolbarActionView::GetDragOperationsForTest(const gfx::Point& point) {
+  return views::View::GetDragOperations(point);
+}
+
 gfx::Size ToolbarActionView::CalculatePreferredSize() const {
   return delegate_->GetToolbarActionSize();
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.h b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
index 1587b5c4..e291be9 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.h
@@ -72,6 +72,9 @@
   // Returns button icon so it can be accessed during tests.
   gfx::ImageSkia GetIconForTest();
 
+  // Calls views::View::GetDragOperations() (which is protected).
+  int GetDragOperationsForTest(const gfx::Point& point);
+
  private:
   // views::MenuButton:
   gfx::Size CalculatePreferredSize() const override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index c2c7462..60046a5 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -195,6 +195,21 @@
                                                         GetHighlightPath(host));
       },
       this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](ToolbarButton* host) {
+        // Ensure this doesn't get called when InstallableInkDrops are enabled.
+        DCHECK(
+            !base::FeatureList::IsEnabled(views::kInstallableInkDropFeature));
+        if (host->has_in_product_help_promo_)
+          return GetFeaturePromoHighlightColorForToolbar(
+              host->GetThemeProvider());
+        base::Optional<SkColor> drop_base_color =
+            host->highlight_color_animation_.GetInkDropBaseColor();
+        if (drop_base_color)
+          return *drop_base_color;
+        return GetToolbarInkDropBaseColor(host);
+      },
+      this));
 
   // Make sure icons are flipped by default so that back, forward, etc. follows
   // UI direction.
@@ -583,24 +598,6 @@
                                     : views::LabelButton::GetTooltipText(p);
 }
 
-SkColor ToolbarButton::GetInkDropBaseColor() const {
-  // Ensure this doesn't get called when InstallableInkDrops are enabled.
-  DCHECK(!base::FeatureList::IsEnabled(views::kInstallableInkDropFeature));
-  if (has_in_product_help_promo_)
-    return GetFeaturePromoHighlightColorForToolbar(GetThemeProvider());
-  base::Optional<SkColor> drop_base_color =
-      highlight_color_animation_.GetInkDropBaseColor();
-  if (drop_base_color)
-    return *drop_base_color;
-  return LabelButton::GetInkDropBaseColor();
-}
-
-views::InkDrop* ToolbarButton::GetInkDrop() {
-  if (installable_ink_drop_)
-    return installable_ink_drop_.get();
-  return views::LabelButton::GetInkDrop();
-}
-
 void ToolbarButton::ShowContextMenuForViewImpl(View* source,
                                                const gfx::Point& point,
                                                ui::MenuSourceType source_type) {
@@ -612,6 +609,7 @@
 }
 
 void ToolbarButton::AfterPropertyChange(const void* key, int64_t old_value) {
+  View::AfterPropertyChange(key, old_value);
   if (key == kHasInProductHelpPromoKey)
     SetHasInProductHelpPromo(GetProperty(kHasInProductHelpPromoKey));
 }
@@ -622,7 +620,7 @@
 
   has_in_product_help_promo_ = has_in_product_help_promo;
 
-  // We override GetInkDropBaseColor() and call SetCreateInkDropMaskCallback(),
+  // We call SetInkDropBaseColorCallback() and SetCreateInkDropMaskCallback(),
   // returning the promo values if we are showing an in-product help promo.
   // Calling HostSizeChanged() will force the new mask and color to be fetched.
   //
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.h b/chrome/browser/ui/views/toolbar/toolbar_button.h
index 897f6bb..6900af0b 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.h
@@ -116,8 +116,6 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   std::u16string GetTooltipText(const gfx::Point& p) const override;
-  views::InkDrop* GetInkDrop() override;
-  SkColor GetInkDropBaseColor() const override;
 
   // views::ContextMenuController:
   void ShowContextMenuForViewImpl(View* source,
@@ -303,6 +301,10 @@
 
   // Used instead of the standard InkDrop implementation when
   // |views::kInstallableInkDropFeature| is enabled.
+  // TODO(crbug.com/931964): When InkDrops can be externally installed, connect
+  // this InkDrop when the experiment is enabled. This is currently not working
+  // as a virtual GetInkDrop() override was removed to finish InkDropHostView
+  // migration from the View hierarchy.
   std::unique_ptr<views::InstallableInkDrop> installable_ink_drop_;
 
   // Class responsible for animating highlight color (calling a callback on
diff --git a/chrome/browser/ui/views/toolbar/webui_tab_counter_button.cc b/chrome/browser/ui/views/toolbar/webui_tab_counter_button.cc
index aa0e7a9..7ae0adb1 100644
--- a/chrome/browser/ui/views/toolbar/webui_tab_counter_button.cc
+++ b/chrome/browser/ui/views/toolbar/webui_tab_counter_button.cc
@@ -596,6 +596,7 @@
 
 void WebUITabCounterButton::AfterPropertyChange(const void* key,
                                                 int64_t old_value) {
+  View::AfterPropertyChange(key, old_value);
   if (key != kHasInProductHelpPromoKey)
     return;
   UpdateColors();
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc
index 0c63c5fe9..b11fa18 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h"
 
+#include "base/bind.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -35,6 +36,9 @@
   views::SetHitTestComponent(this, static_cast<int>(HTMENU));
 
   SetInkDropMode(InkDropMode::ON);
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](WebAppMenuButton* host) { return host->GetColor(); }, this));
+
   SetFocusBehavior(FocusBehavior::ALWAYS);
 
   std::u16string application_name = accessible_name;
@@ -95,10 +99,6 @@
       base::UserMetricsAction("HostedAppMenuButtonButton_Clicked"));
 }
 
-SkColor WebAppMenuButton::GetInkDropBaseColor() const {
-  return GetColor();
-}
-
 void WebAppMenuButton::FadeHighlightOff() {
   if (!ShouldEnterHoveredState()) {
     GetInkDrop()->SetHoverHighlightFadeDuration(
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h
index 70810009..4ada3f7 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h
@@ -36,9 +36,6 @@
 
   void ButtonPressed(const ui::Event& event);
 
-  // AppMenuButton:
-  SkColor GetInkDropBaseColor() const override;
-
  protected:
   BrowserView* browser_view() { return browser_view_; }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_browsertest.cc
index 19a49ea..df7e354 100644
--- a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_browsertest.cc
@@ -5,18 +5,21 @@
 #include <memory>
 #include <string>
 #include <utility>
-#include <vector>
 
-#include "base/command_line.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/mock_callback.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/scoped_profile_keep_alive.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/keep_alive_registry/keep_alive_types.h"
+#include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
@@ -45,48 +48,49 @@
 
 // TODO(crbug.com/1105257): Add more tests for the actual user flow when we
 // hook up the dialog with the ProtocolHandlerRegistry.
-// TODO(crbug.com/1105257): Disabled due to testing a dialog with string
-// resources that is not used in production. The string resources are not loaded
-// in testing environment and crashes the test.
 IN_PROC_BROWSER_TEST_F(
     WebAppProtocolHandlerIntentPickerDialogInProcessBrowserTest,
-    DISABLED_ShowWebAppProtocolHandlerIntentPickerDialog) {
+    ShowWebAppProtocolHandlerIntentPickerDialog) {
   views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
                                        "WebAppProtocolHandlerIntentPickerView");
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   GURL protocol_url("web+test://test");
-  std::vector<std::string> app_ids;
+  web_app::AppId test_app_id = InstallTestWebApp(browser()->profile());
 
   base::MockCallback<base::OnceCallback<void(bool)>> show_dialog;
   EXPECT_CALL(show_dialog, Run(false));
 
+  auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>(
+      browser()->profile(),
+      ProfileKeepAliveOrigin::kWebAppPermissionDialogWindow);
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::WEB_APP_INTENT_PICKER, KeepAliveRestartOption::DISABLED);
   WebAppProtocolHandlerIntentPickerView::Show(
-      protocol_url, browser()->profile(), command_line, app_ids,
-      show_dialog.Get());
+      protocol_url, browser()->profile(), test_app_id,
+      std::move(profile_keep_alive), std::move(keep_alive), show_dialog.Get());
 
   waiter.WaitIfNeededAndGet()->CloseWithReason(
       views::Widget::ClosedReason::kEscKeyPressed);
 }
 
-// TODO(crbug.com/1105257): Disabled due to testing a dialog with string
-// resources that is not used in production. The string resources are not loaded
-// in testing environment and crashes the test.
 IN_PROC_BROWSER_TEST_F(
     WebAppProtocolHandlerIntentPickerDialogInProcessBrowserTest,
-    DISABLED_AcceptProtocolHandlerIntentPickerDialog) {
+    AcceptProtocolHandlerIntentPickerDialog) {
   views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{},
                                        "WebAppProtocolHandlerIntentPickerView");
-  base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   GURL protocol_url("web+test://test");
   web_app::AppId test_app_id = InstallTestWebApp(browser()->profile());
-  std::vector<std::string> app_ids = {test_app_id};
 
   base::MockCallback<base::OnceCallback<void(bool)>> show_dialog;
   EXPECT_CALL(show_dialog, Run(true));
 
+  auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>(
+      browser()->profile(),
+      ProfileKeepAliveOrigin::kWebAppPermissionDialogWindow);
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::WEB_APP_INTENT_PICKER, KeepAliveRestartOption::DISABLED);
   WebAppProtocolHandlerIntentPickerView::Show(
-      protocol_url, browser()->profile(), command_line, app_ids,
-      show_dialog.Get());
+      protocol_url, browser()->profile(), test_app_id,
+      std::move(profile_keep_alive), std::move(keep_alive), show_dialog.Get());
 
   waiter.WaitIfNeededAndGet()->CloseWithReason(
       views::Widget::ClosedReason::kAcceptButtonClicked);
@@ -100,23 +104,25 @@
     views::NamedWidgetShownWaiter waiter(
         views::test::AnyWidgetTestPasskey{},
         "WebAppProtocolHandlerIntentPickerView");
-    base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     GURL protocol_url("web+test://test");
     web_app::AppId test_app_id = InstallTestWebApp(browser()->profile());
-    std::vector<std::string> app_ids = {test_app_id};
+    auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>(
+        browser()->profile(),
+        ProfileKeepAliveOrigin::kWebAppPermissionDialogWindow);
+    auto keep_alive = std::make_unique<ScopedKeepAlive>(
+        KeepAliveOrigin::WEB_APP_INTENT_PICKER,
+        KeepAliveRestartOption::DISABLED);
     WebAppProtocolHandlerIntentPickerView::Show(
-        protocol_url, browser()->profile(), command_line, app_ids,
+        protocol_url, browser()->profile(), test_app_id,
+        std::move(profile_keep_alive), std::move(keep_alive),
         base::DoNothing());
     waiter.WaitIfNeededAndGet()->CloseWithReason(
         views::Widget::ClosedReason::kEscKeyPressed);
   }
 };
 
-// TODO(crbug.com/1105257): Disabled due to testing a dialog with string
-// resources that is not used in production. The string resources are not loaded
-// in testing environment and crashes the test.
 IN_PROC_BROWSER_TEST_F(
     WebAppProtocolHandlerIntentPickerDialogInteractiveBrowserTest,
-    DISABLED_InvokeUi_CloseDialog) {
+    InvokeUi_CloseDialog) {
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc
index fee1e6e..07567da 100644
--- a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.cc
@@ -7,32 +7,30 @@
 #include <memory>
 #include <string>
 #include <utility>
-#include <vector>
 
 #include "base/callback_forward.h"
 #include "base/check_op.h"
-#include "base/command_line.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/scoped_profile_keep_alive.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/web_apps/web_app_hover_button.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
-#include "components/prefs/scoped_user_pref_update.h"
 #include "components/url_formatter/elide_url.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/views/border.h"
-#include "ui/views/controls/button/checkbox.h"
 #include "ui/views/controls/scroll_view.h"
-#include "ui/views/controls/separator.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/grid_layout.h"
 #include "ui/views/widget/widget.h"
@@ -40,32 +38,14 @@
 #include "url/gurl.h"
 
 namespace {
-
-// Maximum numbers of web apps we want to show at a time in the dialog.
-// The height of the scroll in the dialog depends on how many app
-// candidates we got and how many we want to show. If there is more than
-// |KMaxAppResults| app candidates, we will show 3.5 apps to let the user
-// know there are more than |kMaxAppResults| apps accessible by scrolling
-// the list.
-constexpr size_t kMaxAppResults = 3;
 // This dialog follows the design that
 // chrome/browser/ui/views/intent_picker_bubble_view.cc created and the
 // main component sizes were also mostly copied over to share the
 // same layout.
 // Main components sizes
-constexpr int kIntentPickerCheckBoxColumnWidth = 288;
 constexpr int kMaxIntentPickerWidth = 320;
 constexpr int kRowHeight = 32;
 constexpr int kTitlePadding = 16;
-constexpr gfx::Insets kSeparatorPadding(0, 0, 16, 0);
-constexpr SkColor kSeparatorColor = SkColorSetARGB(0x1F, 0x0, 0x0, 0x0);
-
-std::unique_ptr<views::Separator> CreateHorizontalSeparator() {
-  auto separator = std::make_unique<views::Separator>();
-  separator->SetColor(kSeparatorColor);
-  separator->SetBorder(views::CreateEmptyBorder(kSeparatorPadding));
-  return separator;
-}
 
 }  // namespace
 
@@ -73,64 +53,52 @@
 void WebAppProtocolHandlerIntentPickerView::Show(
     const GURL& url,
     Profile* profile,
-    const base::CommandLine& command_line,
-    std::vector<std::string> app_ids,
-    base::OnceCallback<void(bool accepted)> close_callback) {
+    const web_app::AppId& app_id,
+    std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive,
+    std::unique_ptr<ScopedKeepAlive> keep_alive,
+    chrome::WebAppProtocolHandlerAcceptanceCallback close_callback) {
   std::unique_ptr<WebAppProtocolHandlerIntentPickerView> view =
       std::make_unique<WebAppProtocolHandlerIntentPickerView>(
-          url, profile, command_line, std::move(app_ids),
-          std::move(close_callback));
-  WebAppProtocolHandlerIntentPickerView* view_ptr = view.get();
-
+          url, profile, app_id, std::move(profile_keep_alive),
+          std::move(keep_alive), std::move(close_callback));
   views::DialogDelegate::CreateDialogWidget(std::move(view),
                                             /*context=*/nullptr,
                                             /*parent=*/nullptr)
       ->Show();
-
-  // Set the first entry as the default selected App, can only be done
-  // after Show();
-  if (!view_ptr->hover_buttons_.empty()) {
-    view_ptr->hover_buttons_[view_ptr->selected_app_tag_]->MarkAsSelected(
-        nullptr);
-    view_ptr->RequestFocus();
-  }
 }
 
 WebAppProtocolHandlerIntentPickerView::WebAppProtocolHandlerIntentPickerView(
     const GURL& url,
     Profile* profile,
-    const base::CommandLine& command_line,
-    std::vector<std::string> app_ids,
-    base::OnceCallback<void(bool accepted)> close_callback)
-    : url_(url),
+    const web_app::AppId& app_id,
+    std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive,
+    std::unique_ptr<ScopedKeepAlive> keep_alive,
+    chrome::WebAppProtocolHandlerAcceptanceCallback close_callback)
+    : url_(std::move(url)),
       profile_(profile),
-      command_line_(command_line),
-      app_ids_(std::move(app_ids)),
-      close_callback_(std::move(close_callback)),
-      // The ScopedKeepAlive ensures the process is alive until the dialog is
-      // closed, and initiates the shutdown at closure if there is nothing
-      // else keeping the browser alive.
-      keep_alive_(std::make_unique<ScopedKeepAlive>(
-          KeepAliveOrigin::WEB_APP_INTENT_PICKER,
-          KeepAliveRestartOption::DISABLED)) {
-  SetDefaultButton(ui::DIALOG_BUTTON_OK);
+      app_id_(std::move(app_id)),
+      // Pass the ScopedProfileKeepAlive into here to ensure the profile is
+      // available until the dialog is closed.
+      profile_keep_alive_(std::move(profile_keep_alive)),
+      // Pass the ScopedKeepAlive into here ensures the process is alive until
+      // the dialog is closed, and initiates the shutdown at closure if there
+      // is nothing else keeping the browser alive.
+      keep_alive_(std::move(keep_alive)),
+      close_callback_(std::move(close_callback)) {
+  SetDefaultButton(ui::DIALOG_BUTTON_CANCEL);
   SetModalType(ui::MODAL_TYPE_WINDOW);
-  std::u16string title =
-      app_ids_.size() > 1
-          ? l10n_util::GetStringUTF16(
-                IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_TITLE)
-          : l10n_util::GetStringUTF16(
-                IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE);
+  std::u16string title = l10n_util::GetStringUTF16(
+      IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_TITLE);
   SetTitle(title);
   SetShowCloseButton(false);
 
   SetButtonLabel(ui::DIALOG_BUTTON_OK,
                  l10n_util::GetStringUTF16(
-                     IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_OK_BUTTON_TEXT));
+                     IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT));
   SetButtonLabel(
       ui::DIALOG_BUTTON_CANCEL,
       l10n_util::GetStringUTF16(
-          IDS_PROTOCOL_HANDLER_INTENT_PICKER_MULTI_CANCEL_BUTTON_TEXT));
+          IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT));
 
   SetAcceptCallback(
       base::BindOnce(&WebAppProtocolHandlerIntentPickerView::OnAccepted,
@@ -155,22 +123,6 @@
                    GetHeightForWidth(kMaxIntentPickerWidth));
 }
 
-const std::string& WebAppProtocolHandlerIntentPickerView::GetSelectedAppId()
-    const {
-  DCHECK_LT(selected_app_tag_, hover_buttons_.size());
-  return hover_buttons_[selected_app_tag_]->app_id();
-}
-
-void WebAppProtocolHandlerIntentPickerView::SetSelectedAppIndex(
-    size_t index,
-    const ui::Event& event) {
-  DCHECK_GE(index, 0u);
-  DCHECK_LT(index, hover_buttons_.size());
-  hover_buttons_[selected_app_tag_]->MarkAsUnselected(nullptr);
-  selected_app_tag_ = index;
-  hover_buttons_[selected_app_tag_]->MarkAsSelected(&event);
-  views::View::RequestFocus();
-}
 
 void WebAppProtocolHandlerIntentPickerView::OnAccepted() {
   RunCloseCallback(/*accepted=*/true);
@@ -198,44 +150,21 @@
   scrollable_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
 
-  if (app_ids_.size() == 1) {
-    // We want the default to be 'Block' when we're requesting permission.
-    SetDefaultButton(ui::DIALOG_BUTTON_CANCEL);
-    SetButtonLabel(
-        ui::DIALOG_BUTTON_OK,
-        l10n_util::GetStringUTF16(
-            IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_OK_BUTTON_TEXT));
-    SetButtonLabel(
-        ui::DIALOG_BUTTON_CANCEL,
-        l10n_util::GetStringUTF16(
-            IDS_PROTOCOL_HANDLER_INTENT_PICKER_SINGLE_CANCEL_BUTTON_TEXT));
-  }
-
   web_app::WebAppProvider* provider = web_app::WebAppProvider::Get(profile_);
   web_app::AppRegistrar& registrar = provider->registrar();
-  hover_buttons_.reserve(app_ids_.size());
-  for (size_t i = 0; i < app_ids_.size(); ++i) {
-    const std::string& app_id = app_ids_[i];
-    auto app_button = std::make_unique<WebAppHoverButton>(
-        base::BindRepeating(
-            &WebAppProtocolHandlerIntentPickerView::SetSelectedAppIndex,
-            base::Unretained(this), i),
-        app_id, provider, registrar.GetAppShortName(app_id),
-        registrar.GetAppStartUrl(app_id));
-    app_button->set_tag(i);
-    hover_buttons_.push_back(app_button.get());
-    scrollable_view->AddChildViewAt(std::move(app_button), i);
-  }
+  auto app_button = std::make_unique<WebAppHoverButton>(
+      views::Button::PressedCallback(), app_id_, provider,
+      registrar.GetAppShortName(app_id_), registrar.GetAppStartUrl(app_id_));
+  app_button->set_tag(0);
+  app_button->SetTooltipAndAccessibleName();
+  scrollable_view->AddChildViewAt(std::move(app_button), 0);
 
   auto scroll_view = std::make_unique<views::ScrollView>();
   scroll_view->SetBackgroundThemeColorId(
       ui::NativeTheme::kColorId_BubbleBackground);
   scroll_view->SetContents(std::move(scrollable_view));
-  // This part gives the scroll a fixed width and height. The height depends on
-  // how many app candidates we got and how many we actually want to show.
-  // The added 0.5 on the else block allow us to let the user know there are
-  // more than |kMaxAppResults| apps accessible by scrolling the list.
-  scroll_view->ClipHeightTo(kRowHeight, (kMaxAppResults + 0.5) * kRowHeight);
+  // This part gives the scroll a fixed height.
+  scroll_view->ClipHeightTo(kRowHeight, 2 * kRowHeight);
 
   constexpr int kColumnSetId = 0;
   views::ColumnSet* cs = layout->AddColumnSet(kColumnSetId);
@@ -246,27 +175,8 @@
 
   layout->StartRowWithPadding(views::GridLayout::kFixedSize, kColumnSetId,
                               views::GridLayout::kFixedSize, kTitlePadding);
-  scroll_view_ = layout->AddView(std::move(scroll_view));
+  layout->AddView(std::move(scroll_view));
   layout->StartRow(views::GridLayout::kFixedSize, kColumnSetId, 0);
-
-  // The checkbox allows the user to opt-in to relaxed security
-  // (i.e. skipping future prompts) for this url.
-  layout->AddView(CreateHorizontalSeparator());
-  // This second ColumnSet has a padding column in order to manipulate the
-  // Checkbox positioning freely.
-  constexpr int kColumnSetIdPadded = 2;
-  views::ColumnSet* cs_padded = layout->AddColumnSet(kColumnSetIdPadded);
-  cs_padded->AddPaddingColumn(views::GridLayout::kFixedSize, kTitlePadding);
-  cs_padded->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
-                       views::GridLayout::kFixedSize,
-                       views::GridLayout::ColumnSize::kFixed,
-                       kIntentPickerCheckBoxColumnWidth, 0);
-  layout->StartRowWithPadding(views::GridLayout::kFixedSize, kColumnSetIdPadded,
-                              views::GridLayout::kFixedSize, 0);
-  remember_selection_checkbox_ = layout->AddView(
-      std::make_unique<views::Checkbox>(l10n_util::GetStringUTF16(
-          IDS_PROTOCOL_HANDLER_INTENT_PICKER_REMEMBER_SELECTION)));
-
   layout->AddPaddingRow(views::GridLayout::kFixedSize, kRowHeight);
 }
 
@@ -284,28 +194,23 @@
 void ShowWebAppProtocolHandlerIntentPicker(
     const GURL& url,
     Profile* profile,
-    const base::CommandLine& command_line,
-    base::OnceCallback<void(bool accepted)> close_callback) {
-  auto registry_ready_callback =
-      [](const GURL& url, Profile* profile,
-         const base::CommandLine& command_line,
-         base::OnceCallback<void(bool accepted)> close_callback) {
-        // TODO(crbug.com/1105257): Provide a list of installed web apps
-        // app_ids.
-        std::vector<std::string> app_ids;
-        WebAppProtocolHandlerIntentPickerView::Show(url, profile, command_line,
-                                                    std::move(app_ids),
-                                                    std::move(close_callback));
-      };
-
+    const web_app::AppId& app_id,
+    WebAppProtocolHandlerAcceptanceCallback close_callback) {
+  // TODO(crbug.com/1105257)::Check if we have permission to
+  // launch the app directly.
+  auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>(
+      profile, ProfileKeepAliveOrigin::kWebAppPermissionDialogWindow);
+  auto keep_alive = std::make_unique<ScopedKeepAlive>(
+      KeepAliveOrigin::WEB_APP_INTENT_PICKER, KeepAliveRestartOption::DISABLED);
   auto* provider = web_app::WebAppProvider::Get(profile);
   DCHECK(provider);
   // Sometimes it is too early for registrar to be populated at this time. We
   // need to wait for it to get the web application info.
   provider->on_registry_ready().Post(
       FROM_HERE,
-      base::BindOnce(std::move(registry_ready_callback), url, profile,
-                     command_line, std::move(close_callback)));
+      base::BindOnce(WebAppProtocolHandlerIntentPickerView::Show, url, profile,
+                     app_id, std::move(profile_keep_alive),
+                     std::move(keep_alive), std::move(close_callback)));
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.h b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.h
index e37a948..c5116d9 100644
--- a/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.h
+++ b/chrome/browser/ui/views/web_apps/web_app_protocol_handler_intent_picker_dialog_view.h
@@ -11,6 +11,9 @@
 
 #include "base/callback_forward.h"
 #include "base/command_line.h"
+#include "chrome/browser/profiles/scoped_profile_keep_alive.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/gfx/geometry/size.h"
@@ -18,12 +21,6 @@
 #include "url/gurl.h"
 
 class Profile;
-class WebAppHoverButton;
-
-namespace views {
-class Checkbox;
-class ScrollView;
-}  // namespace views
 
 // This class extends DialogDelegateView and needs to be owned
 // by the views framework.
@@ -34,9 +31,10 @@
   WebAppProtocolHandlerIntentPickerView(
       const GURL& url,
       Profile* profile,
-      const base::CommandLine& command_line,
-      std::vector<std::string> app_ids,
-      base::OnceCallback<void(bool accepted)> close_callback);
+      const web_app::AppId& app_id,
+      std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive,
+      std::unique_ptr<ScopedKeepAlive> keep_alive,
+      chrome::WebAppProtocolHandlerAcceptanceCallback close_callback);
 
   WebAppProtocolHandlerIntentPickerView(
       const WebAppProtocolHandlerIntentPickerView&) = delete;
@@ -44,42 +42,33 @@
       const WebAppProtocolHandlerIntentPickerView&) = delete;
   ~WebAppProtocolHandlerIntentPickerView() override;
 
-  static void Show(const GURL& url,
-                   Profile* profile,
-                   const base::CommandLine& command_line,
-                   std::vector<std::string> app_ids,
-                   base::OnceCallback<void(bool accepted)> close_callback);
+  static void Show(
+      const GURL& url,
+      Profile* profile,
+      const web_app::AppId& app_id,
+      std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive,
+      std::unique_ptr<ScopedKeepAlive> keep_alive,
+      chrome::WebAppProtocolHandlerAcceptanceCallback close_callback);
 
  private:
   // views::DialogDelegateView:
   gfx::Size CalculatePreferredSize() const override;
 
-  const std::string& GetSelectedAppId() const;
+  const web_app::AppId& GetSelectedAppId() const;
   void OnAccepted();
   void OnCanceled();
   void OnClosed();
   void Initialize();
 
-  // Unselects the current focused app item on the list and
-  // refocus on the selected app item based on the index provided.
-  void SetSelectedAppIndex(size_t index, const ui::Event& event);
-
   // Runs the close_callback_ provided during Show() if it exists.
   void RunCloseCallback(bool accepted);
 
   const GURL url_;
   Profile* const profile_;
-  const base::CommandLine command_line_;
-  const std::vector<std::string> app_ids_;
-  base::OnceCallback<void(bool)> close_callback_;
+  const web_app::AppId app_id_;
+  std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
   std::unique_ptr<ScopedKeepAlive> keep_alive_;
-
-  std::vector<WebAppHoverButton*> hover_buttons_;
-  views::Checkbox* remember_selection_checkbox_ = nullptr;
-  views::ScrollView* scroll_view_ = nullptr;
-
-  // Pre-select the first app on the list.
-  size_t selected_app_tag_ = 0;
+  chrome::WebAppProtocolHandlerAcceptanceCallback close_callback_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_WEB_APPS_WEB_APP_PROTOCOL_HANDLER_INTENT_PICKER_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/guest_os_handler.h b/chrome/browser/ui/webui/settings/chromeos/guest_os_handler.h
index e634038..e7954f7 100644
--- a/chrome/browser/ui/webui/settings/chromeos/guest_os_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/guest_os_handler.h
@@ -9,7 +9,7 @@
 #include "base/scoped_observation.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_manager.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_manager_factory.h"
-#include "chrome/browser/chromeos/usb/cros_usb_detector.h"
+#include "chrome/browser/ash/usb/cros_usb_detector.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 
 class Profile;
diff --git a/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc b/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc
index 0b39e04..dd7b61c 100644
--- a/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc
+++ b/chrome/browser/web_applications/components/web_app_run_on_os_login_win.cc
@@ -21,7 +21,7 @@
   locations.in_startup = true;
 
   return CreatePlatformShortcuts(shortcut_data_dir, locations,
-                                 SHORTCUT_CREATION_BY_USER, shortcut_info);
+                                 SHORTCUT_CREATION_AUTOMATED, shortcut_info);
 }
 
 bool UnregisterRunOnOsLogin(const std::string& app_id,
diff --git a/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc b/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc
index 9630710..4ae49adc 100644
--- a/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc
@@ -69,7 +69,7 @@
 
   void VerifyShortcutCreated() {
     std::vector<base::FilePath> shortcuts = GetShortcuts();
-    EXPECT_GT(shortcuts.size(), 0u);
+    EXPECT_EQ(shortcuts.size(), 1u);
 
     for (const base::FilePath& shortcut : shortcuts) {
       std::wstring cmd_line_string;
@@ -97,6 +97,18 @@
   VerifyShortcutCreated();
 }
 
+TEST_F(WebAppRunOnOsLoginWinTest, RegisterMultipleTimes) {
+  std::unique_ptr<ShortcutInfo> shortcut_info = GetShortcutInfo();
+  bool result = internals::RegisterRunOnOsLogin(*shortcut_info);
+  EXPECT_TRUE(result);
+  VerifyShortcutCreated();
+
+  // There should still only be one shortcut created.
+  result = internals::RegisterRunOnOsLogin(*shortcut_info);
+  EXPECT_TRUE(result);
+  VerifyShortcutCreated();
+}
+
 TEST_F(WebAppRunOnOsLoginWinTest, Unregister) {
   std::unique_ptr<ShortcutInfo> shortcut_info = GetShortcutInfo();
   bool result = internals::RegisterRunOnOsLogin(*shortcut_info);
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
index 9d317227..67e0811c 100644
--- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -445,7 +445,7 @@
   if (!disabled_system_features_pref)
     return;
 
-  for (const auto& entry : *disabled_system_features_pref) {
+  for (const auto& entry : disabled_system_features_pref->GetList()) {
     switch (entry.GetInt()) {
       case policy::SystemFeature::kCamera:
         disabled_system_apps_.insert(SystemAppType::CAMERA);
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index edadc0f..61627deb 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1620323976-ab6c0e7aaf65f1e9dc0cabd3b0ce6598f5cf1e29.profdata
+chrome-win32-master-1620334673-6dc5fe0a935955373e196bc7f5dad400e3a1e55b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 70af7d2..4ab71b6a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1620323976-5ab18a82e85d0a66b800b7665ad68a42d5cb61c6.profdata
+chrome-win64-master-1620334673-2fc8758cdea89c260f068beff8d5460a3aabf2f6.profdata
diff --git a/chrome/common/extensions/api/common_extension_api_unittest.cc b/chrome/common/extensions/api/common_extension_api_unittest.cc
index a35dbc0..d437c65 100644
--- a/chrome/common/extensions/api/common_extension_api_unittest.cc
+++ b/chrome/common/extensions/api/common_extension_api_unittest.cc
@@ -839,7 +839,7 @@
       const base::ListValue* list, const std::string& key,
       const std::string& value) -> const base::DictionaryValue* {
     const base::DictionaryValue* ret = nullptr;
-    for (const auto& val : *list) {
+    for (const auto& val : list->GetList()) {
       const base::DictionaryValue* dict = nullptr;
       if (!val.GetAsDictionary(&dict))
         continue;
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
index 820bcb83..d6fbcdc5 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
@@ -253,7 +253,7 @@
                              const base::ListValue* extension_actions,
                              FileBrowserHandler::List* result,
                              std::u16string* error) {
-  for (const auto& entry : *extension_actions) {
+  for (const auto& entry : extension_actions->GetList()) {
     const base::DictionaryValue* dict;
     if (!entry.GetAsDictionary(&dict)) {
       *error = base::ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
diff --git a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
index 475ed4f..9f44b383 100644
--- a/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
+++ b/chrome/common/extensions/api/url_handlers/url_handlers_parser.cc
@@ -131,9 +131,9 @@
     return false;
   }
 
-  for (auto it = manif_patterns->begin(); it != manif_patterns->end(); ++it) {
+  for (const auto& entry : manif_patterns->GetList()) {
     std::string str_pattern;
-    it->GetAsString(&str_pattern);
+    entry.GetAsString(&str_pattern);
     // TODO(sergeygs): Limit this to non-top-level domains.
     // TODO(sergeygs): Also add a verification to the CWS installer that the
     // URL patterns claimed here belong to the app's author verified sites.
diff --git a/chrome/common/extensions/manifest_handlers/linked_app_icons.cc b/chrome/common/extensions/manifest_handlers/linked_app_icons.cc
index 818e3f8..9102164 100644
--- a/chrome/common/extensions/manifest_handlers/linked_app_icons.cc
+++ b/chrome/common/extensions/manifest_handlers/linked_app_icons.cc
@@ -62,7 +62,7 @@
       return false;
     }
 
-    for (const auto& icon_value : *icons_list) {
+    for (const auto& icon_value : icons_list->GetList()) {
       const base::DictionaryValue* icon_dict = nullptr;
       if (!icon_value.GetAsDictionary(&icon_dict)) {
         *error = base::UTF8ToUTF16(
diff --git a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc b/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
index 77c0222..65e3e58 100644
--- a/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
+++ b/chrome/renderer/media/chrome_key_systems_provider_unittest.cc
@@ -39,7 +39,8 @@
 
   media::EmeConfigRule GetRobustnessConfigRule(
       media::EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
     return requested_robustness.empty() ? media::EmeConfigRule::SUPPORTED
                                         : media::EmeConfigRule::NOT_SUPPORTED;
   }
diff --git a/chrome/services/sharing/nearby/platform/bluetooth_classic_medium.cc b/chrome/services/sharing/nearby/platform/bluetooth_classic_medium.cc
index 0139bd4..72e015af 100644
--- a/chrome/services/sharing/nearby/platform/bluetooth_classic_medium.cc
+++ b/chrome/services/sharing/nearby/platform/bluetooth_classic_medium.cc
@@ -235,7 +235,7 @@
   // TODO(crbug.com/1191815): Remove these logs. They are temporary logs used
   // to debug this issue.
   VLOG(1) << "Device added or changed. Address: " << device->address
-          << ", Name: '" << (device->name ? "<None>" : device->name_for_display)
+          << ", Name: '" << (device->name ? device->name_for_display : "<None>")
           << "'";
 
   // Best-effort attempt to filter out BLE advertisements. BLE advertisements
diff --git a/chrome/services/speech/soda/soda_client.cc b/chrome/services/speech/soda/soda_client.cc
index bc5eb78..a6443191 100644
--- a/chrome/services/speech/soda/soda_client.cc
+++ b/chrome/services/speech/soda/soda_client.cc
@@ -5,9 +5,14 @@
 #include "chrome/services/speech/soda/soda_client.h"
 
 #include "base/logging.h"
+#include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
 #include "build/build_config.h"
 
+#if defined(OS_MAC)
+#include "base/mac/mac_util.h"
+#endif
+
 namespace soda {
 
 SodaClient::SodaClient(base::FilePath library_path)
@@ -62,6 +67,16 @@
 
   if (IsInitialized())
     delete_soda_func_(soda_async_handle_);
+
+#if defined(OS_MAC)
+  // Intentionally do not unload the libsoda.so library after the SodaClient
+  // is destroyed to prevent global destructor functions from running on an
+  // unloaded library. This only applies to older versions of MacOS since
+  // there have been no crashes on 10.15+, likely due to a change in the
+  // __cxa_atexit implementation.
+  if (base::mac::IsAtMostOS10_14())
+    ignore_result(lib_.release());
+#endif  // defined(OS_MAC)
 }
 
 NO_SANITIZE("cfi-icall")
diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/manifest.json b/chrome/test/data/extensions/api_test/webnavigation/iframe/manifest.json
index 9a84adb..8b6e74b 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/iframe/manifest.json
+++ b/chrome/test/data/extensions/api_test/webnavigation/iframe/manifest.json
@@ -4,7 +4,8 @@
   "manifest_version": 2,
   "description": "Tests the webNavigation API events - iframe.",
   "background": {
-    "page": "test_iframe.html"
+    "scripts": ["test_iframe.js"],
+    "persistent": true
   },
   "permissions": ["webNavigation", "tabs"]
 }
diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html
deleted file mode 100644
index f6bcf25..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<script src="_test_resources/api_test/webnavigation/framework.js"></script>
-<script src="test_iframe.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js
index 40cb1d2..360eea86 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/iframe/test_iframe.js
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-onload = async function() {
+const scriptUrl = '_test_resources/api_test/webnavigation/framework.js';
+let loadScript = chrome.test.loadScript(scriptUrl);
+
+loadScript.then(async function() {
   var getURL = chrome.extension.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
@@ -387,4 +390,4 @@
       chrome.tabs.update(tab.id, { url: getURL('h.html') });
     },
   ]);
-};
+});
diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/manifest.json b/chrome/test/data/extensions/api_test/webnavigation/openTab/manifest.json
index 640e563..7d639aca8 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/openTab/manifest.json
+++ b/chrome/test/data/extensions/api_test/webnavigation/openTab/manifest.json
@@ -4,7 +4,8 @@
   "manifest_version": 2,
   "description": "Tests the webNavigation API events - openTab.",
   "background": {
-    "page": "test_openTab.html"
+    "scripts": ["test_openTab.js"],
+    "persistent": true
   },
   "permissions": ["webNavigation", "tabs"]
 }
diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html
deleted file mode 100644
index e2e321e..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<script src="_test_resources/api_test/webnavigation/framework.js"></script>
-<script src="test_openTab.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js
index 6760bf4..e994c93 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/openTab/test_openTab.js
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-onload = async function() {
+const scriptUrl = '_test_resources/api_test/webnavigation/framework.js';
+let loadScript = chrome.test.loadScript(scriptUrl);
+
+loadScript.then(async function() {
   var getURL = chrome.extension.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
@@ -215,4 +218,4 @@
       chrome.tabs.update(tab.id, { url: getURL('c.html') });
     },
   ]);
-};
+});
diff --git a/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/manifest.json b/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/manifest.json
index 9bcec21..041a763 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/manifest.json
+++ b/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/manifest.json
@@ -4,7 +4,8 @@
   "manifest_version": 2,
   "description": "Tests the webNavigation API - pending deletion frame.",
   "background": {
-    "page": "test_pendingDeletion.html"
+    "scripts": ["test_pendingDeletion.js"],
+    "persistent": true
   },
   "permissions": ["webNavigation", "tabs"]
 }
diff --git a/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.html b/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.html
deleted file mode 100644
index 8c8767d..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<script src="_test_resources/api_test/webnavigation/framework.js"></script>
-<script src="test_pendingDeletion.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.js b/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.js
index 3eadcf0..c2a0f11b 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/pendingDeletion/test_pendingDeletion.js
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-window.addEventListener('load', async function() {
+const scriptUrl = '_test_resources/api_test/webnavigation/framework.js';
+let loadScript = chrome.test.loadScript(scriptUrl);
+
+loadScript.then(async function() {
   let config = await promise(chrome.test.getConfig);
 
   let PATH_BASE = "/extensions/api_test/webnavigation/pendingDeletion";
diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/manifest.json b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/manifest.json
index f9d704c..0d5de32 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/manifest.json
+++ b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/manifest.json
@@ -4,7 +4,8 @@
   "manifest_version": 2,
   "description": "Tests the webNavigation API events - referenceFragment.",
   "background": {
-    "page": "test_referenceFragment.html"
+    "scripts": ["test_referenceFragment.js"],
+    "persistent": true
   },
   "permissions": ["webNavigation", "tabs"]
 }
diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html
deleted file mode 100644
index cda19c13..0000000
--- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<script src="_test_resources/api_test/webnavigation/framework.js"></script>
-<script src="test_referenceFragment.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js
index 51e070f9..f909783 100644
--- a/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js
+++ b/chrome/test/data/extensions/api_test/webnavigation/referenceFragment/test_referenceFragment.js
@@ -2,7 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-onload = async function() {
+const scriptUrl = '_test_resources/api_test/webnavigation/framework.js';
+let loadScript = chrome.test.loadScript(scriptUrl);
+
+loadScript.then(async function() {
   var getURL = chrome.extension.getURL;
   let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
 
@@ -152,4 +155,4 @@
       chrome.tabs.update(tab.id, { url: getURL('b.html') });
     },
   ]);
-};
+});
diff --git a/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
index eb959ac..7b923cd 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
@@ -625,6 +625,32 @@
   embedder.setUpNewWindowRequest_(webview, 'guest.html', '', testName);
 }
 
+// This is not a test in and of itself, but a means of creating a webview that
+// is left in an unattached state, so that the C++ side can test it in that
+// state.
+function testNewWindowDeferredAttachmentIndefinitely() {
+  let testName = 'testNewWindowDeferredAttachmentIndefinitely';
+  let webview = embedder.setUpGuest_('foobar');
+
+  webview.addEventListener('newwindow', (e) => {
+    embedder.assertCorrectEvent_(e, '');
+
+    let newwebview = document.createElement('webview');
+    try {
+      e.window.attach(newwebview);
+      embedder.test.succeed();
+    } catch (e) {
+      embedder.test.fail();
+    }
+
+    window.setTimeout(() => {
+      document.querySelector('#webview-tag-container').appendChild(newwebview);
+    }, 999999999);
+  });
+
+  embedder.setUpNewWindowRequest_(webview, 'guest.html', '', testName);
+}
+
 embedder.test.testList = {
   'testNewWindowAttachAfterOpenerDestroyed':
       testNewWindowAttachAfterOpenerDestroyed,
@@ -646,7 +672,9 @@
   'testNewWindowWebRequestRemoveElement': testNewWindowWebRequestRemoveElement,
   'testNewWindowWebViewNameTakesPrecedence':
       testNewWindowWebViewNameTakesPrecedence,
-  'testNewWindowAndUpdateOpener': testNewWindowAndUpdateOpener
+  'testNewWindowAndUpdateOpener': testNewWindowAndUpdateOpener,
+  'testNewWindowDeferredAttachmentIndefinitely':
+      testNewWindowDeferredAttachmentIndefinitely
 };
 
 onload = function() {
diff --git a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_test_utils.js b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_test_utils.js
index e91423c..e39f79e 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/diagnostics_test_utils.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/diagnostics_test_utils.js
@@ -5,15 +5,16 @@
 import {assertTrue} from '../../chai_assert.js';
 
 /**
- * Helper function for getting an array of data-point elements from a
- * diagnostics card.
+ * Helper function for getting an array of data-point elements.
  * @param {?T} element
+ * @param {string=} selector
  * @template T
  * @return {!NodeList<!DataPointElement>}
  */
-export function getDataPointElements(element) {
+export function getDataPointElements(element, selector) {
   return /** @type {!NodeList<!DataPointElement>} */ (
-      element.shadowRoot.querySelectorAll('data-point'));
+      element.shadowRoot.querySelectorAll(
+          selector ? `${selector} > data-point` : 'data-point'));
 }
 
 /**
diff --git a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
index 429d2539..092db857 100644
--- a/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
+++ b/chrome/test/data/webui/chromeos/fake_network_config_mojom.js
@@ -56,6 +56,14 @@
      */
     this.testPin = '';
 
+    /**
+     * @type {chromeos.networkConfig.mojom.AlwaysOnVpnProperties}
+     */
+    this.alwaysOnVpnProperties_ = {
+      mode: chromeos.networkConfig.mojom.AlwaysOnVpnMode.kOff,
+      serviceGuid: '',
+    };
+
     this.resetForTest();
   }
 
@@ -114,6 +122,7 @@
      'setCellularSimState',
      'startConnect',
      'configureNetwork',
+     'getAlwaysOnVpn',
     ].forEach((methodName) => {
       this.resolverMap_.set(methodName, new PromiseResolver());
     });
@@ -503,4 +512,22 @@
       resolve({serverCas: this.serverCas_, userCerts: this.userCerts_});
     });
   }
+
+  /**
+   * @return {!Promise<{
+   *      result: {!chromeos.networkConfig.mojom.AlwaysOnVpnProperties}}>}
+   */
+  getAlwaysOnVpn() {
+    return new Promise(resolve => {
+      this.methodCalled('getAlwaysOnVpn');
+      resolve({properties: this.alwaysOnVpnProperties_});
+    });
+  }
+
+  /**
+   * @param {!chromeos.networkConfig.mojom.AlwaysOnVpnProperties} properties
+   */
+  setAlwaysOnVpn(properties) {
+    this.alwaysOnVpnProperties_ = properties;
+  }
 }
diff --git a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
index 9c4556e..ab3b5b1 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
@@ -14,6 +14,7 @@
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 // #import {eventToPromise, flushTasks, waitAfterNextRender} from 'chrome://test/test_util.m.js';
+// #import {assert} from 'chrome://resources/js/assert.m.js';
 // clang-format on
 
 suite('InternetSubpage', function() {
@@ -525,6 +526,141 @@
               assertEquals(1, allNetworkLists[2].networks.length);
             });
           });
+
+      test('Always-on VPN settings reflects OFF mode', () => {
+        const mojom = chromeos.networkConfig.mojom;
+        mojoApi_.setAlwaysOnVpn({
+          mode: mojom.AlwaysOnVpnMode.kOff,
+          serviceGuid: '',
+        });
+        return initSubpage()
+            .then(() => {
+              initVpn();
+              return flushAsync();
+            })
+            .then(() => {
+              const networkAlwaysOnVpn =
+                  internetSubpage.$$('#alwaysOnVpnSelector');
+              assert(networkAlwaysOnVpn);
+              assertEquals(mojom.AlwaysOnVpnMode.kOff, networkAlwaysOnVpn.mode);
+              assertEquals('', networkAlwaysOnVpn.service);
+            });
+      });
+
+      test('Always-on VPN settings reflects BEST-EFFORT mode', () => {
+        const mojom = chromeos.networkConfig.mojom;
+        mojoApi_.setAlwaysOnVpn({
+          mode: mojom.AlwaysOnVpnMode.kBestEffort,
+          serviceGuid: 'vpn1_guid',
+        });
+        return initSubpage()
+            .then(() => {
+              initVpn();
+              return flushAsync();
+            })
+            .then(() => {
+              const networkAlwaysOnVpn =
+                  internetSubpage.$$('#alwaysOnVpnSelector');
+              assert(networkAlwaysOnVpn);
+              assertEquals(
+                  mojom.AlwaysOnVpnMode.kBestEffort, networkAlwaysOnVpn.mode);
+              assertEquals('vpn1_guid', networkAlwaysOnVpn.service);
+            });
+      });
+
+      test('Always-on VPN settings reflects STRICT mode', () => {
+        const mojom = chromeos.networkConfig.mojom;
+        mojoApi_.setAlwaysOnVpn({
+          mode: mojom.AlwaysOnVpnMode.kStrict,
+          serviceGuid: 'vpn2_guid',
+        });
+        return initSubpage()
+            .then(() => {
+              initVpn();
+              return flushAsync();
+            })
+            .then(() => {
+              const networkAlwaysOnVpn =
+                  internetSubpage.$$('#alwaysOnVpnSelector');
+              assert(networkAlwaysOnVpn);
+              assertEquals(
+                  mojom.AlwaysOnVpnMode.kStrict, networkAlwaysOnVpn.mode);
+              assertEquals('vpn2_guid', networkAlwaysOnVpn.service);
+            });
+      });
+
+      test('Enabled always-on and select a service', () => {
+        const mojom = chromeos.networkConfig.mojom;
+        return initSubpage()
+            .then(() => {
+              initVpn();
+              return flushAsync();
+            })
+            .then(() => {
+              const networkAlwaysOnVpn =
+                  internetSubpage.$$('#alwaysOnVpnSelector');
+              assert(networkAlwaysOnVpn);
+              networkAlwaysOnVpn.mode = mojom.AlwaysOnVpnMode.kBestEffort;
+              networkAlwaysOnVpn.service = 'vpn1_guid';
+              return flushAsync();
+            })
+            .then(() => mojoApi_.getAlwaysOnVpn())
+            .then(result => {
+              assertEquals(
+                  mojom.AlwaysOnVpnMode.kBestEffort, result.properties.mode);
+              assertEquals('vpn1_guid', result.properties.serviceGuid);
+            });
+      });
+
+      test('Enable always-on with STRICT mode and select a service', () => {
+        const mojom = chromeos.networkConfig.mojom;
+        return initSubpage()
+            .then(() => {
+              initVpn();
+              return flushAsync();
+            })
+            .then(() => {
+              const networkAlwaysOnVpn =
+                  internetSubpage.$$('#alwaysOnVpnSelector');
+              assert(networkAlwaysOnVpn);
+              networkAlwaysOnVpn.mode = mojom.AlwaysOnVpnMode.kStrict;
+              networkAlwaysOnVpn.service = 'vpn2_guid';
+              return flushAsync();
+            })
+            .then(() => mojoApi_.getAlwaysOnVpn())
+            .then(result => {
+              assertEquals(
+                  mojom.AlwaysOnVpnMode.kStrict, result.properties.mode);
+              assertEquals('vpn2_guid', result.properties.serviceGuid);
+            });
+      });
+
+      test('Always-on VPN is not shown without networks', () => {
+        return initSubpage().then(() => {
+          const networkAlwaysOnVpn = internetSubpage.$$('#alwaysOnVpnSelector');
+          assert(!networkAlwaysOnVpn);
+        });
+      });
+
+      test('Always-on VPN list contains compatible networks', () => {
+        const mojom = chromeos.networkConfig.mojom;
+        mojoApi_.setAlwaysOnVpn({
+          mode: mojom.AlwaysOnVpnMode.kBestEffort,
+          serviceGuid: '',
+        });
+        return initSubpage()
+            .then(() => {
+              initVpn();
+              return flushAsync();
+            })
+            .then(() => {
+              const networkAlwaysOnVpn =
+                  internetSubpage.$$('#alwaysOnVpnSelector');
+              assert(networkAlwaysOnVpn);
+              // The list should contain 2 compatible networks.
+              assertEquals(5, networkAlwaysOnVpn.networks.length);
+            });
+      });
     });
   });
 });
diff --git a/chromecast/renderer/media/key_systems_cast.cc b/chromecast/renderer/media/key_systems_cast.cc
index dabfd31..c822c8c 100644
--- a/chromecast/renderer/media/key_systems_cast.cc
+++ b/chromecast/renderer/media/key_systems_cast.cc
@@ -65,7 +65,13 @@
 
   EmeConfigRule GetRobustnessConfigRule(
       EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
+    // `hw_secure_requirement` is ignored here because it's a temporary solution
+    // until a larger refactoring of the key system logic is done. It also does
+    // not need to account for it here because if it does introduce an
+    // incompatibility at this point, it will still be caught by the rule logic
+    // in KeySystemConfigSelector: crbug.com/1204284
     if (requested_robustness.empty()) {
 #if defined(OS_ANDROID)
       return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
diff --git a/chromeos/components/media_app_ui/resources/js/error_reporter.js b/chromeos/components/media_app_ui/resources/js/error_reporter.js
index bc8b9d9..a6fcb1e9 100644
--- a/chromeos/components/media_app_ui/resources/js/error_reporter.js
+++ b/chromeos/components/media_app_ui/resources/js/error_reporter.js
@@ -22,8 +22,14 @@
     if (errorArg !== undefined) {
       if (errorArg instanceof Error) {
         message += `\n${errorArg.name}: ${errorArg.message}`;
+      } else if (typeof errorArg === 'string') {
+        message += ', ' + errorArg;
       } else {
-        message += '\n' + JSON.stringify(errorArg);
+        try {
+          message += '\n' + JSON.stringify(errorArg);
+        } catch (e) {
+          message += '<object loop?>';
+        }
       }
     }
   }
@@ -45,6 +51,10 @@
   if (firstError instanceof PromiseRejectionEvent) {
     prefix = 'Unhandled rejection: ';
     firstError = firstError.reason;
+  } else if (
+      typeof firstError === 'object' && firstError !== null &&
+      firstError.constructor) {
+    prefix = firstError.constructor.name + ': ';
   }
 
   if (firstError instanceof Error || firstError instanceof ErrorEvent ||
@@ -52,12 +62,20 @@
     // Note: `ErrorEvent` doesn't have a name field, `DOMException`s are routed
     // through 'onerror' and treated as `ErrorEvents`, we can also have
     // `DOMExceptions` inside a unhandled rejection.
-    errorMessage =
-        `Error: ${firstError.name || 'ErrorEvent'}: ${firstError.message}`;
+    errorMessage = `[${firstError.name || ''}] ${firstError.message}`;
     errorObject = firstError;
+
+    // Events and exceptions won't have stacks. Make one.
+    if (!errorObject.stack) {
+      errorObject.stack = new Error().stack;
+    }
   } else {
     // Should just be a regular object.
-    errorMessage = `Unexpected: ${JSON.stringify(firstError)}`;
+    try {
+      errorMessage = `Unexpected: ${JSON.stringify(firstError)}`;
+    } catch (e) {
+      errorMessage = `Unexpected: <object loop?>`;
+    }
   }
 
   if (!errorObject) {
@@ -95,7 +113,7 @@
     // Still call real console.error.
     realConsoleError(...errors);
     // Send to error reporter.
-    report(...errors);
+    report(...errors, '(from console)');
   };
 }
 
diff --git a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
index 5d81543..f57c3c27 100644
--- a/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
+++ b/chromeos/components/media_app_ui/test/media_app_ui_browsertest.js
@@ -182,6 +182,8 @@
    */
   function suppressConsoleErrorsForErrorTesting() {
     chrome.crashReportPrivate.reportError = function(e) {
+      // Everything should have a non-empty stack.
+      assertEquals(!!e.stackTrace, true);
       reportedErrors.push(e);
     };
     // Set `realConsoleError` in `captureConsoleErrors` to console.log to
@@ -197,30 +199,56 @@
   error.name = 'yikes error';
   const extraData = {b: 'b'};
 
+  const loop = {};
+  loop.loop = loop;
+  class MySpecialException {
+    constructor() {
+      this.loop = loop;
+    }
+  }
+
   console.error('a');
   console.error(error);
   console.error('b', extraData);
   console.error(extraData, extraData, extraData);
   console.error(error, 'foo', extraData, {e: error});
+  console.error(new MySpecialException(), new MySpecialException());
+  console.error(1, 2, 3, 4, 5);
+  console.error(null, null, null);
 
-  assertEquals(5, reportedErrors.length);
+  assertEquals(8, reportedErrors.length);
   // Test handles console.error(string).
-  assertEquals('Unexpected: "a"', reportedErrors[0].message);
+  assertEquals('Unexpected: "a", (from console)', reportedErrors[0].message);
   // Test handles console.error(Error).
-  assertEquals('Error: yikes error: yikes message', reportedErrors[1].message);
+  assertEquals(
+      'Error: [yikes error] yikes message, (from console)',
+      reportedErrors[1].message);
   // Test handles console.error(string, Object).
-  assertEquals('Unexpected: "b"\n{"b":"b"}', reportedErrors[2].message);
+  assertEquals(
+      'Unexpected: "b"\n{"b":"b"}, (from console)', reportedErrors[2].message);
   // Test handles console.error(Object, Object, Object).
   assertEquals(
-      'Unexpected: {"b":"b"}\n{"b":"b"}\n{"b":"b"}', reportedErrors[3].message);
+      'Object: Unexpected: {"b":"b"}\n{"b":"b"}\n{"b":"b"}, (from console)',
+      reportedErrors[3].message);
   // Test handles console.error(string, Object, Error, Object).
   assertEquals(
-      'Error: yikes error: yikes message\n"foo"\n{"b":"b"}\n' +
-          '{"e":{"name":"yikes error"}}',
+      'Error: [yikes error] yikes message, foo\n{"b":"b"}\n' +
+          '{"e":{"name":"yikes error"}}, (from console)',
       reportedErrors[4].message);
+  // Test arbitrary classes.
+  assertEquals(
+      'MySpecialException: Unexpected: <object loop?><object loop?>, ' +
+          '(from console)',
+      reportedErrors[5].message);
+  // Test non-objects.
+  assertEquals(
+      'Unexpected: 1\n2\n3\n4\n5, (from console)', reportedErrors[6].message);
+  assertEquals(
+      'Unexpected: null\nnull\nnull, (from console)',
+      reportedErrors[7].message);
+
   // Note: This is not needed i.e. tests pass without this but it is good
   // practice to reset it since we stub it out for this test.
-
   console.error = originalConsoleError;
 };
 
diff --git a/chromeos/dbus/shill/shill_profile_client.cc b/chromeos/dbus/shill/shill_profile_client.cc
index b33b8da9..05d7b31 100644
--- a/chromeos/dbus/shill/shill_profile_client.cc
+++ b/chromeos/dbus/shill/shill_profile_client.cc
@@ -133,7 +133,7 @@
                                shill::kSetPropertyFunction);
 
   dbus::MessageWriter writer(&method_call);
-  writer.AppendString(shill::kAlwaysOnVpnServiceProperty);
+  writer.AppendString(name);
   writer.AppendVariantOfObjectPath(property);
   GetHelper(profile_path)
       ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
diff --git a/chromeos/network/cellular_esim_profile_handler_impl.cc b/chromeos/network/cellular_esim_profile_handler_impl.cc
index 5a2e200..5ab7df1 100644
--- a/chromeos/network/cellular_esim_profile_handler_impl.cc
+++ b/chromeos/network/cellular_esim_profile_handler_impl.cc
@@ -200,7 +200,7 @@
   }
 
   base::flat_set<std::string> euicc_paths;
-  for (const auto& euicc : *euicc_paths_from_prefs) {
+  for (const auto& euicc : euicc_paths_from_prefs->GetList()) {
     if (!euicc.is_string()) {
       NET_LOG(ERROR) << "Non-string EUICC path: " << euicc;
       continue;
diff --git a/chromeos/network/geolocation_handler.cc b/chromeos/network/geolocation_handler.cc
index dbaaf5f..6692599 100644
--- a/chromeos/network/geolocation_handler.cc
+++ b/chromeos/network/geolocation_handler.cc
@@ -118,10 +118,9 @@
   bool cellular_was_enabled = cellular_enabled_;
   cellular_enabled_ = false;
   wifi_enabled_ = false;
-  for (base::ListValue::const_iterator iter = technologies->begin();
-       iter != technologies->end(); ++iter) {
+  for (const auto& entry : technologies->GetList()) {
     std::string technology;
-    iter->GetAsString(&technology);
+    entry.GetAsString(&technology);
     if (technology == shill::kTypeWifi) {
       wifi_enabled_ = true;
     } else if (technology == shill::kTypeCellular) {
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 1bb188c..a338557 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -514,10 +514,9 @@
   // This stores all GUIDs of policies that have changed or are new.
   std::set<std::string> modified_policies;
 
-  for (base::ListValue::const_iterator it = network_configs_onc.begin();
-       it != network_configs_onc.end(); ++it) {
+  for (const auto& entry : network_configs_onc.GetList()) {
     const base::DictionaryValue* network = nullptr;
-    it->GetAsDictionary(&network);
+    entry.GetAsDictionary(&network);
     DCHECK(network);
 
     std::string guid;
diff --git a/chromeos/network/network_profile_handler.cc b/chromeos/network/network_profile_handler.cc
index f20284d03..24fbc05 100644
--- a/chromeos/network/network_profile_handler.cc
+++ b/chromeos/network/network_profile_handler.cc
@@ -229,10 +229,10 @@
     base::Value properties) {
   // A profile always contains the mode.
   std::string* mode =
-      properties.FindStringPath(shill::kAlwaysOnVpnModeProperty);
+      properties.FindStringKey(shill::kAlwaysOnVpnModeProperty);
   DCHECK(mode);
   std::string* service =
-      properties.FindStringPath(shill::kAlwaysOnVpnServiceProperty);
+      properties.FindStringKey(shill::kAlwaysOnVpnServiceProperty);
   std::move(callback).Run(*mode, service ? *service : std::string());
 }
 
diff --git a/chromeos/network/network_state_handler.cc b/chromeos/network/network_state_handler.cc
index 7b13c12..7b3a504 100644
--- a/chromeos/network/network_state_handler.cc
+++ b/chromeos/network/network_state_handler.cc
@@ -1251,7 +1251,7 @@
   managed_list->clear();
   // Updates managed_list and request updates for new entries.
   std::set<std::string> list_entries;
-  for (auto& iter : entries) {
+  for (const auto& iter : entries.GetList()) {
     std::string path;
     iter.GetAsString(&path);
     if (path.empty() || path == shill::kFlimflamServicePath) {
diff --git a/chromeos/network/network_util.cc b/chromeos/network/network_util.cc
index cd75072e..cd671740 100644
--- a/chromeos/network/network_util.cc
+++ b/chromeos/network/network_util.cc
@@ -141,7 +141,7 @@
                               std::vector<CellularScanResult>* scan_results) {
   scan_results->clear();
   scan_results->reserve(list.GetSize());
-  for (const auto& value : list) {
+  for (const auto& value : list.GetList()) {
     const base::DictionaryValue* dict;
     if (!value.GetAsDictionary(&dict))
       return false;
diff --git a/chromeos/network/onc/onc_mapper.cc b/chromeos/network/onc/onc_mapper.cc
index 73351cba..2736778 100644
--- a/chromeos/network/onc/onc_mapper.cc
+++ b/chromeos/network/onc/onc_mapper.cc
@@ -115,7 +115,7 @@
 
   std::unique_ptr<base::ListValue> result_array(new base::ListValue);
   int original_index = 0;
-  for (const auto& entry : onc_array) {
+  for (const auto& entry : onc_array.GetList()) {
     std::unique_ptr<base::Value> result_entry;
     result_entry =
         MapEntry(original_index, *array_signature.onc_array_entry_signature,
diff --git a/chromeos/network/onc/onc_merger.cc b/chromeos/network/onc/onc_merger.cc
index 438ba19d1..072f082 100644
--- a/chromeos/network/onc/onc_merger.cc
+++ b/chromeos/network/onc/onc_merger.cc
@@ -54,10 +54,9 @@
   if (!policy.GetListWithoutPathExpansion(::onc::kRecommended,
                                           &recommended_value))
     return;
-  for (base::ListValue::const_iterator it = recommended_value->begin();
-       it != recommended_value->end(); ++it) {
+  for (const auto& value : recommended_value->GetList()) {
     std::string entry;
-    if (it->GetAsString(&entry))
+    if (value.GetAsString(&entry))
       result->SetKey(entry, base::Value(true));
   }
 }
diff --git a/chromeos/network/onc/onc_utils.cc b/chromeos/network/onc/onc_utils.cc
index 589afea..691afc4be 100644
--- a/chromeos/network/onc/onc_utils.cc
+++ b/chromeos/network/onc/onc_utils.cc
@@ -167,7 +167,7 @@
 CertPEMsByGUIDMap GetServerAndCACertsByGUID(
     const base::ListValue& certificates) {
   CertPEMsByGUIDMap certs_by_guid;
-  for (const auto& entry : certificates) {
+  for (const auto& entry : certificates.GetList()) {
     const base::DictionaryValue* cert = nullptr;
     bool entry_is_dictionary = entry.GetAsDictionary(&cert);
     DCHECK(entry_is_dictionary);
@@ -272,7 +272,7 @@
   }
 
   std::unique_ptr<base::ListValue> pem_list(new base::ListValue);
-  for (const auto& entry : *guid_ref_list) {
+  for (const auto& entry : guid_ref_list->GetList()) {
     std::string guid_ref;
     bool entry_is_string = entry.GetAsString(&guid_ref);
     DCHECK(entry_is_string);
@@ -502,10 +502,9 @@
 const base::DictionaryValue* GetNetworkConfigByGUID(
     const base::ListValue& network_configs,
     const std::string& guid) {
-  for (base::ListValue::const_iterator it = network_configs.begin();
-       it != network_configs.end(); ++it) {
-    const base::DictionaryValue* network = NULL;
-    it->GetAsDictionary(&network);
+  for (const auto& entry : network_configs.GetList()) {
+    const base::DictionaryValue* network = nullptr;
+    entry.GetAsDictionary(&network);
     DCHECK(network);
 
     std::string current_guid;
@@ -514,7 +513,7 @@
     if (current_guid == guid)
       return network;
   }
-  return NULL;
+  return nullptr;
 }
 
 // Returns the first Ethernet NetworkConfiguration from |network_configs| with
@@ -522,10 +521,9 @@
 const base::DictionaryValue* GetNetworkConfigForEthernetWithoutEAP(
     const base::ListValue& network_configs) {
   VLOG(2) << "Search for ethernet policy without EAP.";
-  for (base::ListValue::const_iterator it = network_configs.begin();
-       it != network_configs.end(); ++it) {
-    const base::DictionaryValue* network = NULL;
-    it->GetAsDictionary(&network);
+  for (const auto& entry : network_configs.GetList()) {
+    const base::DictionaryValue* network = nullptr;
+    entry.GetAsDictionary(&network);
     DCHECK(network);
 
     std::string type;
@@ -533,7 +531,7 @@
     if (type != ::onc::network_type::kEthernet)
       continue;
 
-    const base::DictionaryValue* ethernet = NULL;
+    const base::DictionaryValue* ethernet = nullptr;
     network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
                                                &ethernet);
 
@@ -543,7 +541,7 @@
     if (auth == ::onc::ethernet::kAuthenticationNone)
       return network;
   }
-  return NULL;
+  return nullptr;
 }
 
 // Returns the NetworkConfiguration object for |network| from
@@ -810,7 +808,7 @@
 
 void ExpandStringsInNetworks(const VariableExpander& variable_expander,
                              base::ListValue* network_configs) {
-  for (auto& entry : *network_configs) {
+  for (auto& entry : network_configs->GetList()) {
     base::DictionaryValue* network = nullptr;
     entry.GetAsDictionary(&network);
     DCHECK(network);
@@ -1051,8 +1049,8 @@
 bool ResolveServerCertRefsInNetworks(const CertPEMsByGUIDMap& certs_by_guid,
                                      base::ListValue* network_configs) {
   bool success = true;
-  for (base::ListValue::iterator it = network_configs->begin();
-       it != network_configs->end();) {
+  for (auto it = network_configs->GetList().begin();
+       it != network_configs->GetList().end();) {
     base::DictionaryValue* network = nullptr;
     it->GetAsDictionary(&network);
     if (!ResolveServerCertRefsInNetwork(certs_by_guid, network)) {
@@ -1251,10 +1249,9 @@
 
   bool ethernet_not_found = false;
   int networks_created = 0;
-  for (base::ListValue::const_iterator it = expanded_networks->begin();
-       it != expanded_networks->end(); ++it) {
-    const base::DictionaryValue* network = NULL;
-    it->GetAsDictionary(&network);
+  for (const auto& entry : expanded_networks->GetList()) {
+    const base::DictionaryValue* network = nullptr;
+    entry.GetAsDictionary(&network);
     DCHECK(network);
 
     // Remove irrelevant fields.
@@ -1402,7 +1399,7 @@
 }
 
 bool HasUserPasswordSubsitutionVariable(base::ListValue* network_configs) {
-  for (auto& entry : *network_configs) {
+  for (auto& entry : network_configs->GetList()) {
     base::DictionaryValue* network = nullptr;
     entry.GetAsDictionary(&network);
     DCHECK(network);
diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc
index 911fd19c..0de56f6 100644
--- a/chromeos/network/onc/onc_validator.cc
+++ b/chromeos/network/onc/onc_validator.cc
@@ -289,7 +289,7 @@
   }
 
   std::unique_ptr<base::ListValue> repaired_recommended(new base::ListValue);
-  for (const auto& entry : *recommended_list) {
+  for (const auto& entry : recommended_list->GetList()) {
     std::string field_name;
     if (!entry.GetAsString(&field_name)) {
       NOTREACHED();  // The types of field values are already verified.
@@ -494,7 +494,7 @@
   const base::ListValue* list = NULL;
   if (object.GetListWithoutPathExpansion(field_name, &list)) {
     path_.push_back(field_name);
-    for (const auto& entry : *list) {
+    for (const auto& entry : list->GetList()) {
       std::string value;
       if (!entry.GetAsString(&value)) {
         NOTREACHED();  // The types of field values are already verified.
diff --git a/chromeos/network/prohibited_technologies_handler.cc b/chromeos/network/prohibited_technologies_handler.cc
index 24ca9b80..c511307 100644
--- a/chromeos/network/prohibited_technologies_handler.cc
+++ b/chromeos/network/prohibited_technologies_handler.cc
@@ -65,7 +65,7 @@
   // Build up prohibited network type list and save it for furthur use when
   // enforced
   session_prohibited_technologies_.clear();
-  for (const auto& item : *prohibited_list) {
+  for (const auto& item : prohibited_list->GetList()) {
     std::string prohibited_technology;
     bool item_is_string = item.GetAsString(&prohibited_technology);
     DCHECK(item_is_string);
diff --git a/chromeos/network/shill_property_handler.cc b/chromeos/network/shill_property_handler.cc
index 8a39933..1e32fa69 100644
--- a/chromeos/network/shill_property_handler.cc
+++ b/chromeos/network/shill_property_handler.cc
@@ -398,10 +398,9 @@
   std::set<std::string> new_requested_updates;
   NET_LOG(DEBUG) << "UpdateProperties: " << ManagedState::TypeToString(type)
                  << ": " << entries.GetSize();
-  for (base::ListValue::const_iterator iter = entries.begin();
-       iter != entries.end(); ++iter) {
+  for (const auto& entry : entries.GetList()) {
     std::string path;
-    iter->GetAsString(&path);
+    entry.GetAsString(&path);
     if (path.empty())
       continue;
 
@@ -423,7 +422,7 @@
       (type == ManagedState::MANAGED_TYPE_NETWORK) ? observed_networks_
                                                    : observed_devices_;
   ShillPropertyObserverMap new_observed;
-  for (const auto& entry : entries) {
+  for (const auto& entry : entries.GetList()) {
     std::string path;
     entry.GetAsString(&path);
     if (path.empty())
@@ -631,9 +630,8 @@
   const base::ListValue* ip_configs;
   if (!ip_config_list_value.GetAsList(&ip_configs))
     return;
-  for (base::ListValue::const_iterator iter = ip_configs->begin();
-       iter != ip_configs->end(); ++iter) {
-    RequestIPConfig(type, path, *iter);
+  for (const auto& entry : ip_configs->GetList()) {
+    RequestIPConfig(type, path, entry);
   }
 }
 
diff --git a/chromeos/network/shill_property_handler_unittest.cc b/chromeos/network/shill_property_handler_unittest.cc
index e42d8315..d319cc81 100644
--- a/chromeos/network/shill_property_handler_unittest.cc
+++ b/chromeos/network/shill_property_handler_unittest.cc
@@ -136,10 +136,9 @@
     if (type.empty())
       return;
     entries_[type].clear();
-    for (base::ListValue::const_iterator iter = entries.begin();
-         iter != entries.end(); ++iter) {
+    for (const auto& entry : entries.GetList()) {
       std::string path;
-      if (iter->GetAsString(&path))
+      if (entry.GetAsString(&path))
         entries_[type].push_back(path);
     }
   }
diff --git a/chromeos/network/stub_cellular_networks_provider.cc b/chromeos/network/stub_cellular_networks_provider.cc
index f4c611e..a4b8e65f 100644
--- a/chromeos/network/stub_cellular_networks_provider.cc
+++ b/chromeos/network/stub_cellular_networks_provider.cc
@@ -161,6 +161,8 @@
     if (base::Contains(all_iccids, iccid_eid_pair.first))
       continue;
 
+    NET_LOG(EVENT) << "Adding stub cellular network for ICCID="
+                   << iccid_eid_pair.first << " EID=" << iccid_eid_pair.second;
     network_added = true;
     new_stub_networks.push_back(NetworkState::CreateNonShillCellularNetwork(
         iccid_eid_pair.first, iccid_eid_pair.second,
@@ -192,6 +194,8 @@
 
     if (shill_iccids.contains(network->iccid()) ||
         !esim_and_slot_iccids.contains(network->iccid())) {
+      NET_LOG(EVENT) << "Removing stub cellular network for ICCID="
+                     << network->iccid() << " EID=" << network->eid();
       network_removed = true;
       it = network_list.erase(it);
       continue;
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 33036f7..c1db83a 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-92-4484.0-1619431291-benchmark-92.0.4489.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-92-4484.0-1620039320-benchmark-92.0.4492.0-r1.orderfile.xz
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index 74f113f1..1d56be0 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -773,7 +773,6 @@
         base::SysInfo::NumberOfProcessors() - start_params_.num_cores_disabled;
     DCHECK_LT(0, cpus);
 
-    DCHECK(is_dev_mode_);
     std::vector<std::string> kernel_cmdline = GenerateKernelCmdline(
         start_params_, file_system_status, *is_dev_mode_, is_host_on_vm_,
         GetChromeOsChannelFromLsbRelease());
diff --git a/components/arc_strings.grdp b/components/arc_strings.grdp
index d2ab35c..50548d37 100644
--- a/components/arc_strings.grdp
+++ b/components/arc_strings.grdp
@@ -13,17 +13,17 @@
   <message name="IDS_ASH_ARC_APP_COMPAT_RESIZE_CONFIRM_DONT_ASK_ME" desc="Label for check box that prevents the dialog from being shown in the future">
     Don't ask me again for this app
   </message>
-  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_PHONE" desc="Label for button to resize the window to phone size">
+  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_PHONE" desc="Item in a list of possible sizes for the app. Makes the app in a portrait, phone size.">
     Phone
   </message>
-  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_TABLET" desc="Label for button to resize the window to tablet size">
+  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_TABLET" desc="Item in a list of possible sizes for the app. Makes the app bigger and wider in a tablet size.">
     Tablet
   </message>
-  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_DESKTOP" desc="Label for button to resize the window to desktop size">
-    Desktop
+  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_DESKTOP" desc="Item in a list of possible sizes for the app. Makes the app expand to the whole screen.">
+    Maximized
   </message>
-  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_RESIZE_SETTINGS" desc="Label for button to open resize settings">
-    Resize Settings
+  <message name="IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_RESIZE_SETTINGS" desc="Label for the button to allow the user to go to resize settings.">
+    Settings
   </message>
   <message name="IDS_ARC_COMPAT_MODE_SPLASH_SCREEN_TITLE" desc="A Dialog title of splash screen to advertise the app is designed for mobile.">
     This app is designed for mobile
diff --git a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_DESKTOP.png.sha1 b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_DESKTOP.png.sha1
index 5303e052..cd3efe48 100644
--- a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_DESKTOP.png.sha1
+++ b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_DESKTOP.png.sha1
@@ -1 +1 @@
-67aece4bf2458b01f47e9c175c90cc836ff2f412
\ No newline at end of file
+6e4a6e86d83eab4d63ac64dcbfb0320b060fd659
\ No newline at end of file
diff --git a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_PHONE.png.sha1 b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_PHONE.png.sha1
index 5303e052..cd3efe48 100644
--- a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_PHONE.png.sha1
+++ b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_PHONE.png.sha1
@@ -1 +1 @@
-67aece4bf2458b01f47e9c175c90cc836ff2f412
\ No newline at end of file
+6e4a6e86d83eab4d63ac64dcbfb0320b060fd659
\ No newline at end of file
diff --git a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_RESIZE_SETTINGS.png.sha1 b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_RESIZE_SETTINGS.png.sha1
index 5303e052..cd3efe48 100644
--- a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_RESIZE_SETTINGS.png.sha1
+++ b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_RESIZE_SETTINGS.png.sha1
@@ -1 +1 @@
-67aece4bf2458b01f47e9c175c90cc836ff2f412
\ No newline at end of file
+6e4a6e86d83eab4d63ac64dcbfb0320b060fd659
\ No newline at end of file
diff --git a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_TABLET.png.sha1 b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_TABLET.png.sha1
index 5303e052..cd3efe48 100644
--- a/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_TABLET.png.sha1
+++ b/components/arc_strings_grdp/IDS_ARC_COMPAT_MODE_RESIZE_TOGGLE_MENU_TABLET.png.sha1
@@ -1 +1 @@
-67aece4bf2458b01f47e9c175c90cc836ff2f412
\ No newline at end of file
+6e4a6e86d83eab4d63ac64dcbfb0320b060fd659
\ No newline at end of file
diff --git a/components/cdm/renderer/android_key_systems.cc b/components/cdm/renderer/android_key_systems.cc
index 18e3fdc..a6d72f3 100644
--- a/components/cdm/renderer/android_key_systems.cc
+++ b/components/cdm/renderer/android_key_systems.cc
@@ -73,7 +73,13 @@
 
   EmeConfigRule GetRobustnessConfigRule(
       media::EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
+    // `hw_secure_requirement` is ignored here because it's a temporary solution
+    // until a larger refactoring of the key system logic is done. It also does
+    // not need to account for it here because if it does introduce an
+    // incompatibility at this point, it will still be caught by the rule logic
+    // in KeySystemConfigSelector: crbug.com/1204284
     return requested_robustness.empty() ? EmeConfigRule::SUPPORTED
                                         : EmeConfigRule::NOT_SUPPORTED;
   }
diff --git a/components/cdm/renderer/external_clear_key_key_system_properties.cc b/components/cdm/renderer/external_clear_key_key_system_properties.cc
index f0ff61f1..231036f 100644
--- a/components/cdm/renderer/external_clear_key_key_system_properties.cc
+++ b/components/cdm/renderer/external_clear_key_key_system_properties.cc
@@ -53,7 +53,8 @@
 
 media::EmeConfigRule ExternalClearKeyProperties::GetRobustnessConfigRule(
     media::EmeMediaType media_type,
-    const std::string& requested_robustness) const {
+    const std::string& requested_robustness,
+    const bool* /*hw_secure_requirement*/) const {
   return requested_robustness.empty() ? media::EmeConfigRule::SUPPORTED
                                       : media::EmeConfigRule::NOT_SUPPORTED;
 }
diff --git a/components/cdm/renderer/external_clear_key_key_system_properties.h b/components/cdm/renderer/external_clear_key_key_system_properties.h
index 328400f..81f8dab 100644
--- a/components/cdm/renderer/external_clear_key_key_system_properties.h
+++ b/components/cdm/renderer/external_clear_key_key_system_properties.h
@@ -27,7 +27,8 @@
   media::SupportedCodecs GetSupportedCodecs() const override;
   media::EmeConfigRule GetRobustnessConfigRule(
       media::EmeMediaType media_type,
-      const std::string& requested_robustness) const override;
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const override;
   media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
       const override;
   media::EmeFeatureSupport GetPersistentStateSupport() const override;
diff --git a/components/cdm/renderer/widevine_key_system_properties.cc b/components/cdm/renderer/widevine_key_system_properties.cc
index f3e9336..bf3da04 100644
--- a/components/cdm/renderer/widevine_key_system_properties.cc
+++ b/components/cdm/renderer/widevine_key_system_properties.cc
@@ -4,6 +4,7 @@
 
 #include "components/cdm/renderer/widevine_key_system_properties.h"
 
+#include "base/compiler_specific.h"
 #include "base/feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -111,7 +112,8 @@
 
 EmeConfigRule WidevineKeySystemProperties::GetRobustnessConfigRule(
     EmeMediaType media_type,
-    const std::string& requested_robustness) const {
+    const std::string& requested_robustness,
+    const bool* hw_secure_requirement) const {
   Robustness robustness = ConvertRobustness(requested_robustness);
   if (robustness == Robustness::INVALID)
     return EmeConfigRule::NOT_SUPPORTED;
@@ -137,10 +139,12 @@
     return EmeConfigRule::NOT_SUPPORTED;
   }
 
+  bool hw_secure_codecs_required =
+      hw_secure_requirement && *hw_secure_requirement;
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Hardware security requires HWDRM or remote attestation, both of these
   // require an identifier.
-  if (robustness >= Robustness::HW_SECURE_CRYPTO)
+  if (robustness >= Robustness::HW_SECURE_CRYPTO || hw_secure_codecs_required)
 #if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
     return EmeConfigRule::IDENTIFIER_AND_HW_SECURE_CODECS_REQUIRED;
 #else
@@ -158,19 +162,20 @@
   }
 #elif defined(OS_ANDROID)
   // On Android, require hardware secure codecs for SW_SECURE_DECODE and above.
-  if (robustness >= Robustness::SW_SECURE_DECODE) {
+  if (robustness >= Robustness::SW_SECURE_DECODE || hw_secure_codecs_required)
     return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
-  }
 #elif defined(OS_WIN)
   // On Windows, hardware security uses MediaFoundation-based CDM which requires
   // identifier and persistent state.
-  if (robustness >= Robustness::HW_SECURE_CRYPTO)
+  if (robustness >= Robustness::HW_SECURE_CRYPTO || hw_secure_codecs_required)
     return EmeConfigRule::IDENTIFIER_PERSISTENCE_AND_HW_SECURE_CODECS_REQUIRED;
 #else
   // On other platforms, require hardware secure codecs for HW_SECURE_CRYPTO and
   // above.
   if (robustness >= Robustness::HW_SECURE_CRYPTO)
     return EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
+
+  ALLOW_UNUSED_LOCAL(hw_secure_codecs_required);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
   return EmeConfigRule::SUPPORTED;
diff --git a/components/cdm/renderer/widevine_key_system_properties.h b/components/cdm/renderer/widevine_key_system_properties.h
index f6e53c09..6eed985 100644
--- a/components/cdm/renderer/widevine_key_system_properties.h
+++ b/components/cdm/renderer/widevine_key_system_properties.h
@@ -49,7 +49,8 @@
   media::SupportedCodecs GetSupportedHwSecureCodecs() const override;
   media::EmeConfigRule GetRobustnessConfigRule(
       media::EmeMediaType media_type,
-      const std::string& requested_robustness) const override;
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const override;
   media::EmeSessionTypeSupport GetPersistentLicenseSessionSupport()
       const override;
   media::EmeFeatureSupport GetPersistentStateSupport() const override;
diff --git a/components/guest_view/browser/guest_view_manager.cc b/components/guest_view/browser/guest_view_manager.cc
index 789421b..77ba8a04 100644
--- a/components/guest_view/browser/guest_view_manager.cc
+++ b/components/guest_view/browser/guest_view_manager.cc
@@ -237,6 +237,19 @@
   return nullptr;
 }
 
+void GuestViewManager::ForEachUnattachedGuest(
+    content::WebContents* owner_web_contents,
+    base::RepeatingCallback<void(content::WebContents*)> callback) {
+  for (const auto& guest : guest_web_contents_by_instance_id_) {
+    auto* guest_view = GuestViewBase::FromWebContents(guest.second);
+
+    if (guest_view->owner_web_contents() == owner_web_contents &&
+        !guest_view->attached()) {
+      callback.Run(guest_view->web_contents());
+    }
+  }
+}
+
 bool GuestViewManager::ForEachGuest(WebContents* owner_web_contents,
                                     const GuestCallback& callback) {
   for (const auto& guest : guest_web_contents_by_instance_id_) {
diff --git a/components/guest_view/browser/guest_view_manager.h b/components/guest_view/browser/guest_view_manager.h
index 6375b65..1947870 100644
--- a/components/guest_view/browser/guest_view_manager.h
+++ b/components/guest_view/browser/guest_view_manager.h
@@ -125,6 +125,9 @@
                                              int element_instance_id);
 
   // BrowserPluginGuestManager implementation.
+  void ForEachUnattachedGuest(
+      content::WebContents* owner_web_contents,
+      base::RepeatingCallback<void(content::WebContents*)> callback) override;
   bool ForEachGuest(content::WebContents* owner_web_contents,
                     const GuestCallback& callback) override;
   content::WebContents* GetFullPageGuest(
diff --git a/components/metrics/data_use_tracker.cc b/components/metrics/data_use_tracker.cc
index 9b98ad76..23eb81a7 100644
--- a/components/metrics/data_use_tracker.cc
+++ b/components/metrics/data_use_tracker.cc
@@ -9,6 +9,7 @@
 
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "components/metrics/metrics_pref_names.h"
 #include "components/prefs/scoped_user_pref_update.h"
@@ -139,7 +140,8 @@
     base::Time key_date;
     if (base::Time::FromUTCString(it.key().c_str(), &key_date) &&
         key_date > week_ago)
-      user_pref_new_dict.Set(it.key(), it.value().CreateDeepCopy());
+      user_pref_new_dict.Set(it.key(),
+                             base::Value::ToUniquePtrValue(it.value().Clone()));
   }
   local_state_->Set(pref_name, user_pref_new_dict);
 }
diff --git a/components/offline_pages/core/offline_page_feature.cc b/components/offline_pages/core/offline_page_feature.cc
index b582556..f291f46 100644
--- a/components/offline_pages/core/offline_page_feature.cc
+++ b/components/offline_pages/core/offline_page_feature.cc
@@ -23,9 +23,6 @@
 const base::Feature kOffliningRecentPagesFeature{
     "OfflineRecentPages", base::FEATURE_ENABLED_BY_DEFAULT};
 
-const base::Feature kOfflinePagesCTFeature{"OfflinePagesCT",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-
 const base::Feature kOfflinePagesLivePageSharingFeature{
     "OfflinePagesLivePageSharing", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -59,10 +56,6 @@
   return base::FeatureList::IsEnabled(kOffliningRecentPagesFeature);
 }
 
-bool IsOfflinePagesCTEnabled() {
-  return base::FeatureList::IsEnabled(kOfflinePagesCTFeature);
-}
-
 bool IsOfflinePagesLivePageSharingEnabled() {
   return base::FeatureList::IsEnabled(kOfflinePagesLivePageSharingFeature);
 }
diff --git a/components/offline_pages/core/offline_page_feature.h b/components/offline_pages/core/offline_page_feature.h
index 53d8dd67..04cfb57 100644
--- a/components/offline_pages/core/offline_page_feature.h
+++ b/components/offline_pages/core/offline_page_feature.h
@@ -11,7 +11,6 @@
 namespace offline_pages {
 
 extern const base::Feature kOffliningRecentPagesFeature;
-extern const base::Feature kOfflinePagesCTFeature;
 extern const base::Feature kOfflinePagesLivePageSharingFeature;
 extern const base::Feature kBackgroundLoaderForDownloadsFeature;
 extern const base::Feature kPrefetchingOfflinePagesFeature;
@@ -31,9 +30,6 @@
 // Returns true if offlining of recent pages (aka 'Last N pages') is enabled.
 bool IsOffliningRecentPagesEnabled();
 
-// Returns true if offline CT features are enabled.  See crbug.com/620421.
-bool IsOfflinePagesCTEnabled();
-
 // Returns true if live page sharing of offline page is enabled.
 bool IsOfflinePagesLivePageSharingEnabled();
 
diff --git a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
index 4f9b7a5..e8732dd 100644
--- a/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
+++ b/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/AutocompleteMatch.java
@@ -219,6 +219,11 @@
         mNativeMatch = 0;
     }
 
+    @CalledByNative
+    private void setDestinationUrl(GURL url) {
+        mUrl = url;
+    }
+
     public int getType() {
         return mType;
     }
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index c162ff5..63492485 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -669,6 +669,9 @@
 
   match->destination_url = GURL(template_url->url_ref().ReplaceSearchTerms(
       search_terms_args, template_url_service_->search_terms_data()));
+#if defined(OS_ANDROID)
+  match->UpdateJavaDestinationUrl();
+#endif
 }
 
 void AutocompleteController::InlineTailPrefixes() {
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h
index a19c755..7dfc234 100644
--- a/components/omnibox/browser/autocomplete_match.h
+++ b/components/omnibox/browser/autocomplete_match.h
@@ -226,6 +226,8 @@
 
   // Update the Java object with clipboard content.
   void UpdateClipboardContent(JNIEnv* env);
+  // Update the Java object with new destination URL.
+  void UpdateJavaDestinationUrl();
 #endif
 
 #if (!defined(OS_ANDROID) || BUILDFLAG(ENABLE_VR)) && !defined(OS_IOS)
diff --git a/components/omnibox/browser/autocomplete_match_android.cc b/components/omnibox/browser/autocomplete_match_android.cc
index 98f0896..34c1b480 100644
--- a/components/omnibox/browser/autocomplete_match_android.cc
+++ b/components/omnibox/browser/autocomplete_match_android.cc
@@ -174,3 +174,12 @@
       j_post_content_type, j_post_content,
       ToJavaByteArray(env, clipboard_image_data));
 }
+
+void AutocompleteMatch::UpdateJavaDestinationUrl() {
+  if (java_match_) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_AutocompleteMatch_setDestinationUrl(
+        env, *java_match_,
+        url::GURLAndroid::FromNativeGURL(env, destination_url));
+  }
+}
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 1891231a..414917a5 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -1694,11 +1694,9 @@
 bool OmniboxEditModel::AllowKeywordSpaceTriggering() const {
   PrefService* pref_service =
       autocomplete_controller()->autocomplete_provider_client()->GetPrefs();
-  return (OmniboxFieldTrial::GetKeywordSpaceTrigger() !=
-          OmniboxFieldTrial::SPACE_TRIGGERING_DISABLED) &&
-         (!base::FeatureList::IsEnabled(
-              omnibox::kKeywordSpaceTriggeringSetting) ||
-          pref_service->GetBoolean(omnibox::kKeywordSpaceTriggeringEnabled));
+  return !base::FeatureList::IsEnabled(
+             omnibox::kKeywordSpaceTriggeringSetting) ||
+         pref_service->GetBoolean(omnibox::kKeywordSpaceTriggeringEnabled);
 }
 
 bool OmniboxEditModel::MaybeAcceptKeywordBySpace(
@@ -1707,14 +1705,6 @@
     return false;
 
   size_t keyword_length = new_text.length() - 1;
-  if (OmniboxFieldTrial::GetKeywordSpaceTrigger() ==
-      OmniboxFieldTrial::DOUBLE_SPACE_TRIGGERS_KEYWORD) {
-    // Check if the second character after a keyword is a space.  The first
-    // character is checked as usual below.
-    if (!IsSpaceCharForAcceptingKeyword(new_text[keyword_length]))
-      return false;
-    keyword_length--;
-  }
   return is_keyword_hint_ && (keyword_.length() == keyword_length) &&
          IsSpaceCharForAcceptingKeyword(new_text[keyword_length]) &&
          !new_text.compare(0, keyword_length, keyword_, 0, keyword_length) &&
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 8ed3007..b4aa194 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -863,18 +863,6 @@
   return base::FeatureList::IsEnabled(omnibox::kDisableCGIParamMatching);
 }
 
-OmniboxFieldTrial::KeywordSpaceTrigger
-OmniboxFieldTrial::GetKeywordSpaceTrigger() {
-  if (base::GetFieldTrialParamByFeatureAsBool(
-          omnibox::kKeywordSpaceTriggering,
-          kKeywordSpaceTriggeringDoubleSpaceParam, false))
-    return DOUBLE_SPACE_TRIGGERS_KEYWORD;
-  else if (base::FeatureList::IsEnabled(omnibox::kKeywordSpaceTriggering))
-    return SINGLE_SPACE_TRIGGERS_KEYWORD;
-  else
-    return SPACE_TRIGGERING_DISABLED;
-}
-
 const char OmniboxFieldTrial::kBundledExperimentFieldTrialName[] =
     "OmniboxBundledExperimentV1";
 const char OmniboxFieldTrial::kDisableProvidersRule[] = "DisableProviders";
@@ -1029,9 +1017,6 @@
 extern const char OmniboxFieldTrial::kBookmarkPathsUiDynamicReplaceUrl[] =
     "OmniboxBookmarkPathsUiDynamicReplaceUrl";
 
-extern const char OmniboxFieldTrial::kKeywordSpaceTriggeringDoubleSpaceParam[] =
-    "KeywordSpaceTriggeringDoubleSpace";
-
 std::string OmniboxFieldTrial::internal::GetValueForRuleInContext(
     const std::string& rule,
     OmniboxEventProto::PageClassification page_classification) {
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index 1a5db07..ab43f61 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -429,16 +429,6 @@
 // suggestions.
 bool ShouldDisableCGIParamMatching();
 
-enum KeywordSpaceTrigger {
-  SPACE_TRIGGERING_DISABLED = 0,
-  SINGLE_SPACE_TRIGGERS_KEYWORD = 1,
-  DOUBLE_SPACE_TRIGGERS_KEYWORD = 2,
-};
-
-// Returns whether space triggering is disabled, triggered by single space
-// (default), or double space (double space keyword triggering is enabled).
-KeywordSpaceTrigger GetKeywordSpaceTrigger();
-
 // ---------------------------------------------------------
 // Clipboard URL suggestions:
 
@@ -576,9 +566,6 @@
 extern const char kBookmarkPathsUiAppendAfterTitle[];
 extern const char kBookmarkPathsUiDynamicReplaceUrl[];
 
-// Parameter names used for scoped search/keyword mode experiments.
-extern const char kKeywordSpaceTriggeringDoubleSpaceParam[];
-
 namespace internal {
 // The bundled omnibox experiment comes with a set of parameters
 // (key-value pairs).  Each key indicates a certain rule that applies in
diff --git a/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc b/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc
index 9d2dabc..1db134a 100644
--- a/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc
+++ b/components/omnibox/browser/omnibox_pedal_implementations_unittest.cc
@@ -28,7 +28,9 @@
 
 TEST_F(OmniboxPedalImplementationsTest, PedalClearBrowsingDataExecutes) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(omnibox::kOmniboxPedalsBatch2);
+  feature_list.InitWithFeatures(
+      {omnibox::kOmniboxPedalsBatch2, omnibox::kOmniboxPedalsBatch2NonEnglish},
+      {});
   MockAutocompleteProviderClient client;
   OmniboxPedalProvider provider(client, true);
   const OmniboxPedal* pedal = provider.FindPedalMatch(u"clear browser data");
@@ -47,7 +49,9 @@
 TEST_F(OmniboxPedalImplementationsTest,
        UnorderedSynonymExpressionsAreConceptMatches) {
   base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(omnibox::kOmniboxPedalsBatch2);
+  feature_list.InitWithFeatures(
+      {omnibox::kOmniboxPedalsBatch2, omnibox::kOmniboxPedalsBatch2NonEnglish},
+      {});
   const std::vector<std::vector<const char*>> literal_concept_expressions = {
       // clang-format off
       // Note: The lists below are auto-generated from raw synonym group data.
diff --git a/components/omnibox/browser/omnibox_pedal_provider.cc b/components/omnibox/browser/omnibox_pedal_provider.cc
index 9c868d6..62d154fa 100644
--- a/components/omnibox/browser/omnibox_pedal_provider.cc
+++ b/components/omnibox/browser/omnibox_pedal_provider.cc
@@ -8,6 +8,7 @@
 
 #include "base/i18n/case_conversion.h"
 #include "base/i18n/char_iterator.h"
+#include "base/i18n/rtl.h"
 #include "base/json/json_reader.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/stl_util.h"
@@ -185,6 +186,12 @@
 }
 
 void OmniboxPedalProvider::LoadPedalConcepts() {
+  // The locale is a two-letter language code, possibly followed by a dash and
+  // country code. English locales include "en", "en-US", and "en-GB" while
+  // non-English locales never start with "en".
+  const bool locale_is_english =
+      base::i18n::GetConfiguredLocale().substr(0, 2) == "en";
+
   // Load concept data then parse to base::Value in order to construct Pedals.
   std::string uncompressed_data =
       ui::ResourceBundle::GetSharedInstance().LoadLocalizedResourceString(
@@ -225,10 +232,13 @@
   for (const auto& pedal_value : concept_data->FindKey("pedals")->GetList()) {
     DCHECK(pedal_value.is_dict());
     const int id = pedal_value.FindIntKey("id").value();
-    // These IDs are the first and last for batch 2.
+    // These IDs are the first and last for batch 2. Skip loading if batch 2 is
+    // not enabled for the current locale.
     if (id >= static_cast<int>(OmniboxPedalId::RUN_CHROME_SAFETY_CHECK) &&
         id <= static_cast<int>(OmniboxPedalId::CHANGE_GOOGLE_PASSWORD) &&
-        !OmniboxFieldTrial::IsPedalsBatch2Enabled()) {
+        !(OmniboxFieldTrial::IsPedalsBatch2Enabled() &&
+          (locale_is_english ||
+           OmniboxFieldTrial::IsPedalsBatch2NonEnglishEnabled()))) {
       continue;
     }
     const auto pedal_iter = pedals_.find(static_cast<OmniboxPedalId>(id));
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index 769fe77f..6ea76f2 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -296,11 +296,6 @@
 const base::Feature kOmniboxAssistantVoiceSearch{
     "OmniboxAssistantVoiceSearch", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Feature used to control whether space, double space, or neither triggers
-// keyword mode. When disabled, space triggering is disabled.
-const base::Feature kKeywordSpaceTriggering{"OmniboxKeywordSpaceTriggering",
-                                            base::FEATURE_ENABLED_BY_DEFAULT};
-
 // When enabled, a setting is added to chrome://settings/searchEngines to
 // control whether <space> can be used to trigger keyword mode.
 const base::Feature kKeywordSpaceTriggeringSetting{
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 87a313e..8f4fd3f 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -79,7 +79,6 @@
 // popup).
 extern const base::Feature kIntranetRedirectBehaviorPolicyRollout;
 extern const base::Feature kOmniboxAssistantVoiceSearch;
-extern const base::Feature kKeywordSpaceTriggering;
 extern const base::Feature kKeywordSpaceTriggeringSetting;
 // Experiment to introduce new security indicators for HTTPS.
 extern const base::Feature kUpdatedConnectionSecurityIndicators;
diff --git a/components/os_crypt/BUILD.gn b/components/os_crypt/BUILD.gn
index 573aff57..bd37641 100644
--- a/components/os_crypt/BUILD.gn
+++ b/components/os_crypt/BUILD.gn
@@ -52,6 +52,10 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
   defines = [ "IS_OS_CRYPT_IMPL" ]
 
+  if (allow_runtime_configurable_key_storage) {
+    defines += [ "ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE" ]
+  }
+
   if ((is_posix || is_fuchsia) && !is_apple &&
       (!(is_linux || is_chromeos_lacros) || is_chromecast)) {
     sources += [ "os_crypt_posix.cc" ]
diff --git a/components/os_crypt/features.gni b/components/os_crypt/features.gni
index a145e05..73b6e870 100644
--- a/components/os_crypt/features.gni
+++ b/components/os_crypt/features.gni
@@ -9,4 +9,10 @@
   # Whether to use libgnome-keyring (deprecated by libsecret).
   # See http://crbug.com/466975 and http://crbug.com/355223.
   use_gnome_keyring = (is_linux || is_chromeos_lacros) && use_glib
+
+  # Whether to make account and service names for the crypto key storage
+  # configurable at runtime for embedders.
+  #
+  # Currently only has an effect on macOS via KeychainPassword
+  allow_runtime_configurable_key_storage = false
 }
diff --git a/components/os_crypt/keychain_password_mac.h b/components/os_crypt/keychain_password_mac.h
index 6fda024..40b2522 100644
--- a/components/os_crypt/keychain_password_mac.h
+++ b/components/os_crypt/keychain_password_mac.h
@@ -9,6 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/no_destructor.h"
 
 namespace crypto {
 class AppleKeychain;
@@ -16,6 +17,12 @@
 
 class COMPONENT_EXPORT(OS_CRYPT) KeychainPassword {
  public:
+#if defined(ALLOW_RUNTIME_CONFIGURABLE_KEY_STORAGE)
+  using KeychainNameType = base::NoDestructor<std::string>;
+#else
+  using KeychainNameType = const base::NoDestructor<std::string>;
+#endif
+
   KeychainPassword(const crypto::AppleKeychain& keychain);
   ~KeychainPassword();
 
@@ -28,8 +35,8 @@
   std::string GetPassword() const;
 
   // The service and account names used in Chrome's Safe Storage keychain item.
-  static COMPONENT_EXPORT(OS_CRYPT) const char service_name[];
-  static COMPONENT_EXPORT(OS_CRYPT) const char account_name[];
+  static COMPONENT_EXPORT(OS_CRYPT) KeychainNameType service_name;
+  static COMPONENT_EXPORT(OS_CRYPT) KeychainNameType account_name;
 
  private:
   const crypto::AppleKeychain& keychain_;
diff --git a/components/os_crypt/keychain_password_mac.mm b/components/os_crypt/keychain_password_mac.mm
index 6654c46..f8973f5 100644
--- a/components/os_crypt/keychain_password_mac.mm
+++ b/components/os_crypt/keychain_password_mac.mm
@@ -48,11 +48,11 @@
 // the encryption keyword.  So as to not lose encrypted data when system
 // locale changes we DO NOT LOCALIZE.
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-const char KeychainPassword::service_name[] = "Chrome Safe Storage";
-const char KeychainPassword::account_name[] = "Chrome";
+KeychainPassword::KeychainNameType KeychainPassword::service_name("Chrome Safe Storage");
+KeychainPassword::KeychainNameType KeychainPassword::account_name("Chrome");
 #else
-const char KeychainPassword::service_name[] = "Chromium Safe Storage";
-const char KeychainPassword::account_name[] = "Chromium";
+KeychainPassword::KeychainNameType KeychainPassword::service_name("Chromium Safe Storage");
+KeychainPassword::KeychainNameType KeychainPassword::account_name("Chromium");
 #endif
 
 KeychainPassword::KeychainPassword(const AppleKeychain& keychain)
@@ -64,8 +64,9 @@
   UInt32 password_length = 0;
   void* password_data = nullptr;
   OSStatus error = keychain_.FindGenericPassword(
-      strlen(service_name), service_name, strlen(account_name), account_name,
-      &password_length, &password_data, nullptr);
+      service_name->size(), service_name->c_str(),
+      account_name->size(), account_name->c_str(), &password_length,
+      &password_data, nullptr);
 
   if (error == noErr) {
     std::string password =
@@ -75,8 +76,8 @@
   }
 
   if (error == errSecItemNotFound) {
-    std::string password =
-        AddRandomPasswordToKeychain(keychain_, service_name, account_name);
+    std::string password = AddRandomPasswordToKeychain(
+        keychain_, *service_name, *account_name);
     return password;
   }
 
diff --git a/components/printing/common/print.mojom b/components/printing/common/print.mojom
index 4fde003..f4fa2ff 100644
--- a/components/printing/common/print.mojom
+++ b/components/printing/common/print.mojom
@@ -276,6 +276,7 @@
   // Tells the RenderFrame to switch the CSS to print media type, render every
   // requested page using the print preview document's frame/node, and then
   // switch the CSS back to display media type.
+  [EnableIf=enable_print_preview]
   PrintForSystemDialog();
 
   // Tells the RenderFrame to initiate print preview for the entire document.
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index b888b7c3..fca930aa 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1230,6 +1230,7 @@
   // just return.
 }
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 void PrintRenderFrameHelper::PrintForSystemDialog() {
   ScopedIPC scoped_ipc(weak_ptr_factory_.GetWeakPtr());
   if (ipc_nesting_level_ > 1)
@@ -1259,7 +1260,6 @@
   // just return.
 }
 
-#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
 void PrintRenderFrameHelper::SetPrintPreviewUI(
     mojo::PendingAssociatedRemote<mojom::PrintPreviewUI> preview) {
   preview_ui_.Bind(std::move(preview));
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index ae14b4e..6a1fa397 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -232,8 +232,8 @@
 
   // printing::mojom::PrintRenderFrame:
   void PrintRequestedPages() override;
-  void PrintForSystemDialog() override;
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+  void PrintForSystemDialog() override;
   void SetPrintPreviewUI(
       mojo::PendingAssociatedRemote<mojom::PrintPreviewUI> preview) override;
   void InitiatePrintPreview(
diff --git a/components/variations/child_process_field_trial_syncer.cc b/components/variations/child_process_field_trial_syncer.cc
index 6825bdd..eac36795 100644
--- a/components/variations/child_process_field_trial_syncer.cc
+++ b/components/variations/child_process_field_trial_syncer.cc
@@ -13,26 +13,49 @@
 
 namespace variations {
 
+namespace {
+
+ChildProcessFieldTrialSyncer* g_instance = nullptr;
+
+}  // namespace
+
+// static
+ChildProcessFieldTrialSyncer* ChildProcessFieldTrialSyncer::CreateInstance(
+    FieldTrialActivatedCallback activated_callback) {
+  DCHECK(!g_instance);
+  g_instance = new ChildProcessFieldTrialSyncer(std::move(activated_callback));
+  g_instance->Init();
+  return g_instance;
+}
+
+// static
+void ChildProcessFieldTrialSyncer::DeleteInstanceForTesting() {
+  DCHECK(g_instance);
+  delete g_instance;
+  g_instance = nullptr;
+  // Revert the effect of calling variations::InitCrashKeys() in Init().
+  ClearCrashKeysInstanceForTesting();  // IN-TEST
+}
+
 ChildProcessFieldTrialSyncer::ChildProcessFieldTrialSyncer(
-    base::FieldTrialList::Observer* observer)
-    : observer_(observer) {}
+    FieldTrialActivatedCallback activated_callback)
+    : activated_callback_(std::move(activated_callback)) {}
 
-ChildProcessFieldTrialSyncer::~ChildProcessFieldTrialSyncer() {}
+ChildProcessFieldTrialSyncer::~ChildProcessFieldTrialSyncer() = default;
 
-void ChildProcessFieldTrialSyncer::InitFieldTrialObserving(
-    const base::CommandLine& command_line) {
+void ChildProcessFieldTrialSyncer::Init() {
   // Set up initial set of crash dump data for field trials in this process.
   variations::InitCrashKeys();
 
   // Listen for field trial activations to report them to the browser.
-  base::FieldTrialList::AddObserver(observer_);
+  base::FieldTrialList::AddObserver(this);
 
   // Some field trials may have been activated before this point. Notify the
   // browser of these activations now. To detect these, take the set difference
   // of currently active trials with the initially active trials.
   base::FieldTrial::ActiveGroups initially_active_trials;
-  base::FieldTrialList::GetInitiallyActiveFieldTrials(command_line,
-                                                      &initially_active_trials);
+  base::FieldTrialList::GetInitiallyActiveFieldTrials(
+      *base::CommandLine::ForCurrentProcess(), &initially_active_trials);
   std::set<std::string> initially_active_trials_set;
   for (const auto& entry : initially_active_trials) {
     initially_active_trials_set.insert(std::move(entry.trial_name));
@@ -42,18 +65,34 @@
   base::FieldTrialList::GetActiveFieldTrialGroups(&current_active_trials);
   for (const auto& trial : current_active_trials) {
     if (!base::Contains(initially_active_trials_set, trial.trial_name))
-      observer_->OnFieldTrialGroupFinalized(trial.trial_name, trial.group_name);
+      activated_callback_.Run(trial.trial_name);
   }
 }
 
-void ChildProcessFieldTrialSyncer::OnSetFieldTrialGroup(
+void ChildProcessFieldTrialSyncer::SetFieldTrialGroupFromBrowser(
     const std::string& trial_name,
     const std::string& group_name) {
+  DCHECK(!in_set_field_trial_group_from_browser_.Get());
+  in_set_field_trial_group_from_browser_.Set(true);
+
   base::FieldTrial* trial =
       base::FieldTrialList::CreateFieldTrial(trial_name, group_name);
   // Ensure the trial is marked as "used" by calling group() on it if it is
   // marked as activated.
   trial->group();
+
+  in_set_field_trial_group_from_browser_.Set(false);
+}
+
+void ChildProcessFieldTrialSyncer::OnFieldTrialGroupFinalized(
+    const std::string& trial_name,
+    const std::string& group_name) {
+  // It is not necessary to notify the browser if this is invoked from
+  // SetFieldTrialGroupFromBrowser().
+  if (in_set_field_trial_group_from_browser_.Get())
+    return;
+
+  activated_callback_.Run(trial_name);
 }
 
 }  // namespace variations
diff --git a/components/variations/child_process_field_trial_syncer.h b/components/variations/child_process_field_trial_syncer.h
index e16e3fe..ed6b570 100644
--- a/components/variations/child_process_field_trial_syncer.h
+++ b/components/variations/child_process_field_trial_syncer.h
@@ -1,3 +1,4 @@
+
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
@@ -7,40 +8,61 @@
 
 #include <string>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
-
-namespace base {
-class CommandLine;
-}
+#include "base/threading/thread_local.h"
 
 namespace variations {
 
-// ChildProcessFieldTrialSyncer is a utility class that's responsible for
-// syncing the "activated" state of field trials between browser and child
+// Syncs the "activated" state of field trials between browser and child
 // processes. Specifically, when a field trial is activated in the browser, it
-// also activates it in the child process and when a field trial is activated
-// in the child process, it notifies the browser process to activate it.
-class ChildProcessFieldTrialSyncer {
+// also activates it in the child process and when a field trial is activated in
+// the child process, it notifies the browser process to activate it.
+class ChildProcessFieldTrialSyncer : public base::FieldTrialList::Observer {
  public:
-  // ChildProcessFieldTrialSyncer constructor taking an externally owned
-  // |observer| param that's responsible for sending IPCs to the browser process
-  // when a trial is activated. The |observer| must outlive this object.
-  explicit ChildProcessFieldTrialSyncer(
-      base::FieldTrialList::Observer* observer);
-  ~ChildProcessFieldTrialSyncer();
+  using FieldTrialActivatedCallback =
+      base::RepeatingCallback<void(const std::string& trial_name)>;
 
-  // Initializes field trial state change observation and notifies the browser
-  // of any field trials that might have already been activated.
-  void InitFieldTrialObserving(const base::CommandLine& command_line);
+  // Creates and returns the global ChildProcessFieldTrialSyncer instance for
+  // this process. Immediately invokes |activated_callback| for each currently
+  // active field trial. Then, |activated_callback| is invoked as field trial
+  // are activated. |activated_callback| may be invoked from any sequence and
+  // must therefore be thread-safe. CreateInstance() must not be called
+  // concurrently with activating a field trial. The created instance is never
+  // destroyed because it would be difficult to ensure that no field trial is
+  // activated concurrently with unregistering it as an observer of
+  // FieldTrialList (see FieldTrialList::RemoveObserver).
+  static ChildProcessFieldTrialSyncer* CreateInstance(
+      FieldTrialActivatedCallback activated_callback);
 
-  // Handler for messages from the browser process notifying the child process
-  // that a field trial was activated.
-  void OnSetFieldTrialGroup(const std::string& trial_name,
-                            const std::string& group_name);
+  // Deletes the global ChildProcessFieldTrialSyncer instance.
+  static void DeleteInstanceForTesting();
+
+  // Must be invoked when the browser process notifies this child process that a
+  // field trial was activated.
+  void SetFieldTrialGroupFromBrowser(const std::string& trial_name,
+                                     const std::string& group_name);
 
  private:
-  base::FieldTrialList::Observer* const observer_;
+  explicit ChildProcessFieldTrialSyncer(
+      FieldTrialActivatedCallback activated_callback);
+  ~ChildProcessFieldTrialSyncer() override;
+
+  // Initializes field trial state change observation and invokes |callback_|
+  // for any field trials that might have already been activated.
+  void Init();
+
+  // base::FieldTrialList::Observer:
+  void OnFieldTrialGroupFinalized(const std::string& trial_name,
+                                  const std::string& group_name) override;
+
+  // Whether SetFieldTrialGroupFromBrowser() is being called on the current
+  // thread.
+  base::ThreadLocalBoolean in_set_field_trial_group_from_browser_;
+
+  // Callback to invoke when a field trial is activated.
+  const FieldTrialActivatedCallback activated_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(ChildProcessFieldTrialSyncer);
 };
diff --git a/components/variations/child_process_field_trial_syncer_unittest.cc b/components/variations/child_process_field_trial_syncer_unittest.cc
index b2b7ee9..bab5252 100644
--- a/components/variations/child_process_field_trial_syncer_unittest.cc
+++ b/components/variations/child_process_field_trial_syncer_unittest.cc
@@ -5,53 +5,18 @@
 #include "components/variations/child_process_field_trial_syncer.h"
 
 #include <string>
-#include <utility>
 #include <vector>
 
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/metrics/field_trial.h"
-#include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/task_environment.h"
-#include "components/variations/variations_crash_keys.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace variations {
 
-namespace {
-
-// TestFieldTrialObserver to listen to be notified by the child process syncer.
-class TestFieldTrialObserver : public base::FieldTrialList::Observer {
- public:
-  TestFieldTrialObserver() {}
-  ~TestFieldTrialObserver() override { ClearCrashKeysInstanceForTesting(); }
-
-  // base::FieldTrialList::Observer:
-  void OnFieldTrialGroupFinalized(const std::string& trial_name,
-                                  const std::string& group_name) override {
-    observed_entries_.push_back(std::make_pair(trial_name, group_name));
-  }
-
-  size_t observed_entries_count() const { return observed_entries_.size(); }
-
-  std::pair<std::string, std::string> get_observed_entry(int i) const {
-    return observed_entries_[i];
-  }
-
- private:
-  std::vector<std::pair<std::string, std::string>> observed_entries_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestFieldTrialObserver);
-};
-
-// Needed because make_pair("a", "b") doesn't convert to std::string pair.
-std::pair<std::string, std::string> MakeStringPair(const std::string& a,
-                                                   const std::string& b) {
-  return std::make_pair(a, b);
-}
-
-}  // namespace
-
 TEST(ChildProcessFieldTrialSyncerTest, FieldTrialState) {
   base::test::TaskEnvironment task_environment;
 
@@ -75,24 +40,24 @@
   // Active trial 2 before creating the syncer.
   trial2->group();
 
-  TestFieldTrialObserver observer;
-  ChildProcessFieldTrialSyncer syncer(&observer);
-  syncer.InitFieldTrialObserving(*base::CommandLine::ForCurrentProcess());
+  std::vector<std::string> observed_trial_names;
+  auto callback =
+      base::BindLambdaForTesting([&](const std::string& trial_name) {
+        observed_trial_names.push_back(trial_name);
+      });
 
-  // The observer should be notified of activated entries that were not activate
+  ChildProcessFieldTrialSyncer::CreateInstance(callback);
+
+  // The callback should be invoked for activated trials that were not specified
   // on the command line. In this case, trial 2. (Trial 1 was already active via
   // command line and so its state shouldn't be notified.)
-  ASSERT_EQ(1U, observer.observed_entries_count());
-  EXPECT_EQ(MakeStringPair("B", "G2"), observer.get_observed_entry(0));
+  EXPECT_THAT(observed_trial_names, testing::ElementsAre("B"));
 
   // Now, activate trial 3, which should also get reflected.
   trial3->group();
-  // Notifications from field trial activation actually happen via posted tasks,
-  // so invoke the run loop.
-  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(observed_trial_names, testing::ElementsAre("B", "C"));
 
-  ASSERT_EQ(2U, observer.observed_entries_count());
-  EXPECT_EQ(MakeStringPair("C", "G3"), observer.get_observed_entry(1));
+  ChildProcessFieldTrialSyncer::DeleteInstanceForTesting();
 }
 
 }  // namespace variations
diff --git a/components/variations/variations_crash_keys.cc b/components/variations/variations_crash_keys.cc
index 9ff3f31..e0df470 100644
--- a/components/variations/variations_crash_keys.cc
+++ b/components/variations/variations_crash_keys.cc
@@ -104,11 +104,11 @@
   UpdateCrashKeys();
 
   ui_thread_task_runner_ = base::SequencedTaskRunnerHandle::Get();
-  base::FieldTrialList::SetSynchronousObserver(this);
+  base::FieldTrialList::AddObserver(this);
 }
 
 VariationsCrashKeys::~VariationsCrashKeys() {
-  base::FieldTrialList::RemoveSynchronousObserver(this);
+  base::FieldTrialList::RemoveObserver(this);
   g_num_variations_crash_key.Clear();
   g_variations_crash_key.Clear();
 }
diff --git a/components/variations/variations_ids_provider.cc b/components/variations/variations_ids_provider.cc
index 7237221..f04b45d63 100644
--- a/components/variations/variations_ids_provider.cc
+++ b/components/variations/variations_ids_provider.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 
 #include "base/base64.h"
-#include "base/memory/singleton.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -44,7 +43,8 @@
 
 // static
 VariationsIdsProvider* VariationsIdsProvider::GetInstance() {
-  return base::Singleton<VariationsIdsProvider>::get();
+  static base::NoDestructor<VariationsIdsProvider> instance;
+  return instance.get();
 }
 
 variations::mojom::VariationsHeadersPtr
diff --git a/components/variations/variations_ids_provider.h b/components/variations/variations_ids_provider.h
index 2c63f04..273407b 100644
--- a/components/variations/variations_ids_provider.h
+++ b/components/variations/variations_ids_provider.h
@@ -14,6 +14,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
+#include "base/no_destructor.h"
 #include "base/observer_list.h"
 #include "base/synchronization/lock.h"
 #include "components/variations/proto/study.pb.h"
@@ -21,11 +22,6 @@
 #include "components/variations/variations.mojom.h"
 #include "components/variations/variations_associated_data.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}
-
 namespace variations {
 class VariationsClient;
 
@@ -131,7 +127,7 @@
   void ResetForTesting();
 
  private:
-  friend struct base::DefaultSingletonTraits<VariationsIdsProvider>;
+  friend class base::NoDestructor<VariationsIdsProvider>;
 
   typedef std::pair<VariationID, IDCollectionKey> VariationIDEntry;
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index e8c0651..849f9eff 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -2947,6 +2947,10 @@
   RunRegressionTest(FILE_PATH_LITERAL("ignored-crash.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, MissingParent) {
+  RunRegressionTest(FILE_PATH_LITERAL("missing-parent.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, RemovePseudoContent) {
   RunRegressionTest(FILE_PATH_LITERAL("remove-pseudo-content.html"));
 }
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index fc0bba7..cd537fb8 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -1454,6 +1454,112 @@
   ExpectCached(*delete_observers[3], /*cached=*/true, /*backgrounded=*/false);
 }
 
+// Tests that RenderFrameHost::ForEachFrame behaves correctly when bfcached.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, ForEachFrame) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
+  GURL url_e(embedded_test_server()->GetURL("e.com", "/title1.html"));
+
+  std::vector<RenderFrameDeletedObserver*> rfh_observers;
+
+  // 1) Navigate to a(b(c),d).
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_d = rfh_a->child_at(1)->current_frame_host();
+  RenderFrameDeletedObserver a_observer(rfh_a), b_observer(rfh_b),
+      c_observer(rfh_c), d_observer(rfh_d);
+  rfh_observers.insert(rfh_observers.end(),
+                       {&a_observer, &b_observer, &c_observer, &d_observer});
+
+  // Ensure the visited frames are what we would expect for the page before
+  // entering bfcache.
+  EXPECT_THAT(CollectAllFrames(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
+
+  // 2) Navigate to e.
+  EXPECT_TRUE(NavigateToURL(shell(), url_e));
+  RenderFrameHostImpl* rfh_e = current_frame_host();
+  RenderFrameDeletedObserver e_observer(rfh_e);
+  rfh_observers.push_back(&e_observer);
+  ASSERT_THAT(rfh_observers, Each(Not(Deleted())));
+  EXPECT_THAT(Elements({rfh_a, rfh_b, rfh_c, rfh_d}),
+              Each(InBackForwardCache()));
+  EXPECT_THAT(rfh_e, Not(InBackForwardCache()));
+
+  // When starting iteration from the primary frame, we shouldn't see any of the
+  // frames in bfcache.
+  EXPECT_THAT(CollectAllFrames(rfh_e), testing::ElementsAre(rfh_e));
+
+  // When starting iteration from a bfcached RFH, we should see the frame itself
+  // and its descendants in breadth first order.
+  EXPECT_THAT(CollectAllFrames(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
+
+  // Ensure that starting iteration from a subframe of a bfcached frame also
+  // works.
+  EXPECT_THAT(CollectAllFrames(rfh_b), testing::ElementsAre(rfh_b, rfh_c));
+}
+
+// Tests that RenderFrameHostImpl::ForEachFrameIncludingSpeculative behaves
+// correctly when a FrameTreeNode has both a speculative RFH and a bfcached
+// RFH.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
+                       ForEachFrameWithSpeculative) {
+  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
+
+  std::vector<RenderFrameDeletedObserver*> rfh_observers;
+
+  // 1) Navigate to a.
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = current_frame_host();
+  RenderFrameDeletedObserver a_observer(rfh_a);
+  rfh_observers.push_back(&a_observer);
+
+  // 2) Navigate to b.
+  EXPECT_TRUE(NavigateToURL(shell(), url_b));
+  RenderFrameHostImpl* rfh_b = current_frame_host();
+  RenderFrameDeletedObserver b_observer(rfh_b);
+  rfh_observers.push_back(&b_observer);
+  ASSERT_THAT(rfh_observers, Each(Not(Deleted())));
+
+  // 3) Begin navigation to c.
+  TestNavigationManager nav_manager(web_contents(), url_c);
+  shell()->LoadURL(url_c);
+  ASSERT_TRUE(nav_manager.WaitForRequestStart());
+
+  RenderFrameHostImpl* rfh_c =
+      rfh_b->frame_tree_node()->render_manager()->speculative_frame_host();
+  ASSERT_TRUE(rfh_c);
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache,
+            rfh_a->lifecycle_state());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
+            rfh_b->lifecycle_state());
+  EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
+            rfh_c->lifecycle_state());
+
+  // When starting iteration from the bfcached RFH, we should not see the
+  // speculative RFH.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_a),
+              testing::ElementsAre(rfh_a));
+
+  // When starting iteration from the primary frame, we shouldn't see the
+  // bfcached RFH, but we should see the speculative RFH.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_b),
+              testing::UnorderedElementsAre(rfh_b, rfh_c));
+
+  // When starting iteration from the speculative RFH, we should only see
+  // the speculative RFH. In particular, we should not see the bfcached RFH.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_c),
+              testing::ElementsAre(rfh_c));
+}
+
 // Similar to BackForwardCacheBrowserTest.SubframeSurviveCache*
 // Test case: a1(b2) -> c3 -> a1(b2)
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, SubframeSurviveCache1) {
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index eea94ee1..b8e1b64 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1044,6 +1044,9 @@
   if (RenderProcessHost::run_renderer_in_process())
     RenderProcessHostImpl::ShutDownInProcessRenderer();
 
+  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI))
+    BrowserProcessSubThread::ProcessHostCleanUp();
+
   if (parts_) {
     TRACE_EVENT0("shutdown",
                  "BrowserMainLoop::Subsystem:PostMainMessageLoopRun");
@@ -1185,7 +1188,7 @@
 
   HistogramSynchronizer::GetInstance();
 
-  field_trial_synchronizer_ = base::MakeRefCounted<FieldTrialSynchronizer>();
+  FieldTrialSynchronizer::CreateInstance();
 
   // cc assumes a single client name for metrics in a process, which is
   // is inconsistent with single process mode where both the renderer and
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index 69b0916..e116a3d 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -91,7 +91,6 @@
 class BrowserMainParts;
 class BrowserOnlineStateObserver;
 class BrowserThreadImpl;
-class FieldTrialSynchronizer;
 class MediaKeysListenerManagerImpl;
 class MediaStreamManager;
 class SaveFileManager;
@@ -351,12 +350,6 @@
   std::unique_ptr<mojo::core::ScopedIPCSupport> mojo_ipc_support_;
   std::unique_ptr<MediaKeysListenerManagerImpl> media_keys_listener_manager_;
 
-  // The FieldTrialSynchronizer tells child processes when a trial gets
-  // activated. This is mostly an optimization, as a consequence if renderers
-  // know a trial is already active they don't need to send anything to the
-  // browser.
-  scoped_refptr<FieldTrialSynchronizer> field_trial_synchronizer_;
-
   // |user_input_monitor_| has to outlive |audio_manager_|, so declared first.
   std::unique_ptr<media::UserInputMonitor> user_input_monitor_;
 
diff --git a/content/browser/browser_process_sub_thread.cc b/content/browser/browser_process_sub_thread.cc
index c291069..3937915 100644
--- a/content/browser/browser_process_sub_thread.cc
+++ b/content/browser/browser_process_sub_thread.cc
@@ -115,11 +115,6 @@
       ProcessHostCleanUp();
   }
 
-  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI) &&
-      BrowserThread::CurrentlyOn(BrowserThread::UI)) {
-    ProcessHostCleanUp();
-  }
-
   notification_service_.reset();
 
 #if defined(OS_WIN)
diff --git a/content/browser/browser_process_sub_thread.h b/content/browser/browser_process_sub_thread.h
index b5f0d86..92bacfb 100644
--- a/content/browser/browser_process_sub_thread.h
+++ b/content/browser/browser_process_sub_thread.h
@@ -53,6 +53,8 @@
   // starting this BrowserProcessSubThread.
   void AllowBlockingForTesting();
 
+  static void ProcessHostCleanUp();
+
  protected:
   void Init() override;
   void Run(base::RunLoop* run_loop) override;
@@ -71,8 +73,6 @@
   // This method encapsulates cleanup that needs to happen on the IO thread.
   void IOThreadCleanUp();
 
-  void ProcessHostCleanUp();
-
   const BrowserThread::ID identifier_;
 
   // BrowserThreads are not allowed to do file I/O nor wait on synchronization
diff --git a/content/browser/field_trial_synchronizer.cc b/content/browser/field_trial_synchronizer.cc
index 98643ff5..0af9ed30 100644
--- a/content/browser/field_trial_synchronizer.cc
+++ b/content/browser/field_trial_synchronizer.cc
@@ -20,33 +20,20 @@
 
 namespace {
 
-void AddFieldTrialToPersistentSystemProfile(const std::string& field_trial_name,
-                                            const std::string& group_name) {
-  // Note this in the persistent profile as it will take a while for a new
-  // "complete" profile to be generated.
-  metrics::GlobalPersistentSystemProfile::GetInstance()->AddFieldTrial(
-      field_trial_name, group_name);
-}
+FieldTrialSynchronizer* g_instance = nullptr;
 
-}  // namespace
-
-FieldTrialSynchronizer::FieldTrialSynchronizer() {
-  bool success = base::FieldTrialList::AddObserver(this);
-  // Ensure the observer was actually registered.
-  DCHECK(success);
-
-  variations::VariationsIdsProvider::GetInstance()->AddObserver(this);
-  NotifyAllRenderersOfVariationsHeader();
-}
-
-void FieldTrialSynchronizer::NotifyAllRenderersOfFieldTrial(
-    const std::string& field_trial_name,
-    const std::string& group_name) {
+// Notifies all renderer processes about the |group_name| that is finalized for
+// the given field trail (|field_trial_name|). This is called on UI thread.
+void NotifyAllRenderersOfFieldTrial(const std::string& field_trial_name,
+                                    const std::string& group_name) {
   // To iterate over RenderProcessHosts, or to send messages to the hosts, we
   // need to be on the UI thread.
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  AddFieldTrialToPersistentSystemProfile(field_trial_name, group_name);
+  // Note this in the persistent profile as it will take a while for a new
+  // "complete" profile to be generated.
+  metrics::GlobalPersistentSystemProfile::GetInstance()->AddFieldTrial(
+      field_trial_name, group_name);
 
   for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
        !it.IsAtEnd(); it.Advance()) {
@@ -63,23 +50,30 @@
   }
 }
 
+}  // namespace
+
+// static
+void FieldTrialSynchronizer::CreateInstance() {
+  // Only 1 instance is allowed per process.
+  DCHECK(!g_instance);
+  g_instance = new FieldTrialSynchronizer();
+}
+
+FieldTrialSynchronizer::FieldTrialSynchronizer() {
+  bool success = base::FieldTrialList::AddObserver(this);
+  // Ensure the observer was actually registered.
+  DCHECK(success);
+
+  variations::VariationsIdsProvider::GetInstance()->AddObserver(this);
+  NotifyAllRenderersOfVariationsHeader();
+}
+
 void FieldTrialSynchronizer::OnFieldTrialGroupFinalized(
     const std::string& field_trial_name,
     const std::string& group_name) {
-  // The FieldTrialSynchronizer may have been created before any BrowserThread
-  // is created, so we don't need to synchronize with child processes in which
-  // case there are no child processes to notify yet. But we want to update the
-  // persistent system profile, thus the histogram data recorded in the reduced
-  // mode will be tagged to its corresponding field trial experiment.
-  if (!BrowserThread::IsThreadInitialized(BrowserThread::UI)) {
-    AddFieldTrialToPersistentSystemProfile(field_trial_name, group_name);
-    return;
-  }
-
-  RunOrPostTaskOnThread(
-      FROM_HERE, BrowserThread::UI,
-      base::BindOnce(&FieldTrialSynchronizer::NotifyAllRenderersOfFieldTrial,
-                     this, field_trial_name, group_name));
+  RunOrPostTaskOnThread(FROM_HERE, BrowserThread::UI,
+                        base::BindOnce(&NotifyAllRenderersOfFieldTrial,
+                                       field_trial_name, group_name));
 }
 
 // static
@@ -128,8 +122,7 @@
 }
 
 FieldTrialSynchronizer::~FieldTrialSynchronizer() {
-  base::FieldTrialList::RemoveObserver(this);
-  variations::VariationsIdsProvider::GetInstance()->RemoveObserver(this);
+  NOTREACHED();
 }
 
 }  // namespace content
diff --git a/content/browser/field_trial_synchronizer.h b/content/browser/field_trial_synchronizer.h
index 9a6bd7c5..47c8264 100644
--- a/content/browser/field_trial_synchronizer.h
+++ b/content/browser/field_trial_synchronizer.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/metrics/field_trial.h"
 #include "components/variations/variations_ids_provider.h"
 
@@ -21,26 +20,24 @@
 // renderers.
 //
 // This class registers itself as an observer of FieldTrialList. FieldTrialList
-// notifies this class by calling it's OnFieldTrialGroupFinalized method when a
+// notifies this class by calling its OnFieldTrialGroupFinalized method when a
 // group is selected (finalized) for a FieldTrial and OnFieldTrialGroupFinalized
 // method sends the FieldTrial's name and the group to all renderer processes.
 // Each renderer process creates the FieldTrial, and by using a 100% probability
-// for the FieldTrial, forces the FieldTrial to have the same group string.
-// This class also registers itself as a VariationsIdsProvider Observer
-// and updates the renderers if the variations header changes.
-
+// for the FieldTrial, forces the FieldTrial to have the same group string. This
+// is mostly an optimization so that renderers don't send anything to the
+// browser when they know that a trial is already active.
+//
+// This class also registers itself as a VariationsIdsProvider Observer and
+// updates the renderers if the variations header changes.
 class FieldTrialSynchronizer
-    : public base::RefCountedThreadSafe<FieldTrialSynchronizer>,
-      public base::FieldTrialList::Observer,
+    : public base::FieldTrialList::Observer,
       public variations::VariationsIdsProvider::Observer {
  public:
-  // Construction also sets up the global singleton instance.  This instance is
-  // used to communicate between the UI and other threads, and is destroyed only
-  // as the main thread (browser_main) terminates, which means all other threads
-  // have completed, and will not need this instance any further. It adds itself
-  // as an observer of FieldTrialList so that it gets notified whenever a group
-  // is finalized in the browser process.
-  FieldTrialSynchronizer();
+  // Creates the global FieldTrialSynchronizer instance for this process. After
+  // this is invoked, renderers are notified whenever a field trial group is
+  // finalized.
+  static void CreateInstance();
 
   // FieldTrialList::Observer methods:
 
@@ -58,16 +55,11 @@
   static void UpdateRendererVariationsHeader(RenderProcessHost* host);
 
  private:
-  // Notify all renderer processes about the |group_name| that is finalized for
-  // the given field trail (|field_trial_name|). This is called on UI thread.
-  void NotifyAllRenderersOfFieldTrial(const std::string& field_trial_name,
-                                      const std::string& group_name);
+  FieldTrialSynchronizer();
+  ~FieldTrialSynchronizer() override;
 
   static void NotifyAllRenderersOfVariationsHeader();
 
-  friend class base::RefCountedThreadSafe<FieldTrialSynchronizer>;
-  ~FieldTrialSynchronizer() override;
-
   DISALLOW_COPY_AND_ASSIGN(FieldTrialSynchronizer);
 };
 
diff --git a/content/browser/prerender/prerender_browsertest.cc b/content/browser/prerender/prerender_browsertest.cc
index 15cab415..9348f86 100644
--- a/content/browser/prerender/prerender_browsertest.cc
+++ b/content/browser/prerender/prerender_browsertest.cc
@@ -43,6 +43,7 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
+#include "content/test/content_browser_test_utils_internal.h"
 #include "content/test/test_content_browser_client.h"
 #include "content/test/test_mojo_binder_policy_applier_unittest.mojom.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -807,6 +808,30 @@
   TestHostPrerenderingState(GetUrl("/page_with_blank_iframe.html"));
 }
 
+// Tests that RenderFrameHost::ForEachFrame behaves correctly when prerendering.
+IN_PROC_BROWSER_TEST_F(PrerenderBrowserTest, ForEachFrame) {
+  const GURL kInitialUrl = GetUrl("/prerender/add_prerender.html");
+  // All frames are same-origin due to prerendering restrictions for
+  // cross-origin.
+  const GURL kPrerenderingUrl =
+      GetUrl("/cross_site_iframe_factory.html?a.test(a.test(a.test),a.test)");
+  ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
+
+  const int host_id = AddPrerender(kPrerenderingUrl);
+  RenderFrameHostImpl* prerendered_render_frame_host =
+      GetPrerenderedMainFrameHost(host_id);
+  RenderFrameHostImpl* rfh_sub_1 =
+      prerendered_render_frame_host->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_sub_1_1 =
+      rfh_sub_1->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_sub_2 =
+      prerendered_render_frame_host->child_at(1)->current_frame_host();
+
+  EXPECT_THAT(CollectAllFrames(prerendered_render_frame_host),
+              testing::ElementsAre(prerendered_render_frame_host, rfh_sub_1,
+                                   rfh_sub_2, rfh_sub_1_1));
+}
+
 class MojoCapabilityControlTestContentBrowserClient
     : public TestContentBrowserClient,
       mojom::TestInterfaceForDefer,
@@ -1301,7 +1326,8 @@
   // Check the event sequence seen in the prerendered page.
   EvalJsResult results = EvalJs(prerender_render_frame_host, "eventsSeen");
   std::vector<std::string> eventsSeen;
-  for (auto& result : results.ExtractList())
+  const base::Value resultsList = results.ExtractList();
+  for (auto& result : resultsList.GetList())
     eventsSeen.push_back(result.GetString());
   EXPECT_THAT(eventsSeen,
               testing::ElementsAreArray(
diff --git a/content/browser/prerender/prerender_host.cc b/content/browser/prerender/prerender_host.cc
index 35238f6..77dc6f8 100644
--- a/content/browser/prerender/prerender_host.cc
+++ b/content/browser/prerender/prerender_host.cc
@@ -43,6 +43,7 @@
 // because the |rfh| root node will not have a current_frame_host value. The
 // root node is set to null in MPArch prerender activation when generating a
 // BackForwardCacheImpl::Entry.
+// TODO(mcnee): Implement in terms of RenderFrameHost::ForEachFrame.
 std::vector<RenderFrameHostImpl*> AllDescendantActiveRenderFrameHosts(
     RenderFrameHostImpl& rfh) {
   std::vector<RenderFrameHostImpl*> result;
diff --git a/content/browser/renderer_host/frame_tree.cc b/content/browser/renderer_host/frame_tree.cc
index 983d2d0..ac6247c0 100644
--- a/content/browser/renderer_host/frame_tree.cc
+++ b/content/browser/renderer_host/frame_tree.cc
@@ -23,6 +23,7 @@
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/navigator.h"
 #include "content/browser/renderer_host/navigator_delegate.h"
+#include "content/browser/renderer_host/render_frame_host_delegate.h"
 #include "content/browser/renderer_host/render_frame_host_factory.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_frame_proxy_host.h"
@@ -48,6 +49,25 @@
   return instances;
 }
 
+// If |node| is the placeholder FrameTreeNode for an embedded frame tree,
+// returns the inner tree's main frame's FrameTreeNode. Otherwise, returns null.
+FrameTreeNode* GetInnerTreeMainFrameNode(FrameTreeNode* node) {
+  // TODO(1196715): Currently, inner frame trees are only implemented using
+  // multiple WebContents. Once pages can be embedded with MPArch, traverse
+  // them here as well.
+  RenderFrameHostImpl* inner_tree_main_frame =
+      node->frame_tree()->render_frame_delegate()->GetMainFrameForInnerDelegate(
+          node);
+
+  if (inner_tree_main_frame) {
+    DCHECK_NE(node->frame_tree(), inner_tree_main_frame->frame_tree());
+    DCHECK(inner_tree_main_frame->frame_tree_node());
+  }
+
+  return inner_tree_main_frame ? inner_tree_main_frame->frame_tree_node()
+                               : nullptr;
+}
+
 }  // namespace
 
 FrameTree::NodeIterator::NodeIterator(const NodeIterator& other) = default;
@@ -58,17 +78,28 @@
   if (current_node_ != root_of_subtree_to_skip_) {
     for (size_t i = 0; i < current_node_->child_count(); ++i) {
       FrameTreeNode* child = current_node_->child_at(i);
-      queue_.push(child);
+      FrameTreeNode* inner_tree_main_ftn = GetInnerTreeMainFrameNode(child);
+      queue_.push((should_descend_into_inner_trees_ && inner_tree_main_ftn)
+                      ? inner_tree_main_ftn
+                      : child);
+    }
+
+    if (should_descend_into_inner_trees_) {
+      for (auto* unattached_node :
+           current_node_->current_frame_host()
+               ->delegate()
+               ->GetUnattachedOwnedNodes(current_node_->current_frame_host())) {
+        queue_.push(unattached_node);
+      }
     }
   }
 
-  if (!queue_.empty()) {
-    current_node_ = queue_.front();
-    queue_.pop();
-  } else {
-    current_node_ = nullptr;
-  }
+  AdvanceNode();
+  return *this;
+}
 
+FrameTree::NodeIterator& FrameTree::NodeIterator::AdvanceSkippingChildren() {
+  AdvanceNode();
   return *this;
 }
 
@@ -76,25 +107,52 @@
   return current_node_ == rhs.current_node_;
 }
 
-FrameTree::NodeIterator::NodeIterator(FrameTreeNode* starting_node,
-                                      FrameTreeNode* root_of_subtree_to_skip)
-    : current_node_(starting_node),
-      root_of_subtree_to_skip_(root_of_subtree_to_skip) {}
+void FrameTree::NodeIterator::AdvanceNode() {
+  if (!queue_.empty()) {
+    current_node_ = queue_.front();
+    queue_.pop();
+  } else {
+    current_node_ = nullptr;
+  }
+}
+
+FrameTree::NodeIterator::NodeIterator(
+    const std::vector<FrameTreeNode*>& starting_nodes,
+    const FrameTreeNode* root_of_subtree_to_skip,
+    bool should_descend_into_inner_trees)
+    : current_node_(nullptr),
+      root_of_subtree_to_skip_(root_of_subtree_to_skip),
+      should_descend_into_inner_trees_(should_descend_into_inner_trees),
+      queue_(base::circular_deque<FrameTreeNode*>(starting_nodes.begin(),
+                                                  starting_nodes.end())) {
+  AdvanceNode();
+}
 
 FrameTree::NodeIterator FrameTree::NodeRange::begin() {
   // We shouldn't be attempting a frame tree traversal while the tree is
-  // being constructed.
-  DCHECK(root_->current_frame_host());
-  return NodeIterator(root_, root_of_subtree_to_skip_);
+  // being constructed or destructed.
+  DCHECK(std::all_of(
+      starting_nodes_.begin(), starting_nodes_.end(),
+      [](FrameTreeNode* ftn) { return ftn->current_frame_host(); }));
+
+  return NodeIterator(starting_nodes_, root_of_subtree_to_skip_,
+                      should_descend_into_inner_trees_);
 }
 
 FrameTree::NodeIterator FrameTree::NodeRange::end() {
-  return NodeIterator(nullptr, nullptr);
+  return NodeIterator({}, nullptr, should_descend_into_inner_trees_);
 }
 
-FrameTree::NodeRange::NodeRange(FrameTreeNode* root,
-                                FrameTreeNode* root_of_subtree_to_skip)
-    : root_(root), root_of_subtree_to_skip_(root_of_subtree_to_skip) {}
+FrameTree::NodeRange::NodeRange(
+    const std::vector<FrameTreeNode*>& starting_nodes,
+    const FrameTreeNode* root_of_subtree_to_skip,
+    bool should_descend_into_inner_trees)
+    : starting_nodes_(starting_nodes),
+      root_of_subtree_to_skip_(root_of_subtree_to_skip),
+      should_descend_into_inner_trees_(should_descend_into_inner_trees) {}
+
+FrameTree::NodeRange::NodeRange(const NodeRange&) = default;
+FrameTree::NodeRange::~NodeRange() = default;
 
 FrameTree::FrameTree(
     BrowserContext* browser_context,
@@ -183,11 +241,29 @@
 }
 
 FrameTree::NodeRange FrameTree::SubtreeNodes(FrameTreeNode* subtree_root) {
-  return NodeRange(subtree_root, nullptr);
+  return NodeRange({subtree_root}, nullptr,
+                   /* should_descend_into_inner_trees */ false);
+}
+
+FrameTree::NodeRange FrameTree::SubtreeAndInnerTreeNodes(
+    RenderFrameHostImpl* parent) {
+  std::vector<FrameTreeNode*> starting_nodes;
+  starting_nodes.reserve(parent->child_count());
+  for (size_t i = 0; i < parent->child_count(); ++i) {
+    FrameTreeNode* child = parent->child_at(i);
+    FrameTreeNode* inner_tree_main_ftn = GetInnerTreeMainFrameNode(child);
+    starting_nodes.push_back(inner_tree_main_ftn ? inner_tree_main_ftn : child);
+  }
+  const std::vector<FrameTreeNode*> unattached_owned_nodes =
+      parent->delegate()->GetUnattachedOwnedNodes(parent);
+  starting_nodes.insert(starting_nodes.end(), unattached_owned_nodes.begin(),
+                        unattached_owned_nodes.end());
+  return NodeRange(starting_nodes, nullptr,
+                   /* should_descend_into_inner_trees */ true);
 }
 
 FrameTree::NodeRange FrameTree::NodesExceptSubtree(FrameTreeNode* node) {
-  return NodeRange(root_, node);
+  return NodeRange({root_}, node, /* should_descend_into_inner_trees */ false);
 }
 
 FrameTreeNode* FrameTree::AddFrame(
diff --git a/content/browser/renderer_host/frame_tree.h b/content/browser/renderer_host/frame_tree.h
index f686af14..e7c2fad 100644
--- a/content/browser/renderer_host/frame_tree.h
+++ b/content/browser/renderer_host/frame_tree.h
@@ -64,6 +64,8 @@
     ~NodeIterator();
 
     NodeIterator& operator++();
+    // Advances the iterator and excludes the children of the current node
+    NodeIterator& AdvanceSkippingChildren();
 
     bool operator==(const NodeIterator& rhs) const;
     bool operator!=(const NodeIterator& rhs) const { return !(*this == rhs); }
@@ -73,26 +75,36 @@
    private:
     friend class NodeRange;
 
-    NodeIterator(FrameTreeNode* starting_node,
-                 FrameTreeNode* root_of_subtree_to_skip);
+    NodeIterator(const std::vector<FrameTreeNode*>& starting_nodes,
+                 const FrameTreeNode* root_of_subtree_to_skip,
+                 bool should_descend_into_inner_trees);
+
+    void AdvanceNode();
 
     FrameTreeNode* current_node_;
-    FrameTreeNode* const root_of_subtree_to_skip_;
+    const FrameTreeNode* const root_of_subtree_to_skip_;
+    const bool should_descend_into_inner_trees_;
     base::queue<FrameTreeNode*> queue_;
   };
 
   class CONTENT_EXPORT NodeRange {
    public:
+    NodeRange(const NodeRange&);
+    ~NodeRange();
+
     NodeIterator begin();
     NodeIterator end();
 
    private:
     friend class FrameTree;
 
-    NodeRange(FrameTreeNode* root, FrameTreeNode* root_of_subtree_to_skip);
+    NodeRange(const std::vector<FrameTreeNode*>& starting_nodes,
+              const FrameTreeNode* root_of_subtree_to_skip,
+              bool should_descend_into_inner_trees);
 
-    FrameTreeNode* const root_;
-    FrameTreeNode* const root_of_subtree_to_skip_;
+    const std::vector<FrameTreeNode*> starting_nodes_;
+    const FrameTreeNode* const root_of_subtree_to_skip_;
+    const bool should_descend_into_inner_trees_;
   };
 
   class CONTENT_EXPORT Delegate {
@@ -206,6 +218,11 @@
   // frame tree, starting from |subtree_root|.
   NodeRange SubtreeNodes(FrameTreeNode* subtree_root);
 
+  // Returns a range to iterate over all FrameTreeNodes in a subtree, starting
+  // from, but not including |parent|, as well as any FrameTreeNodes of inner
+  // frame trees.
+  static NodeRange SubtreeAndInnerTreeNodes(RenderFrameHostImpl* parent);
+
   // Adds a new child frame to the frame tree. |process_id| is required to
   // disambiguate |new_routing_id|, and it must match the process of the
   // |parent| node. Otherwise no child is added and this method returns false.
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index b274e46..490076b 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -150,6 +150,11 @@
   return nullptr;
 }
 
+std::vector<FrameTreeNode*> RenderFrameHostDelegate::GetUnattachedOwnedNodes(
+    RenderFrameHostImpl* owner) {
+  return {};
+}
+
 media::MediaMetricsProvider::RecordAggregateWatchTimeCallback
 RenderFrameHostDelegate::GetRecordAggregateWatchTimeCallback() {
   return base::NullCallback();
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index 2cf2b941..5a520c6 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -511,6 +511,12 @@
   virtual RenderFrameHostImpl* GetMainFrameForInnerDelegate(
       FrameTreeNode* frame_tree_node);
 
+  // Returns FrameTreeNodes that are logically owned by another frame even
+  // though this relationship is not yet reflected in their frame trees. This
+  // can happen, for example, with unattached guests and orphaned portals.
+  virtual std::vector<FrameTreeNode*> GetUnattachedOwnedNodes(
+      RenderFrameHostImpl* owner);
+
   // Registers a new URL handler for the given protocol.
   virtual void RegisterProtocolHandler(RenderFrameHostImpl* host,
                                        const std::string& scheme,
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 3925d8f..01cd1f3 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -616,25 +616,6 @@
 };
 #endif  // BUILDFLAG(ENABLE_MEDIA_REMOTING)
 
-using FrameCallback = base::RepeatingCallback<void(RenderFrameHostImpl*)>;
-void ForEachFrame(RenderFrameHostImpl* root_frame_host,
-                  const FrameCallback& frame_callback) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  FrameTree* frame_tree = root_frame_host->frame_tree();
-  DCHECK_EQ(root_frame_host, frame_tree->GetMainFrame());
-
-  for (FrameTreeNode* node : frame_tree->Nodes()) {
-    RenderFrameHostImpl* frame_host = node->current_frame_host();
-    RenderFrameHostImpl* pending_frame_host =
-        node->render_manager()->speculative_frame_host();
-    if (frame_host)
-      frame_callback.Run(frame_host);
-    if (pending_frame_host)
-      frame_callback.Run(pending_frame_host);
-  }
-}
-
 RenderFrameHostOrProxy LookupRenderFrameHostOrProxy(int process_id,
                                                     int routing_id) {
   RenderFrameHostImpl* rfh =
@@ -1701,6 +1682,7 @@
 }
 
 std::vector<RenderFrameHost*> RenderFrameHostImpl::GetFramesInSubtree() {
+  DCHECK_EQ(frame_tree_node()->current_frame_host(), this);
   std::vector<RenderFrameHost*> frame_hosts;
   for (FrameTreeNode* node : frame_tree_->SubtreeNodes(frame_tree_node()))
     frame_hosts.push_back(node->current_frame_host());
@@ -1719,6 +1701,138 @@
   return false;
 }
 
+namespace {
+
+template <typename RfhType>
+RenderFrameHostImpl::FrameIterationCallbackImpl ContinueIterationWrapper(
+    base::RepeatingCallback<void(RfhType*)> on_frame) {
+  return base::BindRepeating(
+      [](base::RepeatingCallback<void(RfhType*)> on_frame,
+         RenderFrameHostImpl* rfh) {
+        on_frame.Run(rfh);
+        return RenderFrameHost::FrameIterationAction::kContinue;
+      },
+      on_frame);
+}
+
+}  // namespace
+
+void RenderFrameHostImpl::ForEachFrame(FrameIterationCallback on_frame) {
+  // There's no automatic conversion from FrameIterationCallback to
+  // FrameIterationCallbackImpl, so this wrapper just forwards the
+  // RenderFrameHost argument.
+  FrameIterationCallbackImpl on_frame_impl = base::BindRepeating(
+      [](FrameIterationCallback on_frame, RenderFrameHostImpl* rfh) {
+        return on_frame.Run(rfh);
+      },
+      on_frame);
+  ForEachFrame(on_frame_impl);
+}
+
+void RenderFrameHostImpl::ForEachFrame(
+    FrameIterationAlwaysContinueCallback on_frame) {
+  ForEachFrame(ContinueIterationWrapper(on_frame));
+}
+
+void RenderFrameHostImpl::ForEachFrame(FrameIterationCallbackImpl on_frame) {
+  ForEachFrameImpl(on_frame, /* include_speculative */ false);
+}
+
+void RenderFrameHostImpl::ForEachFrame(
+    FrameIterationAlwaysContinueCallbackImpl on_frame) {
+  ForEachFrame(ContinueIterationWrapper(on_frame));
+}
+
+void RenderFrameHostImpl::ForEachFrameIncludingSpeculative(
+    FrameIterationCallbackImpl on_frame) {
+  ForEachFrameImpl(on_frame, /* include_speculative */ true);
+}
+
+void RenderFrameHostImpl::ForEachFrameIncludingSpeculative(
+    FrameIterationAlwaysContinueCallbackImpl on_frame) {
+  ForEachFrameIncludingSpeculative(ContinueIterationWrapper(on_frame));
+}
+
+void RenderFrameHostImpl::ForEachFrameImpl(FrameIterationCallbackImpl on_frame,
+                                           bool include_speculative) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  if (!include_speculative &&
+      lifecycle_state() == LifecycleStateImpl::kSpeculative)
+    return;
+
+  // Since |this| may not be current in its FrameTree, we can't begin iterating
+  // from |frame_tree_node()|, so we special case the first invocation for
+  // |this| and then actually start iterating over the subtree starting with
+  // our children's FrameTreeNodes.
+  bool skip_children_of_starting_frame = false;
+  switch (on_frame.Run(this)) {
+    case FrameIterationAction::kContinue:
+      break;
+    case FrameIterationAction::kSkipChildren:
+      skip_children_of_starting_frame = true;
+      break;
+    case FrameIterationAction::kStop:
+      return;
+  }
+
+  // Potentially include our FrameTreeNode's speculative RenderFrameHost, but
+  // only if |this| is current in its FrameTree.
+  if (include_speculative && frame_tree_node()->current_frame_host() == this) {
+    RenderFrameHostImpl* speculative_frame_host =
+        frame_tree_node()->render_manager()->speculative_frame_host();
+    if (speculative_frame_host) {
+      DCHECK_EQ(speculative_frame_host->child_count(), 0U);
+      switch (on_frame.Run(speculative_frame_host)) {
+        case FrameIterationAction::kContinue:
+        case FrameIterationAction::kSkipChildren:
+          break;
+        case FrameIterationAction::kStop:
+          return;
+      }
+    }
+  }
+
+  if (skip_children_of_starting_frame)
+    return;
+
+  FrameTree::NodeRange ftn_range = FrameTree::SubtreeAndInnerTreeNodes(this);
+  FrameTree::NodeIterator it = ftn_range.begin();
+  const FrameTree::NodeIterator end = ftn_range.end();
+
+  while (it != end) {
+    FrameTreeNode* node = *it;
+    RenderFrameHostImpl* frame_host = node->current_frame_host();
+    if (frame_host) {
+      switch (on_frame.Run(frame_host)) {
+        case FrameIterationAction::kContinue:
+          ++it;
+          break;
+        case FrameIterationAction::kSkipChildren:
+          it.AdvanceSkippingChildren();
+          break;
+        case FrameIterationAction::kStop:
+          return;
+      }
+    }
+
+    if (include_speculative) {
+      RenderFrameHostImpl* speculative_frame_host =
+          node->render_manager()->speculative_frame_host();
+      if (speculative_frame_host) {
+        DCHECK_EQ(speculative_frame_host->child_count(), 0U);
+        switch (on_frame.Run(speculative_frame_host)) {
+          case FrameIterationAction::kContinue:
+          case FrameIterationAction::kSkipChildren:
+            break;
+          case FrameIterationAction::kStop:
+            return;
+        }
+      }
+    }
+  }
+}
+
 int RenderFrameHostImpl::GetFrameTreeNodeId() {
   return frame_tree_node_->frame_tree_node_id();
 }
@@ -2800,11 +2914,16 @@
 
   // TODO(danakj): We only blocked the main frame, so we should only need to
   // resume that?
-  ForEachFrame(this,
-               base::BindRepeating([](RenderFrameHostImpl* render_frame_host) {
-                 if (render_frame_host->IsRenderFrameCreated())
-                   render_frame_host->frame_->ResumeBlockedRequests();
-               }));
+  ForEachFrameIncludingSpeculative(base::BindRepeating(
+      [](RenderFrameHostImpl* main_rfh,
+         RenderFrameHostImpl* render_frame_host) {
+        // Inner frame trees shouldn't be possible here.
+        DCHECK_EQ(render_frame_host->frame_tree(), main_rfh->frame_tree());
+
+        if (render_frame_host->IsRenderFrameCreated())
+          render_frame_host->frame_->ResumeBlockedRequests();
+      },
+      base::Unretained(this)));
 
   if (pending_navigate_) {
     frame_tree_node()->navigator().OnBeginNavigation(
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 1dee8acc..1b1aa38 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -326,6 +326,8 @@
   RenderFrameHostImpl* GetMainFrame() override;
   std::vector<RenderFrameHost*> GetFramesInSubtree() override;
   bool IsDescendantOf(RenderFrameHost*) override;
+  void ForEachFrame(FrameIterationCallback on_frame) override;
+  void ForEachFrame(FrameIterationAlwaysContinueCallback on_frame) override;
   int GetFrameTreeNodeId() override;
   base::UnguessableToken GetDevToolsFrameToken() override;
   base::Optional<base::UnguessableToken> GetEmbeddingToken() override;
@@ -1741,6 +1743,20 @@
   }
   bool IsOuterDelegateFrame() { return is_outer_delegate_frame_; }
 
+  // These are the content internal equivalents of
+  // |RenderFrameHost::ForEachFrame| whose comment can be referred to for
+  // details. Content internals can also access speculative RenderFrameHostImpls
+  // if necessary by using the |ForEachFrameIncludingSpeculative| variations.
+  using FrameIterationCallbackImpl =
+      base::RepeatingCallback<FrameIterationAction(RenderFrameHostImpl*)>;
+  using FrameIterationAlwaysContinueCallbackImpl =
+      base::RepeatingCallback<void(RenderFrameHostImpl*)>;
+  void ForEachFrame(FrameIterationCallbackImpl on_frame);
+  void ForEachFrame(FrameIterationAlwaysContinueCallbackImpl on_frame);
+  void ForEachFrameIncludingSpeculative(FrameIterationCallbackImpl on_frame);
+  void ForEachFrameIncludingSpeculative(
+      FrameIterationAlwaysContinueCallbackImpl on_frame);
+
   bool DocumentUsedWebOTP() override;
 
   scoped_refptr<WebAuthRequestSecurityChecker>
@@ -2660,6 +2676,11 @@
   void ForEachImmediateLocalRoot(
       const base::RepeatingCallback<void(RenderFrameHostImpl*)>& callback);
 
+  // This is the actual implementation of the various overloads of
+  // |ForEachFrame|.
+  void ForEachFrameImpl(FrameIterationCallbackImpl on_frame,
+                        bool include_speculative);
+
   // Returns the mojom::Frame interface for this frame in the renderer process.
   // May be overridden by friend subclasses for e.g. tests which wish to
   // intercept outgoing messages. May only be called when the RenderFrame in the
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index e768178..a4276f2d 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -5294,4 +5294,355 @@
                    ->IsProcessShutdownDelayedForTesting());
 }
 
+// Tests that RenderFrameHost::ForEachFrame visits the correct frames in the
+// correct order.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, ForEachFrame) {
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = root_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_d = rfh_a->child_at(1)->current_frame_host();
+
+  // When starting iteration from the primary frame, we should see the frame
+  // itself and its descendants in breadth first order.
+  EXPECT_THAT(CollectAllFrames(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
+
+  // When starting iteration from a subframe, only it and its descendants should
+  // be seen.
+  EXPECT_THAT(CollectAllFrames(rfh_b), testing::ElementsAre(rfh_b, rfh_c));
+
+  // Test that iteration stops when requested.
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_a->ForEachFrame(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return RenderFrameHost::FrameIterationAction::kStop;
+        }));
+    EXPECT_THAT(visited_frames, testing::ElementsAre(rfh_a));
+  }
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_a->ForEachFrame(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return RenderFrameHost::FrameIterationAction::kSkipChildren;
+        }));
+    EXPECT_THAT(visited_frames, testing::ElementsAre(rfh_a));
+  }
+
+  // Now consider stopping or skipping children at |rfh_b|. If we skip children,
+  // we skip |rfh_c|, but not |rfh_d|. If we stop iteration, we skip both
+  // |rfh_c| and |rfh_d|.
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_a->ForEachFrame(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return rfh == rfh_b
+                     ? RenderFrameHost::FrameIterationAction::kStop
+                     : RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+    EXPECT_THAT(visited_frames, testing::ElementsAre(rfh_a, rfh_b));
+  }
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_a->ForEachFrame(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return rfh == rfh_b
+                     ? RenderFrameHost::FrameIterationAction::kSkipChildren
+                     : RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+    EXPECT_THAT(visited_frames, testing::ElementsAre(rfh_a, rfh_b, rfh_d));
+  }
+}
+
+// Tests that RenderFrameHost::ForEachFrame does not expose speculative RFHs,
+// unless content internal code requests them.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       ForEachFrameSpeculative) {
+  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = root_frame_host();
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_a->lifecycle_state());
+
+  TestNavigationManager nav_manager(web_contents(), url_b);
+  shell()->LoadURL(url_b);
+  ASSERT_TRUE(nav_manager.WaitForRequestStart());
+
+  RenderFrameHostImpl* rfh_b =
+      rfh_a->frame_tree_node()->render_manager()->speculative_frame_host();
+  ASSERT_TRUE(rfh_b);
+  EXPECT_EQ(LifecycleStateImpl::kSpeculative, rfh_b->lifecycle_state());
+
+  // ForEachFrame does not expose the speculative RFH.
+  EXPECT_THAT(CollectAllFrames(rfh_a), testing::ElementsAre(rfh_a));
+
+  // When we request the speculative RFH, we visit it.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_a),
+              testing::UnorderedElementsAre(rfh_a, rfh_b));
+
+  // If ForEachFrame is called on a speculative RFH directly, do nothing.
+  rfh_b->ForEachFrame(base::BindRepeating([](RenderFrameHostImpl* rfh) {
+    ADD_FAILURE() << "Visited speculative RFH";
+    return RenderFrameHost::FrameIterationAction::kStop;
+  }));
+
+  // If we request speculative RFHs and directly call this on a speculative RFH,
+  // just visit the given speculative RFH.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_b),
+              testing::ElementsAre(rfh_b));
+}
+
+// Like ForEachFrameSpeculative, but for a speculative RFH for a subframe
+// navigation.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       ForEachFrameSpeculativeWithSubframes) {
+  IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
+  GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = root_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_a->lifecycle_state());
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_b->lifecycle_state());
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_c->lifecycle_state());
+
+  TestNavigationManager nav_manager(web_contents(), url_d);
+  ASSERT_TRUE(BeginNavigateToURLFromRenderer(rfh_b, url_d));
+  ASSERT_TRUE(nav_manager.WaitForRequestStart());
+
+  RenderFrameHostImpl* rfh_d =
+      rfh_b->frame_tree_node()->render_manager()->speculative_frame_host();
+  ASSERT_TRUE(rfh_d);
+  EXPECT_EQ(LifecycleStateImpl::kSpeculative, rfh_d->lifecycle_state());
+
+  // ForEachFrame does not expose the speculative RFH.
+  EXPECT_THAT(CollectAllFrames(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b, rfh_c));
+
+  // When we request the speculative RFH, we visit it.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_a),
+              testing::UnorderedElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
+
+  // When beginning iteration from the current RFH of the navigating frame, we
+  // also visit the speculative RFH.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_b),
+              testing::UnorderedElementsAre(rfh_b, rfh_d, rfh_c));
+
+  // If ForEachFrame is called on a speculative RFH directly, do nothing.
+  rfh_d->ForEachFrame(base::BindRepeating([](RenderFrameHostImpl* rfh) {
+    ADD_FAILURE() << "Visited speculative RFH";
+    return RenderFrameHost::FrameIterationAction::kStop;
+  }));
+
+  // If we request speculative RFHs and directly call this on a speculative RFH,
+  // just visit the given speculative RFH.
+  EXPECT_THAT(CollectAllFramesIncludingSpeculative(rfh_d),
+              testing::ElementsAre(rfh_d));
+
+  // Test that iteration stops when requested.
+  {
+    // We don't check the RFHs visited in the interest of not overtesting the
+    // ordering of speculative RFHs.
+    bool stopped = false;
+    rfh_a->ForEachFrameIncludingSpeculative(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          EXPECT_FALSE(stopped);
+          if (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative) {
+            stopped = true;
+            return RenderFrameHost::FrameIterationAction::kStop;
+          }
+          return RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+  }
+
+  {
+    bool stopped = false;
+    rfh_b->ForEachFrameIncludingSpeculative(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          EXPECT_FALSE(stopped);
+          if (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative) {
+            stopped = true;
+            return RenderFrameHost::FrameIterationAction::kStop;
+          }
+          return RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+  }
+
+  // Skipping the children of a current RFH whose FrameTreeNode has a
+  // speculative RFH skips the children but still includes the speculative RFH.
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_a->ForEachFrameIncludingSpeculative(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return (rfh == rfh_b)
+                     ? RenderFrameHost::FrameIterationAction::kSkipChildren
+                     : RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+    EXPECT_THAT(visited_frames,
+                testing::UnorderedElementsAre(rfh_a, rfh_b, rfh_d));
+  }
+
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_b->ForEachFrameIncludingSpeculative(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return (rfh == rfh_b)
+                     ? RenderFrameHost::FrameIterationAction::kSkipChildren
+                     : RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+    EXPECT_THAT(visited_frames, testing::UnorderedElementsAre(rfh_b, rfh_d));
+  }
+
+  // Skipping the children of a speculative RFH is not useful, but is included
+  // here for completeness of testing.
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_a->ForEachFrameIncludingSpeculative(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative)
+                     ? RenderFrameHost::FrameIterationAction::kSkipChildren
+                     : RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+    EXPECT_THAT(visited_frames,
+                testing::UnorderedElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
+  }
+
+  {
+    std::vector<RenderFrameHostImpl*> visited_frames;
+    rfh_b->ForEachFrameIncludingSpeculative(
+        base::BindLambdaForTesting([&](RenderFrameHostImpl* rfh) {
+          visited_frames.push_back(rfh);
+          return (rfh->lifecycle_state() == LifecycleStateImpl::kSpeculative)
+                     ? RenderFrameHost::FrameIterationAction::kSkipChildren
+                     : RenderFrameHost::FrameIterationAction::kContinue;
+        }));
+    EXPECT_THAT(visited_frames,
+                testing::UnorderedElementsAre(rfh_b, rfh_d, rfh_c));
+  }
+}
+
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       ForEachFramePendingDeletion) {
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
+  GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = root_frame_host();
+  RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_a->lifecycle_state());
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_b->lifecycle_state());
+  EXPECT_EQ(LifecycleStateImpl::kActive, rfh_c->lifecycle_state());
+  LeaveInPendingDeletionState(rfh_a);
+  LeaveInPendingDeletionState(rfh_b);
+  LeaveInPendingDeletionState(rfh_c);
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_d));
+  RenderFrameHostImpl* rfh_d = root_frame_host();
+
+  // ForEachFrame on the primary RFH does not visit the pending delete RFHs.
+  EXPECT_THAT(CollectAllFrames(rfh_d), testing::ElementsAre(rfh_d));
+
+  // ForEachFrame on the pending delete RFHs only visits the pending delete
+  // RFHs.
+  EXPECT_THAT(CollectAllFrames(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b, rfh_c));
+  EXPECT_THAT(CollectAllFrames(rfh_b), testing::ElementsAre(rfh_b, rfh_c));
+}
+
+// Tests that RenderFrameHost::ForEachFrame visits the frames of an inner
+// WebContents.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       ForEachFrameInnerContents) {
+  GURL url_a(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
+  GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = root_frame_host();
+  WebContentsImpl* inner_contents = static_cast<WebContentsImpl*>(
+      CreateAndAttachInnerContents(rfh_a->child_at(0)->current_frame_host()));
+  ASSERT_TRUE(NavigateToURLFromRenderer(inner_contents, url_b));
+
+  RenderFrameHostImpl* rfh_b = inner_contents->GetMainFrame();
+
+  EXPECT_THAT(CollectAllFrames(rfh_a), testing::ElementsAre(rfh_a, rfh_b));
+}
+
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       ForEachFrameInnerContentsWithSubframes) {
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a(a),a)"));
+  GURL url_b(embedded_test_server()->GetURL(
+      "b.com", "/cross_site_iframe_factory.html?b(c(d),e)"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a_main = root_frame_host();
+  RenderFrameHostImpl* rfh_a_sub1 =
+      rfh_a_main->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_a_sub2 =
+      rfh_a_main->child_at(1)->current_frame_host();
+  WebContentsImpl* inner_contents =
+      static_cast<WebContentsImpl*>(CreateAndAttachInnerContents(
+          rfh_a_sub1->child_at(0)->current_frame_host()));
+  ASSERT_TRUE(NavigateToURLFromRenderer(inner_contents, url_b));
+
+  RenderFrameHostImpl* rfh_b = inner_contents->GetMainFrame();
+  RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_d = rfh_c->child_at(0)->current_frame_host();
+  RenderFrameHostImpl* rfh_e = rfh_b->child_at(1)->current_frame_host();
+
+  EXPECT_THAT(CollectAllFrames(rfh_a_main),
+              testing::ElementsAre(rfh_a_main, rfh_a_sub1, rfh_a_sub2, rfh_b,
+                                   rfh_c, rfh_e, rfh_d));
+}
+
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       ForEachFrameMultipleInnerContents) {
+  // After attaching inner contents, this will be A(B(C),D)
+  GURL url_a(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
+  GURL url_b(embedded_test_server()->GetURL(
+      "b.com", "/cross_site_iframe_factory.html?b(b)"));
+  GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
+  GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), url_a));
+  RenderFrameHostImpl* rfh_a = root_frame_host();
+
+  WebContentsImpl* contents_b = static_cast<WebContentsImpl*>(
+      CreateAndAttachInnerContents(rfh_a->child_at(0)->current_frame_host()));
+  ASSERT_TRUE(NavigateToURLFromRenderer(contents_b, url_b));
+  RenderFrameHostImpl* rfh_b = contents_b->GetMainFrame();
+
+  WebContentsImpl* contents_c = static_cast<WebContentsImpl*>(
+      CreateAndAttachInnerContents(rfh_b->child_at(0)->current_frame_host()));
+  ASSERT_TRUE(NavigateToURLFromRenderer(contents_c, url_c));
+  RenderFrameHostImpl* rfh_c = contents_c->GetMainFrame();
+
+  WebContentsImpl* contents_d = static_cast<WebContentsImpl*>(
+      CreateAndAttachInnerContents(rfh_a->child_at(1)->current_frame_host()));
+  ASSERT_TRUE(NavigateToURLFromRenderer(contents_d, url_d));
+  RenderFrameHostImpl* rfh_d = contents_d->GetMainFrame();
+
+  EXPECT_THAT(CollectAllFrames(rfh_a),
+              testing::ElementsAre(rfh_a, rfh_b, rfh_d, rfh_c));
+}
+
 }  // namespace content
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 24f086e..92ed79f 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -109,6 +109,7 @@
 #include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/ax_inspect_factory.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_plugin_guest_manager.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -8417,6 +8418,27 @@
   return nullptr;
 }
 
+std::vector<FrameTreeNode*> WebContentsImpl::GetUnattachedOwnedNodes(
+    RenderFrameHostImpl* owner) {
+  std::vector<FrameTreeNode*> unattached_owned_nodes;
+  BrowserPluginGuestManager* guest_manager =
+      GetBrowserContext()->GetGuestManager();
+  if (owner == GetMainFrame() && guest_manager) {
+    guest_manager->ForEachUnattachedGuest(
+        this, base::BindRepeating(
+                  [](std::vector<FrameTreeNode*>& unattached_owned_nodes,
+                     WebContents* guest_contents) {
+                    unattached_owned_nodes.push_back(
+                        static_cast<WebContentsImpl*>(guest_contents)
+                            ->frame_tree_.root());
+                  },
+                  std::ref(unattached_owned_nodes)));
+  }
+  // TODO(mcnee): Also include orphaned portals here.
+  // See https://crbug.com/1196715
+  return unattached_owned_nodes;
+}
+
 void WebContentsImpl::IsClipboardPasteContentAllowed(
     const GURL& url,
     const ui::ClipboardFormatType& data_type,
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 2520b41..6d8dfd9 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -716,6 +716,8 @@
   GetRecordAggregateWatchTimeCallback() override;
   RenderFrameHostImpl* GetMainFrameForInnerDelegate(
       FrameTreeNode* frame_tree_node) override;
+  std::vector<FrameTreeNode*> GetUnattachedOwnedNodes(
+      RenderFrameHostImpl* owner) override;
   void RegisterProtocolHandler(RenderFrameHostImpl* source,
                                const std::string& protocol,
                                const GURL& url,
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 8c9ffffd..160f3d9 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -239,6 +239,14 @@
       std::move(endpoint), MOJO_ACCEPT_INVITATION_FLAG_LEAK_TRANSPORT_ENDPOINT);
 }
 
+// Callback passed to variations::ChildProcessFieldTrialSyncer. Notifies the
+// browser process that a field trial group was activated in this process.
+void FieldTrialActivatedCallback(
+    mojo::SharedRemote<mojom::FieldTrialRecorder> recorder,
+    const std::string& trial_name) {
+  recorder->FieldTrialActivated(trial_name);
+}
+
 }  // namespace
 
 // Implements the mojom ChildProcess interface and lives on the IO thread.
@@ -541,23 +549,7 @@
   if (!field_trial_syncer_)
     return;
 
-  handling_set_field_trial_group_notification_ = true;
-  field_trial_syncer_->OnSetFieldTrialGroup(trial_name, group_name);
-  handling_set_field_trial_group_notification_ = false;
-}
-
-void ChildThreadImpl::OnFieldTrialGroupFinalized(
-    const std::string& trial_name,
-    const std::string& group_name) {
-  // If we're currently in SetFieldTrialGroup(), it's a field trial the browser
-  // is telling us about. Don't send a mojo request back to the browser, since
-  // it's unnecessary.
-  if (handling_set_field_trial_group_notification_)
-    return;
-
-  mojo::Remote<mojom::FieldTrialRecorder> field_trial_recorder;
-  BindHostReceiver(field_trial_recorder.BindNewPipeAndPassReceiver());
-  field_trial_recorder->FieldTrialActivated(trial_name);
+  field_trial_syncer_->SetFieldTrialGroupFromBrowser(trial_name, group_name);
 }
 
 void ChildThreadImpl::Init(const Options& options) {
@@ -703,10 +695,14 @@
   // In single-process mode, there is no need to synchronize trials to the
   // browser process (because it's the same process).
   if (!IsInBrowserProcess()) {
+    mojo::PendingRemote<mojom::FieldTrialRecorder> pending_remote;
+    BindHostReceiver(pending_remote.InitWithNewPipeAndPassReceiver());
+    mojo::SharedRemote<mojom::FieldTrialRecorder> shared_remote(
+        std::move(pending_remote));
     field_trial_syncer_ =
-        base::WrapUnique(new variations::ChildProcessFieldTrialSyncer(this));
-    field_trial_syncer_->InitFieldTrialObserving(
-        *base::CommandLine::ForCurrentProcess());
+        variations::ChildProcessFieldTrialSyncer::CreateInstance(
+            base::BindRepeating(&FieldTrialActivatedCallback,
+                                std::move(shared_remote)));
   }
 }
 
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index 49da2478..cefd680be 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -14,7 +14,6 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/metrics/field_trial.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread.h"
 #include "build/build_config.h"
@@ -65,8 +64,7 @@
 
 // The main thread of a child process derives from this class.
 class CONTENT_EXPORT ChildThreadImpl : public IPC::Listener,
-                                       virtual public ChildThread,
-                                       private base::FieldTrialList::Observer {
+                                       virtual public ChildThread {
  public:
   struct CONTENT_EXPORT Options;
 
@@ -100,10 +98,6 @@
   void SetFieldTrialGroup(const std::string& trial_name,
                           const std::string& group_name) override;
 
-  // base::FieldTrialList::Observer:
-  void OnFieldTrialGroupFinalized(const std::string& trial_name,
-                                  const std::string& group_name) override;
-
   IPC::SyncChannel* channel() { return channel_.get(); }
 
   IPC::MessageRouter* GetRouter();
@@ -223,10 +217,8 @@
 
   scoped_refptr<base::SingleThreadTaskRunner> browser_process_io_runner_;
 
-  std::unique_ptr<variations::ChildProcessFieldTrialSyncer> field_trial_syncer_;
-  // Whether we're handling the SetFieldTrialGroup() notification from the
-  // browser process.
-  bool handling_set_field_trial_group_notification_ = false;
+  // Pointer to a global object which is never deleted.
+  variations::ChildProcessFieldTrialSyncer* field_trial_syncer_ = nullptr;
 
   std::unique_ptr<base::WeakPtrFactory<ChildThreadImpl>>
       channel_connected_factory_;
diff --git a/content/public/browser/browser_plugin_guest_manager.cc b/content/public/browser/browser_plugin_guest_manager.cc
index a82dd01f..8cc941cf 100644
--- a/content/public/browser/browser_plugin_guest_manager.cc
+++ b/content/public/browser/browser_plugin_guest_manager.cc
@@ -6,7 +6,7 @@
 
 namespace content {
 
-bool BrowserPluginGuestManager::ForEachGuest(WebContents* embedder_web_contents,
+bool BrowserPluginGuestManager::ForEachGuest(WebContents* owner_web_contents,
                                              const GuestCallback& callback) {
   return false;
 }
diff --git a/content/public/browser/browser_plugin_guest_manager.h b/content/public/browser/browser_plugin_guest_manager.h
index 1335aa1..26c9f37 100644
--- a/content/public/browser/browser_plugin_guest_manager.h
+++ b/content/public/browser/browser_plugin_guest_manager.h
@@ -16,13 +16,20 @@
 // operations outside of the content layer.
 class CONTENT_EXPORT BrowserPluginGuestManager {
  public:
-  virtual ~BrowserPluginGuestManager() {}
+  virtual ~BrowserPluginGuestManager() = default;
 
-  // Iterates over all WebContents belonging to a given |embedder_web_contents|,
+  // Iterates over guest WebContents that belong to a given
+  // |owner_web_contents|, but have not yet been attached.
+  virtual void ForEachUnattachedGuest(
+      WebContents* owner_web_contents,
+      base::RepeatingCallback<void(WebContents*)> callback) {}
+
+  // Prefer using |RenderFrameHost::ForEachFrame|.
+  // Iterates over all WebContents belonging to a given |owner_web_contents|,
   // calling |callback| for each. If one of the callbacks returns true, then
   // the iteration exits early.
   using GuestCallback = base::RepeatingCallback<bool(WebContents*)>;
-  virtual bool ForEachGuest(WebContents* embedder_web_contents,
+  virtual bool ForEachGuest(WebContents* owner_web_contents,
                             const GuestCallback& callback);
 
   // Returns the "full page" guest if there is one. That is, if there is a
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index bd9316bf..9bf669e 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -242,6 +242,7 @@
 
   // Returns a vector of all RenderFrameHosts in the subtree rooted at |this|.
   // The results may be in different processes.
+  // Does not consider inner frame trees.
   // TODO(https://crbug.com/1013740): Consider exposing a way for the browser
   // process to run a function across a subtree in all renderers rather than
   // exposing the RenderFrameHosts of the frames here.
@@ -252,8 +253,37 @@
   // GetParent().
   // This is a strict relationship, a RenderFrameHost is never an ancestor of
   // itself.
+  // This does not consider inner frame trees.
   virtual bool IsDescendantOf(RenderFrameHost* ancestor) = 0;
 
+  // |ForEachFrame| traverses this RenderFrameHost and all of its descendants,
+  // including frames in any inner frame trees, in breadth-first order.
+  // Examples of features that have inner frame trees are portals or GuestViews.
+  // Note: The RenderFrameHost parameter is not guaranteed to have a live
+  // RenderFrame counterpart in the renderer process. Callbacks should check
+  // IsRenderFrameLive(), as sending IPC messages to it in this case will fail
+  // silently.
+  //
+  // The callback returns a FrameIterationAction which determines if/how
+  // iteration on subsequent frames continues. The FrameIterationAction may be
+  // omitted, in which case kContinue will be assumed.
+  enum class FrameIterationAction {
+    // Includes the children of the visited frame for subsequent traversal and
+    // continues traversal to the next frame.
+    kContinue,
+    // Continues traversal to the next frame but does not include the children
+    // of the visited frame for subsequent traversal.
+    kSkipChildren,
+    // Does not continue traversal.
+    kStop
+  };
+  using FrameIterationCallback =
+      base::RepeatingCallback<FrameIterationAction(RenderFrameHost*)>;
+  using FrameIterationAlwaysContinueCallback =
+      base::RepeatingCallback<void(RenderFrameHost*)>;
+  virtual void ForEachFrame(FrameIterationCallback on_frame) = 0;
+  virtual void ForEachFrame(FrameIterationAlwaysContinueCallback on_frame) = 0;
+
   // Returns the FrameTreeNode ID for this frame. This ID is browser-global and
   // uniquely identifies a frame that hosts content. The identifier is fixed at
   // the creation of the frame and stays constant for the lifetime of the frame.
@@ -298,10 +328,10 @@
   // of its size.
   virtual const base::Optional<gfx::Size>& GetFrameSize() = 0;
 
-  // Returns the distance from this frame to the root frame.
+  // Returns the distance from this frame to its main frame.
   virtual size_t GetFrameDepth() = 0;
 
-  // Returns true if the frame is out of process.
+  // Returns true if the frame is out of process relative to its parent.
   virtual bool IsCrossProcessSubframe() = 0;
 
   // Indicates whether this frame is in a cross-origin isolated agent cluster.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 9eff6e4b..2ef7c06 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1815,6 +1815,13 @@
   return rfh->frame_tree_node()->child_at(index)->current_frame_host();
 }
 
+std::vector<RenderFrameHost*> CollectAllFrames(RenderFrameHost* starting_rfh) {
+  std::vector<RenderFrameHost*> visited_frames;
+  starting_rfh->ForEachFrame(base::BindLambdaForTesting(
+      [&](RenderFrameHost* rfh) { visited_frames.push_back(rfh); }));
+  return visited_frames;
+}
+
 bool ExecuteWebUIResourceTest(WebContents* web_contents,
                               const std::vector<int>& js_resource_ids) {
   // Inject WebUI test runner script first prior to other scripts required to
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 0b9dcad..f911e8f 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -872,6 +872,10 @@
 // RenderFrameHost.  Returns nullptr if such child frame does not exist.
 RenderFrameHost* ChildFrameAt(RenderFrameHost* frame, size_t index);
 
+// Returns the frames visited by |RenderFrameHost::ForEachFrame| in the same
+// order.
+std::vector<RenderFrameHost*> CollectAllFrames(RenderFrameHost* starting_rfh);
+
 // Executes the WebUI resource test runner injecting each resource ID in
 // |js_resource_ids| prior to executing the tests.
 //
diff --git a/content/test/content_browser_test_utils_internal.cc b/content/test/content_browser_test_utils_internal.cc
index a684b358..857a329 100644
--- a/content/test/content_browser_test_utils_internal.cc
+++ b/content/test/content_browser_test_utils_internal.cc
@@ -20,6 +20,7 @@
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/thread_pool.h"
+#include "base/test/bind.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/renderer_host/delegated_frame_host.h"
@@ -158,6 +159,22 @@
   return root->child_at(root->child_count() - 1)->current_frame_host();
 }
 
+std::vector<RenderFrameHostImpl*> CollectAllFrames(
+    RenderFrameHostImpl* starting_rfh) {
+  std::vector<RenderFrameHostImpl*> visited_frames;
+  starting_rfh->ForEachFrame(base::BindLambdaForTesting(
+      [&](RenderFrameHostImpl* rfh) { visited_frames.push_back(rfh); }));
+  return visited_frames;
+}
+
+std::vector<RenderFrameHostImpl*> CollectAllFramesIncludingSpeculative(
+    RenderFrameHostImpl* starting_rfh) {
+  std::vector<RenderFrameHostImpl*> visited_frames;
+  starting_rfh->ForEachFrameIncludingSpeculative(base::BindLambdaForTesting(
+      [&](RenderFrameHostImpl* rfh) { visited_frames.push_back(rfh); }));
+  return visited_frames;
+}
+
 Shell* OpenBlankWindow(WebContentsImpl* web_contents) {
   FrameTreeNode* root = web_contents->GetFrameTree()->root();
   ShellAddedObserver new_shell_observer;
diff --git a/content/test/content_browser_test_utils_internal.h b/content/test/content_browser_test_utils_internal.h
index 0fac28a4..ae21989 100644
--- a/content/test/content_browser_test_utils_internal.h
+++ b/content/test/content_browser_test_utils_internal.h
@@ -83,6 +83,13 @@
                                 const GURL& url,
                                 bool wait_for_navigation);
 
+// Returns the frames visited by |RenderFrameHostImpl::ForEachFrame| in the same
+// order.
+std::vector<RenderFrameHostImpl*> CollectAllFrames(
+    RenderFrameHostImpl* starting_rfh);
+std::vector<RenderFrameHostImpl*> CollectAllFramesIncludingSpeculative(
+    RenderFrameHostImpl* starting_rfh);
+
 // Open a new popup passing no URL to window.open, which results in a blank page
 // and no last committed entry. Returns the newly created shell. Also saves the
 // reference to the opened window in the "last_opened_window" variable in JS.
diff --git a/content/test/data/accessibility/regression/missing-parent-expected-blink.txt b/content/test/data/accessibility/regression/missing-parent-expected-blink.txt
new file mode 100644
index 0000000..5d65ffe
--- /dev/null
+++ b/content/test/data/accessibility/regression/missing-parent-expected-blink.txt
@@ -0,0 +1,4 @@
+rootWebArea name='done'
+++genericContainer ignored
+++++genericContainer
+++++++image
diff --git a/content/test/data/accessibility/regression/missing-parent.html b/content/test/data/accessibility/regression/missing-parent.html
new file mode 100644
index 0000000..9f9e30f1
--- /dev/null
+++ b/content/test/data/accessibility/regression/missing-parent.html
@@ -0,0 +1,32 @@
+<!--
+@WAIT-FOR:done
+-->
+<!-- Applying the class "strange" turns <col> into a LayoutImage, which
+     cannot have children, yet a preexisting descendants exists that tries to
+     find its parent, triggering a DCHECK -->
+<style>
+.strange { text-align: center; content: url(data:image/png,aaa); }
+</style>
+
+<foo></foo>
+
+<script>
+const col = document.createElement('col');
+const span = document.createElement('span');
+const bar = document.createElement('bar');
+const baz = document.createElement('baz');
+
+// Create structure manually as parser will refuse:
+// <foo><col><span><bar>
+document.querySelector('foo').appendChild(col);
+col.appendChild(span);
+span.appendChild(bar);
+bar.appendChild(baz);
+
+document.addEventListener('DOMContentLoaded', () => {
+  setTimeout(() => {
+    document.querySelector('foo').className = 'strange';
+    document.title = 'done';
+  }, 50);
+});
+</script>
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 995cd660..d5fc567 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -162,7 +162,7 @@
 crbug.com/979444 [ linux ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ mac no-asan no-passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 crbug.com/979444 [ win ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
-crbug.com/979444 [ android angle-disabled no-passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/979444 [ android angle-disabled ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 
 # Flaky timeouts on Intel Windows, Linux and Mac
 crbug.com/1085222 [ win10 intel-0x3e92 ] deqp/functional/gles3/shaderoperator/binary_operator_* [ Failure ]
@@ -631,21 +631,10 @@
 [ android qualcomm ] WebglExtension_WEBGL_compressed_texture_s3tc_srgb [ Skip ]
 
 # Video tests are flaky. Sometimes the video is black.
-crbug.com/948894 [ android no-passthrough ] conformance/textures/video/* [ RetryOnFailure ]
-crbug.com/948894 [ android no-passthrough ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-crbug.com/948894 [ android no-passthrough ] conformance2/textures/video/* [ RetryOnFailure ]
-crbug.com/948894 [ android no-passthrough ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
-
-# These video tests crash on passthrough Pixel 2 and Pixel 4
-crbug.com/1205418 [ android passthrough ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/image_bitmap_from_video/* [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/tex-video-using-tex-unit-non-zero.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/texture-npot-video.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/video-rotation.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/video/* [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance2/textures/image_bitmap_from_video/* [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance2/textures/video/* [ Failure ]
+crbug.com/948894 [ android ] conformance/textures/video/* [ RetryOnFailure ]
+crbug.com/948894 [ android ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
+crbug.com/948894 [ android ] conformance2/textures/video/* [ RetryOnFailure ]
+crbug.com/948894 [ android ] conformance2/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 
 crbug.com/angleproject/4417 [ android ] conformance2/rendering/framebuffer-render-to-layer.html [ Failure ]
 
@@ -710,13 +699,13 @@
 crbug.com/1179432 [ android android-pixel-4 angle-disabled no-passthrough ] deqp/functional/gles3/texturespecification/basic_teximage2d_2d_01.html [ RetryOnFailure ]
 crbug.com/1179432 [ android android-pixel-4 angle-disabled no-passthrough ] deqp/functional/gles3/transformfeedback/* [ RetryOnFailure ]
 
-crbug.com/1191030 [ android android-pixel-4 no-passthrough ] conformance/textures/misc/video-rotation.html [ Failure ]
+crbug.com/1191030 [ android android-pixel-4 ] conformance/textures/misc/video-rotation.html [ Failure ]
 
 # Android ANGLE GLES
 # TODO(crbug.com/979444): once this is passing on the passthrough
 # command decoder, simplify the RetryOnFailure expectation at the top
 # of this file to just not declare any OS.
-# crbug.com/979444 [ android angle-opengles ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
+crbug.com/979444 [ android angle-opengles ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
 
 crbug.com/angleproject/3684 [ android angle-opengles ] conformance2/renderbuffers/multisample-with-full-sample-counts.html [ Failure ]
 crbug.com/967410 [ android angle-opengles ] conformance2/textures/misc/npot-video-sizing.html [ 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 8df8d093..208daff9 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
@@ -551,34 +551,25 @@
 crbug.com/352645 [ android android-webview-instrumentation ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ Skip ]
 crbug.com/352645 [ android android-webview-instrumentation ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Skip ]
 crbug.com/352645 [ android android-webview-instrumentation ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ Skip ]
-crbug.com/352645 [ android android-webview-instrumentation angle-disabled no-passthrough ] conformance/textures/misc/texture-npot-video.html [ Skip ]
+crbug.com/352645 [ android android-webview-instrumentation angle-disabled ] conformance/textures/misc/texture-npot-video.html [ Skip ]
 
 # These video tests appear to be flaky.
 # TODO(crbug.com/1178518): This crbug caused the test to fail consistently. It
 # should be probably back to flaky once the bug is fixed. So uncomment below test once the bug is fixed.
 # crbug.com/834933 [ android android-chromium ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ RetryOnFailure ]
-crbug.com/733599 [ android angle-disabled no-passthrough ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/733599 [ android angle-disabled no-passthrough ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/733599 [ android angle-disabled ] conformance/textures/video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/733599 [ android angle-disabled ] conformance/textures/video/tex-2d-luminance-luminance-unsigned_byte.html [ RetryOnFailure ]
 # TODO(crbug.com/1178518): This crbug caused the test to fail consistently. It
 # should be probably back to flaky once the bug is fixed. So uncomment below test once the bug is fixed.
 # crbug.com/834933 [ android android-chromium ] conformance/textures/video/tex-2d-rgb-rgb-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/834933 [ android android-chromium no-passthrough ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ]
-crbug.com/891456 [ android no-passthrough ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
-
-# These video tests crash on passthrough Nexus 5X, Shield TV, Pixel 2 and Pixel 4
-crbug.com/1205418 [ android passthrough ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/image_bitmap_from_video/* [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/tex-video-using-tex-unit-non-zero.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/texture-npot-video.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/misc/video-rotation.html [ Failure ]
-crbug.com/1205418 [ android passthrough ] conformance/textures/video/* [ Failure ]
+crbug.com/834933 [ android android-chromium ] conformance/textures/video/tex-2d-rgba-rgba-unsigned_byte.html [ RetryOnFailure ]
+crbug.com/891456 [ android ] conformance/textures/image_bitmap_from_video/tex-2d-rgba-rgba-unsigned_short_5_5_5_1.html [ RetryOnFailure ]
 
 # This crashes in Android WebView on the Nexus 6, preventing the
 # suite from running further. Rather than add multiple
 # suppressions, skip it until it's passing at least in content
 # shell.
-crbug.com/499555 [ android android-nexus-6 no-passthrough ] conformance/extensions/oes-texture-float-with-video.html [ Skip ]
+crbug.com/499555 [ android android-nexus-6 ] conformance/extensions/oes-texture-float-with-video.html [ Skip ]
 
 # The test that runs after conformance/state/gl-object-get-calls.html is failing
 # on Nexus 5/6 consistently and other Android bots (eg. Nexus 5X) sometimes.
@@ -600,7 +591,7 @@
 crbug.com/678850 [ android android-nexus-5 ] conformance/more/functions/vertexAttribPointerBadArgs.html [ Failure ]
 crbug.com/678850 [ android android-nexus-5 ] conformance/attribs/gl-vertexattribpointer.html [ Failure ]
 
-crbug.com/891456 [ android android-nexus-5 no-passthrough ] conformance/extensions/oes-texture-float-with-video.html [ RetryOnFailure ]
+crbug.com/891456 [ android android-nexus-5 ] conformance/extensions/oes-texture-float-with-video.html [ RetryOnFailure ]
 
 # Nexus 5X
 # Was timing out randomly on android_optional_gpu_tests_rel, but became so
@@ -623,7 +614,7 @@
 # Timing out on this device for unknown reasons.
 crbug.com/1099148 [ android android-nexus-5x ] deqp/data/gles2/shaders/swizzles.html [ Skip ]
 crbug.com/1122644 [ android android-nexus-5x ] conformance/textures/misc/texture-upload-size.html [ Skip ]
-crbug.com/1163306 [ android android-nexus-5x no-passthrough ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
+crbug.com/1163306 [ android android-nexus-5x ] conformance/textures/image_bitmap_from_video/* [ RetryOnFailure ]
 
 # Nexus 6 (Adreno 420)
 crbug.com/499555 [ android android-nexus-6 ] conformance/context/context-attributes-alpha-depth-stencil-antialias.html [ Failure ]
@@ -666,7 +657,7 @@
 crbug.com/891456 [ android android-nexus-9 ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ]
 crbug.com/1025790 [ android android-nexus-9 ] conformance/textures/misc/tex-image-canvas-corruption.html [ Skip ]
 crbug.com/478572 [ android android-nexus-9 ] deqp/data/gles2/shaders/functions.html [ Failure ]
-crbug.com/891456 [ android android-shield-android-tv no-passthrough ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
+crbug.com/891456 [ android android-shield-android-tv ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
 crbug.com/891456 [ android android-shield-android-tv ] conformance/extensions/oes-texture-half-float-with-video.html [ Failure ]
 crbug.com/891456 [ android android-shield-android-tv ] conformance/textures/image_bitmap_from_video/tex-2d-luminance_alpha-luminance_alpha-unsigned_byte.html [ RetryOnFailure ]
 crbug.com/1025790 [ android android-shield-android-tv ] conformance/textures/misc/tex-image-canvas-corruption.html [ Skip ]
@@ -690,8 +681,8 @@
 crbug.com/1175223 [ android android-pixel-4 angle-opengles passthrough ] conformance/extensions/angle-instanced-arrays-out-of-bounds.html [ Failure ]
 crbug.com/1175226 [ android android-pixel-4 no-passthrough no-swiftshader-gl ] conformance/rendering/blending.html [ Failure ]
 crbug.com/1179432 [ android android-pixel-4 no-passthrough ] conformance/rendering/polygon-offset.html [ RetryOnFailure ]
-crbug.com/1191030 [ android android-pixel-4 no-passthrough ] conformance/textures/misc/video-rotation.html [ Failure ]
-crbug.com/1187332 [ android android-pixel-4 no-passthrough ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
+crbug.com/1191030 [ android android-pixel-4 ] conformance/textures/misc/video-rotation.html [ Failure ]
+crbug.com/1187332 [ android android-pixel-4 ] conformance/textures/misc/texture-corner-case-videos.html [ RetryOnFailure ]
 
 # Misc failures
 crbug.com/angleproject/2988 [ android angle-opengles ] conformance/context/context-size-change.html [ Failure ]
diff --git a/dbus/values_util.cc b/dbus/values_util.cc
index 2f9f82a..6f73a84 100644
--- a/dbus/values_util.cc
+++ b/dbus/values_util.cc
@@ -270,7 +270,7 @@
       value.GetAsList(&list);
       dbus::MessageWriter array_writer(nullptr);
       writer->OpenArray("v", &array_writer);
-      for (const auto& value_in_list : *list) {
+      for (const auto& value_in_list : list->GetList()) {
         AppendValueDataAsVariant(&array_writer, value_in_list);
       }
       writer->CloseContainer(&array_writer);
diff --git a/extensions/browser/api/system_network/system_network_api_unittest.cc b/extensions/browser/api/system_network/system_network_api_unittest.cc
index 15d0e75..d07f1e8 100644
--- a/extensions/browser/api/system_network/system_network_api_unittest.cc
+++ b/extensions/browser/api/system_network/system_network_api_unittest.cc
@@ -37,7 +37,7 @@
   base::ListValue* value = static_cast<base::ListValue*>(result.get());
   ASSERT_TRUE(value->GetSize() > 0);
 
-  for (const auto& network_interface_value : *value) {
+  for (const auto& network_interface_value : value->GetList()) {
     NetworkInterface network_interface;
     ASSERT_TRUE(NetworkInterface::Populate(network_interface_value,
                                            &network_interface));
diff --git a/fuchsia/engine/renderer/web_engine_content_renderer_client.cc b/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
index be17bddf..d4f27da5 100644
--- a/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
+++ b/fuchsia/engine/renderer/web_engine_content_renderer_client.cc
@@ -66,7 +66,8 @@
 
   media::EmeConfigRule GetRobustnessConfigRule(
       media::EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
     // Only empty robustness string is currently supported.
     if (requested_robustness.empty()) {
       return media::EmeConfigRule::HW_SECURE_CODECS_REQUIRED;
diff --git a/gpu/command_buffer/service/image_reader_gl_owner.cc b/gpu/command_buffer/service/image_reader_gl_owner.cc
index 018b8197..60384d2 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner.cc
+++ b/gpu/command_buffer/service/image_reader_gl_owner.cc
@@ -325,9 +325,9 @@
   current_image_ref_.emplace(this, image, std::move(scoped_acquire_fence_fd));
 }
 
-void ImageReaderGLOwner::EnsureTexImageBound(GLuint service_id) {
+void ImageReaderGLOwner::EnsureTexImageBound() {
   if (current_image_ref_)
-    current_image_ref_->EnsureBound(service_id);
+    current_image_ref_->EnsureBound();
 }
 
 std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
@@ -513,19 +513,17 @@
   return base::ScopedFD(HANDLE_EINTR(dup(ready_fence_.get())));
 }
 
-void ImageReaderGLOwner::ScopedCurrentImageRef::EnsureBound(GLuint service_id) {
-  // Same |image_| can be bound multiple times to different |service_id|. So
-  // even if |image_bound_| is true, it might not be for current |service_id|.
-  // Hence we still need to create and bind egl image to the |service_id|.
-  // Also continue to wait on the fence even if it was waited upon during
-  // previous EnsureBound() calls on same image since this call could be on a
-  // different context. Insert an EGL fence and make server wait for image to be
-  // available.
+void ImageReaderGLOwner::ScopedCurrentImageRef::EnsureBound() {
+  if (image_bound_)
+    return;
+
+  // Insert an EGL fence and make server wait for image to be available.
   if (!InsertEglFenceAndWait(GetReadyFence()))
     return;
 
   // Create EGL image from the AImage and bind it to the texture.
-  if (!CreateAndBindEglImage(image_, service_id, &texture_owner_->loader_))
+  if (!CreateAndBindEglImage(image_, texture_owner_->GetTextureId(),
+                             &texture_owner_->loader_))
     return;
 
   image_bound_ = true;
diff --git a/gpu/command_buffer/service/image_reader_gl_owner.h b/gpu/command_buffer/service/image_reader_gl_owner.h
index a67c4b0..0917fd3 100644
--- a/gpu/command_buffer/service/image_reader_gl_owner.h
+++ b/gpu/command_buffer/service/image_reader_gl_owner.h
@@ -37,7 +37,7 @@
       const base::RepeatingClosure& frame_available_cb) override;
   gl::ScopedJavaSurface CreateJavaSurface() const override;
   void UpdateTexImage() override;
-  void EnsureTexImageBound(GLuint service_id) override;
+  void EnsureTexImageBound() override;
   void ReleaseBackBuffers() override;
   std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
   GetAHardwareBuffer() override;
@@ -66,7 +66,7 @@
     ~ScopedCurrentImageRef();
     AImage* image() const { return image_; }
     base::ScopedFD GetReadyFence() const;
-    void EnsureBound(GLuint service_id);
+    void EnsureBound();
 
    private:
     ImageReaderGLOwner* texture_owner_;
diff --git a/gpu/command_buffer/service/mock_texture_owner.cc b/gpu/command_buffer/service/mock_texture_owner.cc
index cfb0751..efaa5eb 100644
--- a/gpu/command_buffer/service/mock_texture_owner.cc
+++ b/gpu/command_buffer/service/mock_texture_owner.cc
@@ -24,7 +24,7 @@
   ON_CALL(*this, GetTextureId()).WillByDefault(Return(fake_texture_id));
   ON_CALL(*this, GetContext()).WillByDefault(Return(fake_context));
   ON_CALL(*this, GetSurface()).WillByDefault(Return(fake_surface));
-  ON_CALL(*this, EnsureTexImageBound(_)).WillByDefault(Invoke([this] {
+  ON_CALL(*this, EnsureTexImageBound()).WillByDefault(Invoke([this] {
     CHECK(expect_update_tex_image);
   }));
   ON_CALL(*this, RunWhenBufferIsAvailable(_))
diff --git a/gpu/command_buffer/service/mock_texture_owner.h b/gpu/command_buffer/service/mock_texture_owner.h
index 000ce00..45983f4 100644
--- a/gpu/command_buffer/service/mock_texture_owner.h
+++ b/gpu/command_buffer/service/mock_texture_owner.h
@@ -32,7 +32,7 @@
   MOCK_CONST_METHOD0(GetSurface, gl::GLSurface*());
   MOCK_CONST_METHOD0(CreateJavaSurface, gl::ScopedJavaSurface());
   MOCK_METHOD0(UpdateTexImage, void());
-  MOCK_METHOD1(EnsureTexImageBound, void(GLuint));
+  MOCK_METHOD0(EnsureTexImageBound, void());
   MOCK_METHOD0(ReleaseBackBuffers, void());
   MOCK_METHOD1(OnTextureDestroyed, void(gpu::gles2::AbstractTexture*));
   MOCK_METHOD1(SetFrameAvailableCallback, void(const base::RepeatingClosure&));
diff --git a/gpu/command_buffer/service/shared_image_video.cc b/gpu/command_buffer/service/shared_image_video.cc
index da9a5f5..da8ba72 100644
--- a/gpu/command_buffer/service/shared_image_video.cc
+++ b/gpu/command_buffer/service/shared_image_video.cc
@@ -13,7 +13,6 @@
 #include "components/viz/common/resources/resource_sizes.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/abstract_texture.h"
-#include "gpu/command_buffer/service/abstract_texture_impl_shared_context_state.h"
 #include "gpu/command_buffer/service/ahardwarebuffer_utils.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
@@ -43,6 +42,7 @@
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
     scoped_refptr<StreamTextureSharedImageInterface> stream_texture_sii,
+    std::unique_ptr<gles2::AbstractTexture> abstract_texture,
     scoped_refptr<SharedContextState> context_state,
     bool is_thread_safe)
     : SharedImageBackingAndroid(
@@ -58,6 +58,7 @@
           is_thread_safe,
           base::ScopedFD()),
       stream_texture_sii_(std::move(stream_texture_sii)),
+      abstract_texture_(std::move(abstract_texture)),
       context_state_(std::move(context_state)) {
   DCHECK(stream_texture_sii_);
   DCHECK(context_state_);
@@ -87,11 +88,10 @@
 }
 
 bool SharedImageVideo::ProduceLegacyMailbox(MailboxManager* mailbox_manager) {
-  // Android does not use legacy mailbox anymore. Hence marking this as
-  // NOTREACHED() now. Once all platform stops using legacy mailbox, this method
-  // can be removed.
-  NOTREACHED();
-  return false;
+  DCHECK(abstract_texture_);
+  mailbox_manager->ProduceTexture(mailbox(),
+                                  abstract_texture_->GetTextureBase());
+  return true;
 }
 
 size_t SharedImageVideo::EstimatedSizeForMemTracking() const {
@@ -148,20 +148,14 @@
 class SharedImageRepresentationGLTextureVideo
     : public SharedImageRepresentationGLTexture {
  public:
-  SharedImageRepresentationGLTextureVideo(
-      SharedImageManager* manager,
-      SharedImageVideo* backing,
-      MemoryTypeTracker* tracker,
-      std::unique_ptr<gles2::AbstractTexture> texture)
+  SharedImageRepresentationGLTextureVideo(SharedImageManager* manager,
+                                          SharedImageVideo* backing,
+                                          MemoryTypeTracker* tracker,
+                                          gles2::Texture* texture)
       : SharedImageRepresentationGLTexture(manager, backing, tracker),
-        texture_(std::move(texture)) {}
+        texture_(texture) {}
 
-  gles2::Texture* GetTexture() override {
-    auto* texture = gles2::Texture::CheckedCast(texture_->GetTextureBase());
-    DCHECK(texture);
-
-    return texture;
-  }
+  gles2::Texture* GetTexture() override { return texture_; }
 
   bool BeginAccess(GLenum mode) override {
     // This representation should only be called for read or overlay.
@@ -169,14 +163,14 @@
            mode == GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
 
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
-    video_backing->BeginGLReadAccess(texture_->service_id());
+    video_backing->BeginGLReadAccess();
     return true;
   }
 
   void EndAccess() override {}
 
  private:
-  std::unique_ptr<gles2::AbstractTexture> texture_;
+  gles2::Texture* texture_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTextureVideo);
 };
@@ -189,20 +183,18 @@
       SharedImageManager* manager,
       SharedImageVideo* backing,
       MemoryTypeTracker* tracker,
-      std::unique_ptr<gles2::AbstractTexture> abstract_texture)
+      scoped_refptr<gles2::TexturePassthrough> texture)
       : SharedImageRepresentationGLTexturePassthrough(manager,
                                                       backing,
                                                       tracker),
-        abstract_texture_(std::move(abstract_texture)),
-        passthrough_texture_(gles2::TexturePassthrough::CheckedCast(
-            abstract_texture_->GetTextureBase())) {
+        texture_(std::move(texture)) {
     // TODO(https://crbug.com/1172769): Remove this CHECK.
-    CHECK(passthrough_texture_);
+    CHECK(texture_);
   }
 
   const scoped_refptr<gles2::TexturePassthrough>& GetTexturePassthrough()
       override {
-    return passthrough_texture_;
+    return texture_;
   }
 
   bool BeginAccess(GLenum mode) override {
@@ -211,15 +203,14 @@
            mode == GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM);
 
     auto* video_backing = static_cast<SharedImageVideo*>(backing());
-    video_backing->BeginGLReadAccess(passthrough_texture_->service_id());
+    video_backing->BeginGLReadAccess();
     return true;
   }
 
   void EndAccess() override {}
 
  private:
-  std::unique_ptr<gles2::AbstractTexture> abstract_texture_;
-  scoped_refptr<gles2::TexturePassthrough> passthrough_texture_;
+  scoped_refptr<gles2::TexturePassthrough> texture_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageRepresentationGLTexturePassthroughVideo);
 };
@@ -334,14 +325,15 @@
   // which should result in no image.
   if (!stream_texture_sii_->HasTextureOwner())
     return nullptr;
-
-  // Generate an abstract texture.
-  auto texture = GenAbstractTexture(context_state_, /*passthrough=*/false);
-  if (!texture)
-    return nullptr;
+  // TODO(vikassoni): We would want to give the TextureOwner's underlying
+  // Texture, but it was not set with the correct size. The AbstractTexture,
+  // that we use for legacy mailbox, is correctly set.
+  auto* texture =
+      gles2::Texture::CheckedCast(abstract_texture_->GetTextureBase());
+  DCHECK(texture);
 
   return std::make_unique<SharedImageRepresentationGLTextureVideo>(
-      manager, this, tracker, std::move(texture));
+      manager, this, tracker, texture);
 }
 
 // TODO(vikassoni): Currently GLRenderer doesn't support overlays with shared
@@ -355,11 +347,13 @@
   // which should result in no image.
   if (!stream_texture_sii_->HasTextureOwner())
     return nullptr;
-
-  // Generate an abstract texture.
-  auto texture = GenAbstractTexture(context_state_, /*passthrough=*/true);
-  if (!texture)
-    return nullptr;
+  // TODO(vikassoni): We would want to give the TextureOwner's underlying
+  // Texture, but it was not set with the correct size. The AbstractTexture,
+  // that we use for legacy mailbox, is correctly set.
+  scoped_refptr<gles2::TexturePassthrough> texture =
+      gles2::TexturePassthrough::CheckedCast(
+          abstract_texture_->GetTextureBase());
+  DCHECK(texture);
 
   return std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
       manager, this, tracker, std::move(texture));
@@ -384,61 +378,32 @@
   }
 
   DCHECK(context_state->GrContextIsGL());
-  const bool passthrough = Passthrough();
-  auto texture = GenAbstractTexture(context_state, passthrough);
-  if (!texture)
-    return nullptr;
+  auto* texture_base = stream_texture_sii_->GetTextureBase();
+  DCHECK(texture_base);
 
+  // In GL mode, create the SharedImageRepresentationGLTexture*Video
+  // representation to use with SharedImageRepresentationVideoSkiaGL.
   std::unique_ptr<gpu::SharedImageRepresentationGLTextureBase>
       gl_representation;
-  if (passthrough) {
-    gl_representation =
-        std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
-            manager, this, tracker, std::move(texture));
-  } else {
+  if (texture_base->GetType() == gpu::TextureBase::Type::kValidated) {
     gl_representation =
         std::make_unique<SharedImageRepresentationGLTextureVideo>(
-            manager, this, tracker, std::move(texture));
+            manager, this, tracker, gles2::Texture::CheckedCast(texture_base));
+  } else {
+    gl_representation =
+        std::make_unique<SharedImageRepresentationGLTexturePassthroughVideo>(
+            manager, this, tracker,
+            gles2::TexturePassthrough::CheckedCast(texture_base));
   }
+
   return SharedImageRepresentationSkiaGL::Create(std::move(gl_representation),
                                                  std::move(context_state),
                                                  manager, this, tracker);
 }
 
-bool SharedImageVideo::Passthrough() {
-  auto* texture_base = stream_texture_sii_->GetTextureBase();
-  DCHECK(texture_base);
-
-  return (texture_base->GetType() == gpu::TextureBase::Type::kPassthrough);
-}
-
-std::unique_ptr<gles2::AbstractTexture> SharedImageVideo::GenAbstractTexture(
-    scoped_refptr<SharedContextState> context_state,
-    const bool passthrough) {
-  std::unique_ptr<gles2::AbstractTexture> texture;
-  if (passthrough) {
-    texture =
-        std::make_unique<gles2::AbstractTextureImplOnSharedContextPassthrough>(
-            GL_TEXTURE_EXTERNAL_OES, context_state);
-  } else {
-    texture = std::make_unique<gles2::AbstractTextureImplOnSharedContext>(
-        GL_TEXTURE_EXTERNAL_OES, GL_RGBA, size().width(), size().height(), 1, 0,
-        GL_RGBA, GL_UNSIGNED_BYTE, context_state);
-  }
-
-  // If TextureOwner binds texture implicitly on update, that means it will use
-  // TextureOwner texture_id to update and bind. Hence use TextureOwner
-  // texture_id in abstract texture via BindStreamTextureImage().
-  if (stream_texture_sii_->TextureOwnerBindsTextureOnUpdate()) {
-    texture->BindStreamTextureImage(
-        stream_texture_sii_.get(),
-        stream_texture_sii_->GetTextureBase()->service_id());
-  }
-  return texture;
-}
-
-void SharedImageVideo::BeginGLReadAccess(const GLuint service_id) {
-  stream_texture_sii_->UpdateAndBindTexImage(service_id);
+void SharedImageVideo::BeginGLReadAccess() {
+  // Render the codec image.
+  stream_texture_sii_->UpdateAndBindTexImage();
 }
 
 // Representation of SharedImageVideo as an overlay plane.
diff --git a/gpu/command_buffer/service/shared_image_video.h b/gpu/command_buffer/service/shared_image_video.h
index fa6c0b7..230fdfb 100644
--- a/gpu/command_buffer/service/shared_image_video.h
+++ b/gpu/command_buffer/service/shared_image_video.h
@@ -37,6 +37,7 @@
       GrSurfaceOrigin surface_origin,
       SkAlphaType alpha_type,
       scoped_refptr<StreamTextureSharedImageInterface> stream_texture_sii,
+      std::unique_ptr<gles2::AbstractTexture> abstract_texture,
       scoped_refptr<SharedContextState> shared_context_state,
       bool is_thread_safe);
 
@@ -88,18 +89,12 @@
   friend class SharedImageRepresentationVideoSkiaVk;
   friend class SharedImageRepresentationOverlayVideo;
 
-  // Whether we're using the passthrough command decoder and should generate
-  // passthrough textures.
-  bool Passthrough();
-
-  // Helper method to generate an abstract texture.
-  std::unique_ptr<gles2::AbstractTexture> GenAbstractTexture(
-      scoped_refptr<SharedContextState> context_state,
-      const bool passthrough);
-
-  void BeginGLReadAccess(const GLuint service_id);
+  void BeginGLReadAccess();
 
   scoped_refptr<StreamTextureSharedImageInterface> stream_texture_sii_;
+
+  // |abstract_texture_| is only used for legacy mailbox.
+  std::unique_ptr<gles2::AbstractTexture> abstract_texture_;
   scoped_refptr<SharedContextState> context_state_;
 
   DISALLOW_COPY_AND_ASSIGN(SharedImageVideo);
diff --git a/gpu/command_buffer/service/stream_texture_shared_image_interface.h b/gpu/command_buffer/service/stream_texture_shared_image_interface.h
index 668843a6..ffdd3af 100644
--- a/gpu/command_buffer/service/stream_texture_shared_image_interface.h
+++ b/gpu/command_buffer/service/stream_texture_shared_image_interface.h
@@ -18,7 +18,7 @@
 class GPU_GLES2_EXPORT StreamTextureSharedImageInterface : public gl::GLImage {
  public:
   enum class BindingsMode {
-    // Ensures that the texture is bound to the latest image, if
+    // Ensures that the TextureOwner's texture is bound to the latest image, if
     // it requires explicit binding.
     kEnsureTexImageBound,
 
@@ -39,14 +39,9 @@
   // or not.
   virtual bool IsUsingGpuMemory() const = 0;
 
-  // Update texture image to the most recent frame and bind it to the provided
-  // texture |service_id| if TextureOwner does not implicitly binds texture
-  // during the update.
-  // If TextureOwner() always binds texture implicitly during the update, then
-  // it will always bind it to TextureOwner's texture id and not to the
-  // |service_id|.
-  virtual void UpdateAndBindTexImage(GLuint service_id) = 0;
-
+  // Update the texture image to the most recent frame and bind it to the
+  // texture.
+  virtual void UpdateAndBindTexImage() = 0;
   virtual bool HasTextureOwner() const = 0;
   virtual TextureBase* GetTextureBase() const = 0;
 
@@ -58,10 +53,6 @@
   // the overlay promotion. Return true if it could render to overlay correctly.
   virtual bool RenderToOverlay() = 0;
 
-  // Whether TextureOwner's implementation binds texture to TextureOwner owned
-  // texture_id during the texture update.
-  virtual bool TextureOwnerBindsTextureOnUpdate() = 0;
-
  protected:
   ~StreamTextureSharedImageInterface() override = default;
 };
diff --git a/gpu/command_buffer/service/surface_texture_gl_owner.cc b/gpu/command_buffer/service/surface_texture_gl_owner.cc
index e4f06fdc..d088cd92 100644
--- a/gpu/command_buffer/service/surface_texture_gl_owner.cc
+++ b/gpu/command_buffer/service/surface_texture_gl_owner.cc
@@ -74,7 +74,7 @@
     surface_texture_->UpdateTexImage();
 }
 
-void SurfaceTextureGLOwner::EnsureTexImageBound(GLuint service_id) {
+void SurfaceTextureGLOwner::EnsureTexImageBound() {
   NOTREACHED();
 }
 
diff --git a/gpu/command_buffer/service/surface_texture_gl_owner.h b/gpu/command_buffer/service/surface_texture_gl_owner.h
index 29db890..e3198a8 100644
--- a/gpu/command_buffer/service/surface_texture_gl_owner.h
+++ b/gpu/command_buffer/service/surface_texture_gl_owner.h
@@ -32,7 +32,7 @@
       const base::RepeatingClosure& frame_available_cb) override;
   gl::ScopedJavaSurface CreateJavaSurface() const override;
   void UpdateTexImage() override;
-  void EnsureTexImageBound(GLuint service_id) override;
+  void EnsureTexImageBound() override;
   void ReleaseBackBuffers() override;
   std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
   GetAHardwareBuffer() override;
diff --git a/gpu/command_buffer/service/texture_owner.h b/gpu/command_buffer/service/texture_owner.h
index 25c33c5..f58dde4 100644
--- a/gpu/command_buffer/service/texture_owner.h
+++ b/gpu/command_buffer/service/texture_owner.h
@@ -82,10 +82,10 @@
   // Update the texture image using the latest available image data.
   virtual void UpdateTexImage() = 0;
 
-  // Ensures that the latest texture image is bound to the provided texture
-  // service_id. Should only be used if the TextureOwner requires explicit
-  // binding of the image after an update.
-  virtual void EnsureTexImageBound(GLuint service_id) = 0;
+  // Ensures that the latest texture image is bound to the texture target.
+  // Should only be used if the TextureOwner requires explicit binding of the
+  // image after an update.
+  virtual void EnsureTexImageBound() = 0;
 
   // Transformation matrix if any associated with the texture image.
   virtual void ReleaseBackBuffers() = 0;
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index f568b8b..eb8af40 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -133,8 +133,8 @@
   return true;
 }
 
-void StreamTexture::UpdateAndBindTexImage(GLuint service_id) {
-  UpdateTexImage(BindingsMode::kEnsureTexImageBound, service_id);
+void StreamTexture::UpdateAndBindTexImage() {
+  UpdateTexImage(BindingsMode::kEnsureTexImageBound);
 }
 
 bool StreamTexture::HasTextureOwner() const {
@@ -153,40 +153,25 @@
   return false;
 }
 
-bool StreamTexture::TextureOwnerBindsTextureOnUpdate() {
-  DCHECK(texture_owner_);
-  return texture_owner_->binds_texture_on_update();
-}
-
 void StreamTexture::OnContextLost() {
   texture_owner_ = nullptr;
 }
 
-void StreamTexture::UpdateTexImage(BindingsMode bindings_mode,
-                                   GLuint service_id) {
+void StreamTexture::UpdateTexImage(BindingsMode bindings_mode) {
   DCHECK(texture_owner_.get());
 
-  if (!has_pending_frame_) {
-    // Same frame can be bound multiple times to a different |service_id|. For
-    // eg: SharedImageVideo::ProduceGLTexture/ProduceSkia() and hence
-    // BeginAccess() can happen multiple times with the same frame by the time
-    // next frame is available. In those cases new service_id is generated and
-    // the frame although doesn't require any update, still needs to be bound to
-    // new service_id.
-    EnsureBoundIfNeeded(bindings_mode, service_id);
-    return;
-  }
+  if (!has_pending_frame_) return;
 
   std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current;
   base::Optional<ScopedRestoreTextureBinding> scoped_restore_texture;
-  if (texture_owner_->binds_texture_on_update()) {
+  if (texture_owner_->binds_texture_on_update() ||
+      (bindings_mode == BindingsMode::kEnsureTexImageBound)) {
     // If the texture_owner() binds the texture while doing the texture update
-    // (UpdateTexImage), like in SurfaceTexture case, then make sure that the
-    // texture owner's context is made current. This is because the texture
-    // which will be bound was generated on TextureOwner's context.
-    // For AImageReader case, the texture which will be bound will not
-    // necessarily be TextureOwner's texture and hence caller is responsible to
-    // handle making correct context current before binding the texture.
+    // (UpdateTexImage), like in SurfaceTexture case, OR if it was explicitly
+    // specified to bind the texture via bindings_mode, then only make the
+    // context current. For AImageReader, since we only acquire the latest image
+    // from it during the texture update process, there is no need to make it's
+    // context current if its not specified via bindings_mode.
     scoped_make_current = MakeCurrent(context_state_.get());
 
     // If updating the image will implicitly update the texture bindings then
@@ -197,24 +182,18 @@
     }
   }
   texture_owner_->UpdateTexImage();
-  EnsureBoundIfNeeded(bindings_mode, service_id);
+  EnsureBoundIfNeeded(bindings_mode);
   has_pending_frame_ = false;
 }
 
-void StreamTexture::EnsureBoundIfNeeded(BindingsMode mode, GLuint service_id) {
+void StreamTexture::EnsureBoundIfNeeded(BindingsMode mode) {
   DCHECK(texture_owner_);
 
-  if (texture_owner_->binds_texture_on_update()) {
-    if (mode == BindingsMode::kEnsureTexImageBound) {
-      DCHECK_EQ(service_id, texture_owner_->GetTextureId());
-    }
+  if (texture_owner_->binds_texture_on_update())
     return;
-  }
   if (mode != BindingsMode::kEnsureTexImageBound)
     return;
-
-  DCHECK_GT(service_id, static_cast<unsigned>(0));
-  texture_owner_->EnsureTexImageBound(service_id);
+  texture_owner_->EnsureTexImageBound();
 }
 
 bool StreamTexture::CopyTexImage(unsigned target) {
@@ -227,21 +206,17 @@
   GLint texture_id;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
 
-  // CopyTexImage will only be called for TextureOwner's SurfaceTexture
-  // implementation which binds texture to TextureOwner's texture_id on update.
-  // Also ensure that the CopyTexImage() is always called on TextureOwner's
-  // context.
-  DCHECK(texture_owner_->binds_texture_on_update());
-  DCHECK(texture_owner_->GetContext()->IsCurrent(nullptr));
+  // The following code only works if we're being asked to copy into
+  // |texture_id_|. Copying into a different texture is not supported.
+  // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
+  // glGetIntegerv() parameter. In this case the value of |texture_id| will be
+  // zero and we assume that it is properly bound to |texture_id_|.
   if (texture_id > 0 &&
       static_cast<unsigned>(texture_id) != texture_owner_->GetTextureId())
     return false;
 
-  // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
-  // glGetIntegerv() parameter. In this case the value of |texture_id| will be
-  // zero and we assume that it is properly bound to TextureOwner's texture id..
-  UpdateTexImage(BindingsMode::kEnsureTexImageBound,
-                 texture_owner_->GetTextureId());
+  UpdateTexImage(BindingsMode::kEnsureTexImageBound);
+
   return true;
 }
 
@@ -256,10 +231,7 @@
   if (rotated_visible_size_.IsEmpty())
     return;
 
-  // We only need TextureOwner to get the latest image and do not require it to
-  // be bound to a texture if TextureOwner does not binds texture on update.
-  // Hence pass service_id as 0.
-  UpdateTexImage(BindingsMode::kRestoreIfBound, /*service_id=*/0);
+  UpdateTexImage(BindingsMode::kEnsureTexImageBound);
 
   gfx::Rect visible_rect;
   gfx::Size coded_size;
@@ -335,16 +307,34 @@
   // need to ensure that it gets updated here.
 
   auto scoped_make_current = MakeCurrent(context_state_.get());
+
+  bool use_passthrough =
+      context_state_->feature_info()->is_passthrough_cmd_decoder();
+  std::unique_ptr<gles2::AbstractTexture> legacy_mailbox_texture;
+  if (use_passthrough) {
+    legacy_mailbox_texture =
+        std::make_unique<gles2::AbstractTextureImplOnSharedContextPassthrough>(
+            GL_TEXTURE_EXTERNAL_OES, context_state_);
+  } else {
+    legacy_mailbox_texture =
+        std::make_unique<gles2::AbstractTextureImplOnSharedContext>(
+            GL_TEXTURE_EXTERNAL_OES, GL_RGBA, coded_size.width(),
+            coded_size.height(), 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+            context_state_);
+  }
+  legacy_mailbox_texture->BindStreamTextureImage(
+      this, texture_owner_->GetTextureId());
+
   auto mailbox = gpu::Mailbox::GenerateForSharedImage();
 
   // TODO(vikassoni): Hardcoding colorspace to SRGB. Figure how if we have a
   // colorspace and wire it here.
   auto shared_image = std::make_unique<SharedImageVideo>(
       mailbox, coded_size, gfx::ColorSpace::CreateSRGB(),
-      kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, this, context_state_,
-      false);
+      kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, this,
+      std::move(legacy_mailbox_texture), context_state_, false);
   channel_->shared_image_stub()->factory()->RegisterBacking(
-      std::move(shared_image), /*allow_legacy_mailbox=*/false);
+      std::move(shared_image), true /* allow_legacy_mailbox */);
 
   return mailbox;
 }
@@ -408,8 +398,7 @@
 
   // Using BindingsMode::kDontRestoreIfBound here since we do not want to bind
   // the image. We just want to get the AHardwareBuffer from the latest image.
-  // Hence pass service_id as 0.
-  UpdateTexImage(BindingsMode::kDontRestoreIfBound, /*service_id=*/0);
+  UpdateTexImage(BindingsMode::kDontRestoreIfBound);
   return texture_owner_->GetAHardwareBuffer();
 }
 
diff --git a/gpu/ipc/service/stream_texture_android.h b/gpu/ipc/service/stream_texture_android.h
index 2f49e65..f1a4e69 100644
--- a/gpu/ipc/service/stream_texture_android.h
+++ b/gpu/ipc/service/stream_texture_android.h
@@ -82,27 +82,17 @@
   // gpu::StreamTextureSharedImageInterface implementation.
   void ReleaseResources() override {}
   bool IsUsingGpuMemory() const override;
-  void UpdateAndBindTexImage(GLuint service_id) override;
+  void UpdateAndBindTexImage() override;
   bool HasTextureOwner() const override;
   TextureBase* GetTextureBase() const override;
   void NotifyOverlayPromotion(bool promotion, const gfx::Rect& bounds) override;
   bool RenderToOverlay() override;
-  bool TextureOwnerBindsTextureOnUpdate() override;
 
   // SharedContextState::ContextLostObserver implementation.
   void OnContextLost() override;
 
-  // Update the TextureOwner to get the latest image. Also bind the latest image
-  // to the provided |service_id| if TextureOwner does not binds texture on
-  // update. If |bindings_mode| is other than kEnsureTexImageBound, then
-  // |service_id| is not required.
-  void UpdateTexImage(BindingsMode bindings_mode, GLuint service_id);
-
-  // Ensure that the latest image is bound to the texture |service_id| if
-  // TextureOwner does not binds texture on update. If TextureOwner binds
-  // texture on update, then it will always be bound to the TextureOwners
-  // texture and |service_id| will be ignored.
-  void EnsureBoundIfNeeded(BindingsMode mode, GLuint service_id);
+  void UpdateTexImage(BindingsMode bindings_mode);
+  void EnsureBoundIfNeeded(BindingsMode mode);
   gpu::Mailbox CreateSharedImage(const gfx::Size& coded_size);
 
   // Called when a new frame is available for the SurfaceOwner.
diff --git a/infra/config/generated/cr-buildbucket-dev.cfg b/infra/config/generated/cr-buildbucket-dev.cfg
index 623cbf4..1d11d9e 100644
--- a/infra/config/generated/cr-buildbucket-dev.cfg
+++ b/infra/config/generated/cr-buildbucket-dev.cfg
@@ -205,7 +205,7 @@
       service_account: "chromium-ci-builder-dev@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 8e06a464..716caf9 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -3516,7 +3516,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -3580,7 +3580,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -5380,7 +5380,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -5444,7 +5444,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -6066,7 +6066,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -6130,7 +6130,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -7186,7 +7186,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -10540,7 +10540,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -10605,7 +10605,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -10669,7 +10669,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -10734,7 +10734,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -10798,7 +10798,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -10862,7 +10862,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -11295,7 +11295,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -11731,7 +11731,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -12043,7 +12043,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -12107,7 +12107,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -14961,7 +14961,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -15027,7 +15027,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -15093,7 +15093,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -15159,7 +15159,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -15724,7 +15724,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -15793,7 +15793,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23291,7 +23291,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23421,7 +23421,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23489,7 +23489,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23557,7 +23557,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23626,7 +23626,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23694,7 +23694,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23762,7 +23762,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23830,7 +23830,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23898,7 +23898,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -23966,7 +23966,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -24034,7 +24034,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -24102,7 +24102,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -24170,7 +24170,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -24238,7 +24238,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -24306,7 +24306,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -27782,7 +27782,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -27908,7 +27908,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28159,7 +28159,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28224,7 +28224,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28288,7 +28288,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28478,7 +28478,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28543,7 +28543,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28608,7 +28608,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28672,7 +28672,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28799,7 +28799,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28864,7 +28864,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28928,7 +28928,7 @@
       service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -28992,7 +28992,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -29056,7 +29056,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -29121,7 +29121,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -33244,7 +33244,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -33274,7 +33274,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -33304,7 +33304,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -33334,7 +33334,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -33524,7 +33524,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -33608,7 +33608,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -33638,7 +33638,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -34093,7 +34093,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -34126,7 +34126,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -34264,7 +34264,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -34294,7 +34294,7 @@
       service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -39102,7 +39102,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -39254,7 +39254,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42304,7 +42304,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42368,7 +42368,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42432,7 +42432,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42496,7 +42496,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42560,7 +42560,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42624,7 +42624,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42688,7 +42688,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42752,7 +42752,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42816,7 +42816,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42880,7 +42880,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -42944,7 +42944,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -43008,7 +43008,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -43072,7 +43072,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44112,7 +44112,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44176,7 +44176,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44305,7 +44305,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44376,7 +44376,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44447,7 +44447,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44518,7 +44518,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44589,7 +44589,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44660,7 +44660,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44731,7 +44731,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44802,7 +44802,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44873,7 +44873,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -44944,7 +44944,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -45015,7 +45015,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -45086,7 +45086,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -45157,7 +45157,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -46690,7 +46690,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -50597,7 +50597,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -50664,7 +50664,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -50732,7 +50732,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -50799,7 +50799,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -50867,7 +50867,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -50934,7 +50934,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51002,7 +51002,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51070,7 +51070,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51138,7 +51138,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51202,7 +51202,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51269,7 +51269,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51336,7 +51336,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51404,7 +51404,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51472,7 +51472,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51540,7 +51540,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51608,7 +51608,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51676,7 +51676,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51744,7 +51744,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51812,7 +51812,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51880,7 +51880,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -51948,7 +51948,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52016,7 +52016,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52084,7 +52084,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52152,7 +52152,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52220,7 +52220,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52288,7 +52288,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52356,7 +52356,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52424,7 +52424,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52491,7 +52491,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52559,7 +52559,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52627,7 +52627,7 @@
       }
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "chromium.resultdb.result_sink"
@@ -52801,7 +52801,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -52866,7 +52866,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -55558,7 +55558,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -55586,7 +55586,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -55919,7 +55919,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -55948,7 +55948,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -55976,7 +55976,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -56128,7 +56128,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
@@ -56160,7 +56160,7 @@
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
       experiments {
         key: "chromium.chromium_tests.use_rbe_cas"
-        value: 20
+        value: 50
       }
       experiments {
         key: "luci.use_realms"
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index 7f0aec8..b2536b5 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -588,7 +588,7 @@
     # TODO(crbug.com/1143122): remove this.
     experiments = experiments or {}
     if os and os.category == os_category.MAC:
-        experiments["chromium.chromium_tests.use_rbe_cas"] = 20
+        experiments["chromium.chromium_tests.use_rbe_cas"] = 50
     kwargs["experiments"] = experiments
 
     configure_kitchen = defaults.get_value("configure_kitchen", configure_kitchen)
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 2a6a6727..992fb7df 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -1061,6 +1061,7 @@
         disable_reuse = True,
         add_default_excludes = False,
     ),
+    os = os.LINUX_BIONIC_REMOVE,
 )
 
 try_.chromium_linux_builder(
@@ -1201,6 +1202,7 @@
     name = "linux-clang-tidy-dbg",
     executable = "recipe:tricium_clang_tidy_wrapper",
     goma_jobs = goma.jobs.J150,
+    os = os.LINUX_BIONIC_REMOVE,
 )
 
 try_.chromium_linux_builder(
@@ -1556,11 +1558,13 @@
 try_.chromium_linux_builder(
     name = "tricium-oilpan-analysis",
     executable = "recipe:tricium_oilpan",
+    os = os.LINUX_BIONIC_REMOVE,
 )
 
 try_.chromium_linux_builder(
     name = "tricium-simple",
     executable = "recipe:tricium_simple",
+    os = os.LINUX_BIONIC_REMOVE,
 )
 
 try_.chromium_mac_builder(
diff --git a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
index cbf799b..af465d3 100644
--- a/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_screen_view_controller.mm
@@ -235,6 +235,8 @@
 }
 
 - (void)viewWillAppear:(BOOL)animated {
+  [super viewWillAppear:animated];
+
   // Only add the scroll view delegate after all the view layouts are fully
   // done.
   dispatch_async(dispatch_get_main_queue(), ^{
diff --git a/ios/chrome/browser/ui/first_run/resources/BUILD.gn b/ios/chrome/browser/ui/first_run/resources/BUILD.gn
index 00ee4ba..ce9af4e 100644
--- a/ios/chrome/browser/ui/first_run/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/resources/BUILD.gn
@@ -64,6 +64,16 @@
   ]
 }
 
+imageset("welcome_screen_banner") {
+  sources = [
+    "welcome_screen_banner.imageset/Contents.json",
+    "welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png",
+    "welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png",
+    "welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png",
+    "welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png",
+  ]
+}
+
 imageset("welcome_metrics_checkmark") {
   sources = [
     "welcome_metrics_checkmark.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/Contents.json b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/Contents.json
new file mode 100644
index 0000000..bb15c9bd
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/Contents.json
@@ -0,0 +1,40 @@
+{
+  "images" : [
+    {
+      "filename" : "welcome_screen_banner_light@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "filename" : "welcome_screen_banner_dark@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "welcome_screen_banner_light@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "filename" : "welcome_screen_banner_dark@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png
new file mode 100644
index 0000000..1d18224
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png
new file mode 100644
index 0000000..46d7045
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_dark@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png
new file mode 100644
index 0000000..f33edf85
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png
new file mode 100644
index 0000000..76319c9
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/welcome_screen_banner.imageset/welcome_screen_banner_light@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/welcome/BUILD.gn b/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
index 2d5cf8b..a65930e 100644
--- a/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
@@ -33,7 +33,10 @@
   deps = [
     "//ios/chrome/browser/ui/first_run:first_run_ui",
     "//ios/chrome/browser/ui/first_run/resources:welcome_metrics_checkmark",
+    "//ios/chrome/browser/ui/first_run/resources:welcome_screen_banner",
+    "//ios/chrome/common",
     "//ios/chrome/common/ui/colors",
+    "//ios/chrome/common/ui/util",
   ]
   frameworks = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
index 16e72f9..e46a3ac0 100644
--- a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
@@ -4,18 +4,130 @@
 
 #import "ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.h"
 
+#import "ios/chrome/browser/ui/first_run/welcome/checkbox_button.h"
+#import "ios/chrome/common/string_util.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/pointer_interaction_util.h"
+
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
+namespace {
+
+constexpr CGFloat kDefaultMargin = 16;
+
+// URL for the terms of service text.
+NSString* const kTermsOfServiceUrl = @"internal://terms-of-service";
+
+}  // namespace
+
+@interface WelcomeScreenViewController ()
+
+@property(nonatomic, strong) CheckboxButton* metricsConsentButton;
+@property(nonatomic, strong) UILabel* termsOfServiceLabel;
+
+@end
+
 @implementation WelcomeScreenViewController
 @dynamic delegate;
 
 - (void)viewDidLoad {
-  // TODO(crbug.com/1189815): set strings and images to the view.
-  self.titleText = @"Test Welcome Screen";
-  self.primaryActionString = @"Test Continue Button";
+  // TODO(crbug.com/1189815): Use final strings and enable localization.
+  self.titleText = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
+                       ? @"Test - Built for your iPad"
+                       : @"Test - Built for your iPhone";
+  self.subtitleText = @"Test - Get more done with a simple, secure and "
+                      @"faster-than-ever Google Chrome";
+  self.bannerImage = [UIImage imageNamed:@"welcome_screen_banner"];
+  self.isTallBanner = YES;
+  self.scrollToEndMandatory = YES;
+  self.primaryActionString = @"Test - Accept and Continue";
+
+  self.metricsConsentButton = [self createMetricsConsentButton];
+  [self.specificContentView addSubview:self.metricsConsentButton];
+
+  self.termsOfServiceLabel = [self createTermsOfServiceLabel];
+  [self.specificContentView addSubview:self.termsOfServiceLabel];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [self.metricsConsentButton.topAnchor
+        constraintGreaterThanOrEqualToAnchor:self.specificContentView
+                                                 .topAnchor],
+    [self.metricsConsentButton.centerXAnchor
+        constraintEqualToAnchor:self.specificContentView.centerXAnchor],
+    [self.metricsConsentButton.widthAnchor
+        constraintEqualToAnchor:self.specificContentView.widthAnchor],
+
+    [self.termsOfServiceLabel.topAnchor
+        constraintEqualToAnchor:self.metricsConsentButton.bottomAnchor
+                       constant:kDefaultMargin],
+    [self.termsOfServiceLabel.centerXAnchor
+        constraintEqualToAnchor:self.specificContentView.centerXAnchor],
+    [self.termsOfServiceLabel.widthAnchor
+        constraintLessThanOrEqualToAnchor:self.specificContentView.widthAnchor],
+    [self.termsOfServiceLabel.bottomAnchor
+        constraintEqualToAnchor:self.specificContentView.bottomAnchor],
+  ]];
+
   [super viewDidLoad];
 }
 
+#pragma mark - Private
+
+// Creates and configures the UMA consent checkbox button.
+- (CheckboxButton*)createMetricsConsentButton {
+  CheckboxButton* button = [[CheckboxButton alloc] initWithFrame:CGRectZero];
+  button.translatesAutoresizingMaskIntoConstraints = NO;
+  // TODO(crbug.com/1189815): Use final strings and enable localization.
+  button.labelText = @"Test - Help improve Chrome by sending usage statistics "
+                     @"and crash reports to Google";
+  button.selected = YES;
+  [button addTarget:self
+                action:@selector(didTapMetricsButton)
+      forControlEvents:UIControlEventTouchUpInside];
+
+  if (@available(iOS 13.4, *)) {
+    button.pointerInteractionEnabled = YES;
+    button.pointerStyleProvider = CreateOpaqueButtonPointerStyleProvider();
+  }
+
+  return button;
+}
+
+// Creates and configures the label for the terms of service, with a formatted
+// link to the full text of the terms of service.
+- (UILabel*)createTermsOfServiceLabel {
+  // TODO(crbug.com/1189815): 1) Handle taps to display the ToS text; 2) Use a
+  // UITextView so only the link part is tappable.
+  UILabel* label = [[UILabel alloc] init];
+  label.numberOfLines = 0;
+  label.textAlignment = NSTextAlignmentCenter;
+  label.translatesAutoresizingMaskIntoConstraints = NO;
+  label.adjustsFontForContentSizeCategory = YES;
+
+  NSDictionary* textAttributes = @{
+    NSForegroundColorAttributeName : [UIColor colorNamed:kTextSecondaryColor],
+    NSFontAttributeName :
+        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1]
+  };
+  NSDictionary* linkAttributes = @{
+    NSForegroundColorAttributeName : [UIColor colorNamed:kBlueColor],
+    NSFontAttributeName :
+        [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1],
+    NSLinkAttributeName : [NSURL URLWithString:kTermsOfServiceUrl]
+  };
+  NSAttributedString* attributedString = AttributedStringFromStringWithLink(
+      @"Test - By continuing, you agree to the BEGIN_LINKTerms of "
+      @"ServiceEND_LINK",
+      textAttributes, linkAttributes);
+
+  label.attributedText = attributedString;
+  return label;
+}
+
+- (void)didTapMetricsButton {
+  self.metricsConsentButton.selected = !self.metricsConsentButton.selected;
+}
+
 @end
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 dc55c01..c2dedf8 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 @@
-07d798a0b3510d8426cdc7b2b4058cbf1dd63b2a
\ No newline at end of file
+05678c1067df4c3b34be3eb08b10d6187090cbf6
\ 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 3cc5ff7..499baf9 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 @@
-e2602accb467f3d5164ccbb5d4ffb8113cef6f97
\ No newline at end of file
+f08dda4250d5915567e5994bee564b435b77a8c0
\ 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 04eec7f..0197934e 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 @@
-917faa5d5a51807dc37726771c33578170979de3
\ No newline at end of file
+1352605265121957c7f7539a2af83de3a0fdf869
\ 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 03477f3..0e65acf 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 @@
-4d7e381c2e1c2488e4c8ee66d5a17043f6a18085
\ No newline at end of file
+527693c37d8b0ff260d0eac25c35d78b7045ed58
\ 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 5b6d5a0..5a60a7e3 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 @@
-b2f3e4dca9999a4f5bcf030e2240a25f7423dd51
\ No newline at end of file
+cc29e2a2c43bb70e907e9782a997a0a72197e089
\ 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 058cd04..3c75e431 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 @@
-0ae44244f71c832e4236acbb9345389188987320
\ No newline at end of file
+f543ad1571391f7585f45fa5a992eedc6aca8b93
\ 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 dd36128..02ce6fe 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 @@
-67a5c83bf6880887fa0adfaa6a178642a89567bf
\ No newline at end of file
+e2639b8234082cd4bc07ea8b962d2fd585b2ffe8
\ 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 b5d836d..33d619e8 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 @@
-2fea9323edb5bfd848301f30f2769cb4230b27ed
\ No newline at end of file
+8bfc2e7e084e5c692568d1f71d693f58d030cddd
\ 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 ffb8615..d5b4c9a0 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 @@
-85ebb65997ff17831423f685e185ac88530d968d
\ No newline at end of file
+aef4528fa839b6d9bc7b9a30a8df00d45cee7435
\ 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 c451e670..4a55be7 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 @@
-fefea120624adf39063d817c90c7eadb0a007287
\ No newline at end of file
+e03cd74b6726547240c055587258e4a0f18ef502
\ No newline at end of file
diff --git a/media/base/key_system_properties.h b/media/base/key_system_properties.h
index 032f745..0cd832ff 100644
--- a/media/base/key_system_properties.h
+++ b/media/base/key_system_properties.h
@@ -40,9 +40,17 @@
   virtual SupportedCodecs GetSupportedHwSecureCodecs() const;
 
   // Returns the configuration rule for supporting a robustness requirement.
+  // If `hw_secure_requirement` is true, then the key system already has a HW
+  // secure requirement, if false then it already has a requirement to disallow
+  // HW secure; if null then there is no HW secure requirement to apply. This
+  // does not imply that `requested_robustness` should be ignored, both rules
+  // must be applied.
+  // TODO(crbug.com/1204284): Refactor this and remove the
+  // `hw_secure_requirement` argument.
   virtual EmeConfigRule GetRobustnessConfigRule(
       EmeMediaType media_type,
-      const std::string& requested_robustness) const = 0;
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const = 0;
 
   // Returns the support this key system provides for persistent-license
   // sessions.
diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc
index be6b084..03c1007b 100644
--- a/media/base/key_systems.cc
+++ b/media/base/key_systems.cc
@@ -162,7 +162,8 @@
 
   EmeConfigRule GetRobustnessConfigRule(
       EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
     return requested_robustness.empty() ? EmeConfigRule::SUPPORTED
                                         : EmeConfigRule::NOT_SUPPORTED;
   }
@@ -264,7 +265,8 @@
   EmeConfigRule GetRobustnessConfigRule(
       const std::string& key_system,
       EmeMediaType media_type,
-      const std::string& requested_robustness) const override;
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const override;
 
   EmeSessionTypeSupport GetPersistentLicenseSessionSupport(
       const std::string& key_system) const override;
@@ -711,7 +713,8 @@
 EmeConfigRule KeySystemsImpl::GetRobustnessConfigRule(
     const std::string& key_system,
     EmeMediaType media_type,
-    const std::string& requested_robustness) const {
+    const std::string& requested_robustness,
+    const bool* hw_secure_requirement) const {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   auto key_system_iter = key_system_properties_map_.find(key_system);
@@ -719,8 +722,8 @@
     NOTREACHED();
     return EmeConfigRule::NOT_SUPPORTED;
   }
-  return key_system_iter->second->GetRobustnessConfigRule(media_type,
-                                                          requested_robustness);
+  return key_system_iter->second->GetRobustnessConfigRule(
+      media_type, requested_robustness, hw_secure_requirement);
 }
 
 EmeSessionTypeSupport KeySystemsImpl::GetPersistentLicenseSessionSupport(
diff --git a/media/base/key_systems.h b/media/base/key_systems.h
index 34fd6a46..9d2e2b7 100644
--- a/media/base/key_systems.h
+++ b/media/base/key_systems.h
@@ -56,10 +56,18 @@
       const std::vector<std::string>& codecs) const = 0;
 
   // Returns the configuration rule for supporting a robustness requirement.
+  // If `hw_secure_requirement` is true, then the key system already has a HW
+  // secure requirement, if false then it already has a requirement to disallow
+  // HW secure; if null then there is no HW secure requirement to apply. This
+  // does not imply that `requested_robustness` should be ignored, both rules
+  // must be applied.
+  // TODO(crbug.com/1204284): Refactor this and remove the
+  // `hw_secure_requirement` argument.
   virtual EmeConfigRule GetRobustnessConfigRule(
       const std::string& key_system,
       EmeMediaType media_type,
-      const std::string& requested_robustness) const = 0;
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const = 0;
 
   // Returns the support |key_system| provides for persistent-license sessions.
   virtual EmeSessionTypeSupport GetPersistentLicenseSessionSupport(
diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc
index e38e2b3..b0f7001 100644
--- a/media/base/key_systems_unittest.cc
+++ b/media/base/key_systems_unittest.cc
@@ -76,7 +76,8 @@
 
   EmeConfigRule GetRobustnessConfigRule(
       EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
     return requested_robustness.empty() ? EmeConfigRule::SUPPORTED
                                         : EmeConfigRule::NOT_SUPPORTED;
   }
@@ -140,7 +141,8 @@
 
   EmeConfigRule GetRobustnessConfigRule(
       EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* /*hw_secure_requirement*/) const override {
     if (requested_robustness == kRobustnessSupported)
       return EmeConfigRule::SUPPORTED;
     else if (requested_robustness == kRobustnessSecureCodecsRequired)
@@ -206,7 +208,7 @@
 
 EmeConfigRule GetRobustnessConfigRule(const std::string& requested_robustness) {
   return KeySystems::GetInstance()->GetRobustnessConfigRule(
-      kExternal, EmeMediaType::VIDEO, requested_robustness);
+      kExternal, EmeMediaType::VIDEO, requested_robustness, nullptr);
 }
 
 // Adds test container and codec masks.
diff --git a/media/blink/key_system_config_selector.cc b/media/blink/key_system_config_selector.cc
index b99382fb..298b74a 100644
--- a/media/blink/key_system_config_selector.cc
+++ b/media/blink/key_system_config_selector.cc
@@ -243,6 +243,10 @@
     return are_hw_secure_codecs_required_;
   }
 
+  bool AreHwSecureCodesNotAllowed() const {
+    return are_hw_secure_codecs_not_allowed_;
+  }
+
   // Checks whether a rule is compatible with all previously added rules.
   bool IsRuleSupported(EmeConfigRule rule) const {
     switch (rule) {
@@ -511,8 +515,20 @@
         continue;
       requested_robustness_ascii = capability.robustness.Ascii();
     }
+    // Both of these should not be true.
+    DCHECK(!(proposed_config_state.AreHwSecureCodecsRequired() &&
+             proposed_config_state.AreHwSecureCodesNotAllowed()));
+    bool hw_secure_requirement;
+    bool* hw_secure_requirement_ptr = &hw_secure_requirement;
+    if (proposed_config_state.AreHwSecureCodecsRequired())
+      hw_secure_requirement = true;
+    else if (proposed_config_state.AreHwSecureCodesNotAllowed())
+      hw_secure_requirement = false;
+    else
+      hw_secure_requirement_ptr = nullptr;
     EmeConfigRule robustness_rule = key_systems_->GetRobustnessConfigRule(
-        key_system, media_type, requested_robustness_ascii);
+        key_system, media_type, requested_robustness_ascii,
+        hw_secure_requirement_ptr);
 
     // 3.13. If the user agent and implementation definitely support playback of
     //       encrypted media data for the combination of container, media types,
diff --git a/media/blink/key_system_config_selector_unittest.cc b/media/blink/key_system_config_selector_unittest.cc
index 7b65983..ea82741 100644
--- a/media/blink/key_system_config_selector_unittest.cc
+++ b/media/blink/key_system_config_selector_unittest.cc
@@ -285,7 +285,15 @@
   EmeConfigRule GetRobustnessConfigRule(
       const std::string& key_system,
       EmeMediaType media_type,
-      const std::string& requested_robustness) const override {
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const override {
+    // TODO(crbug.com/1204284): Remove the `hw_secure_requirement` parameter.
+    // This only exists as a temporary solution until a larger refactoring is
+    // done. We are only testing the explicit thing it is fixing here.
+    if (hw_secure_requirement && *hw_secure_requirement &&
+        distinctive_identifier == EmeFeatureSupport::NOT_SUPPORTED) {
+      return EmeConfigRule::NOT_SUPPORTED;
+    }
     if (requested_robustness.empty())
       return EmeConfigRule::SUPPORTED;
     if (requested_robustness == kSupportedRobustness)
@@ -1570,6 +1578,24 @@
   EXPECT_TRUE(cdm_config_.use_hw_secure_codecs);
 }
 
+TEST_F(KeySystemConfigSelectorTest,
+       HwSecureCodecAndIdentifier_IdentifierAndHwSecureCodecsDisjoint) {
+  media_permission_->is_granted = false;
+  key_systems_->distinctive_identifier = EmeFeatureSupport::NOT_SUPPORTED;
+
+  std::vector<WebMediaKeySystemMediaCapability> video_capabilities(2);
+  video_capabilities[0].content_type = "require_hw_secure_codec";
+  video_capabilities[0].mime_type = kSupportedVideoContainer;
+  video_capabilities[0].codecs = kRequireHwSecureCodec;
+  video_capabilities[0].robustness = "";
+
+  auto config = EmptyConfiguration();
+  config.video_capabilities = video_capabilities;
+  configs_.push_back(config);
+
+  SelectConfigReturnsError();
+}
+
 // --- Identifier, Persistence and HW Secure Robustness ---
 
 TEST_F(KeySystemConfigSelectorTest,
diff --git a/media/blink/websourcebuffer_impl.cc b/media/blink/websourcebuffer_impl.cc
index 96fb031..ff928eb 100644
--- a/media/blink/websourcebuffer_impl.cc
+++ b/media/blink/websourcebuffer_impl.cc
@@ -50,14 +50,11 @@
   if (time == std::numeric_limits<double>::infinity())
     return kInfiniteDuration;
 
-  // Don't use base::TimeDelta::Max() here, as we want the largest finite time
-  // delta.
-  base::TimeDelta max_time = base::TimeDelta::FromInternalValue(
-      std::numeric_limits<int64_t>::max() - 1);
-  double max_time_in_seconds = max_time.InSecondsF();
+  constexpr double max_time_in_seconds =
+      base::TimeDelta::FiniteMax().InSecondsF();
 
   if (time >= max_time_in_seconds)
-    return max_time;
+    return base::TimeDelta::FiniteMax();
 
   return base::TimeDelta::FromMicroseconds(
       time * base::Time::kMicrosecondsPerSecond);
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index f176cad..3721ceb 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -84,24 +84,17 @@
   if (!output_buffer_renderer_)
     return true;
 
-  GLint texture_id = 0;
-  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_id);
-
-  // CopyTexImage will only be called for TextureOwner's SurfaceTexture
-  // implementation which binds texture to TextureOwner's texture_id on update.
-  DCHECK(output_buffer_renderer_->texture_owner()->binds_texture_on_update());
-  if (texture_id > 0 &&
-      static_cast<unsigned>(texture_id) !=
-          output_buffer_renderer_->texture_owner()->GetTextureId()) {
+  GLint bound_service_id = 0;
+  glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
+  // The currently bound texture should be the texture owner's texture.
+  if (bound_service_id !=
+      static_cast<GLint>(
+          output_buffer_renderer_->texture_owner()->GetTextureId()))
     return false;
-  }
 
-  // On some devices GL_TEXTURE_BINDING_EXTERNAL_OES is not supported as
-  // glGetIntegerv() parameter. In this case the value of |texture_id| will be
-  // zero and we assume that it is properly bound to TextureOwner's texture id.
+
   output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
-      BindingsMode::kEnsureTexImageBound,
-      output_buffer_renderer_->texture_owner()->GetTextureId());
+      BindingsMode::kEnsureTexImageBound);
   return true;
 }
 
@@ -175,9 +168,8 @@
   return output_buffer_renderer_->was_tex_image_bound();
 }
 
-void CodecImage::UpdateAndBindTexImage(GLuint service_id) {
-  RenderToTextureOwnerFrontBuffer(BindingsMode::kEnsureTexImageBound,
-                                  service_id);
+void CodecImage::UpdateAndBindTexImage() {
+  RenderToTextureOwnerFrontBuffer(BindingsMode::kEnsureTexImageBound);
 }
 
 bool CodecImage::HasTextureOwner() const {
@@ -201,12 +193,11 @@
   return output_buffer_renderer_->RenderToTextureOwnerBackBuffer();
 }
 
-bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
-                                                 GLuint service_id) {
+bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
   if (!output_buffer_renderer_)
     return false;
-  return output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(bindings_mode,
-                                                                  service_id);
+  return output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
+      bindings_mode);
 }
 
 bool CodecImage::RenderToOverlay() {
@@ -215,12 +206,6 @@
   return output_buffer_renderer_->RenderToOverlay();
 }
 
-bool CodecImage::TextureOwnerBindsTextureOnUpdate() {
-  if (!output_buffer_renderer_)
-    return false;
-  return output_buffer_renderer_->texture_owner()->binds_texture_on_update();
-}
-
 void CodecImage::ReleaseCodecBuffer() {
   output_buffer_renderer_.reset();
 }
@@ -234,11 +219,7 @@
   if (!output_buffer_renderer_)
     return nullptr;
 
-  // Using BindingsMode::kDontRestoreIfBound here since we do not want to bind
-  // the image. We just want to get the AHardwareBuffer from the latest image.
-  // Hence pass service_id as 0.
-  RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestoreIfBound,
-                                  0 /* service_id */);
+  RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestoreIfBound);
   return output_buffer_renderer_->texture_owner()->GetAHardwareBuffer();
 }
 
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index 531f0d6..d71cda8f 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -99,14 +99,13 @@
   // gpu::StreamTextureSharedImageInterface implementation.
   void ReleaseResources() override;
   bool IsUsingGpuMemory() const override;
-  void UpdateAndBindTexImage(GLuint service_id) override;
+  void UpdateAndBindTexImage() override;
   bool HasTextureOwner() const override;
   gpu::TextureBase* GetTextureBase() const override;
   void NotifyOverlayPromotion(bool promotion, const gfx::Rect& bounds) override;
   // Renders this image to the overlay. Returns true if the buffer is in the
   // overlay front buffer. Returns false if the buffer was invalidated.
   bool RenderToOverlay() override;
-  bool TextureOwnerBindsTextureOnUpdate() override;
 
   // Whether the codec buffer has been rendered to the front buffer.
   bool was_rendered_to_front_buffer() const {
@@ -157,12 +156,7 @@
   // Renders this image to the texture owner front buffer by first rendering
   // it to the back buffer if it's not already there, and then waiting for the
   // frame available event before calling UpdateTexImage().
-  // Also bind the latest image
-  // to the provided |service_id| if TextureOwner does not binds texture on
-  // update. If |bindings_mode| is other than kEnsureTexImageBound, then
-  // |service_id| is not required.
-  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
-                                       GLuint service_id);
+  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
 
   // Whether this image is texture_owner or overlay backed.
   bool is_texture_owner_backed_ = false;
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index bdc1676a..29a09b3 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -207,6 +207,20 @@
   ASSERT_TRUE(i->was_rendered_to_front_buffer());
 }
 
+TEST_F(CodecImageTestExplicitBind, CopyTexImageTriggersFrontBufferRendering) {
+  auto i = NewImage(kTextureOwner);
+  // Verify that the release comes before the wait.
+  InSequence s;
+  EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
+  EXPECT_CALL(*codec_buffer_wait_coordinator_, WaitForFrameAvailable());
+  EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
+              UpdateTexImage());
+  EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
+              EnsureTexImageBound());
+  i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES);
+  ASSERT_TRUE(i->was_rendered_to_front_buffer());
+}
+
 TEST_F(CodecImageTest, ScheduleOverlayPlaneTriggersFrontBufferRendering) {
   auto i = NewImage(kOverlay);
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
@@ -366,8 +380,7 @@
   i->NotifyUnused();
   EXPECT_FALSE(i->RenderToTextureOwnerBackBuffer());
   EXPECT_FALSE(i->RenderToTextureOwnerFrontBuffer(
-      CodecImage::BindingsMode::kEnsureTexImageBound,
-      codec_buffer_wait_coordinator_->texture_owner()->GetTextureId()));
+      CodecImage::BindingsMode::kEnsureTexImageBound));
 }
 
 TEST_F(CodecImageTest, CodedSizeVsVisibleSize) {
diff --git a/media/gpu/android/codec_output_buffer_renderer.cc b/media/gpu/android/codec_output_buffer_renderer.cc
index 22684df..9d93ed1 100644
--- a/media/gpu/android/codec_output_buffer_renderer.cc
+++ b/media/gpu/android/codec_output_buffer_renderer.cc
@@ -14,7 +14,6 @@
 #include "ui/gl/scoped_make_current.h"
 
 namespace media {
-
 namespace {
 
 // Makes |texture_owner|'s context current if it isn't already.
@@ -81,8 +80,7 @@
 }
 
 bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer(
-    BindingsMode bindings_mode,
-    GLuint service_id) {
+    BindingsMode bindings_mode) {
   // Normally, we should have a wait coordinator if we're called.  However, if
   // the renderer is torn down (either VideoFrameSubmitter or the whole process)
   // before we get returns back from viz, then we can be notified that we're
@@ -92,7 +90,7 @@
     return false;
 
   if (phase_ == Phase::kInFrontBuffer) {
-    EnsureBoundIfNeeded(bindings_mode, service_id);
+    EnsureBoundIfNeeded(bindings_mode);
     return true;
   }
   if (phase_ == Phase::kInvalidated)
@@ -102,14 +100,14 @@
   base::Optional<gpu::ScopedRestoreTextureBinding> scoped_restore_texture;
 
   if (codec_buffer_wait_coordinator_->texture_owner()
-          ->binds_texture_on_update()) {
+          ->binds_texture_on_update() ||
+      (bindings_mode == BindingsMode::kEnsureTexImageBound)) {
     // If the texture_owner() binds the texture while doing the texture update
-    // (UpdateTexImage), like in SurfaceTexture case, then make sure that the
-    // texture owner's context is made current. This is because the texture
-    // which will be bound was generated on TextureOwner's context.
-    // For AImageReader case, the texture which will be bound will not
-    // necessarily be TextureOwner's texture and hence caller is responsible to
-    // handle making correct context current before binding the texture.
+    // (UpdateTexImage), like in SurfaceTexture case, OR if it was explicitly
+    // specified to bind the texture via bindings_mode, then only make the
+    // context current. For AImageReader, since we only acquire the latest image
+    // from it during the texture update process, there is no need to make it's
+    // context current if its not specified via bindings_mode.
     scoped_make_current = MakeCurrentIfNeeded(
         codec_buffer_wait_coordinator_->texture_owner().get());
 
@@ -146,30 +144,21 @@
     codec_buffer_wait_coordinator_->WaitForFrameAvailable();
 
   codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
-  EnsureBoundIfNeeded(bindings_mode, service_id);
+  EnsureBoundIfNeeded(bindings_mode);
   return true;
 }
 
-void CodecOutputBufferRenderer::EnsureBoundIfNeeded(BindingsMode mode,
-                                                    GLuint service_id) {
+void CodecOutputBufferRenderer::EnsureBoundIfNeeded(BindingsMode mode) {
   DCHECK(codec_buffer_wait_coordinator_);
 
   if (codec_buffer_wait_coordinator_->texture_owner()
           ->binds_texture_on_update()) {
-    if (mode == BindingsMode::kEnsureTexImageBound) {
-      DCHECK_EQ(
-          service_id,
-          codec_buffer_wait_coordinator_->texture_owner()->GetTextureId());
-    }
     was_tex_image_bound_ = true;
     return;
   }
   if (mode != BindingsMode::kEnsureTexImageBound)
     return;
-
-  DCHECK_GT(service_id, 0u);
-  codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound(
-      service_id);
+  codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound();
   was_tex_image_bound_ = true;
 }
 
@@ -189,11 +178,9 @@
 
 bool CodecOutputBufferRenderer::RenderToFrontBuffer() {
   // This code is used to trigger early rendering of the image before it is used
-  // for compositing, there is no need to bind the image. Hence pass texture
-  // service_id as 0.
+  // for compositing, there is no need to bind the image.
   return codec_buffer_wait_coordinator_
-             ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound,
-                                               0 /* service_id */)
+             ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound)
              : RenderToOverlay();
 }
 
diff --git a/media/gpu/android/codec_output_buffer_renderer.h b/media/gpu/android/codec_output_buffer_renderer.h
index e8c2377..c60e8268 100644
--- a/media/gpu/android/codec_output_buffer_renderer.h
+++ b/media/gpu/android/codec_output_buffer_renderer.h
@@ -39,11 +39,7 @@
   // Renders this image to the texture owner front buffer by first rendering
   // it to the back buffer if it's not already there, and then waiting for the
   // frame available event before calling UpdateTexImage().
-  // Also bind the latest imagecto the provided |service_id| if TextureOwner
-  // does not binds texture on update. If |bindings_mode| is other than
-  // kEnsureTexImageBound, then |service_id| is not required.
-  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
-                                       GLuint service_id);
+  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
 
   // Renders this image to the front buffer of its backing surface.
   // Returns true if the buffer is in the front buffer. Returns false if the
@@ -84,11 +80,7 @@
   // kInFrontBuffer and kInvalidated are terminal.
   enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
 
-  // Ensure that the latest image is bound to the texture |service_id| if
-  // TextureOwner does not binds texture on update. If TextureOwner binds
-  // texture on update, then it will always be bound to the TextureOwners
-  // texture and |service_id| will be ignored.
-  void EnsureBoundIfNeeded(BindingsMode mode, GLuint service_id);
+  void EnsureBoundIfNeeded(BindingsMode mode);
 
   void set_phase_for_testing(Phase phase) { phase_ = phase; }
 
diff --git a/media/gpu/android/direct_shared_image_video_provider.cc b/media/gpu/android/direct_shared_image_video_provider.cc
index 8fcc9ca..11efbbe4 100644
--- a/media/gpu/android/direct_shared_image_video_provider.cc
+++ b/media/gpu/android/direct_shared_image_video_provider.cc
@@ -71,8 +71,10 @@
       .WithArgs(std::move(gpu_init_cb));
 }
 
-void DirectSharedImageVideoProvider::RequestImage(ImageReadyCB cb,
-                                                  const ImageSpec& spec) {
+void DirectSharedImageVideoProvider::RequestImage(
+    ImageReadyCB cb,
+    const ImageSpec& spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner) {
   // It's unclear that we should handle the image group, but since CodecImages
   // have to be registered on it, we do.  If the CodecImage is ever re-used,
   // then part of that re-use would be to call the (then mis-named)
@@ -85,7 +87,8 @@
   // Note: `cb` is only run on successful creation, so this does not use
   // `AsyncCall()` + `Then()` to chain the callbacks.
   gpu_factory_.AsyncCall(&GpuSharedImageVideoFactory::CreateImage)
-      .WithArgs(BindToCurrentLoop(std::move(cb)), spec);
+      .WithArgs(BindToCurrentLoop(std::move(cb)), spec,
+                std::move(texture_owner));
 }
 
 GpuSharedImageVideoFactory::GpuSharedImageVideoFactory(
@@ -110,6 +113,8 @@
     return;
   }
 
+  decoder_helper_ = GLES2DecoderHelper::Create(stub_->decoder_context());
+
   gpu::ContextResult result;
   auto shared_context = GetSharedContext(stub_, &result);
   if (!shared_context) {
@@ -140,7 +145,8 @@
 
 void GpuSharedImageVideoFactory::CreateImage(
     FactoryImageReadyCB image_ready_cb,
-    const SharedImageVideoProvider::ImageSpec& spec) {
+    const SharedImageVideoProvider::ImageSpec& spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // Generate a shared image mailbox.
@@ -149,7 +155,8 @@
 
   TRACE_EVENT0("media", "GpuSharedImageVideoFactory::CreateVideoFrame");
 
-  if (!CreateImageInternal(spec, mailbox, codec_image)) {
+  if (!CreateImageInternal(spec, std::move(texture_owner), mailbox,
+                           codec_image)) {
     return;
   }
 
@@ -185,6 +192,7 @@
 
 bool GpuSharedImageVideoFactory::CreateImageInternal(
     const SharedImageVideoProvider::ImageSpec& spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner,
     gpu::Mailbox mailbox,
     scoped_refptr<CodecImage> image) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
@@ -197,6 +205,28 @@
 
   const auto& coded_size = spec.coded_size;
 
+  // Create a Texture and a CodecImage to back it.
+  // TODO(liberato): Once legacy mailbox support is removed, we don't need to
+  // create this texture.  So, we won't need |texture_owner| either.
+  std::unique_ptr<AbstractTexture> texture = decoder_helper_->CreateTexture(
+      GL_TEXTURE_EXTERNAL_OES, GL_RGBA, coded_size.width(), coded_size.height(),
+      GL_RGBA, GL_UNSIGNED_BYTE);
+
+  // Attach the image to the texture.
+  // Either way, we expect this to be UNBOUND (i.e., decoder-managed).  For
+  // overlays, BindTexImage will return true, causing it to transition to the
+  // BOUND state, and thus receive ScheduleOverlayPlane calls.  For TextureOwner
+  // backed images, BindTexImage will return false, and CopyTexImage will be
+  // tried next.
+  // TODO(liberato): consider not binding this as a StreamTextureImage if we're
+  // using an overlay.  There's no advantage.  We'd likely want to create (and
+  // initialize to a 1x1 texture) a 2D texture above in that case, in case
+  // somebody tries to sample from it.  Be sure that promotion hints still
+  // work properly, though -- they might require a stream texture image.
+  GLuint texture_owner_service_id =
+      texture_owner ? texture_owner->GetTextureId() : 0;
+  texture->BindStreamTextureImage(image.get(), texture_owner_service_id);
+
   gpu::ContextResult result;
   auto shared_context = GetSharedContext(stub_, &result);
   if (!shared_context) {
@@ -214,7 +244,8 @@
   auto shared_image = std::make_unique<gpu::SharedImageVideo>(
       mailbox, coded_size, gfx::ColorSpace::CreateSRGB(),
       kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, std::move(image),
-      std::move(shared_context), false /* is_thread_safe */);
+      std::move(texture), std::move(shared_context),
+      false /* is_thread_safe */);
 
   // Register it with shared image mailbox as well as legacy mailbox. This
   // keeps |shared_image| around until its destruction cb is called.
@@ -222,7 +253,7 @@
   // mailbox.
   DCHECK(stub_->channel()->gpu_channel_manager()->shared_image_manager());
   stub_->channel()->shared_image_stub()->factory()->RegisterBacking(
-      std::move(shared_image), /*allow_legacy_mailbox=*/false);
+      std::move(shared_image), /* legacy_mailbox */ true);
 
   return true;
 }
@@ -231,6 +262,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   DCHECK(stub_);
   stub_ = nullptr;
+  decoder_helper_ = nullptr;
 }
 
 }  // namespace media
diff --git a/media/gpu/android/direct_shared_image_video_provider.h b/media/gpu/android/direct_shared_image_video_provider.h
index fd6549f..e85068d 100644
--- a/media/gpu/android/direct_shared_image_video_provider.h
+++ b/media/gpu/android/direct_shared_image_video_provider.h
@@ -39,7 +39,9 @@
 
   // SharedImageVideoProvider
   void Initialize(GpuInitCB get_stub_cb) override;
-  void RequestImage(ImageReadyCB cb, const ImageSpec& spec) override;
+  void RequestImage(ImageReadyCB cb,
+                    const ImageSpec& spec,
+                    scoped_refptr<gpu::TextureOwner> texture_owner) override;
 
  private:
   base::SequenceBound<GpuSharedImageVideoFactory> gpu_factory_;
@@ -75,17 +77,23 @@
   // create the per-frame texture.  All of that is only needed for legacy
   // mailbox support, where we have to have one texture per CodecImage.
   void CreateImage(FactoryImageReadyCB cb,
-                   const SharedImageVideoProvider::ImageSpec& spec);
+                   const SharedImageVideoProvider::ImageSpec& spec,
+                   scoped_refptr<gpu::TextureOwner> texture_owner);
 
  private:
   // Creates a SharedImage for |mailbox|, and returns success or failure.
   bool CreateImageInternal(const SharedImageVideoProvider::ImageSpec& spec,
+                           scoped_refptr<gpu::TextureOwner> texture_owner,
                            gpu::Mailbox mailbox,
                            scoped_refptr<CodecImage> image);
 
   void OnWillDestroyStub(bool have_context) override;
 
   gpu::CommandBufferStub* stub_ = nullptr;
+
+  // A helper for creating textures. Only valid while |stub_| is valid.
+  std::unique_ptr<GLES2DecoderHelper> decoder_helper_;
+
   bool is_vulkan_ = false;
 
   THREAD_CHECKER(thread_checker_);
diff --git a/media/gpu/android/frame_info_helper.cc b/media/gpu/android/frame_info_helper.cc
index 1287357..6d4b209 100644
--- a/media/gpu/android/frame_info_helper.cc
+++ b/media/gpu/android/frame_info_helper.cc
@@ -80,8 +80,7 @@
       base::Optional<FrameInfo> info;
 
       if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
-              CodecOutputBufferRenderer::BindingsMode::kDontRestoreIfBound,
-              0)) {
+              CodecOutputBufferRenderer::BindingsMode::kDontRestoreIfBound)) {
         gfx::Size coded_size;
         gfx::Rect visible_rect;
         if (texture_owner->GetCodedSizeAndVisibleRect(
diff --git a/media/gpu/android/mock_shared_image_video_provider.cc b/media/gpu/android/mock_shared_image_video_provider.cc
index ecde0e5..c131f4ac 100644
--- a/media/gpu/android/mock_shared_image_video_provider.cc
+++ b/media/gpu/android/mock_shared_image_video_provider.cc
@@ -8,8 +8,11 @@
 
 MockSharedImageVideoProvider::RequestImageArgs::RequestImageArgs(
     ImageReadyCB cb,
-    ImageSpec spec)
-    : cb_(std::move(cb)), spec_(std::move(spec)) {}
+    ImageSpec spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner)
+    : cb_(std::move(cb)),
+      spec_(std::move(spec)),
+      texture_owner_(std::move(texture_owner)) {}
 
 MockSharedImageVideoProvider::RequestImageArgs::~RequestImageArgs() = default;
 
diff --git a/media/gpu/android/mock_shared_image_video_provider.h b/media/gpu/android/mock_shared_image_video_provider.h
index 4412dc9..cd554aa 100644
--- a/media/gpu/android/mock_shared_image_video_provider.h
+++ b/media/gpu/android/mock_shared_image_video_provider.h
@@ -25,8 +25,10 @@
 
   MOCK_METHOD1(Initialize_, void(GpuInitCB& gpu_init_cb));
 
-  void RequestImage(ImageReadyCB cb, const ImageSpec& spec) override {
-    requests_.emplace_back(std::move(cb), spec);
+  void RequestImage(ImageReadyCB cb,
+                    const ImageSpec& spec,
+                    scoped_refptr<gpu::TextureOwner> texture_owner) override {
+    requests_.emplace_back(std::move(cb), spec, std::move(texture_owner));
 
     MockRequestImage();
   }
@@ -57,10 +59,13 @@
 
   // Most recent arguments to RequestImage.
   struct RequestImageArgs {
-    RequestImageArgs(ImageReadyCB cb, ImageSpec spec);
+    RequestImageArgs(ImageReadyCB cb,
+                     ImageSpec spec,
+                     scoped_refptr<gpu::TextureOwner> texture_owner);
     ~RequestImageArgs();
     ImageReadyCB cb_;
     ImageSpec spec_;
+    scoped_refptr<gpu::TextureOwner> texture_owner_;
   };
 
   std::list<RequestImageArgs> requests_;
diff --git a/media/gpu/android/pooled_shared_image_video_provider.cc b/media/gpu/android/pooled_shared_image_video_provider.cc
index b4e3d0a3..a4b10ff 100644
--- a/media/gpu/android/pooled_shared_image_video_provider.cc
+++ b/media/gpu/android/pooled_shared_image_video_provider.cc
@@ -52,8 +52,10 @@
   provider_->Initialize(std::move(gpu_init_cb));
 }
 
-void PooledSharedImageVideoProvider::RequestImage(ImageReadyCB cb,
-                                                  const ImageSpec& spec) {
+void PooledSharedImageVideoProvider::RequestImage(
+    ImageReadyCB cb,
+    const ImageSpec& spec,
+    scoped_refptr<gpu::TextureOwner> texture_owner) {
   // See if the pool matches the requested spec.
   if (pool_spec_ != spec) {
     // Nope -- mark any outstanding images for destruction and start a new pool.
@@ -104,7 +106,7 @@
   auto ready_cb =
       base::BindOnce(&PooledSharedImageVideoProvider::OnImageCreated,
                      weak_factory_.GetWeakPtr(), spec);
-  provider_->RequestImage(std::move(ready_cb), spec);
+  provider_->RequestImage(std::move(ready_cb), spec, std::move(texture_owner));
 }
 
 void PooledSharedImageVideoProvider::OnImageCreated(ImageSpec spec,
diff --git a/media/gpu/android/pooled_shared_image_video_provider.h b/media/gpu/android/pooled_shared_image_video_provider.h
index 8f13b656..a4d0239 100644
--- a/media/gpu/android/pooled_shared_image_video_provider.h
+++ b/media/gpu/android/pooled_shared_image_video_provider.h
@@ -46,7 +46,9 @@
 
   // SharedImageVideoProvider
   void Initialize(GpuInitCB gpu_init_cb) override;
-  void RequestImage(ImageReadyCB cb, const ImageSpec& spec) override;
+  void RequestImage(ImageReadyCB cb,
+                    const ImageSpec& spec,
+                    scoped_refptr<gpu::TextureOwner> texture_owner) override;
 
  private:
   friend class PooledSharedImageVideoProviderTest;
diff --git a/media/gpu/android/pooled_shared_image_video_provider_unittest.cc b/media/gpu/android/pooled_shared_image_video_provider_unittest.cc
index 9a606c74..174c8bb8 100644
--- a/media/gpu/android/pooled_shared_image_video_provider_unittest.cc
+++ b/media/gpu/android/pooled_shared_image_video_provider_unittest.cc
@@ -70,7 +70,7 @@
   // |mock_provider_raw_|.  Have |mock_provider_raw_| return an image, too.
   void RequestAndProvideImage(const SharedImageVideoProvider::ImageSpec& spec) {
     EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(1);
-    provider_->RequestImage(SaveImageRecordCB(), spec);
+    provider_->RequestImage(SaveImageRecordCB(), spec, texture_owner_);
     base::RunLoop().RunUntilIdle();
     Mock::VerifyAndClearExpectations(mock_provider_raw_);
     mock_provider_raw_->ProvideOneRequestedImage();
@@ -155,7 +155,7 @@
 
   // Shouldn't call MockRequestImage a third time.
   EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(0);
-  provider_->RequestImage(SaveImageRecordCB(), spec);
+  provider_->RequestImage(SaveImageRecordCB(), spec, texture_owner_);
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(image_records_.size(), 2u);
 }
@@ -164,7 +164,7 @@
        DeletingProviderWithOutstandingImagesDoesntCrash) {
   // Destroying |provider_| with outstanding images shouldn't break anything.
   SharedImageVideoProvider::ImageSpec spec(gfx::Size(1, 1), 0u);
-  provider_->RequestImage(SaveImageRecordCB(), spec);
+  provider_->RequestImage(SaveImageRecordCB(), spec, texture_owner_);
   base::RunLoop().RunUntilIdle();
   provider_.reset();
   base::RunLoop().RunUntilIdle();
@@ -229,8 +229,8 @@
 
   // Request both images before providing either.
   EXPECT_CALL(*mock_provider_raw_, MockRequestImage()).Times(2);
-  provider_->RequestImage(SaveImageRecordCB(), spec_1);
-  provider_->RequestImage(SaveImageRecordCB(), spec_2);
+  provider_->RequestImage(SaveImageRecordCB(), spec_1, texture_owner_);
+  provider_->RequestImage(SaveImageRecordCB(), spec_2, texture_owner_);
   base::RunLoop().RunUntilIdle();
 
   // Provide the |spec_1| image.  Nothing should be released since it should
diff --git a/media/gpu/android/shared_image_video_provider.h b/media/gpu/android/shared_image_video_provider.h
index 87fa7ec..ffbac1331 100644
--- a/media/gpu/android/shared_image_video_provider.h
+++ b/media/gpu/android/shared_image_video_provider.h
@@ -94,7 +94,9 @@
 
   // Call |cb| when we have a shared image that matches |spec|.  We may call
   // |cb| back before returning, or we might post it for later.
-  virtual void RequestImage(ImageReadyCB cb, const ImageSpec& spec) = 0;
+  virtual void RequestImage(ImageReadyCB cb,
+                            const ImageSpec& spec,
+                            scoped_refptr<gpu::TextureOwner> texture_owner) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SharedImageVideoProvider);
diff --git a/media/gpu/android/video_frame_factory_impl.cc b/media/gpu/android/video_frame_factory_impl.cc
index 6671c30..8be7a01f 100644
--- a/media/gpu/android/video_frame_factory_impl.cc
+++ b/media/gpu/android/video_frame_factory_impl.cc
@@ -202,7 +202,8 @@
     ImageWithInfoReadyCB image_ready_cb) {
   auto info_cb =
       base::BindOnce(&VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady,
-                     weak_factory_.GetWeakPtr(), std::move(image_ready_cb));
+                     weak_factory_.GetWeakPtr(), std::move(image_ready_cb),
+                     codec_buffer_wait_coordinator_);
 
   frame_info_helper_->GetFrameInfo(std::move(buffer_renderer),
                                    std::move(info_cb));
@@ -210,6 +211,7 @@
 
 void VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady(
     ImageWithInfoReadyCB image_ready_cb,
+    scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
     std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
     FrameInfoHelper::FrameInfo frame_info) {
   // If we don't have output buffer here we can't rely on reply from
@@ -235,7 +237,11 @@
 
   auto cb = base::BindOnce(std::move(image_ready_cb),
                            std::move(output_buffer_renderer), frame_info);
-  image_provider_->RequestImage(std::move(cb), image_spec_);
+
+  auto texture_owner = codec_buffer_wait_coordinator
+                           ? codec_buffer_wait_coordinator->texture_owner()
+                           : nullptr;
+  image_provider_->RequestImage(std::move(cb), image_spec_, texture_owner);
 }
 
 // static
diff --git a/media/gpu/android/video_frame_factory_impl.h b/media/gpu/android/video_frame_factory_impl.h
index 0f0ab87..dbee06d 100644
--- a/media/gpu/android/video_frame_factory_impl.h
+++ b/media/gpu/android/video_frame_factory_impl.h
@@ -102,6 +102,7 @@
 
   void CreateVideoFrame_OnFrameInfoReady(
       ImageWithInfoReadyCB image_ready_cb,
+      scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
       std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
       FrameInfoHelper::FrameInfo frame_info);
 
diff --git a/mojo/core/test/BUILD.gn b/mojo/core/test/BUILD.gn
index d3025e4..736a433 100644
--- a/mojo/core/test/BUILD.gn
+++ b/mojo/core/test/BUILD.gn
@@ -11,6 +11,8 @@
     "mock_node_channel_delegate.h",
     "mojo_test_base.cc",
     "mojo_test_base.h",
+    "mojo_test_suite_base.cc",
+    "mojo_test_suite_base.h",
     "test_utils.h",
   ]
 
diff --git a/mojo/core/test/mojo_test_suite_base.cc b/mojo/core/test/mojo_test_suite_base.cc
new file mode 100644
index 0000000..526ca10e
--- /dev/null
+++ b/mojo/core/test/mojo_test_suite_base.cc
@@ -0,0 +1,57 @@
+// Copyright 2021 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 "mojo/core/test/mojo_test_suite_base.h"
+
+#include "base/command_line.h"
+#include "base/strings/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace core {
+namespace test {
+
+namespace {
+
+// Manual tests only run when --run-manual is specified. This allows writing
+// tests that don't run automatically but are still in the same test binary.
+// This is useful so that a team that wants to run a few tests doesn't have to
+// add a new binary that must be compiled on all builds.
+constexpr char kRunManualTestsFlag[] = "run-manual";
+
+// Tests starting with 'MANUAL_' are skipped unless the
+// command line flag `kRunManualTestsFlag`  is supplied.
+constexpr char kManualTestPrefix[] = "MANUAL_";
+
+class SkipManualTests : public testing::EmptyTestEventListener {
+ public:
+  void OnTestStart(const testing::TestInfo& test_info) override {
+    if (base::StartsWith(test_info.name(), kManualTestPrefix,
+                         base::CompareCase::SENSITIVE) &&
+        !base::CommandLine::ForCurrentProcess()->HasSwitch(
+            kRunManualTestsFlag)) {
+      GTEST_SKIP();
+    }
+  }
+};
+
+}  // namespace
+
+MojoTestSuiteBase::MojoTestSuiteBase(int argc, char** argv)
+    : base::TestSuite(argc, argv) {}
+
+#if defined(OS_WIN)
+MojoTestSuiteBase::MojoTestSuiteBase(int argc, wchar_t** argv)
+    : base::TestSuite(argc, argv) {}
+#endif  // defined(OS_WIN)
+
+void MojoTestSuiteBase::Initialize() {
+  base::TestSuite::Initialize();
+  testing::UnitTest::GetInstance()->listeners().Append(
+      std::make_unique<SkipManualTests>().release());
+}
+
+}  // namespace test
+}  // namespace core
+}  // namespace mojo
diff --git a/mojo/core/test/mojo_test_suite_base.h b/mojo/core/test/mojo_test_suite_base.h
new file mode 100644
index 0000000..389830d
--- /dev/null
+++ b/mojo/core/test/mojo_test_suite_base.h
@@ -0,0 +1,33 @@
+// Copyright 2021 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 MOJO_CORE_TEST_MOJO_TEST_SUITE_BASE_H_
+#define MOJO_CORE_TEST_MOJO_TEST_SUITE_BASE_H_
+
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+
+namespace mojo {
+namespace core {
+namespace test {
+
+class MojoTestSuiteBase : public base::TestSuite {
+ public:
+  MojoTestSuiteBase(int argc, char** argv);
+#if defined(OS_WIN)
+  MojoTestSuiteBase(int argc, wchar_t** argv);
+#endif  // defined(OS_WIN)
+
+  MojoTestSuiteBase(const MojoTestSuiteBase&) = delete;
+  MojoTestSuiteBase& operator=(const MojoTestSuiteBase&) = delete;
+
+ protected:
+  void Initialize() override;
+};
+
+}  // namespace test
+}  // namespace core
+}  // namespace mojo
+
+#endif  // MOJO_CORE_TEST_MOJO_TEST_SUITE_BASE_H_
diff --git a/mojo/core/test/run_all_unittests.cc b/mojo/core/test/run_all_unittests.cc
index 41b6f80..1556541 100644
--- a/mojo/core/test/run_all_unittests.cc
+++ b/mojo/core/test/run_all_unittests.cc
@@ -10,12 +10,12 @@
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/multiprocess_test.h"
 #include "base/test/test_io_thread.h"
-#include "base/test/test_suite.h"
 #include "build/build_config.h"
 #include "mojo/core/embedder/configuration.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 #include "mojo/core/test/mojo_test_base.h"
+#include "mojo/core/test/mojo_test_suite_base.h"
 #include "mojo/core/test/multiprocess_test_helper.h"
 #include "mojo/core/test/test_support_impl.h"
 #include "mojo/public/tests/test_support_private.h"
@@ -41,7 +41,7 @@
   signal(SIGABRT, SIG_DFL);
 #endif
 
-  base::TestSuite test_suite(argc, argv);
+  mojo::core::test::MojoTestSuiteBase test_suite(argc, argv);
 
   mojo::core::Configuration mojo_config;
 
diff --git a/mojo/docs/basics.md b/mojo/docs/basics.md
index f28a098..d4e42f6 100644
--- a/mojo/docs/basics.md
+++ b/mojo/docs/basics.md
@@ -164,6 +164,6 @@
 mojo::PendingReceiver<math::mojom::MathImpl> pending_receiver = receiver.Unbind();
 ```
 
-[mojo-idl]: https://chromium.googlesource.com/chromium/src/+/master/mojo/public/tools/bindings/README.md
+[mojo-idl]: https://chromium.googlesource.com/chromium/src/+/main/mojo/public/tools/bindings/README.md
 [gn-template]: https://cs.chromium.org/chromium/src/mojo/public/tools/bindings/mojom.gni
 [message-pipe]: https://cs.chromium.org/chromium/src/mojo/public/cpp/system/message_pipe.h
diff --git a/mojo/docs/mojolpm.md b/mojo/docs/mojolpm.md
index cd31825..51e4e4e 100644
--- a/mojo/docs/mojolpm.md
+++ b/mojo/docs/mojolpm.md
@@ -719,9 +719,9 @@
 ```
 
 [markbrand@google.com]:mailto:markbrand@google.com?subject=[MojoLPM%20Help]:%20&cc=fuzzing@chromium.org
-[libfuzzer]: https://source.chromium.org/chromium/chromium/src/+/master:testing/libfuzzer/getting_started.md
+[libfuzzer]: https://source.chromium.org/chromium/chromium/src/+/main:testing/libfuzzer/getting_started.md
 [Protocol Buffers]: https://developers.google.com/protocol-buffers/docs/cpptutorial
-[libprotobuf-mutator]: https://source.chromium.org/chromium/chromium/src/+/master:testing/libfuzzer/libprotobuf-mutator.md
-[testing in Chromium]: https://source.chromium.org/chromium/chromium/src/+/master:docs/testing/testing_in_chromium.md
+[libprotobuf-mutator]: https://source.chromium.org/chromium/chromium/src/+/main:testing/libfuzzer/libprotobuf-mutator.md
+[testing in Chromium]: https://source.chromium.org/chromium/chromium/src/+/main:docs/testing/testing_in_chromium.md
 [interfaces]: https://source.chromium.org/search?q=interface%5Cs%2B%5Cw%2B%5Cs%2B%7B%20f:%5C.mojom$%20-f:test
 
diff --git a/mojo/public/tools/bindings/README.md b/mojo/public/tools/bindings/README.md
index 0099deab..f7f33a8 100644
--- a/mojo/public/tools/bindings/README.md
+++ b/mojo/public/tools/bindings/README.md
@@ -765,7 +765,7 @@
 **NOTE**: by default, components for both blink and non-blink bindings are generated.
 Use the `disable_variants` target parameter to generate only non-blink bindings.
 You can also generate a `source_set` for one of the variants by defining
-[export_*](https://source.chromium.org/chromium/chromium/src/+/master:mojo/public/tools/bindings/mojom.gni;drc=739b9fbce50310c1dd2b59c279cd90a9319cb6e8;l=318)
+[export_*](https://source.chromium.org/chromium/chromium/src/+/main:mojo/public/tools/bindings/mojom.gni;drc=739b9fbce50310c1dd2b59c279cd90a9319cb6e8;l=318)
 parameters for the `mojom_component` target.
 ***
 
diff --git a/mojo/public/tools/mojom/README.md b/mojo/public/tools/mojom/README.md
index 6a4ff78..e5d17ab00 100644
--- a/mojo/public/tools/mojom/README.md
+++ b/mojo/public/tools/mojom/README.md
@@ -3,7 +3,7 @@
 The Mojom format is an interface definition language (IDL) for describing
 interprocess communication (IPC) messages and data types for use with the
 low-level cross-platform
-[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/master/mojo/public/c/system/README.md).
+[Mojo IPC library](https://chromium.googlesource.com/chromium/src/+/main/mojo/public/c/system/README.md).
 
 This directory consists of a `mojom` Python module, its tests, and supporting
 command-line tools. The Python module implements the parser used by the
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 348030f..d3645f3e 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -2704,7 +2704,7 @@
   base::HistogramTester histogram_tester;
 
   session_->OnAcceptChFrameReceivedViaAlps(
-      {{{"https://www.example.org", "Sec-UA-CH-Platform"}}});
+      {{{"https://www.example.org", "Sec-CH-UA-Platform"}}});
 
   request_.method = "GET";
   request_.url = GURL("https://www.example.org/foo");
@@ -2712,7 +2712,7 @@
   EXPECT_EQ(OK,
             stream_->InitializeStream(&request_, true, DEFAULT_PRIORITY,
                                       net_log_.bound(), callback_.callback()));
-  EXPECT_EQ("Sec-UA-CH-Platform", stream_->GetAcceptChViaAlps());
+  EXPECT_EQ("Sec-CH-UA-Platform", stream_->GetAcceptChViaAlps());
   EXPECT_TRUE(AtEof());
 
   histogram_tester.ExpectBucketCount(
diff --git a/printing/backend/print_backend_unittest.cc b/printing/backend/print_backend_unittest.cc
index d3b65e0..8252f78c 100644
--- a/printing/backend/print_backend_unittest.cc
+++ b/printing/backend/print_backend_unittest.cc
@@ -13,9 +13,8 @@
 // interact with printer drivers installed on a system.  This can be useful on
 // machines which a developer has control over the driver installations, but is
 // less useful on bots which are managed by the infra team.
-// These tests are all disabled by default to avoid causing problems on the
-// bots.  Use with the --gtest_also_run_disabled_tests flag to locally test
-// these interactions.
+// These tests are intended to be run manually by developers using the
+// --run-manual flag.
 class PrintBackendTest : public testing::Test {
  public:
   void SetUp() override {
@@ -34,7 +33,7 @@
 // A developer running these manually can verify that the appropriate test is
 // passing for the given state of installed printer drivers on the system being
 // checked.
-TEST_F(PrintBackendTest, DISABLED_EnumeratePrintersSomeInstalled) {
+TEST_F(PrintBackendTest, MANUAL_EnumeratePrintersSomeInstalled) {
   PrinterList printer_list;
 
   EXPECT_TRUE(GetPrintBackend()->EnumeratePrinters(&printer_list));
@@ -46,7 +45,7 @@
   }
 }
 
-TEST_F(PrintBackendTest, DISABLED_EnumeratePrintersNoneInstalled) {
+TEST_F(PrintBackendTest, MANUAL_EnumeratePrintersNoneInstalled) {
   PrinterList printer_list;
 
   EXPECT_TRUE(GetPrintBackend()->EnumeratePrinters(&printer_list));
diff --git a/services/data_decoder/xml_parser.cc b/services/data_decoder/xml_parser.cc
index 5a86642..eed4d29 100644
--- a/services/data_decoder/xml_parser.cc
+++ b/services/data_decoder/xml_parser.cc
@@ -170,9 +170,7 @@
     ReportError(std::move(callback), "Invalid XML: unbalanced elements");
     return;
   }
-  base::DictionaryValue* dictionary = nullptr;
-  root_element.GetAsDictionary(&dictionary);
-  if (!dictionary || dictionary->empty()) {
+  if (!root_element.is_dict() || root_element.DictEmpty()) {
     ReportError(std::move(callback), "Invalid XML: bad content");
     return;
   }
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 7f83ef1..fa3e00f 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -4675,11 +4675,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -4689,7 +4689,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -4912,11 +4912,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -4926,7 +4926,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 0e496c82..d05cdd11 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -53838,11 +53838,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_with_chrome_apk_Client Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_with_chrome_apk_Client Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_with_chrome_apk_Client Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_with_chrome_apk_Client Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -53852,7 +53852,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54078,11 +54078,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_with_chrome_apk_Implementation Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_with_chrome_apk_Implementation Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_with_chrome_apk_Implementation Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_with_chrome_apk_Implementation Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -54092,7 +54092,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54383,11 +54383,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -54397,7 +54397,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54620,11 +54620,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -54634,7 +54634,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54924,11 +54924,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -54938,7 +54938,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -55161,11 +55161,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.208"
+            "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.209"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.208",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Library Skew Tests For 90.0.4430.209",
         "resultdb": {
           "enable": true
         },
@@ -55175,7 +55175,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M90",
-              "revision": "version:90.0.4430.208"
+              "revision": "version:90.0.4430.209"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 2bbd4a81..a1ebe6e 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -52530,8 +52530,32 @@
   },
   "linux-lacros-version-skew-fyi": {
     "additional_compile_targets": [
-      "chrome",
-      "chrome_sandbox"
+      "chromiumos_preflight"
+    ],
+    "gtest_tests": [
+      {
+        "args": [
+          "--ash-chrome-path=../../lacros_version_skew_tests_M92/chrome"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "lacros_chrome_browsertests_Lacros version skew testing ash 92.0.4499.0",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
+              "location": "lacros_version_skew_tests_M92",
+              "revision": "version:92.0.4499.0"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "lacros_chrome_browsertests",
+        "test_id_prefix": "ninja://chrome/test:lacros_chrome_browsertests/"
+      }
     ]
   },
   "linux-perfetto-rel": {
diff --git a/testing/buildbot/filters/device-lacros.ozone_unittests.filter b/testing/buildbot/filters/device-lacros.ozone_unittests.filter
index 15a2217..98f0373 100644
--- a/testing/buildbot/filters/device-lacros.ozone_unittests.filter
+++ b/testing/buildbot/filters/device-lacros.ozone_unittests.filter
@@ -1,8 +1,5 @@
 # TODO(crbug.com/1204709) Enable following tests on lacros.
 -*/WaylandWindowTest.SetsPropertiesOnShow/*
 
-# TODO(crbug.com/1204706) Enable following tests on lacros.
--*/WaylandCursorFactoryTest.RetainOldThemeUntilNewBufferIsAttached/*
-
 # TODO(crbug.com/1204707) Enable following tests on lacros.
 -*/WaylandInputMethodContextTest.*
\ No newline at end of file
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index e74d06e..ae317c8c 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -4313,6 +4313,10 @@
       'sandbox_linux_unittests': {},
     },
 
+    'linux_lacros_chrome_browsertests_version_skew': {
+      'lacros_chrome_browsertests': { },
+    },
+
     'linux_lacros_cq_gtests': {
       'browser_tests': {
         'swarming': {
@@ -7273,6 +7277,14 @@
       },
     },
 
+    'linux-lacros-version-skew-tests': {
+      'linux_lacros_chrome_browsertests_version_skew': {
+        'variants': [
+          'LACROS_VERSION_SKEW_CANARY',
+        ]
+      },
+    },
+
     'linux_ozone_headless_tests': {
       'linux_ozone_headless_only_gtests': {
         'variants': [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 75e7e34..2f9947a6 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -64,6 +64,21 @@
     },
     'identifier': 'iPhone 6S 12.3.1'
   },
+  'LACROS_VERSION_SKEW_CANARY': {
+    'args': [
+      '--ash-chrome-path=../../lacros_version_skew_tests_M92/chrome',
+    ],
+    'identifier': 'Lacros version skew testing ash 92.0.4499.0',
+    'swarming': {
+      'cipd_packages': [
+        {
+          'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
+          'location': 'lacros_version_skew_tests_M92',
+          'revision': 'version:92.0.4499.0',
+        },
+      ],
+    },
+  },
   'SIM_IPAD_AIR_2_12_4': {
     'args': [
       '--platform',
@@ -355,13 +370,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=90',
     ],
-    'identifier': 'Implementation Library Skew Tests For 90.0.4430.208',
+    'identifier': 'Implementation Library Skew Tests For 90.0.4430.209',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M90',
-          'revision': 'version:90.0.4430.208',
+          'revision': 'version:90.0.4430.209',
         }
       ],
     },
@@ -427,13 +442,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=90',
     ],
-    'identifier': 'Implementation Library Skew Tests For 90.0.4430.208',
+    'identifier': 'Implementation Library Skew Tests For 90.0.4430.209',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M90',
-          'revision': 'version:90.0.4430.208',
+          'revision': 'version:90.0.4430.209',
         }
       ],
     },
@@ -499,13 +514,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=90',
     ],
-    'identifier': 'Client Library Skew Tests For 90.0.4430.208',
+    'identifier': 'Client Library Skew Tests For 90.0.4430.209',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M90',
-          'revision': 'version:90.0.4430.208',
+          'revision': 'version:90.0.4430.209',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index af30fde..c5f9ee1 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3332,9 +3332,11 @@
       },
       'linux-lacros-version-skew-fyi': {
         'additional_compile_targets': [
-          'chrome',
-          'chrome_sandbox',
+          'chromiumos_preflight',
         ],
+        'test_suites': {
+          'gtest_tests': 'linux-lacros-version-skew-tests',
+        },
       },
       'linux-perfetto-rel': {
         'additional_compile_targets': [ 'chrome' ],
diff --git a/testing/scripts/run_android_wpt.pydeps b/testing/scripts/run_android_wpt.pydeps
index 907cc1d..d96a17db 100644
--- a/testing/scripts/run_android_wpt.pydeps
+++ b/testing/scripts/run_android_wpt.pydeps
@@ -26,6 +26,8 @@
 //third_party/blink/tools/blinkpy/common/html_diff.py
 //third_party/blink/tools/blinkpy/common/memoized.py
 //third_party/blink/tools/blinkpy/common/net/__init__.py
+//third_party/blink/tools/blinkpy/common/net/bb_agent.py
+//third_party/blink/tools/blinkpy/common/net/luci_auth.py
 //third_party/blink/tools/blinkpy/common/net/network_transaction.py
 //third_party/blink/tools/blinkpy/common/net/results_fetcher.py
 //third_party/blink/tools/blinkpy/common/net/web.py
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 9d5338a..4751d5cf 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -923,7 +923,7 @@
 // have their rendering throttled on display:none or zero-area.
 const base::Feature kThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes{
     "ThrottleDisplayNoneAndVisibilityHiddenCrossOriginIframes",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Kill switch for the Fledge Interest Group API, i.e. if disabled, the
 // API exposure will be disabled regardless of the OT config.
diff --git a/third_party/blink/public/common/metrics/form_element_pii_type.h b/third_party/blink/public/common/metrics/form_element_pii_type.h
index 64e9486..07a0248 100644
--- a/third_party/blink/public/common/metrics/form_element_pii_type.h
+++ b/third_party/blink/public/common/metrics/form_element_pii_type.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_METRICS_FORM_ELEMENT_PII_TYPE_H_
 
 #include <stdint.h>
-#include <string>
 
 namespace blink {
 
diff --git a/third_party/blink/public/common/navigation/impression.h b/third_party/blink/public/common/navigation/impression.h
index 02323dc..dbf8671 100644
--- a/third_party/blink/public/common/navigation/impression.h
+++ b/third_party/blink/public/common/navigation/impression.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_NAVIGATION_IMPRESSION_H_
 
 #include <stdint.h>
-#include <string>
 
 #include "base/time/time.h"
 #include "third_party/blink/public/common/common_export.h"
diff --git a/third_party/blink/public/common/web_package/signed_exchange_consts.h b/third_party/blink/public/common/web_package/signed_exchange_consts.h
index d9c67d9..37a7707 100644
--- a/third_party/blink/public/common/web_package/signed_exchange_consts.h
+++ b/third_party/blink/public/common/web_package/signed_exchange_consts.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_WEB_PACKAGE_SIGNED_EXCHANGE_CONSTS_H_
 
 #include <stddef.h>
-#include <string>
 
 #include "third_party/blink/public/common/common_export.h"
 
diff --git a/third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h b/third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h
index 589dca15b..ecf8510 100644
--- a/third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h
+++ b/third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h
@@ -25,8 +25,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_MEDIASTREAM_WEB_MEDIA_STREAM_TRACK_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_MEDIASTREAM_WEB_MEDIA_STREAM_TRACK_H_
 
-#include <memory>
-
 #include "base/optional.h"
 #include "media/mojo/mojom/display_media_information.mojom-shared.h"
 #include "third_party/blink/public/platform/web_common.h"
diff --git a/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h b/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h
index 46bcb5c..6b3edf7e 100644
--- a/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h
+++ b/third_party/blink/public/platform/modules/service_worker/web_service_worker_provider_client.h
@@ -37,8 +37,6 @@
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_vector.h"
 
-#include <memory>
-
 namespace blink {
 
 // See WebServiceWorkerProvider for full documentation.
diff --git a/third_party/blink/public/platform/web_crypto.h b/third_party/blink/public/platform/web_crypto.h
index 570ebc77..6488e85 100644
--- a/third_party/blink/public/platform/web_crypto.h
+++ b/third_party/blink/public/platform/web_crypto.h
@@ -39,8 +39,6 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_vector.h"
 
-#include <memory>
-
 #if INSIDE_BLINK
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"  // nogncheck
diff --git a/third_party/blink/public/platform/web_navigation_body_loader.h b/third_party/blink/public/platform/web_navigation_body_loader.h
index 8a5b806..21f9cc5 100644
--- a/third_party/blink/public/platform/web_navigation_body_loader.h
+++ b/third_party/blink/public/platform/web_navigation_body_loader.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_NAVIGATION_BODY_LOADER_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_NAVIGATION_BODY_LOADER_H_
 
-#include <memory>
-
 #include "base/containers/span.h"
 #include "base/optional.h"
 #include "base/time/time.h"
diff --git a/third_party/blink/public/platform/web_resource_request_sender_delegate.h b/third_party/blink/public/platform/web_resource_request_sender_delegate.h
index 49d2f2a..20bc9e34 100644
--- a/third_party/blink/public/platform/web_resource_request_sender_delegate.h
+++ b/third_party/blink/public/platform/web_resource_request_sender_delegate.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RESOURCE_REQUEST_SENDER_DELEGATE_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_RESOURCE_REQUEST_SENDER_DELEGATE_H_
 
-#include <string>
-
 #include "third_party/blink/public/platform/web_common.h"
 
 namespace blink {
diff --git a/third_party/blink/public/web/modules/autofill/web_form_element_observer.h b/third_party/blink/public/web/modules/autofill/web_form_element_observer.h
index 0aaeb22..b685598 100644
--- a/third_party/blink/public/web/modules/autofill/web_form_element_observer.h
+++ b/third_party/blink/public/web/modules/autofill/web_form_element_observer.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_AUTOFILL_WEB_FORM_ELEMENT_OBSERVER_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_AUTOFILL_WEB_FORM_ELEMENT_OBSERVER_H_
 
-#include <memory>
-
 #include "base/callback.h"
 #include "third_party/blink/public/platform/web_common.h"
 
diff --git a/third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h b/third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h
index b5f6786..230b242 100644
--- a/third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h
+++ b/third_party/blink/public/web/modules/media/audio/web_audio_device_factory.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIA_AUDIO_WEB_AUDIO_DEVICE_FACTORY_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIA_AUDIO_WEB_AUDIO_DEVICE_FACTORY_H_
 
-#include <string>
-
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
diff --git a/third_party/blink/public/web/modules/mediastream/web_media_stream_utils.h b/third_party/blink/public/web/modules/mediastream/web_media_stream_utils.h
index 8430f59..b6ab88a 100644
--- a/third_party/blink/public/web/modules/mediastream/web_media_stream_utils.h
+++ b/third_party/blink/public/web/modules/mediastream/web_media_stream_utils.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_WEB_MEDIA_STREAM_UTILS_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_WEB_MEDIA_STREAM_UTILS_H_
 
-#include <memory>
-
 #include "media/capture/video_capture_types.h"
 #include "third_party/blink/public/common/media/video_capture.h"
 #include "third_party/blink/public/platform/web_common.h"
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h
index 4e3a595..88acd1ea 100644
--- a/third_party/blink/public/web/web_ax_object.h
+++ b/third_party/blink/public/web/web_ax_object.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_AX_OBJECT_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_AX_OBJECT_H_
 
-#include <memory>
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/platform/web_private_ptr.h"
 #include "third_party/blink/public/platform/web_vector.h"
diff --git a/third_party/blink/public/web/web_disallow_transition_scope.h b/third_party/blink/public/web/web_disallow_transition_scope.h
index f66515a..5af0a8b 100644
--- a/third_party/blink/public/web/web_disallow_transition_scope.h
+++ b/third_party/blink/public/web/web_disallow_transition_scope.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_DISALLOW_TRANSITION_SCOPE_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_DISALLOW_TRANSITION_SCOPE_H_
 
-#include <memory>
-
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/web/web_document.h"
 
diff --git a/third_party/blink/public/web/web_frame.h b/third_party/blink/public/web/web_frame.h
index ff78064..75f0535 100644
--- a/third_party/blink/public/web/web_frame.h
+++ b/third_party/blink/public/web/web_frame.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_FRAME_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_FRAME_H_
 
-#include <memory>
 #include "cc/paint/paint_canvas.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/frame/tree_scope_type.mojom-shared.h"
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_html_all_collection_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_html_all_collection_custom.cc
index 801da46..a143285 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_html_all_collection_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_html_all_collection_custom.cc
@@ -32,6 +32,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/html_collection_or_element.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/platform/bindings/v8_set_return_value.h"
 
 namespace blink {
 
@@ -56,7 +57,7 @@
           ->ToArrayIndex(info.GetIsolate()->GetCurrentContext())
           .ToLocal(&index)) {
     Element* result = impl->AnonymousIndexedGetter(index->Value());
-    V8SetReturnValue(info, result);
+    bindings::V8SetReturnValue(info, result, impl);
     return;
   }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
index 567c0448..1516c55c 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc
@@ -58,6 +58,7 @@
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/v8_set_return_value.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 
 namespace blink {
@@ -219,7 +220,9 @@
     // context container's name content attribute value.
     if (BindingSecurity::ShouldAllowNamedAccessTo(window, child->DomWindow()) ||
         name == child->Owner()->BrowsingContextContainerName()) {
-      V8SetReturnValueFast(info, child->DomWindow(), window);
+      bindings::V8SetReturnValue(
+          info, child->DomWindow(), window,
+          bindings::V8ReturnValue::kMaybeCrossOriginWindow);
       return;
     }
 
@@ -234,7 +237,9 @@
       // TODO(yukishiino): Makes iframe.name update the browsing context name
       // appropriately and makes the new name available in the named access on
       // window.  Then, removes the following two lines.
-      V8SetReturnValueFast(info, child->DomWindow(), window);
+      bindings::V8SetReturnValue(
+          info, child->DomWindow(), window,
+          bindings::V8ReturnValue::kMaybeCrossOriginWindow);
       return;
     }
   }
@@ -251,7 +256,7 @@
     //   undefined, [[Writable]]: false, [[Enumerable]]: false,
     //   [[Configurable]]: true }.
     if (name == "then") {
-      V8SetReturnValueFast(info, v8::Undefined(info.GetIsolate()), window);
+      bindings::V8SetReturnValue(info, v8::Undefined(info.GetIsolate()));
       return;
     }
 
@@ -275,7 +280,9 @@
   if (!has_named_item && has_id_item &&
       !doc->ContainsMultipleElementsWithId(name)) {
     UseCounter::Count(doc, WebFeature::kDOMClobberedVariableAccessed);
-    V8SetReturnValueFast(info, doc->getElementById(name), window);
+    bindings::V8SetReturnValue(
+        info, doc->getElementById(name), window,
+        bindings::V8ReturnValue::kMaybeCrossOriginWindow);
     return;
   }
 
@@ -287,10 +294,13 @@
     // multiple with the same name, but Chrome and Safari does. What's the
     // right behavior?
     if (items->HasExactlyOneItem()) {
-      V8SetReturnValueFast(info, items->item(0), window);
+      bindings::V8SetReturnValue(
+          info, items->item(0), window,
+          bindings::V8ReturnValue::kMaybeCrossOriginWindow);
       return;
     }
-    V8SetReturnValueFast(info, items, window);
+    bindings::V8SetReturnValue(
+        info, items, window, bindings::V8ReturnValue::kMaybeCrossOriginWindow);
     return;
   }
 }
diff --git a/third_party/blink/renderer/bindings/core/v8/custom/v8_xml_http_request_custom.cc b/third_party/blink/renderer/bindings/core/v8/custom/v8_xml_http_request_custom.cc
index 81cf7ea..4e8e60515 100644
--- a/third_party/blink/renderer/bindings/core/v8/custom/v8_xml_http_request_custom.cc
+++ b/third_party/blink/renderer/bindings/core/v8/custom/v8_xml_http_request_custom.cc
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
 #include "third_party/blink/renderer/core/xmlhttprequest/xml_http_request.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/bindings/v8_set_return_value.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -99,19 +100,19 @@
 
     case XMLHttpRequest::kResponseTypeDocument: {
       Document* document = xml_http_request->responseXML(exception_state);
-      V8SetReturnValueFast(info, document, xml_http_request);
+      bindings::V8SetReturnValue(info, document, xml_http_request);
       return;
     }
 
     case XMLHttpRequest::kResponseTypeBlob: {
       Blob* blob = xml_http_request->ResponseBlob();
-      V8SetReturnValueFast(info, blob, xml_http_request);
+      bindings::V8SetReturnValue(info, blob, xml_http_request);
       return;
     }
 
     case XMLHttpRequest::kResponseTypeArrayBuffer: {
       DOMArrayBuffer* array_buffer = xml_http_request->ResponseArrayBuffer();
-      V8SetReturnValueFast(info, array_buffer, xml_http_request);
+      bindings::V8SetReturnValue(info, array_buffer, xml_http_request);
       return;
     }
   }
diff --git a/third_party/blink/renderer/core/accessibility/ax_object_cache.h b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
index 22a8899..d124445 100644
--- a/third_party/blink/renderer/core/accessibility/ax_object_cache.h
+++ b/third_party/blink/renderer/core/accessibility/ax_object_cache.h
@@ -27,8 +27,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ACCESSIBILITY_AX_OBJECT_CACHE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ACCESSIBILITY_AX_OBJECT_CACHE_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/accessibility/axid.h"
 #include "third_party/blink/renderer/core/accessibility/blink_ax_event_intent.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/animation/css_length_interpolation_type.h b/third_party/blink/renderer/core/animation/css_length_interpolation_type.h
index edf28aa0..25b3570 100644
--- a/third_party/blink/renderer/core/animation/css_length_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_length_interpolation_type.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_LENGTH_INTERPOLATION_TYPE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_LENGTH_INTERPOLATION_TYPE_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/animation/css_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/length_property_functions.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/animation/document_timeline.h b/third_party/blink/renderer/core/animation/document_timeline.h
index e780c9a0..72747ffd 100644
--- a/third_party/blink/renderer/core/animation/document_timeline.h
+++ b/third_party/blink/renderer/core/animation/document_timeline.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_DOCUMENT_TIMELINE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_DOCUMENT_TIMELINE_H_
 
-#include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/animation/animation_timeline.h"
diff --git a/third_party/blink/renderer/core/animation/interpolation.h b/third_party/blink/renderer/core/animation/interpolation.h
index f70e08fb..228bab31 100644
--- a/third_party/blink/renderer/core/animation/interpolation.h
+++ b/third_party/blink/renderer/core/animation/interpolation.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLATION_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLATION_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/animation/interpolable_value.h"
 #include "third_party/blink/renderer/core/animation/property_handle.h"
diff --git a/third_party/blink/renderer/core/animation/path_interpolation_functions.h b/third_party/blink/renderer/core/animation/path_interpolation_functions.h
index dddce78..ec664755 100644
--- a/third_party/blink/renderer/core/animation/path_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/path_interpolation_functions.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_PATH_INTERPOLATION_FUNCTIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_PATH_INTERPOLATION_FUNCTIONS_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/animation/interpolation_type.h"
 #include "third_party/blink/renderer/core/svg/svg_path_byte_stream.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_util.h b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
index 7c66719..143ce64 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_util.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_util.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_UTIL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SCROLL_TIMELINE_UTIL_H_
 
-#include <memory>
-
 #include "base/optional.h"
 #include "cc/animation/scroll_timeline.h"
 #include "third_party/blink/renderer/core/animation/scroll_timeline.h"
diff --git a/third_party/blink/renderer/core/animation/size_interpolation_functions.h b/third_party/blink/renderer/core/animation/size_interpolation_functions.h
index a4cad01..32f2064 100644
--- a/third_party/blink/renderer/core/animation/size_interpolation_functions.h
+++ b/third_party/blink/renderer/core/animation/size_interpolation_functions.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SIZE_INTERPOLATION_FUNCTIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_SIZE_INTERPOLATION_FUNCTIONS_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/animation/interpolation_value.h"
 #include "third_party/blink/renderer/core/animation/pairwise_interpolation_value.h"
 #include "third_party/blink/renderer/core/style/fill_layer.h"
diff --git a/third_party/blink/renderer/core/animation/underlying_length_checker.h b/third_party/blink/renderer/core/animation/underlying_length_checker.h
index badae96..69de9c5d 100644
--- a/third_party/blink/renderer/core/animation/underlying_length_checker.h
+++ b/third_party/blink/renderer/core/animation/underlying_length_checker.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_UNDERLYING_LENGTH_CHECKER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_UNDERLYING_LENGTH_CHECKER_H_
 
-#include <memory>
-
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/animation/interpolable_value.h"
 #include "third_party/blink/renderer/core/animation/interpolation_type.h"
diff --git a/third_party/blink/renderer/core/css/invalidation/pending_invalidations.h b/third_party/blink/renderer/core/css/invalidation/pending_invalidations.h
index bbdb8e0f..78105194 100644
--- a/third_party/blink/renderer/core/css/invalidation/pending_invalidations.h
+++ b/third_party/blink/renderer/core/css/invalidation/pending_invalidations.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_PENDING_INVALIDATIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_PENDING_INVALIDATIONS_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/css/invalidation/node_invalidation_sets.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
diff --git a/third_party/blink/renderer/core/css/invalidation/style_invalidator.h b/third_party/blink/renderer/core/css/invalidation/style_invalidator.h
index ffc97ebe..c8365e6 100644
--- a/third_party/blink/renderer/core/css/invalidation/style_invalidator.h
+++ b/third_party/blink/renderer/core/css/invalidation/style_invalidator.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_STYLE_INVALIDATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_INVALIDATION_STYLE_INVALIDATOR_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/css/invalidation/invalidation_flags.h"
 #include "third_party/blink/renderer/core/css/invalidation/pending_invalidations.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
index 4882d97..0605a46 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_state.h
@@ -23,7 +23,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_STYLE_RESOLVER_STATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_RESOLVER_STYLE_RESOLVER_STATE_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/animation/css/css_animation_update.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_name.h"
diff --git a/third_party/blink/renderer/core/dom/context_features_client_impl.h b/third_party/blink/renderer/core/dom/context_features_client_impl.h
index d8130f7..dced5c35 100644
--- a/third_party/blink/renderer/core/dom/context_features_client_impl.h
+++ b/third_party/blink/renderer/core/dom/context_features_client_impl.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CONTEXT_FEATURES_CLIENT_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CONTEXT_FEATURES_CLIENT_IMPL_H_
 
-#include <memory>
-
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/context_features.h"
diff --git a/third_party/blink/renderer/core/dom/increment_load_event_delay_count.h b/third_party/blink/renderer/core/dom/increment_load_event_delay_count.h
index 458de1a..16667b63a 100644
--- a/third_party/blink/renderer/core/dom/increment_load_event_delay_count.h
+++ b/third_party/blink/renderer/core/dom/increment_load_event_delay_count.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_INCREMENT_LOAD_EVENT_DELAY_COUNT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_INCREMENT_LOAD_EVENT_DELAY_COUNT_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/core/dom/names_map.h b/third_party/blink/renderer/core/dom/names_map.h
index 3a0a318..c0096c1e 100644
--- a/third_party/blink/renderer/core/dom/names_map.h
+++ b/third_party/blink/renderer/core/dom/names_map.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NAMES_MAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NAMES_MAP_H_
 
-#include <memory>
-
 #include "base/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/space_split_string.h"
diff --git a/third_party/blink/renderer/core/events/event_util.h b/third_party/blink/renderer/core/events/event_util.h
index 266e9bf..adcb6b4 100644
--- a/third_party/blink/renderer/core/events/event_util.h
+++ b/third_party/blink/renderer/core/events/event_util.h
@@ -9,8 +9,6 @@
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
-#include <vector>
-
 namespace blink {
 
 namespace event_util {
diff --git a/third_party/blink/renderer/core/execution_context/window_agent_factory.h b/third_party/blink/renderer/core/execution_context/window_agent_factory.h
index 33d750e..61a256d 100644
--- a/third_party/blink/renderer/core/execution_context/window_agent_factory.h
+++ b/third_party/blink/renderer/core/execution_context/window_agent_factory.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_FACTORY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EXECUTION_CONTEXT_WINDOW_AGENT_FACTORY_H_
 
-#include <utility>
-
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
diff --git a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
index 53a5301a..2e9a8ef 100644
--- a/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
+++ b/third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_DEV_TOOLS_AGENT_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EXPORTED_WEB_DEV_TOOLS_AGENT_IMPL_H_
 
-#include <memory>
-
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
diff --git a/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h b/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h
index 1f58149b..8c185cd5 100644
--- a/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h
+++ b/third_party/blink/renderer/core/fetch/blob_bytes_consumer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BLOB_BYTES_CONSUMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BLOB_BYTES_CONSUMER_H_
 
-#include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/mojom/blob/blob.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/fetch/body_stream_buffer.h b/third_party/blink/renderer/core/fetch/body_stream_buffer.h
index cf949e9..763d83eb 100644
--- a/third_party/blink/renderer/core/fetch/body_stream_buffer.h
+++ b/third_party/blink/renderer/core/fetch/body_stream_buffer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_STREAM_BUFFER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BODY_STREAM_BUFFER_H_
 
-#include <memory>
 #include "base/types/pass_key.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/chunked_data_pipe_getter.mojom-blink.h"
diff --git a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h
index c33c32b..5d948cae 100644
--- a/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h
+++ b/third_party/blink/renderer/core/fetch/readable_stream_bytes_consumer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_READABLE_STREAM_BYTES_CONSUMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_READABLE_STREAM_BYTES_CONSUMER_H_
 
-#include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/frame/child_frame_compositing_helper.h b/third_party/blink/renderer/core/frame/child_frame_compositing_helper.h
index c7c49374..d6c4d87 100644
--- a/third_party/blink/renderer/core/frame/child_frame_compositing_helper.h
+++ b/third_party/blink/renderer/core/frame/child_frame_compositing_helper.h
@@ -7,10 +7,6 @@
 
 #include <stdint.h>
 
-#include <memory>
-#include <string>
-#include <vector>
-
 #include "base/macros.h"
 #include "cc/layers/content_layer_client.h"
 #include "cc/layers/surface_layer.h"
diff --git a/third_party/blink/renderer/core/frame/dom_timer_coordinator.h b/third_party/blink/renderer/core/frame/dom_timer_coordinator.h
index 349df57d..7cada03 100644
--- a/third_party/blink/renderer/core/frame/dom_timer_coordinator.h
+++ b/third_party/blink/renderer/core/frame/dom_timer_coordinator.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DOM_TIMER_COORDINATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DOM_TIMER_COORDINATOR_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/frame/fullscreen_controller.h b/third_party/blink/renderer/core/frame/fullscreen_controller.h
index 5682ffb..95957d8a 100644
--- a/third_party/blink/renderer/core/frame/fullscreen_controller.h
+++ b/third_party/blink/renderer/core/frame/fullscreen_controller.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FULLSCREEN_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FULLSCREEN_CONTROLLER_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/geometry/int_size.h"
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index c02bf531..af30ea2 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -780,10 +780,6 @@
 
 LocalDOMWindow::~LocalDOMWindow() = default;
 
-LocalFrame* LocalDOMWindow::GetFrame() const {
-  return To<LocalFrame>(DOMWindow::GetFrame());
-}
-
 void LocalDOMWindow::Dispose() {
   // Oilpan: should the LocalDOMWindow be GCed along with its LocalFrame without
   // the frame having first notified its observers of imminent destruction, the
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.h b/third_party/blink/renderer/core/frame/local_dom_window.h
index caba4a42..3e7444e 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.h
+++ b/third_party/blink/renderer/core/frame/local_dom_window.h
@@ -42,6 +42,7 @@
 #include "third_party/blink/renderer/core/events/page_transition_event.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
 #include "third_party/blink/renderer/core/loader/frame_loader.h"
 #include "third_party/blink/renderer/core/scroll/scrollable_area.h"
@@ -122,7 +123,7 @@
     return token_;
   }
 
-  LocalFrame* GetFrame() const;
+  LocalFrame* GetFrame() const { return To<LocalFrame>(DOMWindow::GetFrame()); }
 
   ScriptController& GetScriptController() const { return *script_controller_; }
 
diff --git a/third_party/blink/renderer/core/frame/page_scale_constraints_set.h b/third_party/blink/renderer/core/frame/page_scale_constraints_set.h
index c981356..f6ffae33 100644
--- a/third_party/blink/renderer/core/frame/page_scale_constraints_set.h
+++ b/third_party/blink/renderer/core/frame/page_scale_constraints_set.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_PAGE_SCALE_CONSTRAINTS_SET_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_PAGE_SCALE_CONSTRAINTS_SET_H_
 
-#include <memory>
-
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/frame/page_scale_constraints.h"
diff --git a/third_party/blink/renderer/core/frame/settings.h b/third_party/blink/renderer/core/frame/settings.h
index 5b6ab45..e9fe691 100644
--- a/third_party/blink/renderer/core/frame/settings.h
+++ b/third_party/blink/renderer/core/frame/settings.h
@@ -28,8 +28,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SETTINGS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SETTINGS_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/public/common/css/navigation_controls.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
diff --git a/third_party/blink/renderer/core/geometry/dom_matrix_read_only.h b/third_party/blink/renderer/core/geometry/dom_matrix_read_only.h
index 7ebfe51..840305a 100644
--- a/third_party/blink/renderer/core/geometry/dom_matrix_read_only.h
+++ b/third_party/blink/renderer/core/geometry/dom_matrix_read_only.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_GEOMETRY_DOM_MATRIX_READ_ONLY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_GEOMETRY_DOM_MATRIX_READ_ONLY_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/bindings/core/v8/string_or_unrestricted_double_sequence.h"
 #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_draw_listener.h b/third_party/blink/renderer/core/html/canvas/canvas_draw_listener.h
index 19e48e4b6..400e3891 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_draw_listener.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_draw_listener.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CANVAS_CANVAS_DRAW_LISTENER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CANVAS_CANVAS_DRAW_LISTENER_H_
 
-#include <memory>
-
 #include "base/memory/weak_ptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
diff --git a/third_party/blink/renderer/core/html/conversion_measurement_parsing.h b/third_party/blink/renderer/core/html/conversion_measurement_parsing.h
index 052ff20..470075a 100644
--- a/third_party/blink/renderer/core/html/conversion_measurement_parsing.h
+++ b/third_party/blink/renderer/core/html/conversion_measurement_parsing.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CONVERSION_MEASUREMENT_PARSING_H_
 
 #include <stdint.h>
-#include <memory>
 
 #include "base/optional.h"
 #include "third_party/blink/public/platform/web_impression.h"
diff --git a/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h b/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h
index 45879cc..0ba2bcb5 100644
--- a/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h
+++ b/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_COLOR_CHOOSER_UI_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_COLOR_CHOOSER_UI_CONTROLLER_H_
 
-#include <memory>
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/choosers/color_chooser.mojom-blink.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/html/parser/html_tokenizer.h b/third_party/blink/renderer/core/html/parser/html_tokenizer.h
index 1cae3e2..e040f2b 100644
--- a/third_party/blink/renderer/core/html/parser/html_tokenizer.h
+++ b/third_party/blink/renderer/core/html/parser/html_tokenizer.h
@@ -27,8 +27,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_TOKENIZER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PARSER_HTML_TOKENIZER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/inspector/dev_tools_emulator.h b/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
index 7febae45..4125bc9a 100644
--- a/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
+++ b/third_party/blink/renderer/core/inspector/dev_tools_emulator.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_DEV_TOOLS_EMULATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_DEV_TOOLS_EMULATOR_H_
 
-#include <memory>
 #include "base/optional.h"
 #include "third_party/blink/public/common/widget/device_emulation_params.h"
 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_agent.h b/third_party/blink/renderer/core/inspector/inspector_media_agent.h
index f15aaa04..4203e3b 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_media_agent.h
@@ -5,9 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_MEDIA_AGENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_INSPECTOR_MEDIA_AGENT_H_
 
-#include <memory>
-#include <vector>
-
 #include "third_party/blink/public/web/web_media_inspector.h"
 #include "third_party/blink/renderer/core/inspector/inspected_frames.h"
 #include "third_party/blink/renderer/core/inspector/inspector_base_agent.h"
diff --git a/third_party/blink/renderer/core/layout/floating_objects.h b/third_party/blink/renderer/core/layout/floating_objects.h
index 3f74eb7..f3e320b 100644
--- a/third_party/blink/renderer/core/layout/floating_objects.h
+++ b/third_party/blink/renderer/core/layout/floating_objects.h
@@ -25,8 +25,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_FLOATING_OBJECTS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_FLOATING_OBJECTS_H_
 
-#include <memory>
-
 #include "base/dcheck_is_on.h"
 #include "base/types/pass_key.h"
 #include "third_party/blink/renderer/platform/geometry/layout_rect.h"
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index d353fed..db299f2a 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -24,8 +24,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_H_
 
-#include <memory>
-
 #include "base/dcheck_is_on.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.h b/third_party/blink/renderer/core/layout/layout_block_flow.h
index 6151a7e..949f4a9 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.h
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.h
@@ -36,8 +36,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_FLOW_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_FLOW_H_
 
-#include <memory>
-
 #include "base/dcheck_is_on.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/api/line_layout_item.h"
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h
index efabb1f..8d78ce3 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.h
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -24,7 +24,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BOX_MODEL_OBJECT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BOX_MODEL_OBJECT_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h"
 #include "third_party/blink/renderer/core/layout/content_change_type.h"
diff --git a/third_party/blink/renderer/core/layout/layout_box_test.cc b/third_party/blink/renderer/core/layout/layout_box_test.cc
index 7dff6df4..8690524 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -6,6 +6,7 @@
 
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/dom/dom_token_list.h"
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
 #include "third_party/blink/renderer/core/layout/layout_image.h"
@@ -1752,36 +1753,48 @@
 
 TEST_P(LayoutBoxTest, SetNeedsOverflowRecalcLayoutBox) {
   SetBodyInnerHTML(R"HTML(
+    <style>
+    .transform { transform: translateX(10px); }
+    </style>
     <img id="img">
   )HTML");
-  LayoutObject* target = GetLayoutBoxByElementId("img");
+  Element* element = GetElementById("img");
+  LayoutObject* target = element->GetLayoutObject();
   EXPECT_FALSE(target->SelfNeedsLayoutOverflowRecalc());
 
-  target->SetNeedsOverflowRecalc();
+  element->classList().Add("transform");
+  element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
   EXPECT_TRUE(target->PaintingLayer()->NeedsVisualOverflowRecalc());
 
-#if 0  // TODO(crbug.com/1205708): This should pass, but it's not ready yet.
   UpdateAllLifecyclePhasesForTest();
-  target->SetNeedsOverflowRecalc();
+  EXPECT_FALSE(target->SelfNeedsLayoutOverflowRecalc());
+
+  element->classList().Remove("transform");
+  element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
   EXPECT_TRUE(target->PaintingLayer()->NeedsVisualOverflowRecalc());
-#endif
 }
 
 TEST_P(LayoutBoxTest, SetNeedsOverflowRecalcFlexBox) {
   SetBodyInnerHTML(R"HTML(
+    <style>
+    .transform { transform: translateX(10px); }
+    </style>
     <div id="flex" style="display: flex"></div>
   )HTML");
-  LayoutObject* target = GetLayoutBoxByElementId("flex");
+  Element* element = GetElementById("flex");
+  LayoutObject* target = element->GetLayoutObject();
   EXPECT_FALSE(target->SelfNeedsLayoutOverflowRecalc());
 
-  target->SetNeedsOverflowRecalc();
+  element->classList().Add("transform");
+  element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
   EXPECT_TRUE(target->PaintingLayer()->NeedsVisualOverflowRecalc());
 
-#if 0  // TODO(crbug.com/1205708): This should pass, but it's not ready yet.
   UpdateAllLifecyclePhasesForTest();
-  target->SetNeedsOverflowRecalc();
+  EXPECT_FALSE(target->SelfNeedsLayoutOverflowRecalc());
+
+  element->classList().Remove("transform");
+  element->GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
   EXPECT_TRUE(target->PaintingLayer()->NeedsVisualOverflowRecalc());
-#endif
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
index 619d9556..97a36e65 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_builder.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_OFFSET_MAPPING_BUILDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_OFFSET_MAPPING_BUILDER_H_
 
-#include <memory>
-
 #include "base/auto_reset.h"
 #include "base/dcheck_is_on.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.h b/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
index 9481bfb..e91fee9 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_APPCACHE_APPLICATION_CACHE_HOST_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_APPCACHE_APPLICATION_CACHE_HOST_H_
 
-#include <memory>
-
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/third_party/blink/renderer/core/loader/ping_loader.h b/third_party/blink/renderer/core/loader/ping_loader.h
index 3ffb304..00c964b 100644
--- a/third_party/blink/renderer/core/loader/ping_loader.h
+++ b/third_party/blink/renderer/core/loader/ping_loader.h
@@ -32,8 +32,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PING_LOADER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PING_LOADER_H_
 
-#include <memory>
-
 #include "third_party/blink/public/platform/web_url_loader_client.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/loader/progress_tracker.h b/third_party/blink/renderer/core/loader/progress_tracker.h
index 2180633..d9e2145 100644
--- a/third_party/blink/renderer/core/loader/progress_tracker.h
+++ b/third_party/blink/renderer/core/loader/progress_tracker.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PROGRESS_TRACKER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_PROGRESS_TRACKER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/loader/frame_loader_types.h"
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource.h b/third_party/blink/renderer/core/loader/resource/image_resource.h
index b9c180b8..65d1c4d 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource.h
@@ -23,7 +23,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_IMAGE_RESOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_IMAGE_RESOURCE_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_info.h"
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
index 5f1f2c2..ae7661c 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_IMAGE_RESOURCE_CONTENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_IMAGE_RESOURCE_CONTENT_H_
 
-#include <memory>
-
 #include "base/auto_reset.h"
 #include "base/dcheck_is_on.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/loader/resource/mock_image_resource_observer.h b/third_party/blink/renderer/core/loader/resource/mock_image_resource_observer.h
index b2bde7aa..4349f268c 100644
--- a/third_party/blink/renderer/core/loader/resource/mock_image_resource_observer.h
+++ b/third_party/blink/renderer/core/loader/resource/mock_image_resource_observer.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_MOCK_IMAGE_RESOURCE_OBSERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_MOCK_IMAGE_RESOURCE_OBSERVER_H_
 
-#include <memory>
-
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource.h"
 #include "third_party/blink/renderer/core/loader/resource/image_resource_content.h"
diff --git a/third_party/blink/renderer/core/loader/resource/script_resource.h b/third_party/blink/renderer/core/loader/resource/script_resource.h
index 18dcc6c..f6c5707e 100644
--- a/third_party/blink/renderer/core/loader/resource/script_resource.h
+++ b/third_party/blink/renderer/core/loader/resource/script_resource.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_SCRIPT_RESOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_RESOURCE_SCRIPT_RESOURCE_H_
 
-#include <memory>
-
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-shared.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_streamer.h"
diff --git a/third_party/blink/renderer/core/loader/threadable_loader.h b/third_party/blink/renderer/core/loader/threadable_loader.h
index 7c2a504..63dc34a 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader.h
+++ b/third_party/blink/renderer/core/loader/threadable_loader.h
@@ -32,8 +32,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADABLE_LOADER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADABLE_LOADER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/core/loader/threadable_loader_client.h b/third_party/blink/renderer/core/loader/threadable_loader_client.h
index d60d281..90b82b2 100644
--- a/third_party/blink/renderer/core/loader/threadable_loader_client.h
+++ b/third_party/blink/renderer/core/loader/threadable_loader_client.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADABLE_LOADER_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_THREADABLE_LOADER_CLIENT_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.h b/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.h
index 5d09b30b..9c9ef87 100644
--- a/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.h
+++ b/third_party/blink/renderer/core/loader/web_associated_url_loader_impl.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_WEB_ASSOCIATED_URL_LOADER_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_WEB_ASSOCIATED_URL_LOADER_IMPL_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/single_thread_task_runner.h"
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.h b/third_party/blink/renderer/core/page/context_menu_controller.h
index 64777eb..b12cdd5 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.h
+++ b/third_party/blink/renderer/core/page/context_menu_controller.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_CONTEXT_MENU_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_CONTEXT_MENU_CONTROLLER_H_
 
-#include <memory>
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
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 0cc4a809..aa47baf 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
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_COMPOSITING_PAINT_LAYER_COMPOSITOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_COMPOSITING_PAINT_LAYER_COMPOSITOR_H_
 
-#include <memory>
-
 #include "base/dcheck_is_on.h"
 #include "base/gtest_prod_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/paint/find_properties_needing_update.h b/third_party/blink/renderer/core/paint/find_properties_needing_update.h
index d55457bf..3ff5805 100644
--- a/third_party/blink/renderer/core/paint/find_properties_needing_update.h
+++ b/third_party/blink/renderer/core/paint/find_properties_needing_update.h
@@ -9,8 +9,6 @@
 
 #if DCHECK_IS_ON()
 
-#include <memory>
-
 #include "base/optional.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
index ce299ac..8cacbc84 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h
@@ -44,7 +44,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_SCROLLABLE_AREA_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_SCROLLABLE_AREA_H_
 
-#include <memory>
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/scroll_anchor.h"
diff --git a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
index 0e4f2ed4..168ab630 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_stacking_node.h
@@ -45,7 +45,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_STACKING_NODE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_PAINT_LAYER_STACKING_NODE_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h b/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h
index 8de3923..68386d9 100644
--- a/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h
+++ b/third_party/blink/renderer/core/paint/scoped_svg_paint_state.h
@@ -25,8 +25,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SCOPED_SVG_PAINT_STATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_SCOPED_SVG_PAINT_STATE_H_
 
-#include <memory>
-
 #include "base/dcheck_is_on.h"
 #include "third_party/blink/renderer/core/paint/object_paint_properties.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
diff --git a/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.h b/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.h
index 05705727..54158d6 100644
--- a/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.h
+++ b/third_party/blink/renderer/core/permissions_policy/permissions_policy_parser.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_PERMISSIONS_POLICY_PERMISSIONS_POLICY_PARSER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_PERMISSIONS_POLICY_PERMISSIONS_POLICY_PARSER_H_
 
-#include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/common/permissions_policy/permissions_policy.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h
index ed5e7526..548d1d1 100644
--- a/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h
+++ b/third_party/blink/renderer/core/scroll/mac_scrollbar_animator_impl.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_MAC_SCROLLBAR_ANIMATOR_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_MAC_SCROLLBAR_ANIMATOR_IMPL_H_
 
-#include <memory>
-
 #include "base/mac/scoped_nsobject.h"
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/renderer/core/scroll/mac_scrollbar_animator.h"
diff --git a/third_party/blink/renderer/core/scroll/scrollbar_test_suite.h b/third_party/blink/renderer/core/scroll/scrollbar_test_suite.h
index 74bf381..d9f9002 100644
--- a/third_party/blink/renderer/core/scroll/scrollbar_test_suite.h
+++ b/third_party/blink/renderer/core/scroll/scrollbar_test_suite.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_SCROLLBAR_TEST_SUITE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_SCROLLBAR_TEST_SUITE_H_
 
-#include <memory>
 #include "base/single_thread_task_runner.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/blink/public/platform/platform.h"
diff --git a/third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h b/third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h
index 98b5f19..c654121d 100644
--- a/third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h
+++ b/third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h
@@ -4,8 +4,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_SMOOTH_SCROLL_SEQUENCER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SCROLL_SMOOTH_SCROLL_SEQUENCER_H_
 
-#include <utility>
-
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/style/content_data.h b/third_party/blink/renderer/core/style/content_data.h
index ef2746f..c5aebe2e 100644
--- a/third_party/blink/renderer/core/style/content_data.h
+++ b/third_party/blink/renderer/core/style/content_data.h
@@ -26,9 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_CONTENT_DATA_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_CONTENT_DATA_H_
 
-#include <memory>
-#include <utility>
-
 #include "third_party/blink/renderer/core/style/computed_style_constants.h"
 #include "third_party/blink/renderer/core/style/style_image.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
diff --git a/third_party/blink/renderer/core/style/quad_length_value.h b/third_party/blink/renderer/core/style/quad_length_value.h
index 53211ccf..a311fe4 100644
--- a/third_party/blink/renderer/core/style/quad_length_value.h
+++ b/third_party/blink/renderer/core/style/quad_length_value.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_QUAD_LENGTH_VALUE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_QUAD_LENGTH_VALUE_H_
 
-#include <memory>
 #include "third_party/blink/renderer/platform/geometry/length.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/style/reference_clip_path_operation.h b/third_party/blink/renderer/core/style/reference_clip_path_operation.h
index 83a12ff..d7ed973 100644
--- a/third_party/blink/renderer/core/style/reference_clip_path_operation.h
+++ b/third_party/blink/renderer/core/style/reference_clip_path_operation.h
@@ -30,8 +30,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_REFERENCE_CLIP_PATH_OPERATION_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_REFERENCE_CLIP_PATH_OPERATION_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/style/clip_path_operation.h"
 #include "third_party/blink/renderer/core/svg/svg_resource.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
diff --git a/third_party/blink/renderer/core/style/shadow_list.h b/third_party/blink/renderer/core/style/shadow_list.h
index f292cf7..c161e57 100644
--- a/third_party/blink/renderer/core/style/shadow_list.h
+++ b/third_party/blink/renderer/core/style/shadow_list.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_SHADOW_LIST_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_SHADOW_LIST_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/style/shadow_data.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect_outsets.h"
 #include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
index 207e55e..5225dc5 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_chrome_client.h
@@ -29,7 +29,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SVG_GRAPHICS_SVG_IMAGE_CHROME_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SVG_GRAPHICS_SVG_IMAGE_CHROME_CLIENT_H_
 
-#include <memory>
 #include "base/gtest_prod_util.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
diff --git a/third_party/blink/renderer/core/svg/svg_uri_reference.h b/third_party/blink/renderer/core/svg/svg_uri_reference.h
index a5d0e9e..b2fedbd 100644
--- a/third_party/blink/renderer/core/svg/svg_uri_reference.h
+++ b/third_party/blink/renderer/core/svg/svg_uri_reference.h
@@ -21,7 +21,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SVG_SVG_URI_REFERENCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_SVG_SVG_URI_REFERENCE_H_
 
-#include <memory>
 #include "base/callback.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/core/testing/mock_clipboard_host.h b/third_party/blink/renderer/core/testing/mock_clipboard_host.h
index b725a7e..97a724b 100644
--- a/third_party/blink/renderer/core/testing/mock_clipboard_host.h
+++ b/third_party/blink/renderer/core/testing/mock_clipboard_host.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_MOCK_CLIPBOARD_HOST_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_TESTING_MOCK_CLIPBOARD_HOST_H_
 
-#include <map>
-
 #include "build/build_config.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "third_party/blink/public/common/common_export.h"
diff --git a/third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h b/third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h
index b38186d9..97fa92c 100644
--- a/third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h
+++ b/third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_PARENT_EXECUTION_CONTEXT_TASK_RUNNERS_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_PARENT_EXECUTION_CONTEXT_TASK_RUNNERS_H_
 
-#include <memory>
 #include "base/macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/thread_annotations.h"
diff --git a/third_party/blink/renderer/core/workers/shared_worker_client_holder.h b/third_party/blink/renderer/core/workers/shared_worker_client_holder.h
index 1325beb..433e7ca 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_client_holder.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_client_holder.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_SHARED_WORKER_CLIENT_HOLDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_SHARED_WORKER_CLIENT_HOLDER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
diff --git a/third_party/blink/renderer/core/xml/xpath_parser.h b/third_party/blink/renderer/core/xml/xpath_parser.h
index dcf2625f..e0b28f3e 100644
--- a/third_party/blink/renderer/core/xml/xpath_parser.h
+++ b/third_party/blink/renderer/core/xml/xpath_parser.h
@@ -27,7 +27,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_XML_XPATH_PARSER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_XML_XPATH_PARSER_H_
 
-#include <memory>
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/xml/xpath_predicate.h"
 #include "third_party/blink/renderer/core/xml/xpath_step.h"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 870b2293..a1ff2d6 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -640,10 +640,8 @@
   if (!parent_)
     return !IsRoot();
 
-  DCHECK(!parent_->IsDetached())
-      << "Parent was detached:\n"
-      << "* Child = " << ToString(true, true)
-      << "\n* Parent = " << parent_->ToString(true, true);
+  if (parent_->IsDetached())
+    return true;
 
   return false;
 }
@@ -4093,29 +4091,11 @@
   // detached, but the children still exist. One example of this is when
   // a <select size="1"> changes to <select size="2">, where the
   // Role::kMenuListPopup is detached.
-  if (!parent_) {
+  if (IsMissingParent()) {
     DCHECK(!IsVirtualObject())
         << "A virtual object must have a parent, and cannot exist without one. "
            "The parent is set when the object is constructed.";
-    if (IsMissingParent())
-      RepairMissingParent();
-  } else {
-    // If the cached parent is detached, it means that when ClearChildren() was
-    // called, the parent did not find this as a child, and could not
-    // DetachFromParent() on the child.
-    // Hint: one way to debug this illegal condition when it it occurs, is to
-    // locally patch ComputeAccessibilityIsIgnoredButIncludedInTree() so
-    // that it returns true for all objects. This should allow ClearChildren()
-    // on the parent to find the children and call DetachFromParent() on them.
-    DCHECK(!parent_->IsDetached())
-        << "Cached parent cannot be detached:"
-        << "\n* |this| = " << ToString(true, true)
-        << "\n* GetNode() = " << GetNode()
-        << "\n* GetLayoutObject() = " << GetLayoutObject()
-        << "\n* Parent: " << parent_->ToString(true, true)
-        << "\n* GetParentNodeForComputeParent() = "
-        << GetParentNodeForComputeParent(GetNode())
-        << "\n* OwnerShadowHost(): " << GetNode()->OwnerShadowHost();
+    RepairMissingParent();
   }
 
   return parent_;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
index d032fed..23158cb2 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -1148,11 +1148,16 @@
   // Example: parent calls Init() => ComputeAccessibilityIsIgnored() =>
   // CanSetFocusAttribute() => CanBeActiveDescendant() =>
   // IsARIAControlledByTextboxWithActiveDescendant() => GetOrCreate().
-  if (layout_object_mapping_.at(layout_object))
-    return Get(layout_object);
+  if (layout_object_mapping_.at(layout_object)) {
+    AXObject* result = Get(layout_object);
+    DCHECK(result) << "Missing cached AXObject for " << layout_object;
+    return result;
+  }
 
   AXObject* new_obj = CreateFromRenderer(layout_object);
 
+  DCHECK(new_obj) << "Could not create AXObject for " << layout_object;
+
   // Will crash later if we have two objects for the same layoutObject.
   DCHECK(!layout_object_mapping_.at(layout_object))
       << "Already have an AXObject for " << layout_object;
@@ -1738,6 +1743,41 @@
       &AXObjectCacheImpl::UpdateCacheAfterNodeIsAttachedWithCleanLayout, node);
 }
 
+bool AXObjectCacheImpl::IsStillInTree(AXObject* obj) {
+  // Return an AXObject for the node if the AXObject is still in the tree.
+  // If there is a viable included parent, that means it's still in the tree.
+  // Otherwise, repair missing parent, or prune the object if no viable parent
+  // can be found. For example, through CSS changes, an ancestor became an
+  // image, which is always a leaf; therefore, no descendants are "in the tree".
+
+  if (!obj)
+    return false;
+
+  if (obj->IsMissingParent()) {
+    // Parent is missing. Attempt to repair it with a viable recomputed parent.
+    AXObject* ax_parent = obj->ComputeParent();
+    if (!IsStillInTree(ax_parent)) {
+      // Parent is unrepairable, meaning that this AXObject can no longer be
+      // attached to the tree and is no longer viable. Prune it now.
+      Remove(obj);
+      return false;
+    }
+    obj->SetParent(ax_parent);
+    return true;
+  }
+
+  if (!obj->LastKnownIsIncludedInTreeValue()) {
+    // Current object was not included in the tree, therefore, recursively
+    // keep checking up a level until a viable included parent is found.
+    if (!IsStillInTree(obj->CachedParentObject())) {
+      Remove(obj);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void AXObjectCacheImpl::UpdateCacheAfterNodeIsAttachedWithCleanLayout(
     Node* node) {
   if (!node || !node->isConnected())
@@ -1915,8 +1955,11 @@
       << "Unclean document at lifecycle " << document->Lifecycle().ToString();
 #endif  // DCHECK_IS_ON()
 
-  if (obj)
+  if (obj) {
+    if (!IsStillInTree(obj))
+      return;  // Object is no longer in tree, and therefore not viable.
     obj->ChildrenChanged();
+  }
 
   if (optional_node)
     relation_cache_->UpdateRelatedTree(optional_node, obj);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
index cb97ab0..320c821 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h
@@ -212,6 +212,10 @@
   AXObject* Get(const Node*);
   AXObject* Get(const LayoutObject*);
 
+  // Return true if the object is still part of the tree, meaning that ancestors
+  // exist or can be repaired all the way to the root.
+  bool IsStillInTree(AXObject*);
+
   AXObject* FirstAccessibleObjectFromNode(const Node*);
 
   void ChildrenChangedWithCleanLayout(Node* optional_node_for_relation_update,
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h
index df0ded1..fa156f30 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_bridge.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_FETCH_BACKGROUND_FETCH_BRIDGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BACKGROUND_FETCH_BACKGROUND_FETCH_BRIDGE_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom-blink.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_attribute_instance_map.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_attribute_instance_map.h
index 604e9e25..53ddc61 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_attribute_instance_map.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_attribute_instance_map.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_ATTRIBUTE_INSTANCE_MAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_ATTRIBUTE_INSTANCE_MAP_H_
 
-#include <memory>
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.h"
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_descriptor.h"
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_service.h"
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_device.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_device.h
index 7b467e5..13f2376 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_device.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_device.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_DEVICE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_DEVICE_H_
 
-#include <memory>
-
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_descriptor.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_descriptor.h
index fe71c57..9b2b9d6 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_descriptor.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_descriptor.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_REMOTE_GATT_DESCRIPTOR_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth.h"
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_service.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_service.h
index 9380678..246ddfa 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_service.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_service.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_REMOTE_GATT_SERVICE_H_
 
-#include <memory>
 #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/modules/v8/string_or_unsigned_long.h"
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth_device.h"
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.h b/third_party/blink/renderer/modules/cache_storage/cache.h
index e37401b4..6c33b661 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.h
+++ b/third_party/blink/renderer/modules/cache_storage/cache.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CACHE_STORAGE_CACHE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CACHE_STORAGE_CACHE_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
diff --git a/third_party/blink/renderer/modules/cache_storage/cache_storage.h b/third_party/blink/renderer/modules/cache_storage/cache_storage.h
index ecf4e4a..84fbfbb3 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache_storage.h
+++ b/third_party/blink/renderer/modules/cache_storage/cache_storage.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CACHE_STORAGE_CACHE_STORAGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CACHE_STORAGE_CACHE_STORAGE_H_
 
-#include <memory>
 #include "base/macros.h"
 #include "base/optional.h"
 #include "third_party/blink/public/mojom/cache_storage/cache_storage.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard.h b/third_party/blink/renderer/modules/clipboard/clipboard.h
index 00281a286..1bdb00e 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CLIPBOARD_CLIPBOARD_H_
 
-#include <utility>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
diff --git a/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h b/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h
index b5100cb..a2b9b149 100644
--- a/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h
+++ b/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CSSPAINT_BACKGROUND_COLOR_PAINT_WORKLET_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CSSPAINT_BACKGROUND_COLOR_PAINT_WORKLET_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
 #include "third_party/blink/renderer/modules/csspaint/native_paint_worklet.h"
diff --git a/third_party/blink/renderer/modules/eventsource/event_source.h b/third_party/blink/renderer/modules/eventsource/event_source.h
index 68fe666..fa820ad 100644
--- a/third_party/blink/renderer/modules/eventsource/event_source.h
+++ b/third_party/blink/renderer/modules/eventsource/event_source.h
@@ -32,7 +32,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_EVENTSOURCE_EVENT_SOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_EVENTSOURCE_EVENT_SOURCE_H_
 
-#include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
diff --git a/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h b/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h
index b78aa7b1..cb8cfc0 100644
--- a/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h
+++ b/third_party/blink/renderer/modules/filesystem/file_system_callbacks.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_FILE_SYSTEM_CALLBACKS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_FILE_SYSTEM_CALLBACKS_H_
 
-#include <memory>
-
 #include "third_party/blink/public/mojom/filesystem/file_system.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_void_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_entry_callback.h"
diff --git a/third_party/blink/renderer/modules/filesystem/file_writer_base.h b/third_party/blink/renderer/modules/filesystem/file_writer_base.h
index e5bc45da..114dbbb 100644
--- a/third_party/blink/renderer/modules/filesystem/file_writer_base.h
+++ b/third_party/blink/renderer/modules/filesystem/file_writer_base.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_FILE_WRITER_BASE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_FILESYSTEM_FILE_WRITER_BASE_H_
 
-#include <memory>
 #include "base/files/file.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h b/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h
index ec2f800..2dea799 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_dispatcher.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_DISPATCHER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_DISPATCHER_H_
 
-#include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "device/gamepad/public/mojom/gamepad.mojom-blink.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h b/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h
index 449fde4..fd836d8 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_shared_memory_reader.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_SHARED_MEMORY_READER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_GAMEPAD_GAMEPAD_SHARED_MEMORY_READER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "device/gamepad/public/mojom/gamepad.mojom-blink.h"
 #include "device/gamepad/public/mojom/gamepad_hardware_buffer.h"
diff --git a/third_party/blink/renderer/modules/handwriting/handwriting_type_converters.h b/third_party/blink/renderer/modules/handwriting/handwriting_type_converters.h
index ceeb453..bc10b3d 100644
--- a/third_party/blink/renderer/modules/handwriting/handwriting_type_converters.h
+++ b/third_party/blink/renderer/modules/handwriting/handwriting_type_converters.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_HANDWRITING_HANDWRITING_TYPE_CONVERTERS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_HANDWRITING_HANDWRITING_TYPE_CONVERTERS_H_
 
-#include <string>
-
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "third_party/blink/public/mojom/handwriting/handwriting.mojom-blink-forward.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks.h b/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks.h
index d85c6a6..c6976e04 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_CALLBACKS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_CALLBACKS_H_
 
-#include <utility>
-
 #include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
diff --git a/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks_impl.h b/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks_impl.h
index 4b8131c41..8a6b5f44 100644
--- a/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks_impl.h
+++ b/third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks_impl.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_CALLBACKS_IMPL_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_WEB_IDB_DATABASE_CALLBACKS_IMPL_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_database_callbacks.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/modules/manifest/manifest_parser.h b/third_party/blink/renderer/modules/manifest/manifest_parser.h
index c2283c1..e088ea7e 100644
--- a/third_party/blink/renderer/modules/manifest/manifest_parser.h
+++ b/third_party/blink/renderer/modules/manifest/manifest_parser.h
@@ -7,8 +7,6 @@
 
 #include <stdint.h>
 
-#include <memory>
-
 #include "base/macros.h"
 #include "base/optional.h"
 #include "third_party/blink/public/common/manifest/manifest.h"
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.h b/third_party/blink/renderer/modules/mediasession/media_session.h
index 6865e6d..4c19aec 100644
--- a/third_party/blink/renderer/modules/mediasession/media_session.h
+++ b/third_party/blink/renderer/modules/mediasession/media_session.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_SESSION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASESSION_MEDIA_SESSION_H_
 
-#include <memory>
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/mediasession/media_session.mojom-blink.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h b/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h
index 1e99adae..85d37d22 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_device_observer.h
@@ -5,12 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_DEVICE_OBSERVER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MEDIA_STREAM_DEVICE_OBSERVER_H_
 
-#include <list>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
diff --git a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h
index dc05c48..0656b9e 100644
--- a/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h
+++ b/third_party/blink/renderer/modules/mediastream/mock_mojo_media_stream_dispatcher_host.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MOCK_MOJO_MEDIA_STREAM_DISPATCHER_HOST_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_MOCK_MOJO_MEDIA_STREAM_DISPATCHER_HOST_H_
 
-#include <string>
-
 #include "base/macros.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_controller.h b/third_party/blink/renderer/modules/mediastream/user_media_controller.h
index d9152ec2..8a9956f 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_controller.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_controller.h
@@ -25,8 +25,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_USER_MEDIA_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_USER_MEDIA_CONTROLLER_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/modules/mediastream/user_media_client.h"
diff --git a/third_party/blink/renderer/modules/notifications/notification_resources_loader.h b/third_party/blink/renderer/modules/notifications/notification_resources_loader.h
index 1437045..a22cda8 100644
--- a/third_party/blink/renderer/modules/notifications/notification_resources_loader.h
+++ b/third_party/blink/renderer/modules/notifications/notification_resources_loader.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_NOTIFICATION_RESOURCES_LOADER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_NOTIFICATION_RESOURCES_LOADER_H_
 
-#include <memory>
-
 #include "third_party/blink/public/mojom/notifications/notification.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/loader/threaded_icon_loader.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.h b/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.h
index 5371ea1..e101f9f 100644
--- a/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.h
+++ b/third_party/blink/renderer/modules/notifications/service_worker_registration_notifications.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_SERVICE_WORKER_REGISTRATION_NOTIFICATIONS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_NOTIFICATIONS_SERVICE_WORKER_REGISTRATION_NOTIFICATIONS_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/mojom/notifications/notification.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/payments/goods/digital_goods_type_converters.h b/third_party/blink/renderer/modules/payments/goods/digital_goods_type_converters.h
index 0643e86..1a9dd4d 100644
--- a/third_party/blink/renderer/modules/payments/goods/digital_goods_type_converters.h
+++ b/third_party/blink/renderer/modules/payments/goods/digital_goods_type_converters.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_GOODS_DIGITAL_GOODS_TYPE_CONVERTERS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_GOODS_DIGITAL_GOODS_TYPE_CONVERTERS_H_
 
-#include <string>
-
 #include "mojo/public/cpp/bindings/type_converter.h"
 #include "third_party/blink/public/mojom/digital_goods/digital_goods.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_item_details.h"
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
index c333fd8..d6a906ff 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_certificate.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_CERTIFICATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_CERTIFICATE_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_dtls_fingerprint.h"
 #include "third_party/blink/renderer/core/dom/dom_time_stamp.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h b/third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h
index 1c0936c6..d8846c7 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_CERTIFICATE_GENERATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_CERTIFICATE_GENERATOR_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/modules/presentation/presentation_connection.h b/third_party/blink/renderer/modules/presentation/presentation_connection.h
index b735fe8..528cb22 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_connection.h
+++ b/third_party/blink/renderer/modules/presentation/presentation_connection.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_CONNECTION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PRESENTATION_PRESENTATION_CONNECTION_H_
 
-#include <memory>
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/presentation/presentation.mojom-blink.h"
diff --git a/third_party/blink/renderer/modules/push_messaging/push_subscription.h b/third_party/blink/renderer/modules/push_messaging/push_subscription.h
index fb0d2374..311d6565 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_subscription.h
+++ b/third_party/blink/renderer/modules/push_messaging/push_subscription.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_SUBSCRIPTION_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_PUSH_MESSAGING_PUSH_SUBSCRIPTION_H_
 
-#include <memory>
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
diff --git a/third_party/blink/renderer/modules/remoteplayback/availability_callback_wrapper.h b/third_party/blink/renderer/modules/remoteplayback/availability_callback_wrapper.h
index 2cdf5b3..956d88a 100644
--- a/third_party/blink/renderer/modules/remoteplayback/availability_callback_wrapper.h
+++ b/third_party/blink/renderer/modules/remoteplayback/availability_callback_wrapper.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_REMOTEPLAYBACK_AVAILABILITY_CALLBACK_WRAPPER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_REMOTEPLAYBACK_AVAILABILITY_CALLBACK_WRAPPER_H_
 
-#include <memory>
-
 #include "base/callback.h"
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/bindings/name_client.h"
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_controller.h b/third_party/blink/renderer/modules/scheduler/dom_task_controller.h
index aef2cd5..1cd92f7 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task_controller.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_controller.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_CONTROLLER_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/core/dom/abort_controller.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_client.h b/third_party/blink/renderer/modules/service_worker/service_worker_client.h
index 406ed05..94674d60 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_client.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_client.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_CLIENT_H_
 
-#include <memory>
 #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h b/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h
index a33d856..663ac79 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_window_client.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_WINDOW_CLIENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SERVICE_WORKER_SERVICE_WORKER_WINDOW_CLIENT_H_
 
-#include <memory>
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/service_worker/service_worker_client.h"
diff --git a/third_party/blink/renderer/modules/speech/speech_recognition_controller.h b/third_party/blink/renderer/modules/speech/speech_recognition_controller.h
index 807a804..83d15e4 100644
--- a/third_party/blink/renderer/modules/speech/speech_recognition_controller.h
+++ b/third_party/blink/renderer/modules/speech/speech_recognition_controller.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SPEECH_SPEECH_RECOGNITION_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SPEECH_SPEECH_RECOGNITION_CONTROLLER_H_
 
-#include <memory>
-
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/speech/speech_recognizer.mojom-blink.h"
diff --git a/third_party/blink/renderer/modules/storage/storage_controller.h b/third_party/blink/renderer/modules/storage/storage_controller.h
index aa6fc61..4e4a245 100644
--- a/third_party/blink/renderer/modules/storage/storage_controller.h
+++ b/third_party/blink/renderer/modules/storage/storage_controller.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_STORAGE_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_STORAGE_CONTROLLER_H_
 
-#include <memory>
-
 #include "base/callback.h"
 #include "base/sequence_checker.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
diff --git a/third_party/blink/renderer/modules/storage/storage_namespace.h b/third_party/blink/renderer/modules/storage/storage_namespace.h
index 0f7752f9..bb62188 100644
--- a/third_party/blink/renderer/modules/storage/storage_namespace.h
+++ b/third_party/blink/renderer/modules/storage/storage_namespace.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_STORAGE_NAMESPACE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_STORAGE_NAMESPACE_H_
 
-#include <memory>
-
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h"
 #include "third_party/blink/public/mojom/dom_storage/dom_storage.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/webaudio/async_audio_decoder.h b/third_party/blink/renderer/modules/webaudio/async_audio_decoder.h
index 1b77380..6f782e2c 100644
--- a/third_party/blink/renderer/modules/webaudio/async_audio_decoder.h
+++ b/third_party/blink/renderer/modules/webaudio/async_audio_decoder.h
@@ -26,8 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_ASYNC_AUDIO_DECODER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_ASYNC_AUDIO_DECODER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_decode_error_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_decode_success_callback.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_input.h b/third_party/blink/renderer/modules/webaudio/audio_node_input.h
index e720f88..c0c357a2 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_input.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_input.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_NODE_INPUT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_NODE_INPUT_H_
 
-#include <memory>
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_summing_junction.h"
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node_output.h b/third_party/blink/renderer/modules/webaudio/audio_node_output.h
index 4fd70b1..c8387759 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_node_output.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_node_output.h
@@ -26,7 +26,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_NODE_OUTPUT_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_AUDIO_NODE_OUTPUT_H_
 
-#include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_node.h"
diff --git a/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h b/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h
index dbc55f47..865a337f 100644
--- a/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h
+++ b/third_party/blink/renderer/modules/webaudio/inspector_helper_mixin.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_INSPECTOR_HELPER_MIXIN_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBAUDIO_INSPECTOR_HELPER_MIXIN_H_
 
-#include <memory>
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
index 1409648..c7e10c6 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_ENCODER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_ENCODER_H_
 
-#include <memory>
-
 #include "media/base/audio_codecs.h"
 #include "media/base/audio_encoder.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
diff --git a/third_party/blink/renderer/modules/webdatabase/database_task.h b/third_party/blink/renderer/modules/webdatabase/database_task.h
index c39a9b390..44994e9 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_task.h
+++ b/third_party/blink/renderer/modules/webdatabase/database_task.h
@@ -29,8 +29,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_DATABASE_TASK_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBDATABASE_DATABASE_TASK_H_
 
-#include <memory>
-
 #include "base/dcheck_is_on.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access.h b/third_party/blink/renderer/modules/webmidi/midi_access.h
index de97b1c5..be182b6d 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_access.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_access.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_ACCESS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_ACCESS_H_
 
-#include <memory>
 #include "media/midi/midi_service.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
diff --git a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
index 8ef567a..a238a92 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_access_initializer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_ACCESS_INITIALIZER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_ACCESS_INITIALIZER_H_
 
-#include <memory>
 #include "media/midi/midi_service.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/permissions/permission.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.h b/third_party/blink/renderer/modules/websockets/dom_websocket.h
index 883b63d..98b7160 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.h
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.h
@@ -33,7 +33,6 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <memory>
 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
diff --git a/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h b/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h
index 1c9e258..e8c2e95e 100644
--- a/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h
+++ b/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_INSPECTOR_WEBSOCKET_EVENTS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_INSPECTOR_WEBSOCKET_EVENTS_H_
 
-#include <memory>
 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_client.h b/third_party/blink/renderer/modules/websockets/websocket_channel_client.h
index 7d025132..0c1da3b 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_channel_client.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_channel_client.h
@@ -32,7 +32,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_CHANNEL_CLIENT_H_
 
 #include <stdint.h>
-#include <memory>
 #include "base/containers/span.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/modules/websockets/websocket_common.h b/third_party/blink/renderer/modules/websockets/websocket_common.h
index 40f454f..e4507b0 100644
--- a/third_party/blink/renderer/modules/websockets/websocket_common.h
+++ b/third_party/blink/renderer/modules/websockets/websocket_common.h
@@ -7,8 +7,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_COMMON_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBSOCKETS_WEBSOCKET_COMMON_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_anchor.h b/third_party/blink/renderer/modules/xr/xr_anchor.h
index 8ae523b..58ceaf7d 100644
--- a/third_party/blink/renderer/modules/xr/xr_anchor.h
+++ b/third_party/blink/renderer/modules/xr/xr_anchor.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_ANCHOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_ANCHOR_H_
 
-#include <memory>
-
 #include "base/optional.h"
 #include "device/vr/public/mojom/pose.h"
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_hit_test_source.h b/third_party/blink/renderer/modules/xr/xr_hit_test_source.h
index 79868dc..56bf221 100644
--- a/third_party/blink/renderer/modules/xr/xr_hit_test_source.h
+++ b/third_party/blink/renderer/modules/xr/xr_hit_test_source.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_HIT_TEST_SOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_HIT_TEST_SOURCE_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_plane.h b/third_party/blink/renderer/modules/xr/xr_plane.h
index 6d5e08f..90c5ffa 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_H_
 
-#include <memory>
-
 #include "base/optional.h"
 #include "device/vr/public/mojom/pose.h"
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_pose.h b/third_party/blink/renderer/modules/xr/xr_pose.h
index ecb305a..b037c9b 100644
--- a/third_party/blink/renderer/modules/xr/xr_pose.h
+++ b/third_party/blink/renderer/modules/xr/xr_pose.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_POSE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_POSE_H_
 
-#include <utility>
-
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/transforms/transformation_matrix.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_test_utils.h b/third_party/blink/renderer/modules/xr/xr_test_utils.h
index 6c28ad5..0a244716 100644
--- a/third_party/blink/renderer/modules/xr/xr_test_utils.h
+++ b/third_party/blink/renderer/modules/xr/xr_test_utils.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_TEST_UTILS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_TEST_UTILS_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_point_init.h"
 #include "third_party/blink/renderer/core/geometry/dom_point_read_only.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
diff --git a/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.h b/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.h
index d574465..bfbbe90 100644
--- a/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.h
+++ b/third_party/blink/renderer/modules/xr/xr_transient_input_hit_test_source.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_TRANSIENT_INPUT_HIT_TEST_SOURCE_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_TRANSIENT_INPUT_HIT_TEST_SOURCE_H_
 
-#include <memory>
-
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 #include "device/vr/public/mojom/vr_service.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/platform/audio/audio_bus.h b/third_party/blink/renderer/platform/audio/audio_bus.h
index 3b5aa70..8660a6b 100644
--- a/third_party/blink/renderer/platform/audio/audio_bus.h
+++ b/third_party/blink/renderer/platform/audio/audio_bus.h
@@ -29,8 +29,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_BUS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_BUS_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/audio/audio_channel.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
diff --git a/third_party/blink/renderer/platform/audio/audio_destination_consumer.h b/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
index e6b53b4..30ce6936 100644
--- a/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
+++ b/third_party/blink/renderer/platform/audio/audio_destination_consumer.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_AUDIO_AUDIO_DESTINATION_CONSUMER_H_
 
-#include <memory>
 #include "third_party/blink/renderer/platform/platform_export.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
index 66c85f20..32dc609b 100644
--- a/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
+++ b/third_party/blink/renderer/platform/bindings/dom_wrapper_world.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_DOM_WRAPPER_WORLD_H_
 
-#include <memory>
-
 #include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
diff --git a/third_party/blink/renderer/platform/bindings/v8_binding.h b/third_party/blink/renderer/platform/bindings/v8_binding.h
index b6190156..ee19e63 100644
--- a/third_party/blink/renderer/platform/bindings/v8_binding.h
+++ b/third_party/blink/renderer/platform/bindings/v8_binding.h
@@ -161,66 +161,6 @@
       info.GetReturnValue(), string.Impl());
 }
 
-template <typename CallbackInfo>
-inline void V8SetReturnValue(const CallbackInfo& callback_info,
-                             ScriptWrappable* impl,
-                             v8::Local<v8::Object> creation_context) {
-  if (UNLIKELY(!impl)) {
-    V8SetReturnValueNull(callback_info);
-    return;
-  }
-  if (DOMDataStore::SetReturnValue(callback_info.GetReturnValue(), impl))
-    return;
-  v8::Local<v8::Value> wrapper =
-      impl->Wrap(callback_info.GetIsolate(), creation_context);
-  V8SetReturnValue(callback_info, wrapper);
-}
-
-template <typename CallbackInfo>
-inline void V8SetReturnValue(const CallbackInfo& callback_info,
-                             ScriptWrappable* impl) {
-  V8SetReturnValue(callback_info, impl, callback_info.Holder());
-}
-
-template <typename CallbackInfo>
-inline void V8SetReturnValueForMainWorld(const CallbackInfo& callback_info,
-                                         ScriptWrappable* impl) {
-  DCHECK(DOMWrapperWorld::Current(callback_info.GetIsolate()).IsMainWorld());
-  if (UNLIKELY(!impl)) {
-    V8SetReturnValueNull(callback_info);
-    return;
-  }
-  if (DOMDataStore::SetReturnValueForMainWorld(callback_info.GetReturnValue(),
-                                               impl))
-    return;
-  v8::Local<v8::Value> wrapper =
-      impl->Wrap(callback_info.GetIsolate(), callback_info.Holder());
-  V8SetReturnValue(callback_info, wrapper);
-}
-
-template <typename CallbackInfo>
-inline void V8SetReturnValueFast(const CallbackInfo& callback_info,
-                                 ScriptWrappable* impl,
-                                 const ScriptWrappable* wrappable) {
-  if (UNLIKELY(!impl)) {
-    V8SetReturnValueNull(callback_info);
-    return;
-  }
-  if (DOMDataStore::SetReturnValueFast(callback_info.GetReturnValue(), impl,
-                                       callback_info.Holder(), wrappable))
-    return;
-  v8::Local<v8::Value> wrapper =
-      impl->Wrap(callback_info.GetIsolate(), callback_info.Holder());
-  V8SetReturnValue(callback_info, wrapper);
-}
-
-template <typename CallbackInfo, typename T>
-inline void V8SetReturnValueFast(const CallbackInfo& callback_info,
-                                 const v8::Local<T> handle,
-                                 const ScriptWrappable*) {
-  V8SetReturnValue(callback_info, handle);
-}
-
 // Dictionary
 template <class CallbackInfo>
 void V8SetReturnValue(const CallbackInfo& info,
diff --git a/third_party/blink/renderer/platform/bindings/v8_private_property.h b/third_party/blink/renderer/platform/bindings/v8_private_property.h
index d324ffc5..bfe5d2b 100644
--- a/third_party/blink/renderer/platform/bindings/v8_private_property.h
+++ b/third_party/blink/renderer/platform/bindings/v8_private_property.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_BINDINGS_V8_PRIVATE_PROPERTY_H_
 
-#include <memory>
-
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/bindings/v8_binding_macros.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
diff --git a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h
index ed90cb6..f62234db 100644
--- a/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h
+++ b/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h
@@ -12,8 +12,6 @@
 #include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
 
-#include <memory>
-
 namespace blink {
 
 // Unique font lookup implementation for Android, uses two backends: Fonts from
diff --git a/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.h b/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.h
index 92b6d4f..0b717f2 100644
--- a/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.h
+++ b/third_party/blink/renderer/platform/fonts/linux/font_unique_name_lookup_linux.h
@@ -7,8 +7,6 @@
 
 #include "third_party/blink/renderer/platform/fonts/font_unique_name_lookup.h"
 
-#include <memory>
-
 namespace blink {
 
 class FontUniqueNameLookupLinux : public FontUniqueNameLookup {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h
index ea1d1b8..ffdc370 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_VIEW_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_SHAPE_RESULT_VIEW_H_
 
-#include <memory>
 #include "base/containers/span.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result.h"
 #include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
diff --git a/third_party/blink/renderer/platform/fonts/symbols_iterator.h b/third_party/blink/renderer/platform/fonts/symbols_iterator.h
index 665845e..cc65327 100644
--- a/third_party/blink/renderer/platform/fonts/symbols_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/symbols_iterator.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SYMBOLS_ITERATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SYMBOLS_ITERATOR_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
 #include "third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h"
diff --git a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
index 5dcd822..c078f99 100644
--- a/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_ACCELERATED_STATIC_BITMAP_IMAGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_ACCELERATED_STATIC_BITMAP_IMAGE_H_
 
-#include <memory>
-
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
diff --git a/third_party/blink/renderer/platform/graphics/draw_looper_builder.h b/third_party/blink/renderer/platform/graphics/draw_looper_builder.h
index 79b6215..9377d2a2 100644
--- a/third_party/blink/renderer/platform/graphics/draw_looper_builder.h
+++ b/third_party/blink/renderer/platform/graphics/draw_looper_builder.h
@@ -31,8 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DRAW_LOOPER_BUILDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_DRAW_LOOPER_BUILDER_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
index 51180a6..3e70fe4 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/image_layer_bridge.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_IMAGE_LAYER_BRIDGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_GPU_IMAGE_LAYER_BRIDGE_H_
 
-#include <memory>
-
 #include "base/macros.h"
 #include "cc/layers/texture_layer_client.h"
 #include "cc/resources/shared_bitmap_id_registrar.h"
diff --git a/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h b/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h
index df5bb08..79f1916 100644
--- a/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h
+++ b/third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IDENTIFIABILITY_PAINT_OP_DIGEST_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_IDENTIFIABILITY_PAINT_OP_DIGEST_H_
 
-#include <memory>
-
 #include "cc/paint/draw_image.h"
 #include "cc/paint/image_provider.h"
 #include "cc/paint/paint_cache.h"
diff --git a/third_party/blink/renderer/platform/graphics/mailbox_ref.h b/third_party/blink/renderer/platform/graphics/mailbox_ref.h
index 6d8bfac7..83f525ff 100644
--- a/third_party/blink/renderer/platform/graphics/mailbox_ref.h
+++ b/third_party/blink/renderer/platform/graphics/mailbox_ref.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_REF_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MAILBOX_REF_H_
 
-#include <memory>
-
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_checker.h"
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
index 2ca460f..c51e9a8e 100644
--- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
+++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_CANVAS_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_MEMORY_MANAGED_PAINT_CANVAS_H_
 
-#include <memory>
-
 #include "cc/paint/paint_canvas.h"
 #include "cc/paint/record_paint_canvas.h"
 #include "third_party/blink/public/platform/platform.h"
diff --git a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
index 27e6b21c..190096d 100644
--- a/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
+++ b/third_party/blink/renderer/platform/graphics/offscreen_canvas_placeholder.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_PLACEHOLDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_OFFSCREEN_CANVAS_PLACEHOLDER_H_
 
-#include <memory>
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
index e95ecde6..64e9f9c1 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_BUILDER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_PAINT_PAINT_RECORD_BUILDER_H_
 
-#include <memory>
-
 #include "base/optional.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/display_item_client.h"
diff --git a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
index c110365c..c8e57cf 100644
--- a/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
+++ b/third_party/blink/renderer/platform/graphics/surface_layer_bridge.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SURFACE_LAYER_BRIDGE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_SURFACE_LAYER_BRIDGE_H_
 
-#include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
index 2c2bce3..83f65add 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
@@ -31,7 +31,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JXL_JXL_IMAGE_DECODER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_JXL_JXL_IMAGE_DECODER_H_
 
-#include <memory>
 #include "third_party/blink/renderer/platform/image-decoders/fast_shared_buffer_reader.h"
 #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
 
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_stream.h b/third_party/blink/renderer/platform/image-decoders/segment_stream.h
index c65c9d2..b56002e 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_stream.h
+++ b/third_party/blink/renderer/platform/image-decoders/segment_stream.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_SEGMENT_STREAM_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_IMAGE_DECODERS_SEGMENT_STREAM_H_
 
-#include <algorithm>
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/skia/include/core/SkStream.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
index 44b8a66..ca7fed1 100644
--- a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_BYTES_CONSUMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_BYTES_CONSUMER_H_
 
-#include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "base/types/pass_key.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
diff --git a/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h b/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h
index 7af43dd..3ab5faac 100644
--- a/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h
+++ b/third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_DATA_PIPE_BYTES_CONSUMER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_DATA_PIPE_BYTES_CONSUMER_H_
 
-#include <memory>
-
 #include "base/memory/scoped_refptr.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h b/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
index 87e2690..651d2cf 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h
@@ -32,7 +32,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_DESCRIPTOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_DESCRIPTOR_H_
 
-#include <memory>
 #include "third_party/blink/renderer/platform/mediastream/media_stream_component.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_source.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_track_platform.h b/third_party/blink/renderer/platform/mediastream/media_stream_track_platform.h
index 1444c0c..3f0b4d29 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_track_platform.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_track_platform.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_TRACK_PLATFORM_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEDIASTREAM_MEDIA_STREAM_TRACK_PLATFORM_H_
 
-#include <string>
-
 #include "base/callback.h"
 #include "third_party/blink/public/platform/modules/mediastream/web_media_stream_track.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
diff --git a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h
index 96910773..a7f8e98 100644
--- a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h
+++ b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.h
@@ -7,8 +7,6 @@
 
 #include <stdint.h>
 
-#include <string>
-
 #include "mojo/public/cpp/base/big_buffer.h"
 #include "mojo/public/cpp/base/file_path_mojom_traits.h"
 #include "mojo/public/cpp/bindings/array_traits_web_vector.h"
diff --git a/third_party/blink/renderer/platform/network/encoded_form_data.h b/third_party/blink/renderer/platform/network/encoded_form_data.h
index 51433fe9..295099d 100644
--- a/third_party/blink/renderer/platform/network/encoded_form_data.h
+++ b/third_party/blink/renderer/platform/network/encoded_form_data.h
@@ -27,8 +27,6 @@
 // This requires some gymnastics below, to explicitly forward-declare the
 // required types without reference to the generator output headers.
 
-#include <utility>
-
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h b/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h
index 98d58c4..987aa67 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_dtmf_sender_handler.h
@@ -5,8 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_DTMF_SENDER_HANDLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_PEERCONNECTION_RTC_DTMF_SENDER_HANDLER_H_
 
-#include <string>
-
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
diff --git a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
index 948d925..28e680e 100644
--- a/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_COMMON_SCHEDULER_HELPER_H_
 
 #include <stddef.h>
-#include <memory>
 
 #include "base/logging.h"
 #include "base/macros.h"
diff --git a/third_party/blink/renderer/platform/testing/find_cc_layer.h b/third_party/blink/renderer/platform/testing/find_cc_layer.h
index b6e164f..0474865 100644
--- a/third_party/blink/renderer/platform/testing/find_cc_layer.h
+++ b/third_party/blink/renderer/platform/testing/find_cc_layer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FIND_CC_LAYER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_TESTING_FIND_CC_LAYER_H_
 
-#include <string>
 #include "cc/input/scrollbar.h"
 #include "third_party/blink/renderer/platform/graphics/compositor_element_id.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
diff --git a/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h b/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h
index 7860f44..ef7587f 100644
--- a/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h
+++ b/third_party/blink/renderer/platform/text/hyphenation/hyphenator_aosp.h
@@ -23,7 +23,6 @@
  * An implementation of Liang's hyphenation algorithm.
  */
 
-#include <memory>
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace android {
diff --git a/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter.h b/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter.h
index 0e1a75f7..64a3f9c 100644
--- a/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter.h
+++ b/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter.h
@@ -6,7 +6,6 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_LEGACY_WEBRTC_VIDEO_FRAME_ADAPTER_H_
 
 #include <stdint.h>
-#include <map>
 
 #include "base/containers/span.h"
 #include "base/memory/ref_counted.h"
diff --git a/third_party/blink/tools/blinkpy/common/host.py b/third_party/blink/tools/blinkpy/common/host.py
index a2c602c..4b071fc 100644
--- a/third_party/blink/tools/blinkpy/common/host.py
+++ b/third_party/blink/tools/blinkpy/common/host.py
@@ -31,6 +31,7 @@
 
 from blinkpy.common.checkout.git import Git
 from blinkpy.common.net import web
+from blinkpy.common.net.bb_agent import BBAgent
 from blinkpy.common.net.results_fetcher import TestResultsFetcher
 from blinkpy.common.system.system_host import SystemHost
 from blinkpy.web_tests.builder_list import BuilderList
@@ -55,6 +56,7 @@
         # FIXME: PortFactory doesn't belong on this Host object if Port is going to have a Host (circular dependency).
         self.port_factory = PortFactory(self)
 
+        self.bb_agent = BBAgent(self)
         self.builders = BuilderList.load_default_builder_list(self.filesystem)
 
     def git(self, path=None):
diff --git a/third_party/blink/tools/blinkpy/common/net/bb_agent.py b/third_party/blink/tools/blinkpy/common/net/bb_agent.py
new file mode 100644
index 0000000..25f474f
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/net/bb_agent.py
@@ -0,0 +1,60 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import collections
+import json
+import logging
+
+from blinkpy.common.net.luci_auth import LuciAuth
+from blinkpy.common.net.results_fetcher import Build
+from blinkpy.common.memoized import memoized
+from blinkpy.common.path_finder import PathFinder
+
+_log = logging.getLogger(__name__)
+
+
+class BBAgent(object):
+    def __init__(self, host):
+        self._host = host
+
+    @property
+    def bb_bin_path(self):
+        return self._host.filesystem.join(
+            PathFinder(self._host.filesystem).depot_tools_base(), 'bb')
+
+    @memoized
+    def _check_luci_auth(self):
+        try:
+            LuciAuth(self._host).get_access_token()
+        except Exception as ex:
+            _log.exception('Caught an exception when checking luci '
+                           'authentication. Please run `luci-auth login` '
+                           'before trying again.')
+            raise ex
+
+    def get_latest_finished_build(self, builder_name, try_build=False):
+        self._check_luci_auth()
+        builder_path = ('chromium/' +
+                        ('try' if try_build else 'ci') +
+                        '/' + builder_name)
+
+        bb_output = self._host.executive.run_command(
+            [self.bb_bin_path, 'ls', '-1', '-json',
+             '-status', 'ended', builder_path]).strip()
+        if not bb_output:
+            return
+
+        json_output = json.loads(bb_output)
+        return Build(builder_name, json_output['number'],
+                     json_output['id'])
+
+    def get_build_test_results(self, build, step_name):
+        assert build.build_id, 'ID of the build must be provided'
+        bb_output = self._host.executive.run_command(
+            [self.bb_bin_path, 'log', '-nocolor', build.build_id,
+             step_name, 'json.output'])
+
+        if not bb_output:
+            return
+        return json.loads(bb_output)
diff --git a/third_party/blink/tools/blinkpy/common/net/bb_agent_unittest.py b/third_party/blink/tools/blinkpy/common/net/bb_agent_unittest.py
new file mode 100644
index 0000000..f17691eb
--- /dev/null
+++ b/third_party/blink/tools/blinkpy/common/net/bb_agent_unittest.py
@@ -0,0 +1,43 @@
+# Copyright 2021 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from blinkpy.common.host_mock import MockHost
+from blinkpy.common.net.bb_agent import BBAgent
+from blinkpy.common.net.results_fetcher import Build
+from blinkpy.common.system.executive_mock import MockExecutive
+
+
+class BBAgentTest(unittest.TestCase):
+    def setUp(self):
+        self._host = MockHost()
+        self._host.executive = MockExecutive(output='')
+        self._bb_agent = BBAgent(self._host)
+
+    def test_get_latest_build(self):
+        self._bb_agent.get_latest_finished_build('linux-blink-rel')
+        self.assertEqual(self._host.executive.calls[-1],
+                         [self._bb_agent.bb_bin_path, 'ls', '-1', '-json',
+                          '-status', 'ended', 'chromium/ci/linux-blink-rel'])
+
+    def test_get_latest_try_build(self):
+        self._bb_agent.get_latest_finished_build('linux-blink-rel',
+                                                 try_build=True)
+        self.assertEqual(self._host.executive.calls[-1],
+                         [self._bb_agent.bb_bin_path, 'ls', '-1', '-json',
+                          '-status', 'ended', 'chromium/try/linux-blink-rel'])
+
+    def test_get_build_results(self):
+        host = MockHost()
+        host.executive = MockExecutive(
+            output='{"number": 422, "id": "abcd"}')
+
+        bb_agent = BBAgent(host)
+        build = bb_agent.get_latest_finished_build('linux-blink-rel')
+        self.assertEqual(build, Build('linux-blink-rel', 422, 'abcd'))
+        bb_agent.get_build_test_results(build, 'blink_web_tests')
+        self.assertEqual(host.executive.calls[-1],
+                         [bb_agent.bb_bin_path, 'log', '-nocolor',
+                          build.build_id, 'blink_web_tests', 'json.output'])
diff --git a/third_party/blink/tools/blinkpy/common/net/results_fetcher.py b/third_party/blink/tools/blinkpy/common/net/results_fetcher.py
index c8e16a03..ca286ea 100644
--- a/third_party/blink/tools/blinkpy/common/net/results_fetcher.py
+++ b/third_party/blink/tools/blinkpy/common/net/results_fetcher.py
@@ -44,15 +44,17 @@
 RESULTS_SUMMARY_URL_BASE = 'https://storage.googleapis.com/chromium-layout-test-archives'
 
 
-class Build(collections.namedtuple('Build', ('builder_name', 'build_number'))):
+class Build(collections.namedtuple('Build', ('builder_name', 'build_number',
+                                             'build_id'))):
     """Represents a combination of builder and build number.
 
     If build number is None, this represents the latest build
     for a given builder.
     """
 
-    def __new__(cls, builder_name, build_number=None):
-        return super(Build, cls).__new__(cls, builder_name, build_number)
+    def __new__(cls, builder_name, build_number=None, build_id=None):
+        return super(Build, cls).__new__(cls, builder_name,
+                                         build_number, build_id)
 
 
 class TestResultsFetcher(object):
diff --git a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_unittest.py b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_unittest.py
index 87fe0cc0..81527eb 100644
--- a/third_party/blink/tools/blinkpy/tool/commands/rebaseline_unittest.py
+++ b/third_party/blink/tools/blinkpy/tool/commands/rebaseline_unittest.py
@@ -1086,8 +1086,8 @@
         test_baseline_set.add('a/x.html', Build('MOCK Win10'))
         self.assertEqual(str(test_baseline_set), (
             '<TestBaselineSet with:\n'
-            '  a/x.html: Build(builder_name=\'MOCK Mac10.12\', build_number=None), test-mac-mac10.12\n'
-            '  a/x.html: Build(builder_name=\'MOCK Win10\', build_number=None), test-win-win10>'
+            '  a/x.html: Build(builder_name=\'MOCK Mac10.12\', build_number=None, build_id=None), test-mac-mac10.12\n'
+            '  a/x.html: Build(builder_name=\'MOCK Win10\', build_number=None, build_id=None), test-win-win10>'
         ))
 
     def test_getters(self):
diff --git a/third_party/blink/tools/diff_wpt_results.py b/third_party/blink/tools/diff_wpt_results.py
index f4c860f..c6c502b2b 100755
--- a/third_party/blink/tools/diff_wpt_results.py
+++ b/third_party/blink/tools/diff_wpt_results.py
@@ -24,9 +24,12 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import argparse
+import contextlib
 import json
+import logging
 import os
 import sys
+import tempfile
 
 from blinkpy.common.host import Host
 from blinkpy.web_tests.layout_package.bot_test_expectations import BotTestExpectationsFactory
@@ -39,6 +42,8 @@
                'Baseline Flaky Results, Unreliable Comparison\n')
 YES = 'Yes'
 NO = 'No'
+_log = logging.getLogger(os.path.basename(__file__))
+
 
 def map_tests_to_results(output_mp, input_mp, path=''):
     if 'actual' in input_mp:
@@ -50,10 +55,10 @@
 
 class WPTResultsDiffer(object):
 
-    def __init__(self, args, actual_results_map,
+    def __init__(self, args, host, actual_results_map,
                  baseline_results_map, csv_output):
         self._args = args
-        self._host = Host()
+        self._host = host
         self._actual_results_map = actual_results_map
         self._baseline_results_map = baseline_results_map
         self._csv_output = csv_output
@@ -134,14 +139,44 @@
         self._csv_output.write(file_output)
 
 
+@contextlib.contextmanager
+def _get_test_results(host, product, results_path=None):
+    if results_path:
+        json_results_obj = open(results_path, 'r')
+    else:
+        _log.info(('Retrieving test results for '
+                   'product %s using the bb command'), product)
+        specifiers = [product]
+        builders = host.builders.filter_builders(
+            include_specifiers=specifiers)
+        assert len(builders) == 1
+
+        builder_name = builders[0]
+        latest_build = host.bb_agent.get_latest_finished_build(
+            builder_name)
+        _log.debug('The latest build for %s is %d',
+                   builder_name, latest_build.build_number)
+
+        build_results = host.bb_agent.get_build_test_results(
+            latest_build, PRODUCTS_TO_STEPNAMES[product])
+        json_results_obj = tempfile.TemporaryFile()
+        json_results_obj.write(json.dumps(build_results))
+        json_results_obj.seek(0)
+
+    try:
+        yield json_results_obj
+    finally:
+        json_results_obj.close()
+
+
 def main(args):
     parser = argparse.ArgumentParser(prog=os.path.basename(__file__))
-    parser.add_argument('--baseline-test-results', required=True,
+    parser.add_argument('--baseline-test-results', required=False,
                         help='Path to baseline test results JSON file')
     parser.add_argument('--baseline-product', required=True, action='store',
                         choices=PRODUCTS,
                         help='Name of the baseline WPT product')
-    parser.add_argument('--test-results-to-compare', required=True,
+    parser.add_argument('--test-results-to-compare', required=False,
                         help='Path to actual test results JSON file')
     parser.add_argument('--product-to-compare', required=True, action='store',
                         choices=PRODUCTS,
@@ -150,11 +185,20 @@
                         help='Path to CSV output file')
     args = parser.parse_args()
 
+    # TODO(rmhasan): Set the log level using a command line argument
+    logging.basicConfig(level=logging.INFO)
+
     assert args.product_to_compare != args.baseline_product, (
         'Product to compare and the baseline product cannot be the same')
 
-    with open(args.test_results_to_compare, 'r') as actual_results_content, \
-            open(args.baseline_test_results, 'r') as baseline_results_content, \
+    host = Host()
+    actual_results_getter = _get_test_results(
+        host, args.product_to_compare, args.test_results_to_compare)
+    baseline_results_getter = _get_test_results(
+        host, args.baseline_product, args.baseline_test_results)
+
+    with actual_results_getter as actual_results_content,            \
+            baseline_results_getter as baseline_results_content,     \
             open(args.csv_output, 'w') as csv_output:
 
         # Read JSON results files. They must follow the Chromium
@@ -172,7 +216,7 @@
                              baseline_results_json['tests'])
 
         # Create a CSV file which compares tests results to baseline results
-        WPTResultsDiffer(args, tests_to_actual_results,
+        WPTResultsDiffer(args, host, tests_to_actual_results,
                          tests_to_baseline_results, csv_output).create_csv()
 
     return 0
diff --git a/third_party/blink/tools/diff_wpt_results_unittest.py b/third_party/blink/tools/diff_wpt_results_unittest.py
index 6b37e485f..823f719 100755
--- a/third_party/blink/tools/diff_wpt_results_unittest.py
+++ b/third_party/blink/tools/diff_wpt_results_unittest.py
@@ -25,11 +25,18 @@
 
 import copy
 import io
+import logging
+import json
 import unittest
 
+from blinkpy.common.host import Host
+from blinkpy.common.host_mock import MockHost
+from blinkpy.common.system.executive_mock import MockExecutive
+
 from collections import namedtuple
 from diff_wpt_results import (
-    map_tests_to_results, WPTResultsDiffer, CSV_HEADING)
+    map_tests_to_results, WPTResultsDiffer, CSV_HEADING,
+    _get_test_results)
 
 MockArgs = namedtuple('MockArgs', ['product_to_compare', 'baseline_product'])
 TEST_PRODUCT = 'android_weblayer'
@@ -42,6 +49,7 @@
         super(MockWPTResultsDiffer, self).__init__(
             MockArgs(product_to_compare=TEST_PRODUCT,
                      baseline_product=TEST_BASELINE_PRODUCT),
+            MockHost(),
             actual_results_map, baseline_results_map, csv_output)
 
     def _get_bot_expectations(self, product):
@@ -125,6 +133,47 @@
             self.assertEquals(content, CSV_HEADING +
                               'test.html,PASS,MISSING,MISSING RESULTS,{},{},No\n')
 
+    def test_use_bb_to_get_results(self):
+        actual_mp = {'tests': {'test.html': {'actual': 'PASS'}}}
+        baseline_mp = copy.deepcopy(actual_mp)
+        baseline_mp['tests']['test.html']['actual'] = 'FAIL'
+        host = Host()
+
+        def process_cmds(cmd_args):
+            if 'token' in cmd_args:
+                return '00000'
+            elif 'weblayer_shell_wpt' in cmd_args:
+                return json.dumps(actual_mp)
+            elif 'chrome_public_wpt' in cmd_args:
+                return json.dumps(baseline_mp)
+            else:
+                return '{"number": 400, "id":"abcd"}'
+
+        host.executive = MockExecutive(run_command_fn=process_cmds)
+
+        with io.StringIO() as csv_out,                                         \
+                _get_test_results(host, 'android_weblayer') as test_results,   \
+                _get_test_results(host, 'chrome_android') as baseline_results:
+
+            actual_results_json = json.loads(test_results.read())
+            baseline_results_json = json.loads(baseline_results.read())
+
+            tests_to_actual_results = {}
+            tests_to_baseline_results = {}
+            map_tests_to_results(tests_to_actual_results,
+                                 actual_results_json['tests'])
+            map_tests_to_results(tests_to_baseline_results,
+                                 baseline_results_json['tests'])
+
+            MockWPTResultsDiffer(tests_to_actual_results,
+                                 tests_to_baseline_results,
+                                 csv_out).create_csv()
+            csv_out.seek(0)
+            content = csv_out.read()
+            self.assertEquals(content, CSV_HEADING +
+                              ('test.html,PASS,FAIL,DIFFERENT RESULTS,'
+                               '"{FAIL, TIMEOUT, PASS}","{FAIL, CRASH}",No\n'))
 
 if __name__ == '__main__':
+    logging.basicConfig()
     unittest.main()
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index dd43548..d850a32 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby-connections
-Version: dadd6794e46738b7a1db481f4f47e627fc448981
+Version: 93eec2c07b588985da41af77a4589dd0de37c677
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index da58f91..bd7e062 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -45168,7 +45168,6 @@
       label="AutofillRationalizeRepeatedServerPredictions:enabled"/>
   <int value="-1421676705" label="force-webxr-runtime"/>
   <int value="-1420542268" label="AutofillEnableAccountWalletStorage:disabled"/>
-  <int value="-1420517225" label="OmniboxKeywordSpaceTriggering:enabled"/>
   <int value="-1419788257" label="enable-experimental-hotwording"/>
   <int value="-1419449681" label="PermissionChipRequestTypeSensitive:enabled"/>
   <int value="-1417642561" label="AutofillCreditCardUploadFeedback:enabled"/>
@@ -47749,7 +47748,6 @@
   <int value="839230937" label="AutofillEnableCardNicknameUpstream:disabled"/>
   <int value="839798268" label="SafeBrowsingTelemetryForApkDownloads:disabled"/>
   <int value="840057845" label="HardwareMediaKeyHandling:enabled"/>
-  <int value="841123685" label="OmniboxKeywordSpaceTriggering:disabled"/>
   <int value="841276069" label="ChromeHomeDoodle:disabled"/>
   <int value="841343322" label="disable-new-korean-ime"/>
   <int value="841779535" label="password-export:enabled"/>
@@ -64740,6 +64738,7 @@
   <int value="12" label="kSessionRestore"/>
   <int value="13" label="kChromeViewsDelegate"/>
   <int value="14" label="kDevToolsWindow"/>
+  <int value="15" label="kWebAppPermissionDialogWindow"/>
 </enum>
 
 <enum name="ProfileMenuActionableItem">
diff --git a/tools/metrics/histograms/histograms_xml/media/histograms.xml b/tools/metrics/histograms/histograms_xml/media/histograms.xml
index 0d64f9ff..453abbd 100644
--- a/tools/metrics/histograms/histograms_xml/media/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/media/histograms.xml
@@ -2121,6 +2121,18 @@
   </summary>
 </histogram>
 
+<histogram name="Media.GlobalMediaControls.HasDefaultPresentationRequest"
+    enum="Boolean" expires_after="2022-02-01">
+  <owner>takumif@chromium.org</owner>
+  <owner>openscreen-eng@google.com</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    Records whether a media session is from a WebContents with a default
+    PresentationReuqest. Recorded whenever a session is instantiated in the
+    global media controls, even if the user doesn't open the UI to view it.
+  </summary>
+</histogram>
+
 <histogram name="Media.GlobalMediaControls.InteractionDelayAfterPause"
     units="ms" expires_after="2021-10-31">
   <owner>steimel@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 7d35943..2e0384e 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,11 +6,11 @@
         },
         "mac": {
             "hash": "9071b3f46a0807212bc99e56c7a794d17cb17fdc",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/12799556c2729c4f7374f6fe7e2e0b35ffdccbb8/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/2b0fcfa349ac5ccd7851e459ec683a8e687f7c75/trace_processor_shell"
         },
         "linux": {
             "hash": "06b3e695b6dc8db2a271e7bf3605cef7b04d12e5",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/5fdd5351bf7855eafd61cdb7fc76b5ceec938462/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/2b0fcfa349ac5ccd7851e459ec683a8e687f7c75/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/typescript/definitions/bookmarks.d.ts b/tools/typescript/definitions/bookmarks.d.ts
index cbdb564e..77a8a54 100644
--- a/tools/typescript/definitions/bookmarks.d.ts
+++ b/tools/typescript/definitions/bookmarks.d.ts
@@ -35,18 +35,19 @@
 
     export function get(
         idOrIdList: string|string[],
-        callback: (p1: BookmarkTreeNode[]) => void);
+        callback: (p1: BookmarkTreeNode[]) => void): void;
 
     export function getChildren(
-        id: string, callback: (p1: BookmarkTreeNode[]) => void);
+        id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
 
     export function getRecent(
-        numberOfItems: number, callback: (p1: BookmarkTreeNode[]) => void);
+        numberOfItems: number,
+        callback: (p1: BookmarkTreeNode[]) => void): void;
 
-    export function getTree(callback: (p1: BookmarkTreeNode[]) => void);
+    export function getTree(callback: (p1: BookmarkTreeNode[]) => void): void;
 
     export function getSubTree(
-        id: string, callback: (p1: BookmarkTreeNode[]) => void);
+        id: string, callback: (p1: BookmarkTreeNode[]) => void): void;
 
     export function search(
         query: string|{
@@ -54,21 +55,22 @@
           url: string|undefined,
           title: string|undefined
         },
-        callback: (p1: BookmarkTreeNode[]) => void);
+        callback: (p1: BookmarkTreeNode[]) => void): void;
 
     export function create(
-        bookmark: CreateDetails, callback?: (p1: BookmarkTreeNode) => void);
+        bookmark: CreateDetails,
+        callback?: (p1: BookmarkTreeNode) => void): void;
 
     export function move(
         id: string,
         destination: {parentId: string|undefined, index: number|undefined},
-        callback?: (p1: BookmarkTreeNode) => void);
+        callback?: (p1: BookmarkTreeNode) => void): void;
 
     export function update(
         id: string, changes: {title: string|undefined, url: string|undefined},
-        callback?: (p1: BookmarkTreeNode) => void);
+        callback?: (p1: BookmarkTreeNode) => void): void;
 
-    export function remove(id: string, callback?: () => void);
-    export function removeTree(id: string, callback?: () => void);
+    export function remove(id: string, callback?: () => void): void;
+    export function removeTree(id: string, callback?: () => void): void;
   }
 }
diff --git a/tools/typescript/definitions/metrics_private.d.ts b/tools/typescript/definitions/metrics_private.d.ts
index 457a1f18..06d871c3 100644
--- a/tools/typescript/definitions/metrics_private.d.ts
+++ b/tools/typescript/definitions/metrics_private.d.ts
@@ -33,23 +33,26 @@
     }
 
     export function getHistogram(
-        name: string, callback: (p1: Histogram) => void);
-    export function getIsCrashReportingEnabled(callback: (p1: boolean) => void);
-    export function getFieldTrial(name: string, callback: (p1: string) => void);
+        name: string, callback: (p1: Histogram) => void): void;
+    export function getIsCrashReportingEnabled(callback: (p1: boolean) => void):
+        void;
+    export function getFieldTrial(name: string, callback: (p1: string) => void):
+        void;
     export function getVariationParams(
-        name: string, callback: (p1: Object|undefined) => void);
-    export function recordPercentage(metricName: string, value: number);
-    export function recordCount(metricName: string, value: number);
-    export function recordSmallCount(metricName: string, value: number);
-    export function recordMediumCount(metricName: string, value: number);
-    export function recordTime(metricName: string, value: number);
-    export function recordMediumTime(metricName: string, value: number);
-    export function recordLongTime(metricName: string, value: number);
-    export function recordSparseHashable(metricName: string, value: string);
-    export function recordSparseValue(metricName: string, value: number);
-    export function recordValue(metric: MetricType, value: number);
-    export function recordBoolean(metricName: string, value: boolean);
+        name: string, callback: (p1: Object|undefined) => void): void;
+    export function recordPercentage(metricName: string, value: number): void;
+    export function recordCount(metricName: string, value: number): void;
+    export function recordSmallCount(metricName: string, value: number): void;
+    export function recordMediumCount(metricName: string, value: number): void;
+    export function recordTime(metricName: string, value: number): void;
+    export function recordMediumTime(metricName: string, value: number): void;
+    export function recordLongTime(metricName: string, value: number): void;
+    export function recordSparseHashable(metricName: string, value: string):
+        void;
+    export function recordSparseValue(metricName: string, value: number): void;
+    export function recordValue(metric: MetricType, value: number): void;
+    export function recordBoolean(metricName: string, value: boolean): void;
     export function recordEnumerationValue(
-        metricName: string, value: number, enumSize: number);
+        metricName: string, value: number, enumSize: number): void;
   }
 }
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 195e6a8..4ca8addaac 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -102,6 +102,10 @@
     "dragdrop/os_exchange_data_provider_factory.h",
     "ime/utf_offset.cc",
     "ime/utf_offset.h",
+    "interaction/element_identifier.cc",
+    "interaction/element_identifier.h",
+    "interaction/element_tracker.cc",
+    "interaction/element_tracker.h",
     "l10n/formatter.cc",
     "l10n/formatter.h",
     "l10n/l10n_font_util.cc",
@@ -882,6 +886,7 @@
     "class_property_unittest.cc",
     "clipboard/file_info_unittest.cc",
     "ime/utf_offset_unittest.cc",
+    "interaction/element_tracker_unittest.cc",
     "l10n/l10n_util_unittest.cc",
     "l10n/time_format_unittest.cc",
     "layout_unittest.cc",
diff --git a/ui/base/interaction/OWNERS b/ui/base/interaction/OWNERS
new file mode 100644
index 0000000..f8cdc787
--- /dev/null
+++ b/ui/base/interaction/OWNERS
@@ -0,0 +1,2 @@
+dfried@chromium.org
+collinbaker@chromium.org
diff --git a/ui/base/interaction/README.md b/ui/base/interaction/README.md
new file mode 100644
index 0000000..5e3bc4af
--- /dev/null
+++ b/ui/base/interaction/README.md
@@ -0,0 +1,267 @@
+# Elements and Interaction Sequences
+
+This folder contains primitives for locating named elements in different
+application windows as well as following specific sequences of user interactions
+with the UI. It is designed to support things like User Education Tutorials and
+interactive UI testing, and to work with multiple UI presentation frameworks.
+
+See the [Supported Frameworks](#Supported%20Frameworks) section for a list of
+currently-supported  frameworks.
+
+[TOC]
+
+## Named elements
+
+Named elements are the fundamental building blocks of these systems. Each
+supported framework must define how a UI element can be assigned an
+[ElementIdentifier](/ui/base/interaction/element_identifier.h) which its
+[framework implementation](#Framework%20implementation) must be able to read.
+A UI element with a non-null `ElementIdentifier` assigned to it is known as a
+*named element*.
+
+Each `ElementIdentifier` contains a globally unique opaque handle, with its
+underlying value based on the address of a block of memory allocated at compile
+time.
+
+Two named elements with the same identifier are not necessarily equivalent. In
+an application with multiple primary windows (such as a browser with several
+windows and a PWA open), each primary window and all of its secondary UI - menus,
+dialogs, bubbles, etc. - are represented by a single `ElementContext`. Like
+identifiers, contexts are opaque handles that are unique to each primary window.
+To get the context associated with a UI element or window, you will need to call
+a method specific to your framework; see
+[the relevant section](#2.%20Define%20how%20`ElementContext`%20works%20in%20your%20framework)
+below.
+
+`ElementIdentifier` and `ElementContext` both support the methods one might
+expect from a handle or opaque pointer:
+* Assignment (`=`)
+* Equality (`==`, `!=`)
+* Ordering (`<`) - for use in `std::set` and `std::map`
+* Boolean value (`!` and ```explicit operator bool```)
+* Conversion to and from an integer type (`intptr_t`) - for platforms written in
+  languages that don't support pointers
+
+The only falsy/null/zero value is the default-constructed value. No guarantees
+are made about the ordering produced by the `<` operator; only that there is
+one.
+
+### Creating `ElementIdentifier` values for your application
+
+You will want to create named constants representing each identifier you want to
+use in your code. These constants are defined using macros found in
+[element_identifier.h](/ui/base/interaction/element_identifier.h).
+
+Each declaration creates a compile-time constant that can be copied and used
+anywhere in your application - they are valid from the time your application
+starts up until it exits.
+
+Furthermore, every `ElementIdentifier` you declare in code will either be
+default-constructed (and therefore null), or be assigned a copy of one of these
+compile-time constants. You should _never_ construct an `ElementIdentifier` or
+assign a value from anything other than another `ElementIdentifier`.
+
+To create a public, unique `ElementIdentifier` value in a global scope:
+``` cpp
+// This goes in the .h file:
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
+
+// This goes in the .cc file:
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
+```
+To create a class member that is a unique identifier, use:
+``` cpp
+// This declares a unique identifier, MyClass::kClassMemberIdentifier. You could
+// also make the identifier protected or private if you wanted.
+class MyClass {
+ public:
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kClassMemberIdentifier);
+};
+
+// This goes in the .cc file:
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyClass, kClassMemberIdentifier);
+```
+And finally, if you want to create an identifier that's local to a .cc file or
+class method, there is a single-line declaration available, as shown in these
+examples:
+``` cpp
+// This declares a module-local identifier. The anonymous namespace is optional
+// (though recommended) as kModuleLocalIdentifier will also be marked 'static'.
+namespace {
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kModuleLocalIdentifier);
+}
+
+// This declares a method-local identifier.
+/* static */ ui::ElementIdentifier MyClass::GetIdentifier() {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kMethodLocalIdentifier);
+  return kMethodLocalIdentifier;
+}
+```
+Please note that since all `ElementIdentifier`s created using these macros are
+constexpr/compile-time constants, copies of these values can be used outside of
+their originating classes or modules - the value of the `ElementIdentifier` is
+valid anywhere in the application.
+
+## UI Elements and element events
+
+[`ElementTracker`](/ui/base/interaction/element_tracker.h) is a global singleton
+object that allows you to:
+* Retrieve a visible element or elements from a particular context by identifier
+* Register for callbacks when elements with a given identifier and context
+  become visible
+* Register for callbacks when elements with a given identifier and context
+  become hidden or are destroyed
+* Register for callbacks when the user interacts with an element with a given
+  identifier and context (known as _activation_)
+
+`ElementTrackerElement`, a polymorphic class defined in
+[`element_tracker.h`](/ui/base/interaction/element_tracker.h), represents a
+platform-agnostic UI element with an identifier and context. There must be a 1:1
+correspondence between a visible named UI element and an
+`ElementTrackerElement`; the `ElementTrackerElement` is what is passed to
+callbacks when the UI element is shown, hidden, or activated.
+
+Each framework has its own derived version of `ElementTrackerElement` that may
+provide additional information about the element. If you know what platform the
+element is from you may use the `AsA()` template method to dynamically downcast
+to the platform-specific element type. If you are working in an environment with
+multiple presentation frameworks, you can use the `IsA()` method to determine if
+the element is of the expected type.
+
+Here is an example that shows some of the functionality of `ElementTracker` and
+`ElementTrackerElement`. Note that you must specify the `ElementContext` in
+which you are listening:
+``` cpp
+void ListenForShowEvent(ui::ElementIdentifier id, ui::ElementContext context) {
+  auto callback =
+      base::BindRepeating(&MyClass::OnElementShown, base::Unretained(this)));
+  subscription_ = ui::ElementTracker::GetElementTracker()
+      ->AddElementShownCallback(id, context, callback);
+}
+
+void OnElementShown(ui::ElementTrackerElement* element) {
+// Technically you don't need the IsA() call here, since AsA() returns null if
+// the object is the wrong type.
+if (element->IsA<views::ElementTrackerElementViews>()) {
+  views::View* const view =
+      element->AsA<views::ElementTrackerElementViews>()->view();
+  // Do something with the view that was shown here.
+}
+```
+## Defining and following user interaction sequences
+
+*(Add documentation for `InteractionSequence` here)*
+
+...
+
+## Supporting additional UI frameworks
+
+If you want to use ElementTracker with a framework that isn't supported yet, you
+must at minimum do the following:
+1. Derive a class from `ElementTrackerElement` representing visual elements in
+   your framework.
+2. Determine how `ElementContext`s are defined in your framework.
+3. Implement code to create and register your derived element objects with
+   `ElementTracker` when UI elements become visible to the user, send events
+   when they are activated by the user (however you choose to define
+   "activation"), and to unregister them when the element is no longer visible.
+
+See [`ElementTrackerViews`](/ui/views/interaction/element_tracker_views.h) for
+an example implementation.
+
+When you are done, please add the folder containing the implementation code to
+the [Supported Frameworks](#Supported%20Frameworks) section below.
+
+### 1. Derive a class from `ElementTrackerElement`
+
+When you derive a class from `ElementTrackerElement` to use for your UI
+framework, you are obliged to declare specific metadata in order to support
+`IsA()` and `AsA()`. To do this, add the following to the class definition:
+``` cpp
+class ElementTrackerElementMyPlatform {
+ public:
+  // This provides the required ElementTrackerElement metadata support.
+  DECLARE_ELEMENT_TRACKER_METADATA();
+}
+
+// In the corresponding .cc file:
+DEFINE_ELEMENT_TRACKER_METADATA(ElementTrackerElementMyPlatform)
+```
+You will also be expected to pass an immutable identifier and context into the
+constructor, and if the element object stores a pointer or handle to the
+associated UI element in your framework, that reference should not change for
+the life of the element (and the element should not outlive the corresponding
+framework object).
+
+### 2. Define how `ElementContext` works in your framework
+
+You will need a method to generate an `ElementContext` from a window or UI
+element. The context identifies the primary window associated with the UI, such
+as a browser or PWA window, the taskbar of an operating system's GUI, a file
+browser window, etc. Good candidates are the handle of the primary window or the
+address of the framework object that represents it. The value of the handle must
+not change over the lifetime of that window.
+
+If you do not already have one, you will probably want a helper method that
+finds the primary window given a UI element that might be in secondary UI (such
+as a dialog or menu).
+
+In Views, we use the address of the primary window's `Widget` to construct the
+`ElementContext` and we provide the `ElementTrackerViews::GetContextForView()`
+method to fetch it. We also added the `Widget::GetPrimaryWindowWidget()` helper
+method for finding the primary window.
+
+### 3. Managing the lifetime of your elements and sending events
+
+How your platform manages the lifetime of elements is entirely up to you. You
+could create an `ElementTrackerElement` whenever a named UI element in your
+framework becomes visible to the user, or you could have every UI element
+with an associated `ElementIdentifier` hold a permanent `ElementTrackerElement`.
+
+The one requirement is that a single `ElementTrackerElement` must be associated
+with any named UI element, and must remain associated with that implementation
+as long as the element remains visible.
+
+To register or unregister an element or send activation events, get the
+`ElementTrackerFrameworkDelegate` from the `ElementTracker` class and call the
+appropriate method:
+``` cpp
+auto* const delegate = ui::ElementTracker::GetFrameworkDelegate();
+
+// Register a visible element:
+delegate->NotifyElementShown(my_element);
+
+// Notify that the element was activated by the user:
+delegate->NotifyElementActivated(my_element);
+
+// Unregister the element on hide:
+delegate->NotifyElementHidden(my_element);
+```
+"Activation" requires some special discussion here, as what it means to be
+activated is extremely context-specific. For example:
+* A button is activated when the user clicks it
+* A menu item is activated when the user performs the item's action
+* A browser tab is activated when the user selects the tab with the mouse or
+  keyboard
+
+Activation should be the **default** action that occurs when the user directly
+interacts with that UI element. So for example, the _Back_ button in a browser
+can be clicked to return to the previous page, or long-pressed/dragged to open a
+menu containing a list of previous pages. The single-click is the default
+action, and therefore should be the action that results in
+`NotifyElementActivated()` being called.
+
+How you proxy events from UI elements to calls to
+`ElementTrackerFrameworkDelegate` is entirely up to you. In Views we go through
+a mediator object -
+[`ElementTrackerViews`](ui/views/interaction/element_tracker_views.h) - which
+first maps the `Button`, `MenuItem`, etc. to an `ElementTrackerElementViews`
+before passing that object to the appropriate delegate method.
+
+## Supported Frameworks
+
+The following UI frameworks support `ElementTracker` and `InteractionSequence`.
+Please add additional frameworks to this list as they become supported.
+
+* Views:
+  * [`ElementTrackerViews`](/ui/views/interaction/element_tracker_views.h)
diff --git a/ui/base/interaction/element_identifier.cc b/ui/base/interaction/element_identifier.cc
new file mode 100644
index 0000000..79c7f75
--- /dev/null
+++ b/ui/base/interaction/element_identifier.cc
@@ -0,0 +1,21 @@
+// Copyright 2021 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 "ui/base/interaction/element_identifier.h"
+
+namespace ui {
+
+void PrintTo(ElementIdentifier element_identifier, std::ostream* os) {
+  const internal::ElementIdentifierImpl* impl =
+      reinterpret_cast<const internal::ElementIdentifierImpl*>(
+          element_identifier.raw_value());
+  *os << "ElementIdentifier " << impl << " [" << (impl ? impl->name : "")
+      << "]";
+}
+
+void PrintTo(ElementContext element_context, std::ostream* os) {
+  *os << "ElementContext " << static_cast<const void*>(element_context);
+}
+
+}  // namespace ui
diff --git a/ui/base/interaction/element_identifier.h b/ui/base/interaction/element_identifier.h
new file mode 100644
index 0000000..3ff3d581
--- /dev/null
+++ b/ui/base/interaction/element_identifier.h
@@ -0,0 +1,258 @@
+// Copyright 2021 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 UI_BASE_INTERACTION_ELEMENT_IDENTIFIER_H_
+#define UI_BASE_INTERACTION_ELEMENT_IDENTIFIER_H_
+
+#include <stdint.h>
+
+#include <ostream>
+
+#include "base/component_export.h"
+#include "ui/base/class_property.h"
+
+// Overview:
+// ElementIdentifier provides a named opaque value that can be used to identify
+// individual (or potentially groups of) elements in the UI.
+//
+// Unique identifier constants must be both declared and defined. To create a
+// publicly-visible identifier, declare a new unique value in your .h file,
+// with:
+//
+//   DECLARE_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
+//
+// In the corresponding .cc file, make sure it is defined:
+//
+//   DEFINE_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
+//
+// If you want to add an identifier as a class member, use the following:
+//
+//   class MyClass {
+//     DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyClass, kMyIdentifierName);
+//   };
+//
+// Then in the corresponding .cc file, add the following:
+//
+//   DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(MyClass, kMyIdentifierValue);
+//
+// If you want to create an identifier local to a .cc file or to a method, you
+// can instead use the following all-in-one declaration:
+//
+//   DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kMyIdentifierName);
+//
+// That's it! You can now initialize an ElementIdentifier using this value, or
+// pass it directly to a method:
+//
+//   ElementIdentifier my_id = kMyIdentifierName;
+//   ElementIdentifier my_id2 = my_id;
+//   MyFuncThatTakesAnIdentifier(kMyIdentifierName);
+//   MyFuncThatTakesAnIdentifier(my_id2);
+//
+// ElementIdentifier behaves more or less like other mutable primitive types; it
+// is default-constructable (producing a null value) and supports the ==, !=, <,
+// !, and (bool) operators as well as assignment and copy [1]. This means you
+// can use ElementIdentifier as a key in std::set and std::map, and it is safe
+// to use in both DCHECK and test assertions:
+//
+//   ElementIdentifier id1;
+//   ElementIdentifier id2 = kMyIdentifierName;
+//   std::map<ElementIdentifier, int> map;
+//   map.emplace(id2, 4);
+//   DCHECK(!id1);
+//   EXPECT_TRUE(static_cast<bool>(id2));
+//   DCHECK_NE(id1, id2);
+//   EXPECT_FALSE(base::Contains(map, id1));
+//   ASSERT_EQ(4, map[id2]);
+//
+// -----
+//
+// [1] Please note that while operator < will provide a strict ordering, the
+//   specific order of two ElementIdentifier constants may vary by build and
+//   should not be relied upon; operator < is only provided for compatibility
+//   with sorted STL containers.
+
+namespace ui {
+
+namespace internal {
+
+// Defines the underlying value that an ElementIdentifier holds (namely, the
+// address of an instance of this class). Because these objects are only
+// declared statically, the value of an ElementIdentifier is always valid and
+// two ElementIdentifiers are equal if and only if they hold the address of the
+// same instance of this class.
+//
+// Instances of this object are named for debugging/logging purposes only, the
+// value of name() should never be used for any other purpose.
+struct ElementIdentifierImpl {
+  // The name of the identifier; only used in testing via PrintTo().
+  const char* const name;
+};
+
+}  // namespace internal
+
+// Holds a globally-unqiue, value-typed identifier from a set of identifiers
+// which can be declared in any static scope using
+// DECLARE_UNIQUE_ELEMENT_VALUE().
+//
+// This type is comparable and supports operator bool and negation, where
+// default-constructed instances have false value and all other values evaluate
+// as true. It can also be used as the key in std::set, std::map, and similar
+// collections.
+class COMPONENT_EXPORT(UI_BASE) ElementIdentifier final {
+ public:
+  // Creates a null identifier.
+  constexpr ElementIdentifier() = default;
+
+  // Avoid this constructor - it is used internally by the
+  // DECLARE_ELEMENT_IDENTIFIER_VALUE() macro.
+  explicit constexpr ElementIdentifier(
+      const internal::ElementIdentifierImpl* provider)
+      : handle_(provider) {}
+
+  constexpr explicit operator bool() const { return handle_ != nullptr; }
+
+  constexpr bool operator!() const { return !handle_; }
+
+  constexpr bool operator==(const ElementIdentifier& other) const {
+    return handle_ == other.handle_;
+  }
+
+  constexpr bool operator!=(const ElementIdentifier& other) const {
+    return handle_ != other.handle_;
+  }
+
+  constexpr bool operator<(const ElementIdentifier& other) const {
+    return handle_ < other.handle_;
+  }
+
+  // Included for interoperability with PropertyHandler. Only works because this
+  // class contains a single pointer, has a trivial destructor, and has no
+  // virtual methods.
+  intptr_t raw_value() const { return reinterpret_cast<intptr_t>(handle_); }
+
+  // Included for interoperability with PropertyHandler. Can be used when
+  // overriding PropertyHandler::AfterPropertyChange() to recover the old
+  // identifier value.
+  static ElementIdentifier FromRawValue(intptr_t value) {
+    return ElementIdentifier(
+        reinterpret_cast<const internal::ElementIdentifierImpl*>(value));
+  }
+
+ private:
+  // The value of the identifier. Because all non-null values point to static
+  // ElementIdentifierImpl objects this can be treated as a value from a set of
+  // unique, opaque handles.
+  const void* handle_ = nullptr;
+};
+
+// The context of an element is unique to the top-level, primary window that the
+// element is associated with. Elements in secondary UI (bubbles, menus,
+// drop-downs, etc.) will be associated with the same context as elements in the
+// primary window itself.
+//
+// The value used should be consistent across a toolkit and unique between
+// primary windows; a memory address or handle of the window object can
+// typically be used (e.g. in Views, we use the address of the primary window's
+// Widget). A zero or null value should always correspond to "no context".
+//
+// Please note that you must consistently use the same pointer or handle type
+// across your framework when creating contexts; because of the vagaries of C++
+// up- and down-casting (especially with multiple inheritance) constructing an
+// ElementContext from different pointer types can produce different results,
+// even for the same underlying object.
+//
+// ElementContext objects are assignable, have boolean value based on whether
+// the underlying value is null, and support operator < for use in maps and
+// sets.
+class COMPONENT_EXPORT(UI_BASE) ElementContext {
+ public:
+  ElementContext() = default;
+
+  template <class T>
+  explicit ElementContext(T* value)
+      : value_(reinterpret_cast<uintptr_t>(value)) {}
+
+  template <class T>
+  explicit ElementContext(T value) : value_(static_cast<uintptr_t>(value)) {}
+
+  explicit operator const void*() const {
+    return reinterpret_cast<const void*>(value_);
+  }
+  explicit operator uintptr_t() const { return value_; }
+  explicit operator intptr_t() const { return static_cast<intptr_t>(value_); }
+  explicit operator bool() const { return value_ != 0; }
+  bool operator!() const { return !value_; }
+  bool operator==(const ElementContext& other) const {
+    return value_ == other.value_;
+  }
+  bool operator!=(const ElementContext& other) const {
+    return value_ != other.value_;
+  }
+  bool operator<(const ElementContext& other) const {
+    return value_ < other.value_;
+  }
+
+ private:
+  uintptr_t value_ = 0;
+};
+
+COMPONENT_EXPORT(UI_BASE)
+extern void PrintTo(ElementIdentifier element_identifier, std::ostream* os);
+
+COMPONENT_EXPORT(UI_BASE)
+extern void PrintTo(ElementContext element_context, std::ostream* os);
+
+// Required for interoperability with PropertyHandler.
+template <>
+class ClassPropertyCaster<ui::ElementIdentifier> {
+ public:
+  static int64_t ToInt64(ui::ElementIdentifier x) { return x.raw_value(); }
+  static ui::ElementIdentifier FromInt64(int64_t x) {
+    return ui::ElementIdentifier::FromRawValue(x);
+  }
+};
+
+}  // namespace ui
+
+// Declaring identifiers outside a scope:
+
+// Use this code in the .h file to declare a new identifier.
+#define DECLARE_ELEMENT_IDENTIFIER_VALUE(IdentifierName)                     \
+  extern const ui::internal::ElementIdentifierImpl IdentifierName##Provider; \
+  constexpr ui::ElementIdentifier IdentifierName(&IdentifierName##Provider)
+
+// Use this code in the .cc file to define a new identifier.
+#define DEFINE_ELEMENT_IDENTIFIER_VALUE(IdentifierName)                    \
+  constexpr ui::internal::ElementIdentifierImpl IdentifierName##Provider { \
+#IdentifierName                                                        \
+  }
+
+// Declaring identifiers in a class:
+
+// Use this code in your class declaration in its .h file to declare an
+// identifier that is scoped to your class.
+#define DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(ClassName, IdentifierName) \
+  static constexpr ui::internal::ElementIdentifierImpl                    \
+      IdentifierName##Provider{#ClassName "::" #IdentifierName};          \
+  static constexpr ui::ElementIdentifier IdentifierName {                 \
+    &IdentifierName##Provider                                             \
+  }
+
+// Use this code in your class definition .cc file to define the member
+// variables
+#define DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ClassName, IdentifierName) \
+  constexpr ui::internal::ElementIdentifierImpl                          \
+      ClassName::IdentifierName##Provider;                               \
+  constexpr ui::ElementIdentifier ClassName::IdentifierName
+
+// Declaring local identifiers in functions, class methods, or local to a .cc
+// file:
+
+// Use this code to declare a local identifier in a function body.
+#define DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(IdentifierName) \
+  static constexpr ui::internal::ElementIdentifierImpl        \
+      IdentifierName##Provider{#IdentifierName};              \
+  constexpr ui::ElementIdentifier IdentifierName(&IdentifierName##Provider)
+
+#endif  // UI_BASE_INTERACTION_ELEMENT_IDENTIFIER_H_
diff --git a/ui/base/interaction/element_tracker.cc b/ui/base/interaction/element_tracker.cc
new file mode 100644
index 0000000..3f1e739
--- /dev/null
+++ b/ui/base/interaction/element_tracker.cc
@@ -0,0 +1,241 @@
+// Copyright 2021 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 "ui/base/interaction/element_tracker.h"
+
+#include <algorithm>
+#include <iterator>
+#include <list>
+#include <map>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/containers/contains.h"
+#include "ui/base/interaction/element_identifier.h"
+
+namespace ui {
+
+class ElementTracker::ElementData {
+ public:
+  ElementData(ElementTracker* tracker,
+              ElementIdentifier id,
+              ElementContext context)
+      : identifier_(id), context_(context) {
+    auto removal_callback =
+        base::BindRepeating(&ElementTracker::MaybeCleanup,
+                            base::Unretained(tracker), base::Unretained(this));
+    shown_callbacks_.set_removal_callback(removal_callback);
+    activated_callbacks_.set_removal_callback(removal_callback);
+    hidden_callbacks_.set_removal_callback(removal_callback);
+  }
+
+  ~ElementData() = default;
+
+  ElementIdentifier identifier() const { return identifier_; }
+  ElementContext context() const { return context_; }
+
+  bool empty() const {
+    return elements_.empty() && shown_callbacks_.empty() &&
+           activated_callbacks_.empty() && hidden_callbacks_.empty();
+  }
+
+  size_t num_elements() const {
+    // Guaranteed O(1) in C++11 and later.
+    return elements_.size();
+  }
+
+  bool processing_removal() const { return processing_removal_; }
+
+  const std::list<ElementTrackerElement*>& elements() const {
+    return elements_;
+  }
+
+  Subscription AddElementShownCallback(Callback callback) {
+    return shown_callbacks_.Add(callback);
+  }
+
+  Subscription AddElementActivatedCallback(Callback callback) {
+    return activated_callbacks_.Add(callback);
+  }
+
+  Subscription AddElementHiddenCallback(Callback callback) {
+    return hidden_callbacks_.Add(callback);
+  }
+
+  void NotifyElementShown(ElementTrackerElement* element) {
+    DCHECK_EQ(identifier().raw_value(), element->identifier().raw_value());
+    DCHECK_EQ(static_cast<intptr_t>(context()),
+              static_cast<intptr_t>(element->context()));
+    const auto it = elements_.insert(elements_.end(), element);
+    const bool success = element_lookup_.emplace(element, it).second;
+    DCHECK(success);
+    shown_callbacks_.Notify(element);
+  }
+
+  void NotifyElementActivated(ElementTrackerElement* element) {
+    DCHECK(base::Contains(element_lookup_, element));
+    activated_callbacks_.Notify(element);
+  }
+
+  void NotifyElementHidden(ElementTrackerElement* element) {
+    // We don't want to delete this object during a callback while we're in the
+    // middle of cleaning up the object, so put a guard around this operation.
+    base::AutoReset<bool> guard(&processing_removal_, true);
+    const auto it = element_lookup_.find(element);
+    DCHECK(it != element_lookup_.end());
+    elements_.erase(it->second);
+    element_lookup_.erase(it);
+    hidden_callbacks_.Notify(element);
+  }
+
+ private:
+  const ElementIdentifier identifier_;
+  const ElementContext context_;
+  bool processing_removal_ = false;
+
+  // Holds elements in the order they were added to this data block, so that the
+  // first element or the first element that matches some criterion can be
+  // easily found.
+  std::list<ElementTrackerElement*> elements_;
+
+  // Provides a fast lookup into `elements_` by element for checking and
+  // removal. Since there could be many elements (e.g. tabs in a browser) we
+  // don't want removing a series of them to turn into an O(n^2) operation.
+  std::map<ElementTrackerElement*, std::list<ElementTrackerElement*>::iterator>
+      element_lookup_;
+
+  base::RepeatingCallbackList<void(ElementTrackerElement*)> shown_callbacks_;
+  base::RepeatingCallbackList<void(ElementTrackerElement*)>
+      activated_callbacks_;
+  base::RepeatingCallbackList<void(ElementTrackerElement*)> hidden_callbacks_;
+};
+
+ElementTrackerElement::ElementTrackerElement(ElementIdentifier id,
+                                             ElementContext context)
+    : identifier_(id), context_(context) {}
+
+ElementTrackerElement::~ElementTrackerElement() = default;
+
+// static
+ElementTracker* ElementTracker::GetElementTracker() {
+  static base::NoDestructor<ElementTracker> instance;
+  return instance.get();
+}
+
+// static
+ElementTrackerFrameworkDelegate* ElementTracker::GetFrameworkDelegate() {
+  return static_cast<ElementTrackerFrameworkDelegate*>(GetElementTracker());
+}
+
+ElementTrackerElement* ElementTracker::GetUniqueElement(
+    ElementIdentifier id,
+    ElementContext context) {
+  const auto it = element_data_.find(LookupKey(id, context));
+  if (it == element_data_.end() || it->second->num_elements() == 0)
+    return nullptr;
+  DCHECK_EQ(1U, it->second->num_elements());
+  return it->second->elements().front();
+}
+
+ElementTrackerElement* ElementTracker::GetFirstMatchingElement(
+    ElementIdentifier id,
+    ElementContext context) {
+  const auto it = element_data_.find(LookupKey(id, context));
+  if (it == element_data_.end() || it->second->num_elements() == 0)
+    return nullptr;
+  return it->second->elements().front();
+}
+
+ElementTracker::ElementList ElementTracker::GetAllMatchingElements(
+    ElementIdentifier id,
+    ElementContext context) {
+  const auto it = element_data_.find(LookupKey(id, context));
+  ElementList result;
+  if (it != element_data_.end()) {
+    std::copy(it->second->elements().begin(), it->second->elements().end(),
+              std::back_inserter(result));
+  }
+  return result;
+}
+
+bool ElementTracker::IsElementVisible(ElementIdentifier id,
+                                      ElementContext context) {
+  const auto it = element_data_.find(LookupKey(id, context));
+  return it != element_data_.end() && it->second->num_elements() > 0;
+}
+
+ElementTracker::Subscription ElementTracker::AddElementShownCallback(
+    ElementIdentifier id,
+    ElementContext context,
+    Callback callback) {
+  return GetOrAddElementData(id, context)->AddElementShownCallback(callback);
+}
+
+ElementTracker::Subscription ElementTracker::AddElementActivatedCallback(
+    ElementIdentifier id,
+    ElementContext context,
+    Callback callback) {
+  return GetOrAddElementData(id, context)
+      ->AddElementActivatedCallback(callback);
+}
+
+ElementTracker::Subscription ElementTracker::AddElementHiddenCallback(
+    ElementIdentifier id,
+    ElementContext context,
+    Callback callback) {
+  return GetOrAddElementData(id, context)->AddElementHiddenCallback(callback);
+}
+
+ElementTracker::ElementTracker() = default;
+ElementTracker::~ElementTracker() = default;
+
+void ElementTracker::NotifyElementShown(ElementTrackerElement* element) {
+  DCHECK(!base::Contains(element_to_data_lookup_, element));
+  ElementData* const element_data =
+      GetOrAddElementData(element->identifier(), element->context());
+  element_data->NotifyElementShown(element);
+  element_to_data_lookup_.emplace(element, element_data);
+}
+
+void ElementTracker::NotifyElementActivated(ElementTrackerElement* element) {
+  const auto it = element_to_data_lookup_.find(element);
+  DCHECK(it != element_to_data_lookup_.end());
+  it->second->NotifyElementActivated(element);
+}
+
+void ElementTracker::NotifyElementHidden(ElementTrackerElement* element) {
+  const auto it = element_to_data_lookup_.find(element);
+  DCHECK(it != element_to_data_lookup_.end());
+  ElementData* const data = it->second;
+  data->NotifyElementHidden(element);
+  element_to_data_lookup_.erase(it);
+  MaybeCleanup(data);
+}
+
+ElementTracker::ElementData* ElementTracker::GetOrAddElementData(
+    ElementIdentifier id,
+    ElementContext context) {
+  const LookupKey key(id, context);
+  auto it = element_data_.find(key);
+  if (it == element_data_.end()) {
+    const auto result = element_data_.emplace(
+        key, std::make_unique<ElementData>(this, id, context));
+    DCHECK(result.second);
+    it = result.first;
+  }
+  return it->second.get();
+}
+
+void ElementTracker::MaybeCleanup(ElementData* data) {
+  // If there is still data, or we're in the middle of processing an element
+  // being removed, do not clean up this data block.
+  if (!data->empty() || data->processing_removal())
+    return;
+
+  const auto result =
+      element_data_.erase(LookupKey(data->identifier(), data->context()));
+  DCHECK(result);
+}
+
+}  // namespace ui
diff --git a/ui/base/interaction/element_tracker.h b/ui/base/interaction/element_tracker.h
new file mode 100644
index 0000000..817c6a37
--- /dev/null
+++ b/ui/base/interaction/element_tracker.h
@@ -0,0 +1,208 @@
+// Copyright 2021 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 UI_BASE_INTERACTION_ELEMENT_TRACKER_H_
+#define UI_BASE_INTERACTION_ELEMENT_TRACKER_H_
+
+#include <map>
+#include <vector>
+
+#include "base/callback_list.h"
+#include "base/component_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/no_destructor.h"
+#include "base/notreached.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "ui/base/interaction/element_identifier.h"
+
+namespace ui {
+
+// Represents a UI element in a platform-agnostic manner.
+//
+// A pointer to this object may be stored after the element becomes visible, but
+// is only valid until the "element hidden" event is called for this element;
+// see `ElementTracker` below.
+//
+// You should derive a class for each UI framework whose elements you wish to
+// track. See README.md for information on how to create your own framework
+// implementations.
+class COMPONENT_EXPORT(UI_BASE) ElementTrackerElement {
+ public:
+  // Used by IsA() and AsA() methods to do runtime type-checking.
+  using FrameworkIdentifier = ElementIdentifier;
+
+  virtual ~ElementTrackerElement();
+
+  ElementIdentifier identifier() const { return identifier_; }
+  ElementContext context() const { return context_; }
+
+  // Returns whether this element is a specific subtype - for example, a
+  // views::ViewsElementTrackerElement.
+  template <typename T>
+  bool IsA() const {
+    return AsA<T>();
+  }
+
+  // Dynamically casts this element to a specific subtype, such as a
+  // views::ViewsElementTrackerElement, returning null if the element is the
+  // wrong type.
+  template <typename T>
+  T* AsA() {
+    return GetInstanceFrameworkIdentifier() == T::GetFrameworkIdentifier()
+               ? static_cast<T*>(this)
+               : nullptr;
+  }
+
+  // Dynamically casts this element to a specific subtype, such as a
+  // views::ViewsElementTrackerElement, returning null if the element is the
+  // wrong type. This version converts const objects.
+  template <typename T>
+  const T* AsA() const {
+    return GetInstanceFrameworkIdentifier() == T::GetFrameworkIdentifier()
+               ? static_cast<const T*>(this)
+               : nullptr;
+  }
+
+ protected:
+  ElementTrackerElement(ElementIdentifier identifier, ElementContext context);
+
+  // Override this in derived classes with a unique FrameworkIdentifier.
+  // You must also define a static GetFrameworkIdentifier() method that returns
+  // the same value.
+  virtual FrameworkIdentifier GetInstanceFrameworkIdentifier() const = 0;
+
+ private:
+  // The identifier for this element that will be used by ElementTracker to
+  // retrieve it.
+  const ElementIdentifier identifier_;
+
+  // The context of the element, corresponding to the main window the element is
+  // associated with. See the ElementContext documentation in
+  // element_identifier.h for more information on how to create appropriate
+  // contexts for each UI framework.
+  const ElementContext context_;
+};
+
+// These macros can be used to help define platform-specific subclasses of
+// `ElementTrackerElement`.
+#define DECLARE_ELEMENT_TRACKER_METADATA()             \
+  static FrameworkIdentifier GetFrameworkIdentifier(); \
+  FrameworkIdentifier GetInstanceFrameworkIdentifier() const override;
+#define DEFINE_ELEMENT_TRACKER_METADATA(ClassName)                   \
+  ui::ElementTrackerElement::FrameworkIdentifier                     \
+  ClassName::GetFrameworkIdentifier() {                              \
+    DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(k##ClassName##Identifier); \
+    return k##ClassName##Identifier;                                 \
+  }                                                                  \
+  ui::ElementTrackerElement::FrameworkIdentifier                     \
+  ClassName::GetInstanceFrameworkIdentifier() const {                \
+    return GetFrameworkIdentifier();                                 \
+  }
+
+// Provides a delegate for UI framework-specific implementations to notify of
+// element tracker events.
+//
+// An element must be visible before events can be sent for that element;
+// NotifyElementHidden() must be called before the element is destroyed or
+// changes context or identifier.
+class COMPONENT_EXPORT(UI_BASE) ElementTrackerFrameworkDelegate {
+ public:
+  virtual void NotifyElementShown(ElementTrackerElement* element) = 0;
+  virtual void NotifyElementActivated(ElementTrackerElement* element) = 0;
+  virtual void NotifyElementHidden(ElementTrackerElement* element) = 0;
+};
+
+// Tracks elements as they become visible, are activated by the user, and
+// eventually become hidden. Tracks only visible elements.
+//
+// NOT THREAD SAFE. Should only be accessed from the main UI thread.
+class COMPONENT_EXPORT(UI_BASE) ElementTracker
+    : ElementTrackerFrameworkDelegate {
+ public:
+  using Callback = base::RepeatingCallback<void(ElementTrackerElement*)>;
+  using Subscription = base::CallbackListSubscription;
+  using ElementList = std::vector<ElementTrackerElement*>;
+
+  // Gets the element tracker to be used by clients to subscribe to and receive
+  // events.
+  static ElementTracker* GetElementTracker();
+
+  // Gets the delegate to be used by specific UI frameworks to send events.
+  static ElementTrackerFrameworkDelegate* GetFrameworkDelegate();
+
+  // Returns either the one element matching the given `id` and `context`, or
+  // null if there are none. Will generate an error if there is more than one
+  // element with `id|`in `context`. Only visible elements are returned.
+  //
+  // Use when you want to verify that there's only one matching element in the
+  // given context.
+  ElementTrackerElement* GetUniqueElement(ElementIdentifier id,
+                                          ElementContext context);
+
+  // Returns the same result as GetUniqueElement() except that no error is
+  // generated if there is more than one matching element.
+  //
+  // Use when you just need *an* element in the given context, and don't care if
+  // there's more than one.
+  ElementTrackerElement* GetFirstMatchingElement(ElementIdentifier id,
+                                                 ElementContext context);
+
+  // Returns a list of all visible elements with identifier `id` in `context`.
+  // The list may be empty.
+  ElementList GetAllMatchingElements(ElementIdentifier id,
+                                     ElementContext context);
+
+  // Returns whether an element with identifier `id` in `context` is visible.
+  bool IsElementVisible(ElementIdentifier id, ElementContext context);
+
+  // Adds a callback that will be called whenever an element with identifier
+  // `id` in `context` becomes visible.
+  Subscription AddElementShownCallback(ElementIdentifier id,
+                                       ElementContext context,
+                                       Callback callback);
+
+  // Adds a callback that will be called whenever an element with identifier
+  // `id` in `context` is activated by the user.
+  Subscription AddElementActivatedCallback(ElementIdentifier id,
+                                           ElementContext context,
+                                           Callback callback);
+
+  // Adds a callback that will be called whenever an element with identifier
+  // `id` in `context` is hidden.
+  //
+  // Note: the ElementTrackerElement* passed to the callback may not remain
+  // valid after the call, even if the same element object in its UI framework
+  // is re-shown (a new ElementTrackerElement may be generated).
+  Subscription AddElementHiddenCallback(ElementIdentifier id,
+                                        ElementContext context,
+                                        Callback callback);
+
+ private:
+  friend class base::NoDestructor<ElementTracker>;
+  class ElementData;
+  using LookupKey = std::pair<ElementIdentifier, ElementContext>;
+  FRIEND_TEST_ALL_PREFIXES(ElementTrackerTest, CleanupAfterElementHidden);
+  FRIEND_TEST_ALL_PREFIXES(ElementTrackerTest, CleanupAfterCallbacksRemoved);
+
+  ElementTracker();
+  ~ElementTracker();
+
+  // ElementTrackerFrameworkDelegate:
+  void NotifyElementShown(ElementTrackerElement* element) override;
+  void NotifyElementActivated(ElementTrackerElement* element) override;
+  void NotifyElementHidden(ElementTrackerElement* element) override;
+
+  ElementData* GetOrAddElementData(ElementIdentifier id,
+                                   ElementContext context);
+
+  void MaybeCleanup(ElementData* data);
+
+  std::map<LookupKey, std::unique_ptr<ElementData>> element_data_;
+  std::map<ElementTrackerElement*, ElementData*> element_to_data_lookup_;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_INTERACTION_ELEMENT_TRACKER_H_
diff --git a/ui/base/interaction/element_tracker_unittest.cc b/ui/base/interaction/element_tracker_unittest.cc
new file mode 100644
index 0000000..d3c36de
--- /dev/null
+++ b/ui/base/interaction/element_tracker_unittest.cc
@@ -0,0 +1,579 @@
+// Copyright 2021 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 "ui/base/interaction/element_tracker.h"
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/test/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+
+namespace ui {
+
+namespace {
+
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestFrameworkIdentifier);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOtherFrameworkIdentifier);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kElementIdentifier1);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kElementIdentifier2);
+const ElementContext kElementContext1(1);
+const ElementContext kElementContext2(2);
+
+class TestElementBase : public ElementTrackerElement {
+ public:
+  TestElementBase(ElementIdentifier id, ElementContext context)
+      : ElementTrackerElement(id, context) {}
+  ~TestElementBase() override { Hide(); }
+
+  void Show() {
+    if (visible_)
+      return;
+    visible_ = true;
+    ElementTracker::GetFrameworkDelegate()->NotifyElementShown(this);
+  }
+
+  void Activate() {
+    DCHECK(visible_);
+    ElementTracker::GetFrameworkDelegate()->NotifyElementActivated(this);
+  }
+
+  void Hide() {
+    if (!visible_)
+      return;
+    visible_ = false;
+    ElementTracker::GetFrameworkDelegate()->NotifyElementHidden(this);
+  }
+
+ private:
+  bool visible_ = false;
+};
+
+using ElementPtr = std::unique_ptr<TestElementBase>;
+
+class TestElement : public TestElementBase {
+ public:
+  using TestElementBase::TestElementBase;
+
+  static FrameworkIdentifier GetFrameworkIdentifier() {
+    return kTestFrameworkIdentifier;
+  }
+
+  FrameworkIdentifier GetInstanceFrameworkIdentifier() const override {
+    return kTestFrameworkIdentifier;
+  }
+};
+
+class TestElementOtherFramework : public TestElementBase {
+ public:
+  using TestElementBase::TestElementBase;
+
+  static FrameworkIdentifier GetFrameworkIdentifier() {
+    return kOtherFrameworkIdentifier;
+  }
+
+  FrameworkIdentifier GetInstanceFrameworkIdentifier() const override {
+    return kOtherFrameworkIdentifier;
+  }
+};
+
+class TestCallback {
+ public:
+  ElementTracker::Callback GetCallback() {
+    return base::BindLambdaForTesting([&](ElementTrackerElement* element) {
+      ++count_;
+      last_element_ = element;
+      if (on_callback_)
+        on_callback_.Run();
+    });
+  }
+
+  void SetCallback(base::RepeatingClosure on_callback) {
+    on_callback_ = on_callback;
+  }
+
+  size_t count() const { return count_; }
+  const ElementTrackerElement* last_element() const { return last_element_; }
+
+ private:
+  size_t count_ = 0;
+  const ElementTrackerElement* last_element_ = nullptr;
+  base::RepeatingClosure on_callback_;
+};
+
+}  // namespace
+
+TEST(ElementTrackerElementTest, IsATest) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext1);
+
+  EXPECT_TRUE(e1->IsA<TestElement>());
+  EXPECT_FALSE(e1->IsA<TestElementOtherFramework>());
+  EXPECT_FALSE(e2->IsA<TestElement>());
+  EXPECT_TRUE(e2->IsA<TestElementOtherFramework>());
+}
+
+TEST(ElementTrackerElementTest, AsATest) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier2, kElementContext2);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext2);
+
+  EXPECT_EQ(e1.get(), e1->AsA<TestElement>());
+  EXPECT_EQ(nullptr, e1->AsA<TestElementOtherFramework>());
+  EXPECT_EQ(nullptr, e2->AsA<TestElement>());
+  EXPECT_EQ(e2.get(), e2->AsA<TestElementOtherFramework>());
+}
+
+TEST(ElementTrackerTest, GetUniqueElement) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext1);
+  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kElementIdentifier2, kElementContext1));
+  e1->Show();
+  EXPECT_EQ(e1.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
+                          kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kElementIdentifier2, kElementContext1));
+  e2->Show();
+  EXPECT_EQ(e1.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
+                          kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(e2.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
+                          kElementIdentifier2, kElementContext1));
+  e1->Hide();
+  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(e2.get(), ElementTracker::GetElementTracker()->GetUniqueElement(
+                          kElementIdentifier2, kElementContext1));
+  e2->Hide();
+  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(nullptr, ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kElementIdentifier2, kElementContext1));
+}
+
+TEST(ElementTrackerTest, GetFirstMatchingElement) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext1);
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier2, kElementContext1));
+  e1->Show();
+  EXPECT_EQ(e1.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier2, kElementContext1));
+  e2->Show();
+  EXPECT_EQ(e1.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(e2.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier2, kElementContext1));
+  e1->Hide();
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(e2.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier2, kElementContext1));
+  e2->Hide();
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier2, kElementContext1));
+}
+
+TEST(ElementTrackerTest, GetFirstMatchingElementWithMultipleElements) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext1);
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  e1->Show();
+  EXPECT_EQ(e1.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  e2->Show();
+  EXPECT_EQ(e1.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  e1->Hide();
+  EXPECT_EQ(e2.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  e1->Show();
+  EXPECT_EQ(e2.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  e2->Hide();
+  EXPECT_EQ(e1.get(),
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+  e1->Hide();
+  EXPECT_EQ(nullptr,
+            ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kElementIdentifier1, kElementContext1));
+}
+
+TEST(ElementTrackerTest, GetAllMatchingElements) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext1);
+  ElementTracker::ElementList expected;
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+  e1->Show();
+  expected = ElementTracker::ElementList{e1.get()};
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+  e2->Show();
+  expected = ElementTracker::ElementList{e1.get(), e2.get()};
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+  e1->Hide();
+  expected = ElementTracker::ElementList{e2.get()};
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+  e1->Show();
+  expected = ElementTracker::ElementList{e2.get(), e1.get()};
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+  e2->Hide();
+  expected = ElementTracker::ElementList{e1.get()};
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+  e1->Hide();
+  expected = ElementTracker::ElementList{};
+  EXPECT_EQ(expected,
+            ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                kElementIdentifier1, kElementContext1));
+}
+
+TEST(ElementTrackerTest, IsElementVisible) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext1);
+  ElementPtr e3 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext2);
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+  e1->Show();
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+  e2->Show();
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+  e3->Show();
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+  e2->Hide();
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+  e1->Hide();
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+  e3->Hide();
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier2, kElementContext1));
+  EXPECT_FALSE(ElementTracker::GetElementTracker()->IsElementVisible(
+      kElementIdentifier1, kElementContext2));
+}
+
+TEST(ElementTrackerTest, AddElementShownCallback) {
+  TestCallback callback;
+  auto subscription =
+      ElementTracker::GetElementTracker()->AddElementShownCallback(
+          kElementIdentifier1, kElementContext1, callback.GetCallback());
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext1);
+  ElementPtr e3 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext2);
+  ElementPtr e4 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  EXPECT_EQ(0U, callback.count());
+  e1->Show();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e2->Show();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e3->Show();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e1->Activate();
+  e1->Hide();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e4->Show();
+  EXPECT_EQ(2U, callback.count());
+  EXPECT_EQ(e4.get(), callback.last_element());
+}
+
+TEST(ElementTrackerTest, AddElementActivatedCallback) {
+  TestCallback callback;
+  auto subscription =
+      ElementTracker::GetElementTracker()->AddElementActivatedCallback(
+          kElementIdentifier1, kElementContext1, callback.GetCallback());
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext1);
+  ElementPtr e3 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext2);
+  ElementPtr e4 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  EXPECT_EQ(0U, callback.count());
+  e1->Show();
+  EXPECT_EQ(0U, callback.count());
+  e2->Show();
+  EXPECT_EQ(0U, callback.count());
+  e3->Show();
+  EXPECT_EQ(0U, callback.count());
+  e4->Show();
+  EXPECT_EQ(0U, callback.count());
+  e1->Activate();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e4->Activate();
+  EXPECT_EQ(2U, callback.count());
+  EXPECT_EQ(e4.get(), callback.last_element());
+  e1->Activate();
+  EXPECT_EQ(3U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e2->Activate();
+  EXPECT_EQ(3U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e3->Activate();
+  EXPECT_EQ(3U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+}
+
+TEST(ElementTrackerTest, AddElementHiddenCallback) {
+  TestCallback callback;
+  auto subscription =
+      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+          kElementIdentifier1, kElementContext1, callback.GetCallback());
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  ElementPtr e2 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier2, kElementContext1);
+  ElementPtr e3 = std::make_unique<TestElementOtherFramework>(
+      kElementIdentifier1, kElementContext2);
+  ElementPtr e4 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  EXPECT_EQ(0U, callback.count());
+  e1->Show();
+  EXPECT_EQ(0U, callback.count());
+  e2->Show();
+  EXPECT_EQ(0U, callback.count());
+  e3->Show();
+  EXPECT_EQ(0U, callback.count());
+  e4->Show();
+  EXPECT_EQ(0U, callback.count());
+  e1->Activate();
+  EXPECT_EQ(0U, callback.count());
+  e4->Activate();
+  EXPECT_EQ(0U, callback.count());
+  e2->Hide();
+  EXPECT_EQ(0U, callback.count());
+  e1->Hide();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e3->Hide();
+  EXPECT_EQ(1U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+  e4->Hide();
+  EXPECT_EQ(2U, callback.count());
+  EXPECT_EQ(e4.get(), callback.last_element());
+  e1->Show();
+  EXPECT_EQ(2U, callback.count());
+  EXPECT_EQ(e4.get(), callback.last_element());
+  e1->Hide();
+  EXPECT_EQ(3U, callback.count());
+  EXPECT_EQ(e1.get(), callback.last_element());
+}
+
+TEST(ElementTrackerTest, CleanupAfterElementHidden) {
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  e1->Show();
+  EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+  {
+    TestCallback callback;
+    auto subscription =
+        ElementTracker::GetElementTracker()->AddElementShownCallback(
+            kElementIdentifier1, kElementContext1, callback.GetCallback());
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+  }
+  e1->Hide();
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+}
+
+TEST(ElementTrackerTest, CleanupAfterCallbacksRemoved) {
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+
+  // Add element shown callback. An element will be shown transiently during the
+  // subscription.
+  {
+    TestCallback callback;
+    auto subscription =
+        ElementTracker::GetElementTracker()->AddElementShownCallback(
+            kElementIdentifier1, kElementContext1, callback.GetCallback());
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+    e1->Show();
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+    e1->Hide();
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+  }
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+
+  // Add element activated callback.
+  {
+    TestCallback callback;
+    auto subscription =
+        ElementTracker::GetElementTracker()->AddElementActivatedCallback(
+            kElementIdentifier1, kElementContext1, callback.GetCallback());
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+  }
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+
+  // Add element hidden callback.
+  {
+    TestCallback callback;
+    auto subscription =
+        ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+            kElementIdentifier1, kElementContext1, callback.GetCallback());
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+  }
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+
+  // Add and remove multiple callbacks.
+  {
+    TestCallback callback;
+    auto sub1 = ElementTracker::GetElementTracker()->AddElementShownCallback(
+        kElementIdentifier1, kElementContext1, callback.GetCallback());
+    auto sub2 =
+        ElementTracker::GetElementTracker()->AddElementActivatedCallback(
+            kElementIdentifier1, kElementContext1, callback.GetCallback());
+    auto sub3 = ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+        kElementIdentifier1, kElementContext1, callback.GetCallback());
+    EXPECT_EQ(1U, ElementTracker::GetElementTracker()->element_data_.size());
+  }
+  EXPECT_TRUE(ElementTracker::GetElementTracker()->element_data_.empty());
+}
+
+// The following test specific conditions that could trigger a UAF or cause
+// similar instability due to changing callback lists during callbacks. These
+// tests may fail all or some builds (specifically asan/msan) if the logic is
+// implemented incorrectly.
+
+TEST(ElementTrackerTest, RemoveCallbackDuringRemove) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  TestCallback callback;
+  ElementTracker::Subscription subscription =
+      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+          e1->identifier(), e1->context(), callback.GetCallback());
+  callback.SetCallback(base::BindLambdaForTesting(
+      [&]() { subscription = ElementTracker::Subscription(); }));
+  e1->Show();
+  EXPECT_EQ(0U, callback.count());
+  e1->Hide();
+  EXPECT_EQ(1U, callback.count());
+}
+
+TEST(ElementTrackerTest, RemoveAndThenAddCallbackDuringRemove) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  TestCallback callback;
+  ElementTracker::Subscription subscription =
+      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+          e1->identifier(), e1->context(), callback.GetCallback());
+  callback.SetCallback(base::BindLambdaForTesting([&]() {
+    subscription = ElementTracker::Subscription();
+    subscription =
+        ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+            e1->identifier(), e1->context(), callback.GetCallback());
+  }));
+  e1->Show();
+  EXPECT_EQ(0U, callback.count());
+  e1->Hide();
+}
+
+TEST(ElementTrackerTest, RemoveAndThenAddDifferentCallbackDuringRemove) {
+  ElementPtr e1 =
+      std::make_unique<TestElement>(kElementIdentifier1, kElementContext1);
+  TestCallback callback;
+  ElementTracker::Subscription subscription =
+      ElementTracker::GetElementTracker()->AddElementHiddenCallback(
+          e1->identifier(), e1->context(), callback.GetCallback());
+  callback.SetCallback(base::BindLambdaForTesting([&]() {
+    subscription = ElementTracker::Subscription();
+    subscription = ElementTracker::GetElementTracker()->AddElementShownCallback(
+        e1->identifier(), e1->context(), callback.GetCallback());
+  }));
+  e1->Show();
+  EXPECT_EQ(0U, callback.count());
+  e1->Hide();
+}
+
+}  // namespace ui
diff --git a/ui/base/models/menu_model.cc b/ui/base/models/menu_model.cc
index d0eb4390..b2621d4 100644
--- a/ui/base/models/menu_model.cc
+++ b/ui/base/models/menu_model.cc
@@ -27,6 +27,10 @@
   return false;
 }
 
+ElementIdentifier MenuModel::GetElementIdentifierAt(int index) const {
+  return ElementIdentifier();
+}
+
 // static
 bool MenuModel::GetModelAndIndexForCommandId(int command_id,
                                              MenuModel** model,
diff --git a/ui/base/models/menu_model.h b/ui/base/models/menu_model.h
index 2f8dd2c..279be87a 100644
--- a/ui/base/models/menu_model.h
+++ b/ui/base/models/menu_model.h
@@ -9,6 +9,7 @@
 
 #include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/models/menu_model_delegate.h"
 #include "ui/base/models/menu_separator_types.h"
 #include "ui/gfx/native_widget_types.h"
@@ -127,6 +128,10 @@
   // the appropriate flag is enabled).
   virtual bool IsNewFeatureAt(int index) const;
 
+  // Returns an application-window-unique identifier that can be used to track
+  // the menu item irrespective of menu-specific command IDs.
+  virtual ElementIdentifier GetElementIdentifierAt(int index) const;
+
   // Returns the model for the submenu at the specified index.
   virtual MenuModel* GetSubmenuModelAt(int index) const = 0;
 
diff --git a/ui/base/models/simple_menu_model.cc b/ui/base/models/simple_menu_model.cc
index 016dab7..976a45c 100644
--- a/ui/base/models/simple_menu_model.cc
+++ b/ui/base/models/simple_menu_model.cc
@@ -330,6 +330,11 @@
   items_[ValidateItemIndex(index)].may_have_mnemonics = may_have_mnemonics;
 }
 
+void SimpleMenuModel::SetElementIdentifierAt(int index,
+                                             ElementIdentifier unique_id) {
+  items_[ValidateItemIndex(index)].unique_id = unique_id;
+}
+
 void SimpleMenuModel::Clear() {
   items_.clear();
   MenuItemsChanged();
@@ -461,6 +466,10 @@
   return items_[ValidateItemIndex(index)].may_have_mnemonics;
 }
 
+ElementIdentifier SimpleMenuModel::GetElementIdentifierAt(int index) const {
+  return items_[ValidateItemIndex(index)].unique_id;
+}
+
 void SimpleMenuModel::ActivatedAt(int index) {
   ActivatedAt(index, 0);
 }
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index 5ab6c93..6127fb6 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -170,6 +170,9 @@
 
   // Sets whether the item at |index| is may have mnemonics.
   void SetMayHaveMnemonicsAt(int index, bool may_have_mnemonics);
+  // Sets an application-window unique identifier associated with this menu item
+  // allowing it to be tracked without knowledge of menu-specific command IDs.
+  void SetElementIdentifierAt(int index, ElementIdentifier unique_id);
 
   // Clears all items. Note that it does not free MenuModel of submenu.
   void Clear();
@@ -198,6 +201,7 @@
   bool IsAlertedAt(int index) const override;
   bool IsNewFeatureAt(int index) const override;
   bool MayHaveMnemonicsAt(int index) const override;
+  ElementIdentifier GetElementIdentifierAt(int index) const override;
   void ActivatedAt(int index) override;
   void ActivatedAt(int index, int event_flags) override;
   MenuModel* GetSubmenuModelAt(int index) const override;
@@ -233,6 +237,7 @@
     bool visible = true;
     bool is_new_feature = false;
     bool may_have_mnemonics = true;
+    ElementIdentifier unique_id;
   };
 
   typedef std::vector<Item> ItemVector;
diff --git a/ui/base/models/simple_menu_model_unittest.cc b/ui/base/models/simple_menu_model_unittest.cc
index 111881c..616b41e8 100644
--- a/ui/base/models/simple_menu_model_unittest.cc
+++ b/ui/base/models/simple_menu_model_unittest.cc
@@ -16,6 +16,9 @@
 
 namespace {
 
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kTestElementID);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kTestElementID);
+
 constexpr int kAlertedCommandId = 2;
 
 class DelegateBase : public SimpleMenuModel::Delegate {
@@ -168,6 +171,18 @@
   ASSERT_TRUE(simple_menu_model.IsNewFeatureAt(1));
 }
 
+TEST(SimpleMenuModelTest, SetElementIdentifierAt) {
+  SimpleMenuModel simple_menu_model(nullptr);
+  simple_menu_model.AddItem(/*command_id*/ 5, u"menu item 0");
+  simple_menu_model.AddItem(/*command_id*/ 6, u"menu item 1");
+
+  simple_menu_model.SetElementIdentifierAt(/*index*/ 1, kTestElementID);
+
+  EXPECT_EQ(ui::ElementIdentifier(),
+            simple_menu_model.GetElementIdentifierAt(0));
+  EXPECT_EQ(kTestElementID, simple_menu_model.GetElementIdentifierAt(1));
+}
+
 TEST(SimpleMenuModelTest, HasIconsViaDelegate) {
   DelegateBase delegate;
   SimpleMenuModel simple_menu_model(&delegate);
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 1c23c05..78a5ddd5 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -359,6 +359,12 @@
 
   SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
   SetInkDropVisibleOpacity(1);
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](views::View* host) {
+        return host->GetNativeTheme()->GetSystemColor(
+            ui::NativeTheme::kColorId_NotificationInkDropBase);
+      },
+      this));
 
   AddChildView(ink_drop_container_);
 
@@ -403,18 +409,6 @@
   button_->DestroyLayer();
 }
 
-std::unique_ptr<views::InkDropRipple>
-NotificationInputContainerMD::CreateInkDropRipple() const {
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetInkDropCenterBasedOnLastEvent(), GetInkDropBaseColor(),
-      GetInkDropVisibleOpacity());
-}
-
-SkColor NotificationInputContainerMD::GetInkDropBaseColor() const {
-  return GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_NotificationInkDropBase);
-}
-
 void NotificationInputContainerMD::OnThemeChanged() {
   InkDropHostView::OnThemeChanged();
   auto* theme = GetNativeTheme();
@@ -579,6 +573,19 @@
         return std::make_unique<NotificationInkDropImpl>(host, host->size());
       },
       this));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+            host->GetInkDropBaseColor(), host->GetInkDropVisibleOpacity());
+      },
+      this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](views::View* host) {
+        return host->GetNativeTheme()->GetSystemColor(
+            ui::NativeTheme::kColorId_NotificationBackgroundActive);
+      },
+      this));
 
   AddChildView(ink_drop_container_);
 
@@ -1479,24 +1486,12 @@
   AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
 }
 
-std::unique_ptr<views::InkDropRipple> NotificationViewMD::CreateInkDropRipple()
-    const {
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      GetPreferredSize(), GetInkDropCenterBasedOnLastEvent(),
-      GetInkDropBaseColor(), GetInkDropVisibleOpacity());
-}
-
 std::vector<views::View*> NotificationViewMD::GetChildrenForLayerAdjustment()
     const {
   return {header_row_, block_all_button_, dont_block_button_,
           settings_done_button_};
 }
 
-SkColor NotificationViewMD::GetInkDropBaseColor() const {
-  return GetNativeTheme()->GetSystemColor(
-      ui::NativeTheme::kColorId_NotificationBackgroundActive);
-}
-
 void NotificationViewMD::InkDropAnimationStarted() {
   header_row_->SetSubpixelRenderingEnabled(false);
 }
diff --git a/ui/message_center/views/notification_view_md.h b/ui/message_center/views/notification_view_md.h
index af14dcba..c80017e9 100644
--- a/ui/message_center/views/notification_view_md.h
+++ b/ui/message_center/views/notification_view_md.h
@@ -127,8 +127,6 @@
   // views::InkDropHostView:
   void AddLayerBeneathView(ui::Layer* layer) override;
   void RemoveLayerBeneathView(ui::Layer* layer) override;
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-  SkColor GetInkDropBaseColor() const override;
   void OnThemeChanged() override;
   void Layout() override;
 
@@ -192,8 +190,6 @@
   void OnMouseEvent(ui::MouseEvent* event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
   void PreferredSizeChanged() override;
-  std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
-  SkColor GetInkDropBaseColor() const override;
   void UpdateWithNotification(const Notification& notification) override;
   void UpdateCornerRadius(int top_radius, int bottom_radius) override;
   NotificationControlButtonsView* GetControlButtonsView() const override;
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 0a5bb26f..7ba461f 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -197,6 +197,7 @@
     "focus/widget_focus_manager.h",
     "image_model_utils.h",
     "input_event_activation_protector.h",
+    "interaction/element_tracker_views.h",
     "layout/animating_layout_manager.h",
     "layout/box_layout.h",
     "layout/box_layout_view.h",
@@ -400,6 +401,7 @@
     "focus/widget_focus_manager.cc",
     "image_model_utils.cc",
     "input_event_activation_protector.cc",
+    "interaction/element_tracker_views.cc",
     "layout/animating_layout_manager.cc",
     "layout/box_layout.cc",
     "layout/box_layout_view.cc",
@@ -1156,6 +1158,7 @@
     "focus/focus_manager_unittest.cc",
     "focus/focus_traversal_unittest.cc",
     "image_model_utils_unittest.cc",
+    "interaction/element_tracker_views_unittest.cc",
     "layout/animating_layout_manager_unittest.cc",
     "layout/box_layout_unittest.cc",
     "layout/box_layout_view_unittest.cc",
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index 12a5929b..9883bdb 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -26,7 +26,7 @@
 namespace views {
 
 // static
-constexpr gfx::Size InkDropHostView::kDefaultInkDropSize;
+constexpr gfx::Size InkDropHostView::kDefaultSquareInkDropSize;
 
 InkDropHostView::InkDropHostViewEventHandlerDelegate::
     InkDropHostViewEventHandlerDelegate(InkDropHostView* host_view)
@@ -45,8 +45,27 @@
   return host_view_->ink_drop_mode_ == InkDropMode::ON;
 }
 
+InkDropHostView::ViewLayerTransformObserver::ViewLayerTransformObserver(
+    InkDropHostView* host)
+    : host_(host) {
+  observation_.Observe(host);
+}
+
+InkDropHostView::ViewLayerTransformObserver::~ViewLayerTransformObserver() =
+    default;
+
+void InkDropHostView::ViewLayerTransformObserver::OnViewLayerTransformed(
+    View* observed_view) {
+  DCHECK_EQ(observed_view, host_);
+  // Notify the ink drop that we have transformed so it can adapt
+  // accordingly.
+  if (host_->HasInkDrop())
+    host_->GetInkDrop()->HostTransformChanged(host_->GetTransform());
+}
+
 InkDropHostView::InkDropHostView()
-    : ink_drop_event_handler_delegate_(this),
+    : host_view_transform_observer_(this),
+      ink_drop_event_handler_delegate_(this),
       ink_drop_event_handler_(this, &ink_drop_event_handler_delegate_) {}
 
 InkDropHostView::~InkDropHostView() {
@@ -162,8 +181,12 @@
 SkColor InkDropHostView::GetInkDropBaseColor() const {
   if (ink_drop_base_color_callback_)
     return ink_drop_base_color_callback_.Run();
-  NOTREACHED();
-  return gfx::kPlaceholderColor;
+  DCHECK(ink_drop_base_color_);
+  return ink_drop_base_color_.value_or(gfx::kPlaceholderColor);
+}
+
+void InkDropHostView::SetInkDropBaseColor(SkColor color) {
+  ink_drop_base_color_ = color;
 }
 
 void InkDropHostView::SetInkDropBaseColorCallback(
@@ -231,6 +254,10 @@
   GetEventHandler()->AnimateInkDrop(state, event);
 }
 
+bool InkDropHostView::HasInkDrop() const {
+  return !!ink_drop_;
+}
+
 InkDrop* InkDropHostView::GetInkDrop() {
   if (!ink_drop_) {
     if (ink_drop_mode_ == InkDropMode::OFF)
@@ -293,38 +320,18 @@
   ink_drop_mask_.reset();
 }
 
-std::unique_ptr<InkDropRipple> InkDropHostView::CreateInkDropForSquareRipple(
+std::unique_ptr<InkDropRipple> InkDropHostView::CreateSquareInkDropRipple(
     const gfx::Point& center_point,
     const gfx::Size& size) const {
+  constexpr float kLargeInkDropScale = 1.333f;
+  const gfx::Size large_size = gfx::ScaleToCeiledSize(size, kLargeInkDropScale);
   auto ripple = std::make_unique<SquareInkDropRipple>(
-      CalculateLargeInkDropSize(size), ink_drop_large_corner_radius_, size,
+      large_size, ink_drop_large_corner_radius_, size,
       ink_drop_small_corner_radius_, center_point, GetInkDropBaseColor(),
       GetInkDropVisibleOpacity());
   return ripple;
 }
 
-bool InkDropHostView::HasInkDrop() const {
-  return !!ink_drop_;
-}
-
-// static
-gfx::Size InkDropHostView::CalculateLargeInkDropSize(
-    const gfx::Size& small_size) {
-  // The scale factor to compute the large size of the default
-  // SquareInkDropRipple.
-  constexpr float kLargeInkDropScale = 1.333f;
-  return gfx::ScaleToCeiledSize(gfx::Size(small_size), kLargeInkDropScale);
-}
-
-void InkDropHostView::OnLayerTransformed(const gfx::Transform& old_transform,
-                                         ui::PropertyChangeReason reason) {
-  View::OnLayerTransformed(old_transform, reason);
-
-  // Notify the ink drop that we have transformed so it can adapt accordingly.
-  if (HasInkDrop())
-    GetInkDrop()->HostTransformChanged(GetTransform());
-}
-
 const InkDropEventHandler* InkDropHostView::GetEventHandler() const {
   if (ink_drop_event_handler_override_)
     return ink_drop_event_handler_override_;
@@ -384,6 +391,7 @@
 ADD_PROPERTY_METADATA(base::RepeatingCallback<SkColor()>,
                       InkDropBaseColorCallback)
 ADD_READONLY_PROPERTY_METADATA(bool, Highlighted)
+ADD_PROPERTY_METADATA(SkColor, InkDropBaseColor, ui::metadata::SkColorConverter)
 ADD_PROPERTY_METADATA(float, InkDropVisibleOpacity)
 ADD_PROPERTY_METADATA(base::Optional<float>, InkDropHighlightOpacity)
 ADD_PROPERTY_METADATA(int, InkDropLargeCornerRadius)
diff --git a/ui/views/animation/ink_drop_host_view.h b/ui/views/animation/ink_drop_host_view.h
index bcfa225..4a62b1cc 100644
--- a/ui/views/animation/ink_drop_host_view.h
+++ b/ui/views/animation/ink_drop_host_view.h
@@ -82,12 +82,10 @@
   GetRemoveInkDropLayerCallback() const;
 
   // Returns a configured InkDrop. To override default behavior call
-  // SetCreateInkDropRippleCallback().
+  // SetCreateInkDropCallback().
   std::unique_ptr<InkDrop> CreateInkDrop();
 
-  // Callback version of CreateInkDrop(). Note that this is called in the base
-  // implementation of CreateInkDrop(), so if "it's not working", check the
-  // class hierarchy for overrides.
+  // Replace CreateInkDrop() behavior.
   void SetCreateInkDropCallback(
       base::RepeatingCallback<std::unique_ptr<InkDrop>()> callback);
 
@@ -97,11 +95,9 @@
 
   // Creates and returns the visual effect used for press. Used by InkDropImpl
   // instances.
-  virtual std::unique_ptr<InkDropRipple> CreateInkDropRipple() const;
+  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const;
 
-  // Callback version of CreateInkDropRipple(). Note that this is called in the
-  // base implementation of CreateInkDropRipple(), so if "it's not working",
-  // check the class hierarchy for overrides.
+  // Replaces CreateInkDropRipple() behavior.
   void SetCreateInkDropRippleCallback(
       base::RepeatingCallback<std::unique_ptr<InkDropRipple>()> callback);
 
@@ -115,12 +111,11 @@
   gfx::Point GetInkDropCenterBasedOnLastEvent() const;
 
   // Creates and returns the visual effect used for hover and focus. Used by
-  // InkDropImpl instances.
+  // InkDropImpl instances. To override behavior call
+  // SetCreateInkDropHighlightCallback().
   std::unique_ptr<InkDropHighlight> CreateInkDropHighlight() const;
 
-  // Callback version of CreateInkDropHighlight(). Note that this is called in
-  // the base implementation of CreateInkDropHighlight(), so if "it's not
-  // working", check the class hierarchy for overrides.
+  // Replaces CreateInkDropHighlight() behavior.
   void SetCreateInkDropHighlightCallback(
       base::RepeatingCallback<std::unique_ptr<InkDropHighlight>()> callback);
 
@@ -139,11 +134,16 @@
   GetCreateInkDropMaskCallback() const;
 
   // Returns the base color for the ink drop.
-  virtual SkColor GetInkDropBaseColor() const;
+  SkColor GetInkDropBaseColor() const;
 
-  // Callback version of GetInkDropBaseColor(). Note that this is called in the
-  // base implementation of GetInkDropBaseColor(), so if "it's not working",
-  // check the class hierarchy for overrides.
+  // Sets the base color for the ink drop.
+  void SetInkDropBaseColor(SkColor color);
+
+  // Callback version of GetInkDropBaseColor(). If possible, prefer using
+  // SetInkDropBaseColor(). If a callback has been set by previous configuration
+  // and you want to use the base version of GetInkDropBaseColor() that's
+  // reading SetInkDropBaseColor(), you need to reset the callback by calling
+  // SetInkDropBaseColorCallback({}).
   void SetInkDropBaseColorCallback(base::RepeatingCallback<SkColor()> callback);
 
   // Only here to support metadata.
@@ -188,15 +188,14 @@
   // them.
   void AnimateInkDrop(InkDropState state, const ui::LocatedEvent* event);
 
+  // Returns true if an ink drop instance has been created.
+  bool HasInkDrop() const;
+
   // Provides public access to |ink_drop_| so that factory methods can configure
   // the inkdrop. Implements lazy initialization of |ink_drop_| so as to avoid
   // virtual method calls during construction since subclasses should be able to
   // call SetInkDropMode() during construction.
-  //
-  // WARNING: please don't override this; this is only virtual for the
-  // InstallableInkDrop refactor. TODO(crbug.com/931964): make non-virtual when
-  // this isn't necessary anymore.
-  virtual InkDrop* GetInkDrop();
+  InkDrop* GetInkDrop();
 
   // Returns whether the ink drop should be considered "highlighted" (in or
   // animating into "highlight visible" steady state).
@@ -214,29 +213,29 @@
   void AddInkDropLayer(ui::Layer* ink_drop_layer);
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer);
 
- protected:
   // Size used by default for the SquareInkDropRipple.
-  static constexpr gfx::Size kDefaultInkDropSize = gfx::Size(24, 24);
+  static constexpr gfx::Size kDefaultSquareInkDropSize = gfx::Size(24, 24);
 
   // Creates a SquareInkDropRipple centered on |center_point|.
-  std::unique_ptr<InkDropRipple> CreateInkDropForSquareRipple(
+  std::unique_ptr<InkDropRipple> CreateSquareInkDropRipple(
       const gfx::Point& center_point,
-      const gfx::Size& size = kDefaultInkDropSize) const;
-
-  // Returns true if an ink drop instance has been created.
-  bool HasInkDrop() const;
-
-  // Returns a large ink drop size based on the |small_size| that works well
-  // with the SquareInkDropRipple animation durations.
-  static gfx::Size CalculateLargeInkDropSize(const gfx::Size& small_size);
-
-  // View:
-  void OnLayerTransformed(const gfx::Transform& old_transform,
-                          ui::PropertyChangeReason reason) override;
+      const gfx::Size& size = kDefaultSquareInkDropSize) const;
 
  private:
   friend class test::InkDropHostViewTestApi;
 
+  class ViewLayerTransformObserver : public ViewObserver {
+   public:
+    explicit ViewLayerTransformObserver(InkDropHostView* host);
+    ~ViewLayerTransformObserver() override;
+
+    void OnViewLayerTransformed(View* observed_view) override;
+
+   private:
+    base::ScopedObservation<View, ViewObserver> observation_{this};
+    InkDropHostView* const host_;
+  };
+
   class InkDropHostViewEventHandlerDelegate
       : public InkDropEventHandler::Delegate {
    public:
@@ -272,6 +271,9 @@
   // Defines what type of |ink_drop_| to create.
   InkDropMode ink_drop_mode_ = InkDropMode::OFF;
 
+  // Used to observe View and inform the InkDrop of host-transform changes.
+  ViewLayerTransformObserver host_view_transform_observer_;
+
   // Should not be accessed directly. Use GetInkDrop() instead.
   std::unique_ptr<InkDrop> ink_drop_;
 
@@ -284,6 +286,9 @@
 
   float ink_drop_visible_opacity_ = 0.175f;
 
+  // The color of the ripple and hover.
+  base::Optional<SkColor> ink_drop_base_color_;
+
   // TODO(pbos): Audit call sites to make sure highlight opacity is either
   // always set or using the default value. Then make this a non-optional float.
   base::Optional<float> ink_drop_highlight_opacity_;
@@ -314,6 +319,7 @@
 VIEW_BUILDER_PROPERTY(int, InkDropLargeCornerRadius)
 VIEW_BUILDER_PROPERTY(InkDropHostView::InkDropMode, InkDropMode)
 VIEW_BUILDER_PROPERTY(int, InkDropSmallCornerRadius)
+VIEW_BUILDER_PROPERTY(SkColor, InkDropBaseColor)
 VIEW_BUILDER_PROPERTY(float, InkDropVisibleOpacity)
 END_VIEW_BUILDER
 
diff --git a/ui/views/animation/ink_drop_host_view_unittest.cc b/ui/views/animation/ink_drop_host_view_unittest.cc
index 6f67c53..39ee1c5 100644
--- a/ui/views/animation/ink_drop_host_view_unittest.cc
+++ b/ui/views/animation/ink_drop_host_view_unittest.cc
@@ -42,6 +42,7 @@
           return ink_drop;
         },
         this));
+    SetInkDropBaseColor(gfx::kPlaceholderColor);
   }
 
   // Accessors to InkDropHostView internals.
@@ -51,12 +52,6 @@
 
   TestInkDrop* last_created_inkdrop() const { return last_created_inkdrop_; }
 
- protected:
-  // InkDropHostView:
-  SkColor GetInkDropBaseColor() const override {
-    return gfx::kPlaceholderColor;
-  }
-
  private:
   int on_ink_drop_created_count_ = 0;
   TestInkDrop* last_created_inkdrop_ = nullptr;
@@ -278,20 +273,14 @@
   EXPECT_TRUE(callback_called);
 }
 
-// A very basic InkDropHostView that only changes the GetInkDropBaseColor to
+// A very basic InkDropHostView that only calls SetInkDropBaseColor to
 // avoid hitting a NOTREACHED.
 class BasicTestInkDropHostView : public InkDropHostView {
  public:
-  BasicTestInkDropHostView() = default;
+  BasicTestInkDropHostView() { SetInkDropBaseColor(gfx::kPlaceholderColor); }
   BasicTestInkDropHostView(const BasicTestInkDropHostView&) = delete;
   BasicTestInkDropHostView& operator=(const BasicTestInkDropHostView&) = delete;
   ~BasicTestInkDropHostView() override = default;
-
- protected:
-  // InkDropHostView:
-  SkColor GetInkDropBaseColor() const override {
-    return gfx::kPlaceholderColor;
-  }
 };
 
 // Tests the existence of layer clipping or layer masking when certain path
diff --git a/ui/views/animation/test/test_ink_drop_host.cc b/ui/views/animation/test/test_ink_drop_host.cc
index ae61fa73..b820164 100644
--- a/ui/views/animation/test/test_ink_drop_host.cc
+++ b/ui/views/animation/test/test_ink_drop_host.cc
@@ -101,18 +101,20 @@
         return highlight;
       },
       this));
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](TestInkDropHost* host) -> std::unique_ptr<views::InkDropRipple> {
+        auto ripple = std::make_unique<TestInkDropRipple>(
+            host->size(), 0, host->size(), 0, gfx::Point(), SK_ColorBLACK,
+            0.175f);
+        if (host->disable_timers_for_test_)
+          ripple->GetTestApi()->SetDisableAnimationTimers(true);
+        host->num_ink_drop_ripples_created_++;
+        host->last_ink_drop_ripple_ = ripple.get();
+        return ripple;
+      },
+      this));
 }
 
 TestInkDropHost::~TestInkDropHost() = default;
 
-std::unique_ptr<InkDropRipple> TestInkDropHost::CreateInkDropRipple() const {
-  std::unique_ptr<InkDropRipple> ripple(new TestInkDropRipple(
-      size(), 0, size(), 0, gfx::Point(), SK_ColorBLACK, 0.175f));
-  if (disable_timers_for_test_)
-    ripple->GetTestApi()->SetDisableAnimationTimers(true);
-  num_ink_drop_ripples_created_++;
-  last_ink_drop_ripple_ = ripple.get();
-  return ripple;
-}
-
 }  // namespace views
diff --git a/ui/views/animation/test/test_ink_drop_host.h b/ui/views/animation/test/test_ink_drop_host.h
index 93636fb..8f02336 100644
--- a/ui/views/animation/test/test_ink_drop_host.h
+++ b/ui/views/animation/test/test_ink_drop_host.h
@@ -42,9 +42,6 @@
     disable_timers_for_test_ = disable_timers_for_test;
   }
 
-  // InkDropHostView:
-  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override;
-
  private:
   int num_ink_drop_layers_added_ = 0;
   int num_ink_drop_layers_removed_ = 0;
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index 85ab5aa..469bdc91 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -73,6 +73,14 @@
     return bubble_delegate->anchor_widget()->GetThemeProvider();
   }
 
+  Widget* GetPrimaryWindowWidget() override {
+    BubbleDialogDelegateView* const bubble_delegate =
+        static_cast<BubbleDialogDelegateView*>(widget_delegate());
+    if (!bubble_delegate || !bubble_delegate->anchor_widget())
+      return Widget::GetPrimaryWindowWidget();
+    return bubble_delegate->anchor_widget()->GetPrimaryWindowWidget();
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(BubbleWidget);
 };
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index 56df070..ca033721 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -374,6 +374,17 @@
   EXPECT_TRUE(bubble_widget->IsVisible());
 }
 
+TEST_F(BubbleDialogDelegateViewTest, GetPrimaryWindowWidget) {
+  std::unique_ptr<Widget> anchor_widget =
+      CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
+  BubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+  EXPECT_EQ(anchor_widget.get(), anchor_widget->GetPrimaryWindowWidget());
+  EXPECT_EQ(anchor_widget.get(), bubble_widget->GetPrimaryWindowWidget());
+}
+
 // Test that setting WidgetDelegate::SetCanActivate() to false makes the
 // widget created via BubbleDialogDelegateView::CreateBubble() not activatable.
 TEST_F(BubbleDialogDelegateViewTest, NotActivatable) {
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc
index 11fc16a..6b861260 100644
--- a/ui/views/controls/button/button.cc
+++ b/ui/views/controls/button/button.cc
@@ -11,6 +11,7 @@
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/class_property.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
@@ -29,8 +30,10 @@
 #include "ui/views/controls/button/radio_button.h"
 #include "ui/views/controls/button/toggle_button.h"
 #include "ui/views/controls/focus_ring.h"
+#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/painter.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/view_class_properties.h"
 
 #if defined(USE_AURA)
 #include "ui/aura/client/capture_client.h"
@@ -286,13 +289,6 @@
   return show_ink_drop_when_hot_tracked_;
 }
 
-void Button::SetInkDropBaseColor(SkColor color) {
-  if (color == ink_drop_base_color_)
-    return;
-  ink_drop_base_color_ = color;
-  OnPropertyChanged(&ink_drop_base_color_, kPropertyEffectsNone);
-}
-
 void Button::SetHasInkDropActionOnClick(bool value) {
   if (value == has_ink_drop_action_on_click_)
     return;
@@ -574,18 +570,12 @@
     SchedulePaint();
 }
 
-SkColor Button::GetInkDropBaseColor() const {
-  return InkDropHostView::GetInkDropBaseColor();
-}
-
 void Button::AnimationProgressed(const gfx::Animation* animation) {
   SchedulePaint();
 }
 
 Button::Button(PressedCallback callback)
-    : AnimationDelegateViews(this),
-      callback_(std::move(callback)),
-      ink_drop_base_color_(gfx::kPlaceholderColor) {
+    : AnimationDelegateViews(this), callback_(std::move(callback)) {
   SetFocusBehavior(PlatformStyle::kDefaultFocusBehavior);
   SetProperty(kIsButtonProperty, true);
   hover_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(150));
@@ -600,8 +590,9 @@
         return ink_drop;
       },
       base::Unretained(this)));
-  SetInkDropBaseColorCallback(base::BindRepeating(
-      [](Button* button) { return button->ink_drop_base_color_; }, this));
+  // TODO(pbos): Investigate not setting a default color so that we can DCHECK
+  // if one hasn't been set.
+  SetInkDropBaseColor(gfx::kPlaceholderColor);
 }
 
 void Button::RequestFocusFromEvent() {
@@ -615,6 +606,14 @@
                    ui::LocatedEvent::FromIfValid(&event));
   }
 
+  // If we have an associated help context ID, notify that system that we have
+  // been activated.
+  const ui::ElementIdentifier element_id = GetProperty(kElementIdentifierKey);
+  if (element_id) {
+    views::ElementTrackerViews::GetInstance()->NotifyViewActivated(element_id,
+                                                                   this);
+  }
+
   if (callback_)
     callback_.Run(event);
 }
@@ -693,7 +692,6 @@
 ADD_PROPERTY_METADATA(bool, AnimateOnStateChange)
 ADD_PROPERTY_METADATA(bool, HasInkDropActionOnClick)
 ADD_PROPERTY_METADATA(bool, HideInkDropWhenShowingContextMenu)
-ADD_PROPERTY_METADATA(SkColor, InkDropBaseColor, ui::metadata::SkColorConverter)
 ADD_PROPERTY_METADATA(bool, InstallFocusRingOnFocus)
 ADD_PROPERTY_METADATA(bool, RequestFocusOnPress)
 ADD_PROPERTY_METADATA(ButtonState, State)
diff --git a/ui/views/controls/button/button.h b/ui/views/controls/button/button.h
index 96df742..7a1af7f 100644
--- a/ui/views/controls/button/button.h
+++ b/ui/views/controls/button/button.h
@@ -166,8 +166,6 @@
   void SetShowInkDropWhenHotTracked(bool value);
   bool GetShowInkDropWhenHotTracked() const;
 
-  void SetInkDropBaseColor(SkColor color);
-
   void SetHasInkDropActionOnClick(bool value);
   bool GetHasInkDropActionOnClick() const;
 
@@ -212,11 +210,6 @@
   void OnFocus() override;
   void OnBlur() override;
 
-  // Overridden from InkDropHostView:
-  // This just returns InkDropHostView::GetInkDropBaseColor(). It's overridden
-  // so that ADD_PROPERTY_METADATA can retrieve the InkDropBaseColor property.
-  SkColor GetInkDropBaseColor() const override;
-
   // Overridden from views::AnimationDelegateViews:
   void AnimationProgressed(const gfx::Animation* animation) override;
 
@@ -342,9 +335,6 @@
   // tracked with SetHotTracked().
   bool show_ink_drop_when_hot_tracked_ = false;
 
-  // The color of the ripple and hover.
-  SkColor ink_drop_base_color_;
-
   // The focus ring for this Button.
   FocusRing* focus_ring_ = nullptr;
 
@@ -370,7 +360,6 @@
 VIEW_BUILDER_PROPERTY(bool, AnimateOnStateChange)
 VIEW_BUILDER_PROPERTY(bool, HasInkDropActionOnClick)
 VIEW_BUILDER_PROPERTY(bool, HideInkDropWhenShowingContextMenu)
-VIEW_BUILDER_PROPERTY(SkColor, InkDropBaseColor)
 VIEW_BUILDER_PROPERTY(bool, InstallFocusRingOnFocus)
 VIEW_BUILDER_PROPERTY(bool, RequestFocusOnPress)
 VIEW_BUILDER_PROPERTY(Button::ButtonState, State)
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index 38324337..b61ed837 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -55,6 +56,21 @@
   SetHasInkDropActionOnClick(true);
   views::InkDrop::UseInkDropWithoutAutoHighlight(this,
                                                  /*highlight_on_hover=*/false);
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](Checkbox* host) {
+        // The "small" size is 21dp, the large size is 1.33 * 21dp = 28dp.
+        return host->CreateSquareInkDropRipple(
+            host->image()->GetMirroredContentsBounds().CenterPoint(),
+            gfx::Size(21, 21));
+      },
+      this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](Checkbox* host) {
+        // Usually ink-drop ripples match the text color. Checkboxes use the
+        // color of the unchecked, enabled icon.
+        return host->GetIconImageColor(IconState::ENABLED);
+      },
+      this));
 
   // Limit the checkbox height to match the legacy appearance.
   const gfx::Size preferred_size(LabelButton::CalculatePreferredSize());
@@ -155,18 +171,6 @@
   UpdateImage();
 }
 
-std::unique_ptr<InkDropRipple> Checkbox::CreateInkDropRipple() const {
-  // The "small" size is 21dp, the large size is 1.33 * 21dp = 28dp.
-  return CreateInkDropForSquareRipple(
-      image()->GetMirroredContentsBounds().CenterPoint(), gfx::Size(21, 21));
-}
-
-SkColor Checkbox::GetInkDropBaseColor() const {
-  // Usually ink-drop ripples match the text color. Checkboxes use the color of
-  // the unchecked, enabled icon.
-  return GetIconImageColor(IconState::ENABLED);
-}
-
 SkPath Checkbox::GetFocusRingPath() const {
   SkPath path;
   gfx::Rect bounds = image()->GetMirroredContentsBounds();
diff --git a/ui/views/controls/button/checkbox.h b/ui/views/controls/button/checkbox.h
index 9b834f9b..f72b517 100644
--- a/ui/views/controls/button/checkbox.h
+++ b/ui/views/controls/button/checkbox.h
@@ -58,8 +58,6 @@
 
   // LabelButton:
   void OnThemeChanged() override;
-  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override;
-  SkColor GetInkDropBaseColor() const override;
 
   // Returns the path to draw the focus ring around for this Checkbox.
   virtual SkPath GetFocusRingPath() const;
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc
index 59be7db..34f3def0 100644
--- a/ui/views/controls/button/md_text_button.cc
+++ b/ui/views/controls/button/md_text_button.cc
@@ -8,6 +8,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/i18n/case_conversion.h"
 #include "base/memory/ptr_util.h"
 #include "build/build_config.h"
@@ -37,6 +38,13 @@
   SetInkDropMode(InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
   SetShowInkDropWhenHotTracked(true);
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](MdTextButton* host) {
+        return color_utils::DeriveDefaultIconColor(
+            host->label()->GetEnabledColor());
+      },
+      this));
+
   SetCornerRadius(LayoutProvider::Get()->GetCornerRadiusMetric(Emphasis::kLow));
   SetHorizontalAlignment(gfx::ALIGN_CENTER);
 
@@ -102,10 +110,6 @@
   UpdateColors();
 }
 
-SkColor MdTextButton::GetInkDropBaseColor() const {
-  return color_utils::DeriveDefaultIconColor(label()->GetEnabledColor());
-}
-
 void MdTextButton::StateChanged(ButtonState old_state) {
   LabelButton::StateChanged(old_state);
   UpdateColors();
diff --git a/ui/views/controls/button/md_text_button.h b/ui/views/controls/button/md_text_button.h
index ebe4f96..12c52e2 100644
--- a/ui/views/controls/button/md_text_button.h
+++ b/ui/views/controls/button/md_text_button.h
@@ -43,7 +43,6 @@
 
   // LabelButton:
   void OnThemeChanged() override;
-  SkColor GetInkDropBaseColor() const override;
   void SetEnabledTextColors(base::Optional<SkColor> color) override;
   void SetText(const std::u16string& text) override;
   PropertyEffects UpdateStyleToIndicateDefaultStatus() override;
diff --git a/ui/views/controls/button/menu_button.cc b/ui/views/controls/button/menu_button.cc
index 960b272a..bd20d92 100644
--- a/ui/views/controls/button/menu_button.cc
+++ b/ui/views/controls/button/menu_button.cc
@@ -11,6 +11,7 @@
 #include "ui/events/event.h"
 #include "ui/views/controls/button/button_controller_delegate.h"
 #include "ui/views/controls/button/menu_button_controller.h"
+#include "ui/views/view_class_properties.h"
 
 namespace views {
 
diff --git a/ui/views/controls/button/menu_button_controller.cc b/ui/views/controls/button/menu_button_controller.cc
index ee45a1b..6056c0a 100644
--- a/ui/views/controls/button/menu_button_controller.cc
+++ b/ui/views/controls/button/menu_button_controller.cc
@@ -9,14 +9,17 @@
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/types/event_type.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/button_controller_delegate.h"
 #include "ui/views/controls/button/menu_button.h"
+#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/mouse_constants.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/widget/root_view.h"
 #include "ui/views/widget/widget.h"
 
@@ -231,6 +234,15 @@
     bool increment_pressed_lock_called = false;
     increment_pressed_lock_called_ = &increment_pressed_lock_called;
 
+    // Since regular Button logic isn't used, we need to instead notify that the
+    // menu button was activated here.
+    const ui::ElementIdentifier id =
+        button()->GetProperty(views::kElementIdentifierKey);
+    if (id) {
+      views::ElementTrackerViews::GetInstance()->NotifyViewActivated(id,
+                                                                     button());
+    }
+
     // Allow for the button callback to delete this.
     auto ref = weak_factory_.GetWeakPtr();
 
diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc
index afc4d205..29770ea 100644
--- a/ui/views/controls/button/toggle_button.cc
+++ b/ui/views/controls/button/toggle_button.cc
@@ -137,6 +137,19 @@
   SetHasInkDropActionOnClick(true);
   views::InkDrop::UseInkDropForSquareRipple(this,
                                             /*highlight_on_hover=*/false);
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](ToggleButton* host) {
+        gfx::Rect rect = host->thumb_view_->GetLocalBounds();
+        rect.Inset(-ThumbView::GetShadowOutsets());
+        return host->CreateSquareInkDropRipple(rect.CenterPoint());
+      },
+      this));
+  SetInkDropBaseColorCallback(base::BindRepeating(
+      [](ToggleButton* host) {
+        return host->GetTrackColor(host->GetIsOn() || host->HasFocus());
+      },
+      this));
+
   SetAddInkDropLayerCallback(base::BindRepeating(
       &InkDropHostView::AddInkDropLayer, base::Unretained(thumb_view_)));
   SetRemoveInkDropLayerCallback(base::BindRepeating(
@@ -322,16 +335,6 @@
   canvas->Restore();
 }
 
-std::unique_ptr<InkDropRipple> ToggleButton::CreateInkDropRipple() const {
-  gfx::Rect rect = thumb_view_->GetLocalBounds();
-  rect.Inset(-ThumbView::GetShadowOutsets());
-  return CreateInkDropForSquareRipple(rect.CenterPoint());
-}
-
-SkColor ToggleButton::GetInkDropBaseColor() const {
-  return GetTrackColor(GetIsOn() || HasFocus());
-}
-
 void ToggleButton::AnimationProgressed(const gfx::Animation* animation) {
   if (animation == &slide_animation_) {
     // TODO(varkha, estade): The thumb is using its own view. Investigate if
diff --git a/ui/views/controls/button/toggle_button.h b/ui/views/controls/button/toggle_button.h
index b9c2e702..e727c337 100644
--- a/ui/views/controls/button/toggle_button.h
+++ b/ui/views/controls/button/toggle_button.h
@@ -69,8 +69,6 @@
   // Button:
   void NotifyClick(const ui::Event& event) override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
-  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override;
-  SkColor GetInkDropBaseColor() const override;
 
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override;
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 84bbcf5..7a0fc97a 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -70,6 +70,15 @@
     SetHasInkDropActionOnClick(true);
     InkDrop::UseInkDropForSquareRipple(this,
                                        /*highlight_on_hover=*/false);
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+              host->GetNativeTheme()->GetSystemColor(
+                  ui::NativeTheme::kColorId_LabelEnabledColor),
+              host->GetInkDropVisibleOpacity());
+        },
+        this));
   }
 
   ~TransparentButton() override = default;
@@ -87,16 +96,6 @@
     return hover_animation().GetCurrentValue();
   }
 
-  // Button:
-  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override {
-    return std::unique_ptr<views::InkDropRipple>(
-        new views::FloodFillInkDropRipple(
-            size(), GetInkDropCenterBasedOnLastEvent(),
-            GetNativeTheme()->GetSystemColor(
-                ui::NativeTheme::kColorId_LabelEnabledColor),
-            GetInkDropVisibleOpacity()));
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(TransparentButton);
 };
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc
index 360fbc3..c8f85cc 100644
--- a/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -80,6 +80,15 @@
     SetHasInkDropActionOnClick(true);
     InkDrop::UseInkDropForSquareRipple(this,
                                        /*highlight_on_hover=*/false);
+    SetCreateInkDropRippleCallback(base::BindRepeating(
+        [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
+          return std::make_unique<views::FloodFillInkDropRipple>(
+              host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+              style::GetColor(*host, style::CONTEXT_TEXTFIELD,
+                              style::STYLE_PRIMARY),
+              host->GetInkDropVisibleOpacity());
+        },
+        this));
   }
   Arrow(const Arrow&) = delete;
   Arrow& operator=(const Arrow&) = delete;
@@ -89,16 +98,8 @@
     return hover_animation().GetCurrentValue();
   }
 
-  // Button:
-  // Similar to Combobox's TransparentButton.
-  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override {
-    return std::make_unique<views::FloodFillInkDropRipple>(
-        size(), GetInkDropCenterBasedOnLastEvent(),
-        style::GetColor(*this, style::CONTEXT_TEXTFIELD, style::STYLE_PRIMARY),
-        GetInkDropVisibleOpacity());
-  }
-
  private:
+  // Button:
   void PaintButtonContents(gfx::Canvas* canvas) override {
     gfx::ScopedCanvas scoped_canvas(canvas);
     canvas->ClipRect(GetContentsBounds());
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index 42f8806..e9edd19 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -42,8 +42,10 @@
 #include "ui/views/controls/menu/menu_scroll_view_container.h"
 #include "ui/views/controls/menu/submenu_view.h"
 #include "ui/views/drag_utils.h"
+#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/mouse_constants.h"
 #include "ui/views/view.h"
+#include "ui/views/view_class_properties.h"
 #include "ui/views/view_constants.h"
 #include "ui/views/view_tracker.h"
 #include "ui/views/views_delegate.h"
@@ -1728,6 +1730,16 @@
 }
 
 void MenuController::Accept(MenuItemView* item, int event_flags) {
+  // This counts as activation of a menu item. We don't put this logic in
+  // ReallyAccept() because we expect activation to happen while the element is
+  // visible to the user, but ReallyAccept() is called on Mac *after* the menu
+  // is closed.
+  if (item) {
+    const ui::ElementIdentifier id = item->GetProperty(kElementIdentifierKey);
+    if (id)
+      views::ElementTrackerViews::GetInstance()->NotifyViewActivated(id, item);
+  }
+
 #if defined(OS_MAC)
   menu_closure_animation_ = std::make_unique<MenuClosureAnimationMac>(
       item, item->GetParentMenuItem()->GetSubmenu(),
diff --git a/ui/views/controls/menu/menu_host.cc b/ui/views/controls/menu/menu_host.cc
index 59776a3..f1332232 100644
--- a/ui/views/controls/menu/menu_host.cc
+++ b/ui/views/controls/menu/menu_host.cc
@@ -305,6 +305,11 @@
     native_widget_private()->SetCapture();
 }
 
+Widget* MenuHost::GetPrimaryWindowWidget() {
+  return owner_ ? owner_->GetPrimaryWindowWidget()
+                : Widget::GetPrimaryWindowWidget();
+}
+
 void MenuHost::OnWidgetDestroying(Widget* widget) {
   DCHECK_EQ(owner_, widget);
   owner_->RemoveObserver(this);
diff --git a/ui/views/controls/menu/menu_host.h b/ui/views/controls/menu/menu_host.h
index b1872ec..18408265 100644
--- a/ui/views/controls/menu/menu_host.h
+++ b/ui/views/controls/menu/menu_host.h
@@ -80,6 +80,7 @@
   void OnOwnerClosing() override;
   void OnDragWillStart() override;
   void OnDragComplete() override;
+  Widget* GetPrimaryWindowWidget() override;
 
   // WidgetObserver:
   void OnWidgetDestroying(Widget* widget) override;
diff --git a/ui/views/controls/menu/menu_model_adapter.cc b/ui/views/controls/menu/menu_model_adapter.cc
index 2f7db743..765dbd2 100644
--- a/ui/views/controls/menu/menu_model_adapter.cc
+++ b/ui/views/controls/menu/menu_model_adapter.cc
@@ -4,15 +4,19 @@
 
 #include "ui/views/controls/menu/menu_model_adapter.h"
 
+#include <list>
+#include <memory>
 #include <utility>
 
 #include "base/check.h"
 #include "base/notreached.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/base/models/menu_model.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/controls/menu/submenu_view.h"
+#include "ui/views/view_class_properties.h"
 
 namespace views {
 
@@ -118,6 +122,10 @@
   menu_item_view->set_is_new(model->IsNewFeatureAt(model_index));
   menu_item_view->set_may_have_mnemonics(
       model->MayHaveMnemonicsAt(model_index));
+  const ui::ElementIdentifier element_id =
+      model->GetElementIdentifierAt(model_index);
+  if (element_id)
+    menu_item_view->SetProperty(kElementIdentifierKey, element_id);
 
   return menu_item_view;
 }
diff --git a/ui/views/interaction/element_tracker_views.cc b/ui/views/interaction/element_tracker_views.cc
new file mode 100644
index 0000000..e02129db
--- /dev/null
+++ b/ui/views/interaction/element_tracker_views.cc
@@ -0,0 +1,239 @@
+// Copyright 2021 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 "ui/views/interaction/element_tracker_views.h"
+
+#include <list>
+#include <map>
+
+#include "base/containers/contains.h"
+#include "base/no_destructor.h"
+#include "base/scoped_multi_source_observation.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/views/view.h"
+#include "ui/views/view_observer.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace views {
+
+namespace {
+
+// Returns whether the specified view is visible to the user. Takes the view
+// hierarchy and widget into account.
+bool IsViewVisibleToUser(View* view, bool force_widget_visible = false) {
+  const Widget* const widget = view->GetWidget();
+  if (!widget || (!force_widget_visible && !widget->IsVisible()))
+    return false;
+  for (; view; view = view->parent()) {
+    if (!view->GetVisible())
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+ElementTrackerElementViews::ElementTrackerElementViews(
+    View* view,
+    ui::ElementIdentifier identifier,
+    ui::ElementContext context)
+    : ElementTrackerElement(identifier, context), view_(view) {}
+
+ElementTrackerElementViews::~ElementTrackerElementViews() = default;
+
+DEFINE_ELEMENT_TRACKER_METADATA(ElementTrackerElementViews)
+
+class ElementTrackerViews::ElementDataViews : public ViewObserver,
+                                              public WidgetObserver {
+ public:
+  ElementDataViews(ElementTrackerViews* tracker,
+                   ui::ElementIdentifier identifier)
+      : tracker_(tracker), id_(identifier) {}
+  ~ElementDataViews() override = default;
+
+  void AddView(View* view) {
+    if (base::Contains(view_data_lookup_, view))
+      return;
+
+    const auto it = view_data_.insert(view_data_.end(),
+                                      ViewData(view, GetContextForView(view)));
+    view_data_lookup_.emplace(view, it);
+    view_observer_.AddObservation(view);
+    tracker_->MaybeObserveWidget(view->GetWidget());
+    UpdateVisible(view);
+  }
+
+  void RemoveView(View* view) {
+    const auto it = view_data_lookup_.find(view);
+    if (it == view_data_lookup_.end())
+      return;
+    if (it->second->visible()) {
+      ui::ElementTracker::GetFrameworkDelegate()->NotifyElementHidden(
+          it->second->element.get());
+    }
+    view_observer_.RemoveObservation(view);
+    view_data_.erase(it->second);
+    view_data_lookup_.erase(it);
+    if (view_data_.empty())
+      tracker_->element_data_.erase(id_);
+  }
+
+  void NotifyViewActivated(View* view) {
+    const auto it = view_data_lookup_.find(view);
+    DCHECK(it != view_data_lookup_.end());
+    if (it->second->visible()) {
+      ui::ElementTracker::GetFrameworkDelegate()->NotifyElementActivated(
+          it->second->element.get());
+    } else {
+      DLOG(WARNING)
+          << "View " << view->GetClassName()
+          << " activated before it was made visible. This probably happened"
+             " during a test; it should never happen in a live browser.";
+    }
+  }
+
+  // When a widget we were previously watching because it had not yet been shown
+  // becomes visible, we manually update the visibility of any view on that
+  // widget.
+  void UpdateViewVisibilityForWidget(Widget* widget) {
+    for (auto& entry : view_data_) {
+      if (!entry.visible() && entry.view->GetWidget() == widget)
+        UpdateVisible(entry.view, /* is_remove */ false,
+                      /* force_widget_visible */ true);
+    }
+  }
+
+ private:
+  struct ViewData {
+    explicit ViewData(View* v, ui::ElementContext initial_context)
+        : view(v), context(initial_context) {}
+    bool visible() const { return static_cast<bool>(element); }
+    View* const view;
+    ui::ElementContext context;
+    std::unique_ptr<ElementTrackerElementViews> element;
+  };
+
+  using ViewDataList = std::list<ViewData>;
+
+  // ViewObserver:
+  void OnViewVisibilityChanged(View* observed_view,
+                               View* starting_view) override {
+    UpdateVisible(observed_view);
+  }
+
+  void OnViewAddedToWidget(View* observed_view) override {
+    tracker_->MaybeObserveWidget(observed_view->GetWidget());
+    UpdateVisible(observed_view);
+  }
+
+  void OnViewRemovedFromWidget(View* observed_view) override {
+    UpdateVisible(observed_view, /* is_remove */ true);
+  }
+
+  void OnViewIsDeleting(View* observed_view) override {
+    RemoveView(observed_view);
+  }
+
+  void UpdateVisible(View* view,
+                     bool is_remove = false,
+                     bool force_widget_visible = false) {
+    const auto it = view_data_lookup_.find(view);
+    DCHECK(it != view_data_lookup_.end());
+    ViewData& data = *it->second;
+    const ui::ElementContext old_context = data.context;
+    data.context = is_remove ? ui::ElementContext() : GetContextForView(view);
+    const bool was_visible = data.visible();
+    const bool visible =
+        it->second->context && IsViewVisibleToUser(view, force_widget_visible);
+    if (visible && !was_visible) {
+      data.element =
+          std::make_unique<ElementTrackerElementViews>(view, id_, data.context);
+      ui::ElementTracker::GetFrameworkDelegate()->NotifyElementShown(
+          data.element.get());
+    } else if (!visible && was_visible) {
+      ui::ElementTracker::GetFrameworkDelegate()->NotifyElementHidden(
+          data.element.get());
+      data.element.reset();
+    }
+    DCHECK(!visible || !was_visible || old_context == data.context)
+        << "We should always get a removed-from-widget notification before an "
+           "added-to-widget notification, the context should never change "
+           "while a view is visible.";
+  }
+
+  ElementTrackerViews* const tracker_;
+  const ui::ElementIdentifier id_;
+  ViewDataList view_data_;
+  std::map<View*, ViewDataList::iterator> view_data_lookup_;
+  base::ScopedMultiSourceObservation<View, ViewObserver> view_observer_{this};
+};
+
+ElementTrackerViews::ElementTrackerViews() = default;
+ElementTrackerViews::~ElementTrackerViews() = default;
+
+// static
+ElementTrackerViews* ElementTrackerViews::GetInstance() {
+  static base::NoDestructor<ElementTrackerViews> instance;
+  return instance.get();
+}
+
+// static
+ui::ElementContext ElementTrackerViews::GetContextForView(View* view) {
+  const Widget* const widget = view->GetWidget();
+  return widget ? ui::ElementContext(widget->GetPrimaryWindowWidget())
+                : ui::ElementContext();
+}
+
+void ElementTrackerViews::RegisterView(ui::ElementIdentifier element_id,
+                                       View* view) {
+  auto it = element_data_.find(element_id);
+  if (it == element_data_.end()) {
+    it = element_data_
+             .emplace(element_id,
+                      std::make_unique<ElementDataViews>(this, element_id))
+             .first;
+  }
+  it->second->AddView(view);
+}
+
+void ElementTrackerViews::UnregisterView(ui::ElementIdentifier element_id,
+                                         View* view) {
+  DCHECK(view);
+  const auto it = element_data_.find(element_id);
+  DCHECK(it != element_data_.end());
+  it->second->RemoveView(view);
+}
+
+void ElementTrackerViews::NotifyViewActivated(ui::ElementIdentifier element_id,
+                                              View* view) {
+  DCHECK(view);
+  const auto it = element_data_.find(element_id);
+  DCHECK(it != element_data_.end());
+  it->second->NotifyViewActivated(view);
+}
+
+void ElementTrackerViews::OnWidgetVisibilityChanged(Widget* widget,
+                                                    bool visible) {
+  if (!visible)
+    return;
+  for (auto& entry : element_data_)
+    entry.second->UpdateViewVisibilityForWidget(widget);
+  widget_observer_.RemoveObservation(widget);
+}
+
+void ElementTrackerViews::OnWidgetDestroying(Widget* widget) {
+  widget_observer_.RemoveObservation(widget);
+}
+
+void ElementTrackerViews::MaybeObserveWidget(Widget* widget) {
+  if (!widget || widget->IsVisible() ||
+      widget_observer_.IsObservingSource(widget)) {
+    return;
+  }
+  widget_observer_.AddObservation(widget);
+}
+
+}  // namespace views
diff --git a/ui/views/interaction/element_tracker_views.h b/ui/views/interaction/element_tracker_views.h
new file mode 100644
index 0000000..389172d
--- /dev/null
+++ b/ui/views/interaction/element_tracker_views.h
@@ -0,0 +1,91 @@
+// Copyright 2021 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 UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_
+#define UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "base/scoped_multi_source_observation.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/views/views_export.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_observer.h"
+
+namespace views {
+
+class View;
+
+// Wraps a View in an ui::ElementTrackerElement.
+class VIEWS_EXPORT ElementTrackerElementViews
+    : public ui::ElementTrackerElement {
+ public:
+  ElementTrackerElementViews(View* view,
+                             ui::ElementIdentifier identifier,
+                             ui::ElementContext context);
+  ~ElementTrackerElementViews() override;
+
+  View* view() { return view_; }
+  const View* view() const { return view_; }
+
+  DECLARE_ELEMENT_TRACKER_METADATA()
+
+ private:
+  View* const view_;
+};
+
+// Manages ElementTrackerElements associated with View objects.
+class VIEWS_EXPORT ElementTrackerViews : private WidgetObserver {
+ public:
+  using ViewList = std::vector<View*>;
+
+  // Gets the global instance of the tracker for Views.
+  static ElementTrackerViews* GetInstance();
+
+  // Returns the context associated with a particular View. The context will be
+  // the same across all Views associated with a root Widget (such as an
+  // application window).
+  static ui::ElementContext GetContextForView(View* view);
+
+  // Called by View after the kUniqueElementKey property is set.
+  void RegisterView(ui::ElementIdentifier element_id, View* view);
+
+  // Called by View if the kUniqueElementKey property is changed from a non-null
+  // value.
+  void UnregisterView(ui::ElementIdentifier element_id, View* view);
+
+  // Called by a View when the user activates it (clicks a button, selects a
+  // menu item, etc.)
+  void NotifyViewActivated(ui::ElementIdentifier element_id, View* view);
+
+ private:
+  friend class base::NoDestructor<ElementTrackerViews>;
+  class ElementDataViews;
+
+  ElementTrackerViews();
+  ~ElementTrackerViews() override;
+
+  // WidgetObserver:
+  void OnWidgetVisibilityChanged(Widget* widget, bool visible) override;
+  void OnWidgetDestroying(Widget* widget) override;
+
+  // We do not get notified at the View level if a view's widget has not yet
+  // been shown. We need this notification to know when the view is actually
+  // visible to the user. So if a view is added to the trakcer or is added to
+  // a widget, and its widget is not visible, we watch it until it is (or it is
+  // destroyed).
+  void MaybeObserveWidget(Widget* widget);
+
+  std::map<ui::ElementIdentifier, std::unique_ptr<ElementDataViews>>
+      element_data_;
+  base::ScopedMultiSourceObservation<Widget, WidgetObserver> widget_observer_{
+      this};
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_INTERACTION_ELEMENT_TRACKER_VIEWS_H_
diff --git a/ui/views/interaction/element_tracker_views_unittest.cc b/ui/views/interaction/element_tracker_views_unittest.cc
new file mode 100644
index 0000000..79f5c29
--- /dev/null
+++ b/ui/views/interaction/element_tracker_views_unittest.cc
@@ -0,0 +1,935 @@
+// Copyright 2021 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 "ui/views/interaction/element_tracker_views.h"
+
+#include <algorithm>
+#include <iterator>
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/test/bind.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/element_tracker.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/dom/dom_code.h"
+#include "ui/events/types/event_type.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/menu_button.h"
+#include "ui/views/test/views_test_base.h"
+#include "ui/views/view_class_properties.h"
+#include "ui/views/widget/widget.h"
+
+namespace views {
+
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementID);
+DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestElementID2);
+
+namespace {
+
+enum ElementEventType { kShown, kActivated, kHidden };
+
+class ElementEventWatcher {
+ public:
+  ElementEventWatcher(ui::ElementIdentifier id,
+                      ui::ElementContext context,
+                      ElementEventType event_type)
+      : id_(id) {
+    auto callback = base::BindRepeating(&ElementEventWatcher::OnEvent,
+                                        base::Unretained(this));
+    ui::ElementTracker* const tracker = ui::ElementTracker::GetElementTracker();
+    switch (event_type) {
+      case ElementEventType::kShown:
+        subscription_ = tracker->AddElementShownCallback(id, context, callback);
+        break;
+      case ElementEventType::kActivated:
+        subscription_ =
+            tracker->AddElementActivatedCallback(id, context, callback);
+        break;
+      case ElementEventType::kHidden:
+        subscription_ =
+            tracker->AddElementHiddenCallback(id, context, callback);
+        break;
+    }
+  }
+
+  int event_count() const { return event_count_; }
+  View* last_view() { return last_view_; }
+
+ private:
+  void OnEvent(ui::ElementTrackerElement* element) {
+    EXPECT_EQ(id_.raw_value(), element->identifier().raw_value());
+    last_view_ = element->AsA<ElementTrackerElementViews>()->view();
+    ++event_count_;
+  }
+
+  const ui::ElementIdentifier id_;
+  ui::ElementTracker::Subscription subscription_;
+  int event_count_ = 0;
+  View* last_view_ = nullptr;
+};
+
+View* ElementToView(ui::ElementTrackerElement* element) {
+  auto* const view_element = element->AsA<ElementTrackerElementViews>();
+  return view_element ? view_element->view() : nullptr;
+}
+
+ElementTrackerViews::ViewList ElementsToViews(
+    ui::ElementTracker::ElementList elements) {
+  ElementTrackerViews::ViewList result;
+  std::transform(elements.begin(), elements.end(), std::back_inserter(result),
+                 [](ui::ElementTrackerElement* element) {
+                   return element->AsA<ElementTrackerElementViews>()->view();
+                 });
+  return result;
+}
+
+}  // namespace
+
+class ElementTrackerViewsTest : public ViewsTestBase {
+ public:
+  ElementTrackerViewsTest() = default;
+  ~ElementTrackerViewsTest() override = default;
+
+  void SetUp() override {
+    ViewsTestBase::SetUp();
+    widget_ = CreateWidget();
+    widget_->Show();
+  }
+
+  void TearDown() override {
+    widget_.reset();
+    ViewsTestBase::TearDown();
+  }
+
+  ui::ElementContext context() const {
+    return ui::ElementContext(widget_.get());
+  }
+
+  std::unique_ptr<Widget> CreateWidget() {
+    auto widget = std::make_unique<Widget>();
+    Widget::InitParams params =
+        CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+    params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+    params.bounds = gfx::Rect(0, 0, 650, 650);
+    widget->Init(std::move(params));
+    return widget;
+  }
+
+ protected:
+  std::unique_ptr<Widget> widget_;
+};
+
+TEST_F(ElementTrackerViewsTest, ViewShownByAddingToWidgetSendsNotification) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kShown);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(0, watcher.event_count());
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest,
+       ViewHiddenByRemovingFromWidgetSendsNotification) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kHidden);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(0, watcher.event_count());
+  auto* const view = widget_->SetContentsView(std::make_unique<View>());
+  auto* const button = view->AddChildView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+  view->RemoveChildViewT(button);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, ViewShownAfterAddingToWidgetSendsNotification) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kShown);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetVisible(false);
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+  button->SetVisible(true);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest,
+       ViewHiddenAfterAddingToWidgetSendsNotification) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kHidden);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+  button->SetVisible(false);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, SettingIDOnVisibleViewSendsNotification) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kShown);
+  auto button_ptr = std::make_unique<LabelButton>();
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, ClearingIDOnVisibleViewSendsNotification) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kHidden);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+  button->ClearProperty(kElementIdentifierKey);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, ChangingIDOnVisibleViewSendsNotification) {
+  ElementEventWatcher shown(kTestElementID, context(),
+                            ElementEventType::kShown);
+  ElementEventWatcher hidden(kTestElementID, context(),
+                             ElementEventType::kHidden);
+  ElementEventWatcher shown2(kTestElementID2, context(),
+                             ElementEventType::kShown);
+  ElementEventWatcher hidden2(kTestElementID2, context(),
+                              ElementEventType::kHidden);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(1, shown.event_count());
+  EXPECT_EQ(0, hidden.event_count());
+  EXPECT_EQ(0, shown2.event_count());
+  EXPECT_EQ(0, hidden2.event_count());
+  button->SetProperty(kElementIdentifierKey, kTestElementID2);
+  EXPECT_EQ(1, shown.event_count());
+  EXPECT_EQ(1, hidden.event_count());
+  EXPECT_EQ(1, shown2.event_count());
+  EXPECT_EQ(0, hidden2.event_count());
+  button->SetVisible(false);
+  EXPECT_EQ(1, shown.event_count());
+  EXPECT_EQ(1, hidden.event_count());
+  EXPECT_EQ(1, shown2.event_count());
+  EXPECT_EQ(1, hidden2.event_count());
+}
+
+TEST_F(ElementTrackerViewsTest, ButtonPressedSendsActivatedSignal) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kActivated);
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(0, watcher.event_count());
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+
+  // Test mouse click.
+  constexpr gfx::Point kPressPoint(10, 10);
+  button->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  button->OnMouseReleased(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+
+  // Test accessible keypress.
+  button->OnKeyPressed(ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE,
+                                    ui::EF_NONE, ui::EventTimeForNow()));
+  button->OnKeyReleased(ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SPACE,
+                                     ui::EF_NONE, ui::EventTimeForNow()));
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, MenuButtonPressedSendsActivatedSignal) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kActivated);
+  size_t pressed_count = 0;
+  auto button_ptr = std::make_unique<MenuButton>(
+      base::BindLambdaForTesting([&](const ui::Event&) { ++pressed_count; }));
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(0, watcher.event_count());
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(0, watcher.event_count());
+
+  // Test mouse click.
+  constexpr gfx::Point kPressPoint(10, 10);
+  button->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  EXPECT_EQ(1U, pressed_count);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+
+  // Test accessible keypress.
+  button->OnKeyPressed(ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE,
+                                    ui::EF_NONE, ui::EventTimeForNow()));
+  button->OnKeyReleased(ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SPACE,
+                                     ui::EF_NONE, ui::EventTimeForNow()));
+  EXPECT_EQ(2U, pressed_count);
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, HandlesCreateWithTheSameIDMultipleTimes) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kShown);
+
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+  widget_->GetRootView()->RemoveChildViewT(button);
+
+  button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button2 = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(button2, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, HandlesReshowingTheSameView) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kShown);
+
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* const button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+
+  button->SetVisible(false);
+  EXPECT_EQ(1, watcher.event_count());
+
+  button->SetVisible(true);
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+}
+
+TEST_F(ElementTrackerViewsTest, CanLookupViewByIdentifier) {
+  // Should initially be null.
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // Because the button is not attached to a widget, it will not be returned for
+  // the current context (which is associated with a widget).
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  // Adding the view to a widget will cause it to be returned in the current
+  // context.
+  auto* button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(
+      button,
+      ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+          kTestElementID, context())));
+
+  // Once the view is destroyed, however, the result should be null again.
+  widget_->GetRootView()->RemoveChildViewT(button);
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  // Create a second view with the same ID and verify that the new pointer is
+  // returned.
+  button = widget_->SetContentsView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(
+      button,
+      ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+          kTestElementID, context())));
+
+  // When the view is deleted, the result once again becomes null.
+  widget_->GetRootView()->RemoveChildViewT(button);
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, CanLookUpElementByIdentifier) {
+  // Should initially be null.
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // Because the button is not attached to a widget, it will not be returned for
+  // the current context (which is associated with a widget).
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  // Adding the view to a widget will cause it to be returned in the current
+  // context.
+  auto* button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_EQ(button, ui::ElementTracker::GetElementTracker()
+                        ->GetUniqueElement(kTestElementID, context())
+                        ->AsA<ElementTrackerElementViews>()
+                        ->view());
+
+  // Hiding the view will make the view not findable through the base element
+  // tracker.
+  button->SetVisible(false);
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  // Showing the view will bring it back.
+  button->SetVisible(true);
+  EXPECT_EQ(button, ui::ElementTracker::GetElementTracker()
+                        ->GetUniqueElement(kTestElementID, context())
+                        ->AsA<ElementTrackerElementViews>()
+                        ->view());
+
+  // Once the view is destroyed, however, the result should be null again.
+  widget_->GetRootView()->RemoveChildViewT(button);
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+
+  // Create a second view with the same ID and verify that the new pointer is
+  // returned.
+  button = widget_->SetContentsView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(button, ui::ElementTracker::GetElementTracker()
+                        ->GetUniqueElement(kTestElementID, context())
+                        ->AsA<ElementTrackerElementViews>()
+                        ->view());
+
+  // When the view is deleted, the result once again becomes null.
+  widget_->GetRootView()->RemoveChildViewT(button);
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, CanGetFirstViewByIdentifier) {
+  // Should initially be null.
+  EXPECT_EQ(nullptr,
+            ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kTestElementID, context()));
+
+  // Add two buttons with the same identifier.
+  auto* contents = widget_->SetContentsView(std::make_unique<View>());
+  auto* button = contents->AddChildView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* button2 = contents->AddChildView(std::make_unique<LabelButton>());
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // The first button should be returned.
+  EXPECT_EQ(
+      button,
+      ElementToView(
+          ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+              kTestElementID, context())));
+
+  // Remove the first button. The second should now be returned.
+  contents->RemoveChildViewT(button);
+  EXPECT_EQ(
+      button2,
+      ElementToView(
+          ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+              kTestElementID, context())));
+
+  // Remove the second button. There will be no matching views.
+  contents->RemoveChildViewT(button2);
+  EXPECT_EQ(nullptr,
+            ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, CanGetFirstElementByIdentifier) {
+  // Should initially be null.
+  EXPECT_EQ(nullptr,
+            ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kTestElementID, context()));
+
+  // Add two buttons with the same identifier.
+  auto* contents = widget_->SetContentsView(std::make_unique<View>());
+  auto* button = contents->AddChildView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* button2 = contents->AddChildView(std::make_unique<LabelButton>());
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // The first button should be returned.
+  EXPECT_EQ(button, ui::ElementTracker::GetElementTracker()
+                        ->GetFirstMatchingElement(kTestElementID, context())
+                        ->AsA<ElementTrackerElementViews>()
+                        ->view());
+
+  // Set the buttons' visibility; this should change whether the element tracker
+  // sees them.
+  button->SetVisible(false);
+  EXPECT_EQ(button2, ui::ElementTracker::GetElementTracker()
+                         ->GetFirstMatchingElement(kTestElementID, context())
+                         ->AsA<ElementTrackerElementViews>()
+                         ->view());
+  button2->SetVisible(false);
+  EXPECT_EQ(nullptr,
+            ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kTestElementID, context()));
+
+  // The second button is now the first to become visible in the base tracker.
+  button2->SetVisible(true);
+  button->SetVisible(true);
+  EXPECT_EQ(button2, ui::ElementTracker::GetElementTracker()
+                         ->GetFirstMatchingElement(kTestElementID, context())
+                         ->AsA<ElementTrackerElementViews>()
+                         ->view());
+
+  // Remove the second button. The first should now be returned.
+  contents->RemoveChildViewT(button2);
+  EXPECT_EQ(button, ui::ElementTracker::GetElementTracker()
+                        ->GetFirstMatchingElement(kTestElementID, context())
+                        ->AsA<ElementTrackerElementViews>()
+                        ->view());
+
+  // Remove the first button. There will be no matching views.
+  contents->RemoveChildViewT(button);
+  EXPECT_EQ(nullptr,
+            ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
+                kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, CanGetAllViewsByIdentifier) {
+  // Should initially be empty.
+  ElementTrackerViews::ViewList expected;
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Add two buttons with the same identifier.
+  auto* contents = widget_->SetContentsView(std::make_unique<View>());
+  auto* button = contents->AddChildView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* button2 = contents->AddChildView(std::make_unique<LabelButton>());
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // All buttons should be returned.
+  expected = ElementTrackerViews::ViewList{button, button2};
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Remove the first button. The second should now be returned.
+  contents->RemoveChildViewT(button);
+  expected = ElementTrackerViews::ViewList{button2};
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Remove the second button. There will be no matching views.
+  contents->RemoveChildViewT(button2);
+  expected.clear();
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+}
+
+TEST_F(ElementTrackerViewsTest, CanGetAllElementsByIdentifier) {
+  // Should initially be empty.
+  ElementTrackerViews::ViewList expected;
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Add two buttons with the same identifier.
+  auto* contents = widget_->SetContentsView(std::make_unique<View>());
+  auto* button = contents->AddChildView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* button2 = contents->AddChildView(std::make_unique<LabelButton>());
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // Both buttons should be returned.
+  expected = ElementTrackerViews::ViewList{button, button2};
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Set the buttons' visibility; this should change whether the element tracker
+  // sees them.
+  button->SetVisible(false);
+  expected = ElementTrackerViews::ViewList{button2};
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+  button2->SetVisible(false);
+  expected.clear();
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // The second button is now the first to become visible in the base tracker.
+  button2->SetVisible(true);
+  button->SetVisible(true);
+  expected = ElementTrackerViews::ViewList{button2, button};
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Remove the second button. Only the first should now be returned.
+  contents->RemoveChildViewT(button2);
+  expected = ElementTrackerViews::ViewList{button};
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+
+  // Remove the first button. There will be no matching views.
+  contents->RemoveChildViewT(button);
+  expected.clear();
+  EXPECT_EQ(expected,
+            ElementsToViews(
+                ui::ElementTracker::GetElementTracker()->GetAllMatchingElements(
+                    kTestElementID, context())));
+}
+
+TEST_F(ElementTrackerViewsTest, CanGetVisibilityByIdentifier) {
+  // Should initially be false.
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+
+  // Because the button is not attached to a widget, it will not be counted as
+  // visible.
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+
+  // Adding the view to a widget will cause it to be counted as visible.
+  auto* button = widget_->SetContentsView(std::move(button_ptr));
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+
+  // Once the view is destroyed, however, the result should be false again.
+  widget_->GetRootView()->RemoveChildViewT(button);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+
+  // Create a second view with the same ID but start it as not visible.
+  button = widget_->SetContentsView(std::make_unique<LabelButton>());
+  button->SetVisible(false);
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+
+  // Now set the visibility to true.
+  button->SetVisible(true);
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+
+  // Set visibility to false again.
+  button->SetVisible(false);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+// The following tests ensure conformity with the different platforms' Views
+// implementation to ensure that Views are reported as visible to the user at
+// the correct times, including during Widget close/delete.
+
+TEST_F(ElementTrackerViewsTest, ParentNotVisibleWhenAddedToWidget) {
+  View* const contents = widget_->SetContentsView(std::make_unique<View>());
+  contents->SetVisible(false);
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->SetVisible(true);
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, WidgetNotVisibleWhenAddedToWidget) {
+  View* const contents = widget_->SetContentsView(std::make_unique<View>());
+  widget_->Hide();
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  widget_->Show();
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, ParentHidden) {
+  View* const contents = widget_->SetContentsView(std::make_unique<View>());
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->SetVisible(false);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, WidgetHidden) {
+  View* const contents = widget_->SetContentsView(std::make_unique<View>());
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  widget_->Hide();
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, WidgetClosed) {
+  View* const contents = widget_->SetContentsView(std::make_unique<View>());
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  widget_->Close();
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, WidgetDestroyed) {
+  View* const contents = widget_->SetContentsView(std::make_unique<View>());
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+  widget_.reset();
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context()));
+}
+
+TEST_F(ElementTrackerViewsTest, WidgetShownAfterAdd) {
+  auto widget = CreateWidget();
+  View* const contents = widget->SetContentsView(std::make_unique<View>());
+  const ui::ElementContext context =
+      ElementTrackerViews::GetContextForView(contents);
+  auto child_ptr = std::make_unique<View>();
+  child_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context));
+  contents->AddChildView(std::move(child_ptr));
+  EXPECT_FALSE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context));
+  widget->Show();
+  EXPECT_TRUE(ui::ElementTracker::GetElementTracker()->IsElementVisible(
+      kTestElementID, context));
+}
+
+// Verifies that Views on different Widgets are differentiated by the system.
+class ElementTrackerTwoWidgetTest : public ElementTrackerViewsTest {
+ public:
+  ElementTrackerTwoWidgetTest() = default;
+  ~ElementTrackerTwoWidgetTest() override = default;
+
+  void SetUp() override {
+    ElementTrackerViewsTest::SetUp();
+
+    widget2_ = CreateWidget();
+    widget2_->Show();
+  }
+
+  void TearDown() override {
+    widget2_.reset();
+    ElementTrackerViewsTest::TearDown();
+  }
+
+  ui::ElementContext context2() const {
+    return ui::ElementContext(widget2_.get());
+  }
+
+ protected:
+  std::unique_ptr<Widget> widget2_;
+};
+
+TEST_F(ElementTrackerTwoWidgetTest, ViewMovedToDifferentWidgetGeneratesEvents) {
+  ElementEventWatcher shown(kTestElementID, context(),
+                            ElementEventType::kShown);
+  ElementEventWatcher hidden(kTestElementID, context(),
+                             ElementEventType::kHidden);
+  ElementEventWatcher shown2(kTestElementID, context2(),
+                             ElementEventType::kShown);
+  ElementEventWatcher hidden2(kTestElementID, context2(),
+                              ElementEventType::kHidden);
+  auto* const view = widget_->SetContentsView(std::make_unique<View>());
+  auto* const view2 = widget2_->SetContentsView(std::make_unique<View>());
+  auto button_ptr = std::make_unique<LabelButton>();
+  button_ptr->SetProperty(kElementIdentifierKey, kTestElementID);
+  // Add to first widget.
+  auto* const button = view->AddChildView(std::move(button_ptr));
+  EXPECT_EQ(1, shown.event_count());
+  EXPECT_EQ(0, hidden.event_count());
+  EXPECT_EQ(0, shown2.event_count());
+  EXPECT_EQ(0, hidden2.event_count());
+  // Move to second widget.
+  view2->AddChildView(button);
+  EXPECT_EQ(1, shown.event_count());
+  EXPECT_EQ(1, hidden.event_count());
+  EXPECT_EQ(1, shown2.event_count());
+  EXPECT_EQ(0, hidden2.event_count());
+  // Destroy the second widget.
+  widget2_.reset();
+  EXPECT_EQ(1, shown.event_count());
+  EXPECT_EQ(1, hidden.event_count());
+  EXPECT_EQ(1, shown2.event_count());
+  EXPECT_EQ(1, hidden2.event_count());
+}
+
+TEST_F(ElementTrackerTwoWidgetTest, CanLookUpViewsOnMultipleWidgets) {
+  auto* button = widget_->SetContentsView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  auto* button2 = widget2_->SetContentsView(std::make_unique<LabelButton>());
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(
+      button,
+      ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+          kTestElementID, context())));
+  EXPECT_EQ(
+      button2,
+      ElementToView(ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+          kTestElementID, context2())));
+  widget_->GetRootView()->RemoveChildViewT(button);
+  widget2_->GetRootView()->RemoveChildViewT(button2);
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context()));
+  EXPECT_EQ(nullptr, ui::ElementTracker::GetElementTracker()->GetUniqueElement(
+                         kTestElementID, context2()));
+}
+
+TEST_F(ElementTrackerTwoWidgetTest,
+       MakingViewsVisibleSendsNotificationsToCorrectListeners) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kShown);
+  ElementEventWatcher watcher2(kTestElementID, context2(),
+                               ElementEventType::kShown);
+  auto* const button =
+      widget_->SetContentsView(std::make_unique<LabelButton>());
+  auto* const button2 =
+      widget2_->SetContentsView(std::make_unique<LabelButton>());
+  EXPECT_EQ(0, watcher.event_count());
+  EXPECT_EQ(0, watcher2.event_count());
+
+  // Each listener should be notified when the appropriate button is shown.
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+  EXPECT_EQ(0, watcher2.event_count());
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+  EXPECT_EQ(1, watcher2.event_count());
+  EXPECT_EQ(button2, watcher2.last_view());
+
+  // Each listener should be notified when the appropriate button is shown.
+  button->SetVisible(false);
+  button->SetVisible(true);
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(1, watcher2.event_count());
+
+  // Hide and show several times to verify events are still set.
+  button->SetVisible(false);
+  button2->SetVisible(false);
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(1, watcher2.event_count());
+  button2->SetVisible(true);
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(2, watcher2.event_count());
+  button->SetVisible(true);
+  EXPECT_EQ(3, watcher.event_count());
+  EXPECT_EQ(2, watcher2.event_count());
+}
+
+TEST_F(ElementTrackerTwoWidgetTest,
+       ButtonPressedSendsNotificationsToCorrectListeners) {
+  ElementEventWatcher watcher(kTestElementID, context(),
+                              ElementEventType::kActivated);
+  ElementEventWatcher watcher2(kTestElementID, context2(),
+                               ElementEventType::kActivated);
+  auto* const button =
+      widget_->SetContentsView(std::make_unique<LabelButton>());
+  auto* const button2 =
+      widget2_->SetContentsView(std::make_unique<LabelButton>());
+  button->SetProperty(kElementIdentifierKey, kTestElementID);
+  button2->SetProperty(kElementIdentifierKey, kTestElementID);
+  EXPECT_EQ(0, watcher.event_count());
+  EXPECT_EQ(0, watcher2.event_count());
+
+  // Test mouse click.
+  constexpr gfx::Point kPressPoint(10, 10);
+  button->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  button->OnMouseReleased(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+  EXPECT_EQ(0, watcher2.event_count());
+
+  // Click other button.
+  button2->OnMousePressed(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  button2->OnMouseReleased(ui::MouseEvent(
+      ui::ET_MOUSE_PRESSED, kPressPoint, kPressPoint, ui::EventTimeForNow(),
+      ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(button, watcher.last_view());
+  EXPECT_EQ(1, watcher2.event_count());
+  EXPECT_EQ(button2, watcher2.last_view());
+
+  // Test accessible keypress.
+  button2->OnKeyPressed(ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE,
+                                     ui::EF_NONE, ui::EventTimeForNow()));
+  button2->OnKeyReleased(ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SPACE,
+                                      ui::EF_NONE, ui::EventTimeForNow()));
+  EXPECT_EQ(1, watcher.event_count());
+  EXPECT_EQ(2, watcher2.event_count());
+  button->OnKeyPressed(ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE,
+                                    ui::EF_NONE, ui::EventTimeForNow()));
+  button->OnKeyReleased(ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SPACE,
+                                     ui::EF_NONE, ui::EventTimeForNow()));
+  EXPECT_EQ(2, watcher.event_count());
+  EXPECT_EQ(2, watcher2.event_count());
+}
+
+}  // namespace views
diff --git a/ui/views/view.cc b/ui/views/view.cc
index aec3d693..bb11066 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -57,6 +57,7 @@
 #include "ui/views/context_menu_controller.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/drag_controller.h"
+#include "ui/views/interaction/element_tracker_views.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/view_observer.h"
@@ -2072,6 +2073,9 @@
 void View::OnLayerTransformed(const gfx::Transform& old_transform,
                               ui::PropertyChangeReason reason) {
   NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, false);
+
+  for (ViewObserver& observer : observers_)
+    observer.OnViewLayerTransformed(this);
 }
 
 void View::OnDeviceScaleFactorChanged(float old_device_scale_factor,
@@ -2270,6 +2274,23 @@
     SchedulePaint();
 }
 
+void View::AfterPropertyChange(const void* key, int64_t old_value) {
+  if (key == kElementIdentifierKey) {
+    const ui::ElementIdentifier old_element_id =
+        ui::ElementIdentifier::FromRawValue(old_value);
+    if (old_element_id) {
+      views::ElementTrackerViews::GetInstance()->UnregisterView(old_element_id,
+                                                                this);
+    }
+    const ui::ElementIdentifier new_element_id =
+        GetProperty(kElementIdentifierKey);
+    if (new_element_id) {
+      views::ElementTrackerViews::GetInstance()->RegisterView(new_element_id,
+                                                              this);
+    }
+  }
+}
+
 void View::OnPropertyChanged(ui::metadata::PropertyKey property,
                              PropertyEffects property_effects) {
   if (property_effects != kPropertyEffectsNone)
diff --git a/ui/views/view.h b/ui/views/view.h
index b2ab04ab..34594ed 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -1546,7 +1546,7 @@
   // Overridden from ui::LayerDelegate:
   void OnPaintLayer(const ui::PaintContext& context) override;
   void OnLayerTransformed(const gfx::Transform& old_transform,
-                          ui::PropertyChangeReason reason) override;
+                          ui::PropertyChangeReason reason) final;
   void OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                   float new_device_scale_factor) override;
 
@@ -1616,6 +1616,14 @@
   static int GetHorizontalDragThreshold();
   static int GetVerticalDragThreshold();
 
+  // PropertyHandler -----------------------------------------------------------
+
+  // Note: you MUST call this base method from derived classes that override it
+  // or else your class  will not properly register for ElementTrackerViews and
+  // won't be available for interactive tests or in-product help/tutorials which
+  // use that system.
+  void AfterPropertyChange(const void* key, int64_t old_value) override;
+
   // Property Support ----------------------------------------------------------
 
   void OnPropertyChanged(ui::metadata::PropertyKey property,
diff --git a/ui/views/view_class_properties.cc b/ui/views/view_class_properties.cc
index 1335c4d3..e8163ea5 100644
--- a/ui/views/view_class_properties.cc
+++ b/ui/views/view_class_properties.cc
@@ -25,6 +25,7 @@
                                        views::HighlightPathGenerator*)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::LayoutAlignment*)
+DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, ui::ElementIdentifier)
 
 namespace views {
 
@@ -42,5 +43,8 @@
                                    kCrossAxisAlignmentKey,
                                    nullptr)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kViewIgnoredByLayoutKey, false)
+DEFINE_UI_CLASS_PROPERTY_KEY(ui::ElementIdentifier,
+                             kElementIdentifierKey,
+                             ui::ElementIdentifier())
 
 }  // namespace views
diff --git a/ui/views/view_class_properties.h b/ui/views/view_class_properties.h
index 260742d4..7cb3698d 100644
--- a/ui/views/view_class_properties.h
+++ b/ui/views/view_class_properties.h
@@ -6,6 +6,7 @@
 #define UI_VIEWS_VIEW_CLASS_PROPERTIES_H_
 
 #include "ui/base/class_property.h"
+#include "ui/base/interaction/element_identifier.h"
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/views_export.h"
 
@@ -64,6 +65,10 @@
 VIEWS_EXPORT extern const ui::ClassProperty<bool>* const
     kViewIgnoredByLayoutKey;
 
+// Tag for the view associated with ui::ElementTracker.
+VIEWS_EXPORT extern const ui::ClassProperty<ui::ElementIdentifier>* const
+    kElementIdentifierKey;
+
 }  // namespace views
 
 // Declaring the template specialization here to make sure that the
@@ -77,6 +82,7 @@
                                         views::HighlightPathGenerator*)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::FlexSpecification*)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::LayoutAlignment*)
+DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, ui::ElementIdentifier)
 DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, bool)
 
 #endif  // UI_VIEWS_VIEW_CLASS_PROPERTIES_H_
diff --git a/ui/views/view_observer.h b/ui/views/view_observer.h
index f8ea6c4..8cd9914 100644
--- a/ui/views/view_observer.h
+++ b/ui/views/view_observer.h
@@ -37,6 +37,12 @@
   // Called when the bounds of |observed_view|'s layer change.
   virtual void OnLayerTargetBoundsChanged(View* observed_view) {}
 
+  // Called when the `observed_view`'s layer transform changes.
+  // TODO(crbug.com/1203386): This is temporarily added to support a migration.
+  // Do not use for new call sites, we should instead figure out how to
+  // migrate this method (and possibly others) into callbacks.
+  virtual void OnViewLayerTransformed(View* observed_view) {}
+
   // Called when View::ViewHierarchyChanged() is called.
   virtual void OnViewHierarchyChanged(
       View* observed_view,
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 0386a6b..fdceab5d 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -507,6 +507,14 @@
   return is_top_level() ? this : native_widget_->GetTopLevelWidget();
 }
 
+Widget* Widget::GetPrimaryWindowWidget() {
+  return GetTopLevelWidget();
+}
+
+const Widget* Widget::GetPrimaryWindowWidget() const {
+  return const_cast<Widget*>(this)->GetPrimaryWindowWidget();
+}
+
 void Widget::SetContentsView(View* view) {
   // Do not SetContentsView() again if it is already set to the same view.
   if (view == GetContentsView())
diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h
index c1ecf559..5be0a91 100644
--- a/ui/views/widget/widget.h
+++ b/ui/views/widget/widget.h
@@ -513,9 +513,21 @@
   // Returns the top level widget in a hierarchy (see is_top_level() for
   // the definition of top level widget.) Will return NULL if called
   // before the widget is attached to the top level widget's hierarchy.
+  //
+  // If you want to get the absolute primary application window, accounting for
+  // e.g. bubble and menu anchoring, use GetPrimaryWindowWidget() instead.
   Widget* GetTopLevelWidget();
   const Widget* GetTopLevelWidget() const;
 
+  // Returns the widget of the primary window this widget is associated with,
+  // such as an application window, accounting for anchoring and other
+  // relationships not accounted for in GetTopLevelWidget().
+  //
+  // Equivalent to GetTopLevelWidget() by default; override in derived classes
+  // that require additional logic.
+  virtual Widget* GetPrimaryWindowWidget();
+  const Widget* GetPrimaryWindowWidget() const;
+
   // Gets/Sets the WidgetDelegate.
   WidgetDelegate* widget_delegate() const { return widget_delegate_; }
 
diff --git a/ui/views/window/frame_caption_button.cc b/ui/views/window/frame_caption_button.cc
index b36dcfa..4487c12 100644
--- a/ui/views/window/frame_caption_button.cc
+++ b/ui/views/window/frame_caption_button.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/animation/slide_animation.h"
@@ -80,6 +81,14 @@
   UpdateInkDropBaseColor();
   views::InkDrop::UseInkDropWithoutAutoHighlight(this,
                                                  /*highlight_on_hover=*/false);
+  SetCreateInkDropRippleCallback(base::BindRepeating(
+      [](FrameCaptionButton* host) -> std::unique_ptr<views::InkDropRipple> {
+        return std::make_unique<views::FloodFillInkDropRipple>(
+            host->size(), host->GetInkdropInsets(host->size()),
+            host->GetInkDropCenterBasedOnLastEvent(),
+            host->GetInkDropBaseColor(), host->GetInkDropVisibleOpacity());
+      },
+      this));
 
   views::HighlightPathGenerator::Install(
       this, std::make_unique<HighlightPathGenerator>(this));
@@ -187,13 +196,6 @@
   return views::PaintInfo::ScaleType::kUniformScaling;
 }
 
-std::unique_ptr<views::InkDropRipple> FrameCaptionButton::CreateInkDropRipple()
-    const {
-  return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), GetInkdropInsets(size()), GetInkDropCenterBasedOnLastEvent(),
-      GetInkDropBaseColor(), GetInkDropVisibleOpacity());
-}
-
 void FrameCaptionButton::SetBackgroundColor(SkColor background_color) {
   if (background_color_ == background_color)
     return;
diff --git a/ui/views/window/frame_caption_button.h b/ui/views/window/frame_caption_button.h
index 1a3f18f..2c9d7b5 100644
--- a/ui/views/window/frame_caption_button.h
+++ b/ui/views/window/frame_caption_button.h
@@ -22,8 +22,6 @@
 
 namespace views {
 
-class InkDropRipple;
-
 // Base class for the window caption buttons (minimize, maximize, restore,
 // close).
 class VIEWS_EXPORT FrameCaptionButton : public views::Button {
@@ -62,7 +60,6 @@
   // views::Button:
   void OnGestureEvent(ui::GestureEvent* event) override;
   views::PaintInfo::ScaleType GetPaintScaleType() const override;
-  std::unique_ptr<InkDropRipple> CreateInkDropRipple() const override;
 
   void SetBackgroundColor(SkColor background_color);
   SkColor GetBackgroundColor() const;