diff --git a/DEPS b/DEPS
index cf77fbb..d00be901 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': '2b8fd2e8e01043bd0e8adf773290ceb774f8bba6',
+  'skia_revision': 'f208f81e1d6884f6b9042f48cde4edc9b2d1926a',
   # 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': 'c703dd554685dada82c43deb8b5566c3649b5310',
+  'v8_revision': 'e09c58db3219b0fb5d409ea7e5d503963ab785e1',
   # 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.
@@ -221,7 +221,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'a766ab14edda8c0d0760364e6e3866a550d15986',
+  'angle_revision': '078a2ba669b6ffea140d3babb12d2265639a647c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -256,7 +256,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '678f5814e36d61e380bd81596b1f427ad09d5a03',
+  'nacl_revision': '2b19997900032d8a4f93597e7b74798d856fe972',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -280,7 +280,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '1b8cf3bcf95e50cafecc633a6f687ed16e3b2193',
+  'catapult_revision': 'bbfed50cf91d86dc24b07f292741f2b20d7d0697',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,7 +288,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '2acbbd12f3085969d5443c0b29a5b299ccc86799',
+  'devtools_frontend_revision': '09cf50393be6504e67dc7dd9c64b1b51418d8700',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -332,7 +332,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '0142a09c2f0c8bb48f2aa13ec375f5e3287ad2e6',
+  'quiche_revision': '9f23586cf08918d9b56b1c2f7468fbabb7354e55',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -553,7 +553,7 @@
   },
 
     'src/ios/third_party/edo/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '73009d72eb528b1108d1540e3cd8d03d0b34280e',
+      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + 'e904b04fc8cbaf6a5d4e20ca5c3cbd1fd8069d64',
       'condition': 'checkout_ios',
   },
 
@@ -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' + '@' + '5874e43f476f4463d92f768a7adc2bb29f92cc24',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f7a90670fcf2f080b669a86f501273bba0178539',
       'condition': 'checkout_ios',
   },
 
@@ -1338,7 +1338,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'a6c4cea7f7ff399ed4f69ec8ca30153ff60264fa',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '3c17caaf951e52670f10349e5a88bed5bc86b271',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1416,7 +1416,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': '3VMKWKF7CpMmSlkl7BVH47knjv3eZcJI9P0dGBoSB_YC'
+              'version': 'PNMyschZLgBf2Dn-ygogmtVo9JfvV8HyeL9WMxQRtF4C'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1549,7 +1549,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'a0b8774ce8cec1dc8f4308810bf05eb8867c62de',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '34974c270a8740dcd63069fffa59741ab0416f1a',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '04615127de529c1b7aad103e61abce816ed0fe6f',
 
   'src/third_party/webrtc':
     Var('webrtc_git') + '/src.git' + '@' + '73604615328f92daaee6dc7f78efa28e1dda2b60',
@@ -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@fd67a614e9904cd36fd2fe7e7b143e0404f438f8',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@82b849c5e44e578cd6f1003596a85db5197921f6',
     'condition': 'checkout_src_internal',
   },
 
@@ -1632,7 +1632,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'Andzyub-HHPGRPXyhIl0D4qKwBWkewnGH_R_JZYaI_sC',
+        'version': 'zKrK4bjL-ONZJnFFRgym4tIWXrNBZUndawC4-L5CNfkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -1643,7 +1643,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'BfEh4fh0X7pglGonHNEFqt0auxRO02-ZOVCIeu2vcn8C',
+        'version': '4Gbb_5Tl9RrfxhSad3jFniGrak1GWfL9WDKJ7OCT6RkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/WATCHLISTS b/WATCHLISTS
index 3d3ce2f..4ad5e8a5 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -110,7 +110,7 @@
     'app_service': {
       'filepath': 'chrome/browser/apps/app_service/'\
                   '|chrome/browser/ui/app_list/app_service/'\
-                  '|chrome/browser/ui/ash/launcher/app_service/'\
+                  '|chrome/browser/ui/ash/shelf/app_service/'\
                   '|chrome/browser/ui/views/apps/app_dialog/' \
                   '|components/services/app_service/',
     },
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index d8765556..afad320 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -677,12 +677,6 @@
     "marker/marker_controller.h",
     "media/media_controller_impl.cc",
     "media/media_controller_impl.h",
-    "media/media_notification_constants.cc",
-    "media/media_notification_constants.h",
-    "media/media_notification_container_impl.cc",
-    "media/media_notification_container_impl.h",
-    "media/media_notification_controller_impl.cc",
-    "media/media_notification_controller_impl.h",
     "metrics/demo_session_metrics_recorder.cc",
     "metrics/demo_session_metrics_recorder.h",
     "metrics/desktop_task_switch_metric_recorder.cc",
@@ -2162,7 +2156,6 @@
     "login/ui/views_utils_unittest.cc",
     "marker/marker_controller_unittest.cc",
     "media/media_controller_unittest.cc",
-    "media/media_notification_controller_impl_unittest.cc",
     "metrics/demo_session_metrics_recorder_unittest.cc",
     "metrics/desktop_task_switch_metric_recorder_unittest.cc",
     "metrics/histogram_macros_unittest.cc",
diff --git a/ash/app_list/README.md b/ash/app_list/README.md
index 40b3b8c..d6d3dbc 100644
--- a/ash/app_list/README.md
+++ b/ash/app_list/README.md
@@ -38,9 +38,6 @@
 dependencies. This includes sync support and communication with the App Service
 (which provides the list of installed apps).
 
-Watch out for `//chrome/browser/ui/ash/launcher`. It contains shelf code, not
-app list. The shelf was originally called the launcher (circa 2012).
-
 ## Data model
 
 ### Apps
@@ -205,3 +202,5 @@
 ## Historical notes
 
 The old demo binary in //ash/app_list/demo was removed in 2021.
+
+The shelf was originally called the launcher (circa 2012).
diff --git a/ash/fast_ink/fast_ink_host.cc b/ash/fast_ink/fast_ink_host.cc
index f16d438..f95b329 100644
--- a/ash/fast_ink/fast_ink_host.cc
+++ b/ash/fast_ink/fast_ink_host.cc
@@ -255,7 +255,7 @@
                                   SK_B32_SHIFT ? gfx::BufferFormat::RGBA_8888
                                                : gfx::BufferFormat::BGRA_8888,
                                   gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
-                                  gpu::kNullSurfaceHandle);
+                                  gpu::kNullSurfaceHandle, nullptr);
   LOG_IF(ERROR, !gpu_memory_buffer_) << "Failed to create GPU memory buffer";
 
   if (ash::switches::ShouldClearFastInkBuffer()) {
diff --git a/ash/fast_ink/view_tree_host_root_view.cc b/ash/fast_ink/view_tree_host_root_view.cc
index b4955adc..d9b21b7e 100644
--- a/ash/fast_ink/view_tree_host_root_view.cc
+++ b/ash/fast_ink/view_tree_host_root_view.cc
@@ -280,7 +280,8 @@
       buffer_size_,
       SK_B32_SHIFT ? gfx::BufferFormat::RGBA_8888
                    : gfx::BufferFormat::BGRA_8888,
-      gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, gpu::kNullSurfaceHandle);
+      gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, gpu::kNullSurfaceHandle,
+      nullptr);
   if (!resource->gpu_memory_buffer) {
     LOG(ERROR) << "Failed to create GPU memory buffer";
     return nullptr;
diff --git a/ash/media/media_notification_constants.cc b/ash/media/media_notification_constants.cc
deleted file mode 100644
index a28c11c2..0000000
--- a/ash/media/media_notification_constants.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/media/media_notification_constants.h"
-
-namespace ash {
-
-const char kMediaSessionNotificationCustomViewType[] = "media-session";
-
-const char kMediaSessionNotifierId[] = "media-session";
-
-}  // namespace ash
diff --git a/ash/media/media_notification_constants.h b/ash/media/media_notification_constants.h
deleted file mode 100644
index dbc4753..0000000
--- a/ash/media/media_notification_constants.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_MEDIA_MEDIA_NOTIFICATION_CONSTANTS_H_
-#define ASH_MEDIA_MEDIA_NOTIFICATION_CONSTANTS_H_
-
-#include "ash/ash_export.h"
-
-namespace ash {
-
-// The custom view type that should be set on media session notifications.
-ASH_EXPORT extern const char kMediaSessionNotificationCustomViewType[];
-
-// The notifier ID associated with the media session service.
-ASH_EXPORT extern const char kMediaSessionNotifierId[];
-
-}  // namespace ash
-
-#endif  // ASH_MEDIA_MEDIA_NOTIFICATION_CONSTANTS_H_
diff --git a/ash/media/media_notification_container_impl.cc b/ash/media/media_notification_container_impl.cc
deleted file mode 100644
index bd83ef1c..0000000
--- a/ash/media/media_notification_container_impl.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/media/media_notification_container_impl.h"
-
-#include "components/media_message_center/media_notification_view_impl.h"
-#include "components/media_message_center/media_session_notification_item.h"
-#include "ui/compositor/layer.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/public/cpp/message_center_constants.h"
-#include "ui/message_center/views/notification_control_buttons_view.h"
-#include "ui/views/background.h"
-#include "ui/views/layout/fill_layout.h"
-#include "ui/views/view_class_properties.h"
-
-namespace ash {
-
-namespace {
-
-// Width/height of the dismiss button.
-constexpr int kDismissButtonIconSideLength = 12;
-
-// Width/height of the colored-background container of the dismiss button.
-constexpr int kControlButtonsContainerSideLength =
-    2 * kDismissButtonIconSideLength;
-
-}  // anonymous namespace
-
-MediaNotificationContainerImpl::MediaNotificationContainerImpl(
-    const message_center::Notification& notification,
-    base::WeakPtr<media_message_center::MediaSessionNotificationItem> item)
-    : message_center::MessageView(notification) {
-  SetLayoutManager(std::make_unique<views::FillLayout>());
-
-  auto control_buttons_container = std::make_unique<views::View>();
-
-  // We paint to a layer here so that we can modify opacity of that layer to
-  // match the opacity of the |control_buttons_view_| layer.
-  control_buttons_container->SetPaintToLayer();
-  control_buttons_container->layer()->SetFillsBoundsOpaquely(false);
-
-  // Vertically center the dismiss icon within the container.
-  constexpr int top_margin =
-      (kControlButtonsContainerSideLength - kDismissButtonIconSideLength) / 2;
-  control_buttons_container->SetProperty(views::kMarginsKey,
-                                         gfx::Insets(top_margin, 0, 0, 0));
-
-  control_buttons_container->SetPreferredSize(gfx::Size(
-      kControlButtonsContainerSideLength, kControlButtonsContainerSideLength));
-  control_buttons_container->SetLayoutManager(
-      std::make_unique<views::FillLayout>());
-  control_buttons_container_ = control_buttons_container.get();
-
-  auto control_buttons_view =
-      std::make_unique<message_center::NotificationControlButtonsView>(this);
-  control_buttons_view->SetBackground(
-      views::CreateSolidBackground(SK_ColorTRANSPARENT));
-  control_buttons_view_ =
-      control_buttons_container_->AddChildView(std::move(control_buttons_view));
-
-  auto view = std::make_unique<media_message_center::MediaNotificationViewImpl>(
-      this, std::move(item), std::move(control_buttons_container),
-      message_center::MessageCenter::Get()->GetSystemNotificationAppName(),
-      message_center::kNotificationWidth,
-      /*should_show_icon=*/true);
-  view_ = AddChildView(std::move(view));
-
-  SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT));
-}
-
-MediaNotificationContainerImpl::~MediaNotificationContainerImpl() = default;
-
-void MediaNotificationContainerImpl::UpdateWithNotification(
-    const message_center::Notification& notification) {
-  MessageView::UpdateWithNotification(notification);
-
-  UpdateControlButtonsVisibilityWithNotification(notification);
-
-  PreferredSizeChanged();
-  Layout();
-  SchedulePaint();
-}
-
-message_center::NotificationControlButtonsView*
-MediaNotificationContainerImpl::GetControlButtonsView() const {
-  return control_buttons_view_;
-}
-
-void MediaNotificationContainerImpl::SetExpanded(bool expanded) {
-  view_->SetExpanded(expanded);
-}
-
-void MediaNotificationContainerImpl::UpdateCornerRadius(int top_radius,
-                                                        int bottom_radius) {
-  MessageView::SetCornerRadius(top_radius, bottom_radius);
-  view_->UpdateCornerRadius(top_radius, bottom_radius);
-}
-
-void MediaNotificationContainerImpl::UpdateControlButtonsVisibility() {
-  message_center::MessageView::UpdateControlButtonsVisibility();
-
-  // The above call may update the opacity of the control buttons to 0 or 1. We
-  // need to update the container layer opacity to match.
-  control_buttons_container_->layer()->SetOpacity(
-      control_buttons_view_->layer()->opacity());
-}
-
-void MediaNotificationContainerImpl::OnExpanded(bool expanded) {
-  PreferredSizeChanged();
-}
-
-void MediaNotificationContainerImpl::OnColorsChanged(SkColor foreground,
-                                                     SkColor background) {
-  // We need to update the foreground and background colors of the dismiss icon
-  // to ensure proper contrast against the artwork.
-  control_buttons_view_->SetButtonIconColors(foreground);
-  control_buttons_container_->SetBackground(views::CreateRoundedRectBackground(
-      background, kControlButtonsContainerSideLength / 2));
-}
-
-void MediaNotificationContainerImpl::OnMouseEvent(ui::MouseEvent* event) {
-  switch (event->type()) {
-    case ui::ET_MOUSE_ENTERED:
-    case ui::ET_MOUSE_EXITED:
-      UpdateControlButtonsVisibility();
-      break;
-    default:
-      break;
-  }
-
-  View::OnMouseEvent(event);
-}
-
-void MediaNotificationContainerImpl::
-    UpdateControlButtonsVisibilityWithNotification(
-        const message_center::Notification& notification) {
-  // Media notifications do not use the settings and snooze buttons.
-  DCHECK(!notification.should_show_settings_button());
-  DCHECK(!notification.should_show_snooze_button());
-
-  control_buttons_view_->ShowCloseButton(!notification.pinned());
-  UpdateControlButtonsVisibility();
-}
-
-}  // namespace ash
diff --git a/ash/media/media_notification_container_impl.h b/ash/media/media_notification_container_impl.h
deleted file mode 100644
index a6f4e91..0000000
--- a/ash/media/media_notification_container_impl.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_MEDIA_MEDIA_NOTIFICATION_CONTAINER_IMPL_H_
-#define ASH_MEDIA_MEDIA_NOTIFICATION_CONTAINER_IMPL_H_
-
-#include "ash/ash_export.h"
-#include "base/containers/flat_set.h"
-#include "components/media_message_center/media_notification_container.h"
-#include "ui/message_center/views/message_view.h"
-
-namespace media_message_center {
-class MediaSessionNotificationItem;
-class MediaNotificationView;
-}  // namespace media_message_center
-
-namespace ash {
-
-// MediaNotificationContainerImpl will show up as a custom notification. It will
-// show the currently playing media and provide playback controls. There will
-// also be control buttons (e.g. close) in the top right corner that will hide
-// and show if the notification is hovered.
-class ASH_EXPORT MediaNotificationContainerImpl
-    : public message_center::MessageView,
-      public media_message_center::MediaNotificationContainer {
- public:
-  explicit MediaNotificationContainerImpl(
-      const message_center::Notification& notification,
-      base::WeakPtr<media_message_center::MediaSessionNotificationItem> item);
-  ~MediaNotificationContainerImpl() override;
-
-  // message_center::MessageView:
-  void UpdateWithNotification(
-      const message_center::Notification& notification) override;
-  message_center::NotificationControlButtonsView* GetControlButtonsView()
-      const override;
-  void SetExpanded(bool expanded) override;
-  void UpdateCornerRadius(int top_radius, int bottom_radius) override;
-  void UpdateControlButtonsVisibility() override;
-
-  // media_message_center::MediaNotificationContainer:
-  void OnExpanded(bool expanded) override;
-  void OnMediaSessionInfoChanged(
-      const media_session::mojom::MediaSessionInfoPtr& session_info) override {}
-  void OnMediaSessionMetadataChanged(
-      const media_session::MediaMetadata& metadata) override {}
-  void OnVisibleActionsChanged(
-      const base::flat_set<media_session::mojom::MediaSessionAction>& actions)
-      override {}
-  void OnMediaArtworkChanged(const gfx::ImageSkia& image) override {}
-  void OnColorsChanged(SkColor foreground, SkColor background) override;
-  void OnHeaderClicked() override {}
-
-  // views::View:
-  void OnMouseEvent(ui::MouseEvent* event) override;
-
- private:
-  void UpdateControlButtonsVisibilityWithNotification(
-      const message_center::Notification& notification);
-
-  // Contains |control_buttons_view_| and puts a circular colored background
-  // behind it to ensure proper contrast.
-  views::View* control_buttons_container_ = nullptr;
-
-  // View containing close and settings buttons.
-  message_center::NotificationControlButtonsView* control_buttons_view_ =
-      nullptr;
-
-  media_message_center::MediaNotificationView* view_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(MediaNotificationContainerImpl);
-};
-
-}  // namespace ash
-
-#endif  // ASH_MEDIA_MEDIA_NOTIFICATION_CONTAINER_IMPL_H_
diff --git a/ash/media/media_notification_controller_impl.cc b/ash/media/media_notification_controller_impl.cc
deleted file mode 100644
index 010ed0c..0000000
--- a/ash/media/media_notification_controller_impl.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/media/media_notification_controller_impl.h"
-
-#include "ash/media/media_notification_constants.h"
-#include "ash/media/media_notification_container_impl.h"
-#include "ash/public/cpp/notification_utils.h"
-#include "ash/public/cpp/session/session_observer.h"
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
-#include "ash/shell_delegate.h"
-#include "base/bind.h"
-#include "base/callback_helpers.h"
-#include "base/containers/contains.h"
-#include "components/media_message_center/media_notification_util.h"
-#include "ui/message_center/message_center.h"
-#include "ui/message_center/notification_blocker.h"
-#include "ui/message_center/public/cpp/notification.h"
-#include "ui/message_center/public/cpp/notifier_id.h"
-#include "ui/message_center/views/message_view_factory.h"
-
-namespace ash {
-
-namespace {
-
-std::unique_ptr<message_center::MessageView> CreateCustomMediaNotificationView(
-    const message_center::Notification& notification) {
-  DCHECK_EQ(kMediaSessionNotificationCustomViewType,
-            notification.custom_view_type());
-  auto* controller = Shell::Get()->media_notification_controller();
-  if (controller)
-    return controller->CreateMediaNotification(notification);
-  return nullptr;
-}
-
-// MediaNotificationBlocker will block media notifications if the screen is
-// locked.
-class MediaNotificationBlocker : public message_center::NotificationBlocker,
-                                 public SessionObserver {
- public:
-  MediaNotificationBlocker(message_center::MessageCenter* message_center,
-                           SessionControllerImpl* session_controller)
-      : message_center::NotificationBlocker(message_center),
-        locked_(session_controller->IsScreenLocked()),
-        session_controller_(session_controller) {
-    message_center->AddNotificationBlocker(this);
-    session_controller->AddObserver(this);
-
-    NotifyBlockingStateChanged();
-  }
-
-  ~MediaNotificationBlocker() override {
-    message_center()->RemoveNotificationBlocker(this);
-    session_controller_->RemoveObserver(this);
-  }
-
-  void CheckState() override {
-    OnLockStateChanged(session_controller_->IsScreenLocked());
-  }
-
-  bool ShouldShowNotification(
-      const message_center::Notification& notification) const override {
-    if (notification.notifier_id() ==
-        message_center::NotifierId(
-            message_center::NotifierType::SYSTEM_COMPONENT,
-            kMediaSessionNotifierId)) {
-      return !locked_;
-    }
-
-    return true;
-  }
-
-  bool ShouldShowNotificationAsPopup(
-      const message_center::Notification& notification) const override {
-    return ShouldShowNotification(notification);
-  }
-
-  void OnLockStateChanged(bool locked) override {
-    if (locked == locked_)
-      return;
-
-    locked_ = locked;
-    NotifyBlockingStateChanged();
-  }
-
- private:
-  bool locked_;
-
-  SessionControllerImpl* const session_controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(MediaNotificationBlocker);
-};
-
-}  // namespace
-
-MediaNotificationControllerImpl::MediaNotificationControllerImpl()
-    : blocker_(std::make_unique<MediaNotificationBlocker>(
-          message_center::MessageCenter::Get(),
-          Shell::Get()->session_controller())) {
-  if (!message_center::MessageViewFactory::HasCustomNotificationViewFactory(
-          kMediaSessionNotificationCustomViewType)) {
-    message_center::MessageViewFactory::SetCustomNotificationViewFactory(
-        kMediaSessionNotificationCustomViewType,
-        base::BindRepeating(&CreateCustomMediaNotificationView));
-  }
-
-  // May be null in tests.
-  media_session::MediaSessionService* service =
-      Shell::Get()->shell_delegate()->GetMediaSessionService();
-  if (!service)
-    return;
-
-  mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_remote;
-  service->BindAudioFocusManager(
-      audio_focus_remote.BindNewPipeAndPassReceiver());
-  service->BindMediaControllerManager(
-      controller_manager_remote.BindNewPipeAndPassReceiver());
-  audio_focus_remote->AddObserver(
-      audio_focus_observer_receiver_.BindNewPipeAndPassRemote());
-}
-
-MediaNotificationControllerImpl::~MediaNotificationControllerImpl() = default;
-
-void MediaNotificationControllerImpl::OnFocusGained(
-    media_session::mojom::AudioFocusRequestStatePtr session) {
-  const std::string id = session->request_id->ToString();
-
-  // If we have an existing unfrozen item then this is a duplicate call and
-  // we should ignore it.
-  auto it = notifications_.find(id);
-  if (it != notifications_.end() && !it->second.frozen())
-    return;
-
-  mojo::Remote<media_session::mojom::MediaController> controller;
-
-  // |controller_manager_remote| may be null in tests where the Media Session
-  // service is unavailable.
-  if (controller_manager_remote) {
-    controller_manager_remote->CreateMediaControllerForSession(
-        controller.BindNewPipeAndPassReceiver(), *session->request_id);
-  }
-
-  if (it != notifications_.end()) {
-    // If the notification was previously frozen then we should reset the
-    // controller because the mojo pipe would have been reset.
-    it->second.SetController(std::move(controller),
-                             std::move(session->session_info));
-  } else {
-    notifications_.emplace(
-        std::piecewise_construct, std::forward_as_tuple(id),
-        std::forward_as_tuple(
-            this, id, session->source_name.value_or(std::string()),
-            std::move(controller), std::move(session->session_info)));
-  }
-}
-
-void MediaNotificationControllerImpl::OnFocusLost(
-    media_session::mojom::AudioFocusRequestStatePtr session) {
-  auto it = notifications_.find(session->request_id->ToString());
-  if (it == notifications_.end())
-    return;
-
-  // If we lost focus then we should freeze the notification as it may regain
-  // focus after a second or so.
-  it->second.Freeze(base::DoNothing());
-}
-
-void MediaNotificationControllerImpl::ShowNotification(const std::string& id) {
-  // If a notification already exists, do nothing.
-  if (message_center::MessageCenter::Get()->FindVisibleNotificationById(id))
-    return;
-
-  std::unique_ptr<message_center::Notification> notification =
-      CreateSystemNotification(
-          message_center::NotificationType::NOTIFICATION_TYPE_CUSTOM, id,
-          std::u16string(), std::u16string(), std::u16string(), GURL(),
-          message_center::NotifierId(
-              message_center::NotifierType::SYSTEM_COMPONENT,
-              kMediaSessionNotifierId),
-          message_center::RichNotificationData(), nullptr, gfx::VectorIcon(),
-          message_center::SystemNotificationWarningLevel::NORMAL);
-
-  // Set the priority to low to prevent the notification showing as a popup and
-  // keep it at the bottom of the list.
-  notification->set_priority(message_center::LOW_PRIORITY);
-
-  notification->set_custom_view_type(kMediaSessionNotificationCustomViewType);
-
-  message_center::MessageCenter::Get()->AddNotification(
-      std::move(notification));
-
-  media_message_center::RecordConcurrentNotificationCount(
-      message_center::MessageCenter::Get()
-          ->FindNotificationsByAppId(kMediaSessionNotifierId)
-          .size());
-}
-
-void MediaNotificationControllerImpl::HideNotification(const std::string& id) {
-  message_center::MessageCenter::Get()->RemoveNotification(id, false);
-}
-
-void MediaNotificationControllerImpl::RemoveItem(const std::string& id) {
-  notifications_.erase(id);
-}
-
-scoped_refptr<base::SequencedTaskRunner>
-MediaNotificationControllerImpl::GetTaskRunner() const {
-  return task_runner_for_testing_;
-}
-
-std::unique_ptr<MediaNotificationContainerImpl>
-MediaNotificationControllerImpl::CreateMediaNotification(
-    const message_center::Notification& notification) {
-  base::WeakPtr<media_message_center::MediaSessionNotificationItem> item;
-
-  auto it = notifications_.find(notification.id());
-  if (it != notifications_.end())
-    item = it->second.GetWeakPtr();
-
-  return std::make_unique<MediaNotificationContainerImpl>(notification,
-                                                          std::move(item));
-}
-
-bool MediaNotificationControllerImpl::HasItemForTesting(
-    const std::string& id) const {
-  return base::Contains(notifications_, id);
-}
-
-}  // namespace ash
diff --git a/ash/media/media_notification_controller_impl.h b/ash/media/media_notification_controller_impl.h
deleted file mode 100644
index 6170360..0000000
--- a/ash/media/media_notification_controller_impl.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_MEDIA_MEDIA_NOTIFICATION_CONTROLLER_IMPL_H_
-#define ASH_MEDIA_MEDIA_NOTIFICATION_CONTROLLER_IMPL_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include "ash/ash_export.h"
-#include "base/macros.h"
-#include "components/media_message_center/media_notification_controller.h"
-#include "components/media_message_center/media_session_notification_item.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "services/media_session/public/mojom/audio_focus.mojom.h"
-#include "services/media_session/public/mojom/media_controller.mojom.h"
-
-namespace message_center {
-class Notification;
-}  // namespace message_center
-
-namespace ash {
-
-namespace {
-class MediaNotificationBlocker;
-}  // namespace
-
-class MediaNotificationContainerImpl;
-
-// MediaNotificationControllerImpl will show/hide media notifications when media
-// sessions are active. These notifications will show metadata and playback
-// controls.
-class ASH_EXPORT MediaNotificationControllerImpl
-    : public media_session::mojom::AudioFocusObserver,
-      public media_message_center::MediaNotificationController {
- public:
-  MediaNotificationControllerImpl();
-  ~MediaNotificationControllerImpl() override;
-
-  // media_session::mojom::AudioFocusObserver:
-  void OnFocusGained(
-      media_session::mojom::AudioFocusRequestStatePtr session) override;
-  void OnFocusLost(
-      media_session::mojom::AudioFocusRequestStatePtr session) override;
-
-  // media_message_center::MediaNotificationController:
-  void ShowNotification(const std::string& id) override;
-  void HideNotification(const std::string& id) override;
-  void RemoveItem(const std::string& id) override;
-  scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override;
-  void LogMediaSessionActionButtonPressed(
-      const std::string& id,
-      media_session::mojom::MediaSessionAction action) override {}
-
-  std::unique_ptr<MediaNotificationContainerImpl> CreateMediaNotification(
-      const message_center::Notification& notification);
-
-  media_message_center::MediaSessionNotificationItem* GetItem(
-      const std::string& id) {
-    auto it = notifications_.find(id);
-    DCHECK(it != notifications_.end());
-    return &it->second;
-  }
-
-  bool HasItemForTesting(const std::string& id) const;
-  void set_task_runner_for_testing(
-      scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing) {
-    task_runner_for_testing_ = task_runner_for_testing;
-  }
-
- private:
-  mojo::Remote<media_session::mojom::MediaControllerManager>
-      controller_manager_remote;
-
-  mojo::Receiver<media_session::mojom::AudioFocusObserver>
-      audio_focus_observer_receiver_{this};
-
-  // Stores a |media_message_center::MediaSessionNotificationItem| for each
-  // media session keyed by its |request_id| in string format.
-  std::map<const std::string,
-           media_message_center::MediaSessionNotificationItem>
-      notifications_;
-
-  // Tick clock used for testing.
-  scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing_;
-
-  std::unique_ptr<MediaNotificationBlocker> blocker_;
-
-  DISALLOW_COPY_AND_ASSIGN(MediaNotificationControllerImpl);
-};
-
-}  // namespace ash
-
-#endif  // ASH_MEDIA_MEDIA_NOTIFICATION_CONTROLLER_IMPL_H_
diff --git a/ash/media/media_notification_controller_impl_unittest.cc b/ash/media/media_notification_controller_impl_unittest.cc
deleted file mode 100644
index ffa8eed..0000000
--- a/ash/media/media_notification_controller_impl_unittest.cc
+++ /dev/null
@@ -1,728 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/media/media_notification_controller_impl.h"
-
-#include <memory>
-
-#include "ash/media/media_notification_constants.h"
-#include "ash/public/cpp/ash_features.h"
-#include "ash/public/cpp/notification_utils.h"
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "base/macros.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/test_mock_time_task_runner.h"
-#include "base/unguessable_token.h"
-#include "components/media_message_center/media_notification_item.h"
-#include "components/media_message_center/media_notification_util.h"
-#include "media/base/media_switches.h"
-#include "services/media_session/public/mojom/audio_focus.mojom.h"
-#include "ui/message_center/message_center.h"
-
-namespace ash {
-
-namespace {
-
-media_session::mojom::MediaSessionInfoPtr BuildMediaSessionInfo(
-    bool is_controllable) {
-  media_session::mojom::MediaSessionInfoPtr session_info(
-      media_session::mojom::MediaSessionInfo::New());
-  session_info->is_controllable = is_controllable;
-  return session_info;
-}
-
-media_session::mojom::AudioFocusRequestStatePtr GetRequestStateWithId(
-    const base::UnguessableToken& id) {
-  media_session::mojom::AudioFocusRequestStatePtr session(
-      media_session::mojom::AudioFocusRequestState::New());
-  session->request_id = id;
-  session->session_info = BuildMediaSessionInfo(true);
-  return session;
-}
-
-}  // namespace
-
-class MediaNotificationControllerImplTest : public AshTestBase {
- public:
-  MediaNotificationControllerImplTest()
-      : task_runner_(new base::TestMockTimeTaskRunner(
-            base::TestMockTimeTaskRunner::Type::kStandalone)) {}
-
-  ~MediaNotificationControllerImplTest() override = default;
-
-  // AshTestBase
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kMediaSessionNotification},
-        {media::kGlobalMediaControlsForChromeOS});
-
-    AshTestBase::SetUp();
-
-    Shell::Get()->media_notification_controller()->set_task_runner_for_testing(
-        task_runner_);
-  }
-
-  void ExpectNotificationCount(unsigned count) {
-    message_center::MessageCenter* message_center =
-        message_center::MessageCenter::Get();
-
-    EXPECT_EQ(count, message_center->GetVisibleNotifications().size());
-
-    // Media notifications should never be shown as a popup so we always check
-    // this is empty.
-    EXPECT_TRUE(message_center->GetPopupNotifications().empty());
-  }
-
-  media_session::MediaMetadata BuildMediaMetadata() {
-    media_session::MediaMetadata metadata;
-    metadata.title = u"title";
-    metadata.artist = u"artist";
-    return metadata;
-  }
-
-  void ExpectHistogramCountRecorded(int count, int size) {
-    histogram_tester_.ExpectBucketCount(
-        media_message_center::kCountHistogramName, count, size);
-  }
-
-  void ExpectHistogramSourceRecorded(
-      media_message_center::MediaNotificationItem::Source source) {
-    histogram_tester_.ExpectUniqueSample(
-        media_message_center::MediaNotificationItem::kSourceHistogramName,
-        static_cast<base::HistogramBase::Sample>(source), 1);
-  }
-
-  void SimulateSessionLock(bool locked) {
-    SessionInfo info;
-    info.state = locked ? session_manager::SessionState::LOCKED
-                        : session_manager::SessionState::ACTIVE;
-    Shell::Get()->session_controller()->SetSessionInfo(info);
-  }
-
-  void SimulateFreezeTimerExpired() {
-    task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(2500));
-  }
-
- private:
-  scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  base::HistogramTester histogram_tester_;
-
-  DISALLOW_COPY_AND_ASSIGN(MediaNotificationControllerImplTest);
-};
-
-// Test toggling the notification multiple times with the same ID. Since the
-// notification is keyed by ID we should only ever show one.
-TEST_F(MediaNotificationControllerImplTest, OnFocusGainedLost_SameId) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id));
-  SimulateFreezeTimerExpired();
-
-  ExpectNotificationCount(0);
-}
-
-// Test toggling the notification multiple times with different IDs. This should
-// show one notification per ID.
-TEST_F(MediaNotificationControllerImplTest, OnFocusGainedLost_MultipleIds) {
-  base::UnguessableToken id1 = base::UnguessableToken::Create();
-  base::UnguessableToken id2 = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id1));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id1.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id2));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id2.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(2);
-  ExpectHistogramCountRecorded(2, 1);
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id1));
-  SimulateFreezeTimerExpired();
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-}
-
-// Test that a notification is hidden when it becomes uncontrollable. We still
-// keep the media_message_center::MediaNotificationItem around in case it
-// becomes controllable again.
-TEST_F(MediaNotificationControllerImplTest,
-       OnFocusGained_ControllableBecomesUncontrollable) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionInfoChanged(BuildMediaSessionInfo(false));
-
-  ExpectNotificationCount(0);
-}
-
-// Test that a notification is shown when it becomes controllable.
-TEST_F(MediaNotificationControllerImplTest,
-       OnFocusGained_NotControllableBecomesControllable) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  media_session::mojom::AudioFocusRequestStatePtr state =
-      GetRequestStateWithId(id);
-  state->session_info->is_controllable = false;
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      std::move(state));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionInfoChanged(BuildMediaSessionInfo(true));
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-}
-
-// Test hiding a notification with an invalid ID.
-TEST_F(MediaNotificationControllerImplTest, OnFocusLost_Noop) {
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(base::UnguessableToken::Create()));
-
-  ExpectNotificationCount(0);
-}
-
-// Test that media notifications have the correct custom view type.
-TEST_F(MediaNotificationControllerImplTest, NotificationHasCustomViewType) {
-  ExpectNotificationCount(0);
-
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  message_center::Notification* notification =
-      message_center::MessageCenter::Get()->FindVisibleNotificationById(
-          id.ToString());
-  EXPECT_TRUE(notification);
-
-  EXPECT_EQ(kMediaSessionNotificationCustomViewType,
-            notification->custom_view_type());
-}
-
-// Test that if we recieve a null media session info that we hide the
-// notification.
-TEST_F(MediaNotificationControllerImplTest, HandleNullMediaSessionInfo) {
-  ExpectNotificationCount(0);
-
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionInfoChanged(nullptr);
-
-  ExpectNotificationCount(0);
-}
-
-TEST_F(MediaNotificationControllerImplTest, MediaMetadata_NoTitle) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  media_session::MediaMetadata metadata;
-  metadata.artist = u"artist";
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(metadata);
-
-  ExpectNotificationCount(0);
-}
-
-TEST_F(MediaNotificationControllerImplTest, MediaMetadataUpdated_MissingInfo) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramCountRecorded(1, 1);
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(media_session::MediaMetadata());
-
-  ExpectNotificationCount(0);
-}
-
-TEST_F(MediaNotificationControllerImplTest, RecordHistogramSource_Unknown) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramSourceRecorded(
-      media_message_center::MediaNotificationItem::Source::kUnknown);
-}
-
-TEST_F(MediaNotificationControllerImplTest, RecordHistogramSource_Web) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  media_session::mojom::AudioFocusRequestStatePtr request =
-      GetRequestStateWithId(id);
-  request->source_name = "web";
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      std::move(request));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramSourceRecorded(
-      media_message_center::MediaNotificationItem::Source::kWeb);
-}
-
-TEST_F(MediaNotificationControllerImplTest, RecordHistogramSource_Assistant) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  media_session::mojom::AudioFocusRequestStatePtr request =
-      GetRequestStateWithId(id);
-  request->source_name = "assistant";
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      std::move(request));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramSourceRecorded(
-      media_message_center::MediaNotificationItem::Source::kAssistant);
-}
-
-TEST_F(MediaNotificationControllerImplTest, RecordHistogramSource_Arc) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  media_session::mojom::AudioFocusRequestStatePtr request =
-      GetRequestStateWithId(id);
-  request->source_name = "arc";
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      std::move(request));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  ExpectHistogramSourceRecorded(
-      media_message_center::MediaNotificationItem::Source::kArc);
-}
-
-// Test that locking the screen will hide the media notifications. Unlocking the
-// screen should re-show the notifications.
-TEST_F(MediaNotificationControllerImplTest, HideWhenScreenLocked) {
-  message_center::MessageCenter* message_center =
-      message_center::MessageCenter::Get();
-
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-
-  // Show a non-media notification that should still be displayed.
-  message_center->AddNotification(
-      CreateSystemNotification("test", std::u16string(), std::u16string(),
-                               "test", base::BindRepeating([]() {})));
-
-  EXPECT_EQ(2u, message_center->GetVisibleNotifications().size());
-
-  // Lock the screen and only the non-media notification should be visible.
-  SimulateSessionLock(true);
-
-  {
-    auto notifications = message_center->GetVisibleNotifications();
-    EXPECT_EQ(1u, notifications.size());
-    EXPECT_EQ("test", (*notifications.begin())->id());
-  }
-
-  // Unlock the screen and both notifications should be visible again.
-  SimulateSessionLock(false);
-  EXPECT_EQ(2u, message_center->GetVisibleNotifications().size());
-}
-
-// Test that when we lose focus we freeze the notification until the timer
-// is fired and then remove it.
-TEST_F(MediaNotificationControllerImplTest, OnFocusLostFreezeUntilTimerFired) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id.ToString())
-                   ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id));
-
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id.ToString())
-                  ->frozen());
-
-  SimulateFreezeTimerExpired();
-  ExpectNotificationCount(0);
-}
-
-// Test that when we lose focus we freeze the notification and then we see
-// the session resume we keep the notification and unfreeze it.
-TEST_F(MediaNotificationControllerImplTest, OnFocusLostFreezeAndResumeSameId) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id.ToString())
-                   ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id));
-
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id.ToString())
-                  ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  // The session comes back and is controllable so we should unfreeze the
-  // notification.
-  ExpectNotificationCount(1);
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id.ToString())
-                   ->frozen());
-}
-
-// Test that when we lose focus we freeze the notification and then we see
-// the session resume but it is missing metadata we hide the notification.
-TEST_F(MediaNotificationControllerImplTest,
-       OnFocusLostFreezeAndResumeSameId_MissingMetadata) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id.ToString())
-                   ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id));
-
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id.ToString())
-                  ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(media_session::MediaMetadata());
-
-  // The session has come back but the metadata is missing data so we should
-  // keep the notification frozen.
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id.ToString())
-                  ->frozen());
-
-  // After the timer has been fired we should hide the notification but still
-  // have the controller.
-  SimulateFreezeTimerExpired();
-  ExpectNotificationCount(0);
-  EXPECT_TRUE(Shell::Get()->media_notification_controller()->HasItemForTesting(
-      id.ToString()));
-}
-
-// Test that when we lose focus we freeze the notification and then we see
-// the session resume but it is not controllable we hide the notification.
-TEST_F(MediaNotificationControllerImplTest,
-       OnFocusLostFreezeAndResumeSameId_NotControllable) {
-  base::UnguessableToken id = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id.ToString())
-                   ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id));
-
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id.ToString())
-                  ->frozen());
-
-  media_session::mojom::AudioFocusRequestStatePtr state =
-      GetRequestStateWithId(id);
-  state->session_info->is_controllable = false;
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      std::move(state));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  // The session has come back but the metadata is not controllable so we should
-  // keep the notification frozen.
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id.ToString())
-                  ->frozen());
-
-  // After the timer has been fired we should hide the notification but still
-  // have the controller.
-  SimulateFreezeTimerExpired();
-  ExpectNotificationCount(0);
-  EXPECT_TRUE(Shell::Get()->media_notification_controller()->HasItemForTesting(
-      id.ToString()));
-}
-
-// Test that when we lose focus we freeze the notification and we see a new
-// session then that does not unfreeze the first notification.
-TEST_F(MediaNotificationControllerImplTest,
-       OnFocusLostFreezeAndDoNotResumeNewId) {
-  base::UnguessableToken id1 = base::UnguessableToken::Create();
-  base::UnguessableToken id2 = base::UnguessableToken::Create();
-
-  ExpectNotificationCount(0);
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id1));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id1.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(1);
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id1.ToString())
-                   ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusLost(
-      GetRequestStateWithId(id1));
-
-  ExpectNotificationCount(1);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id1.ToString())
-                  ->frozen());
-
-  Shell::Get()->media_notification_controller()->OnFocusGained(
-      GetRequestStateWithId(id2));
-
-  Shell::Get()
-      ->media_notification_controller()
-      ->GetItem(id2.ToString())
-      ->MediaSessionMetadataChanged(BuildMediaMetadata());
-
-  ExpectNotificationCount(2);
-  EXPECT_TRUE(Shell::Get()
-                  ->media_notification_controller()
-                  ->GetItem(id1.ToString())
-                  ->frozen());
-  EXPECT_FALSE(Shell::Get()
-                   ->media_notification_controller()
-                   ->GetItem(id2.ToString())
-                   ->frozen());
-
-  SimulateFreezeTimerExpired();
-  ExpectNotificationCount(1);
-
-  EXPECT_FALSE(Shell::Get()->media_notification_controller()->HasItemForTesting(
-      id1.ToString()));
-}
-
-}  // namespace ash
diff --git a/ash/shell.cc b/ash/shell.cc
index 189627c..0390850 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -74,7 +74,6 @@
 #include "ash/login_status.h"
 #include "ash/marker/marker_controller.h"
 #include "ash/media/media_controller_impl.h"
-#include "ash/media/media_notification_controller_impl.h"
 #include "ash/metrics/login_unlock_throughput_recorder.h"
 #include "ash/multi_device_setup/multi_device_notification_presenter.h"
 #include "ash/policy/policy_recommendation_restorer.h"
@@ -880,10 +879,6 @@
   // before it.
   detachable_base_handler_.reset();
 
-  // MediaNotificationControllerImpl depends on MessageCenter and must be
-  // destructed before it.
-  media_notification_controller_.reset();
-
   pcie_peripheral_notification_controller_.reset();
 
   // Destroys the MessageCenter singleton, so must happen late.
@@ -1263,12 +1258,6 @@
   // is started.
   display_manager_->CreateMirrorWindowAsyncIfAny();
 
-  if (base::FeatureList::IsEnabled(features::kMediaSessionNotification) &&
-      !base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) {
-    media_notification_controller_ =
-        std::make_unique<MediaNotificationControllerImpl>();
-  }
-
   // TODO(1091497): Consider combining DisplayHighlightController and
   // DisplayAlignmentController.
   if (features::IsDisplayIdentificationEnabled()) {
diff --git a/ash/shell.h b/ash/shell.h
index c473b45..39a885e4 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -141,7 +141,6 @@
 class MarkerController;
 class TabletModeController;
 class MediaControllerImpl;
-class MediaNotificationControllerImpl;
 class MessageCenterController;
 class MouseCursorEventFilter;
 class MruWindowTracker;
@@ -427,9 +426,6 @@
   }
   MarkerController* marker_controller() { return marker_controller_.get(); }
   MediaControllerImpl* media_controller() { return media_controller_.get(); }
-  MediaNotificationControllerImpl* media_notification_controller() {
-    return media_notification_controller_.get();
-  }
   MessageCenterController* message_center_controller() {
     return message_center_controller_.get();
   }
@@ -721,8 +717,6 @@
   std::unique_ptr<LogoutConfirmationController> logout_confirmation_controller_;
   std::unique_ptr<TabletModeController> tablet_mode_controller_;
   std::unique_ptr<MediaControllerImpl> media_controller_;
-  std::unique_ptr<MediaNotificationControllerImpl>
-      media_notification_controller_;
   std::unique_ptr<MruWindowTracker> mru_window_tracker_;
   std::unique_ptr<MultiDeviceNotificationPresenter>
       multidevice_notification_presenter_;
diff --git a/ash/system/message_center/message_center_utils.cc b/ash/system/message_center/message_center_utils.cc
index 1eeb8d99..2ba090d 100644
--- a/ash/system/message_center/message_center_utils.cc
+++ b/ash/system/message_center/message_center_utils.cc
@@ -4,7 +4,6 @@
 
 #include "ash/system/message_center/message_center_utils.h"
 
-#include "ash/media/media_notification_constants.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/vm_camera_mic_constants.h"
 #include "ui/message_center/message_center.h"
diff --git a/ash/system/message_center/message_center_utils_unittest.cc b/ash/system/message_center/message_center_utils_unittest.cc
index d9dd375c..b9626d3 100644
--- a/ash/system/message_center/message_center_utils_unittest.cc
+++ b/ash/system/message_center/message_center_utils_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/system/message_center/message_center_utils.h"
 
-#include "ash/media/media_notification_constants.h"
 #include "ash/public/cpp/vm_camera_mic_constants.h"
 #include "ash/test/ash_test_base.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/system/unified/notification_counter_view.cc b/ash/system/unified/notification_counter_view.cc
index 140f3f9..c2f8285 100644
--- a/ash/system/unified/notification_counter_view.cc
+++ b/ash/system/unified/notification_counter_view.cc
@@ -6,7 +6,6 @@
 
 #include <algorithm>
 
-#include "ash/media/media_notification_constants.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/vm_camera_mic_constants.h"
 #include "ash/resources/vector_icons/vector_icons.h"
diff --git a/ash/system/unified/notification_icons_controller_unittest.cc b/ash/system/unified/notification_icons_controller_unittest.cc
index 920d1b0..7b8b883 100644
--- a/ash/system/unified/notification_icons_controller_unittest.cc
+++ b/ash/system/unified/notification_icons_controller_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "ash/system/unified/notification_icons_controller.h"
 
-#include "ash/media/media_notification_constants.h"
 #include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/public/cpp/vm_camera_mic_constants.h"
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 169612c..b7093a5 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1938,6 +1938,22 @@
           "allocator/partition_allocator/page_allocator_internals_fuchsia.h",
         ]
       }
+
+      if (current_cpu == "x64") {
+        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+        sources += [ "allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc" ]
+      } else if (current_cpu == "x86") {
+        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+        sources += [ "allocator/partition_allocator/starscan/stack/asm/x86/push_registers_asm.cc" ]
+      } else if (current_cpu == "arm") {
+        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+        sources += [ "allocator/partition_allocator/starscan/stack/asm/arm/push_registers_asm.cc" ]
+      } else if (current_cpu == "arm64") {
+        defines += [ "PA_PCSCAN_STACK_SUPPORTED" ]
+        sources += [ "allocator/partition_allocator/starscan/stack/asm/arm64/push_registers_asm.cc" ]
+      } else {
+        # To support a trampoline for another arch, please refer to v8/src/heap/base.
+      }
     }
   }
 
@@ -3481,6 +3497,7 @@
       "allocator/partition_allocator/starscan/object_bitmap_unittest.cc",
       "allocator/partition_allocator/starscan/pcscan_scheduling_unittest.cc",
       "allocator/partition_allocator/starscan/pcscan_unittest.cc",
+      "allocator/partition_allocator/starscan/stack/stack_unittest.cc",
       "allocator/partition_allocator/thread_cache_unittest.cc",
     ]
   }
diff --git a/base/allocator/partition_allocator/partition_alloc_features.cc b/base/allocator/partition_allocator/partition_alloc_features.cc
index 1c3b0eb..75dda64 100644
--- a/base/allocator/partition_allocator/partition_alloc_features.cc
+++ b/base/allocator/partition_allocator/partition_alloc_features.cc
@@ -44,5 +44,15 @@
 const Feature kPartitionAllocPCScanMUAwareScheduler{
     "PartitionAllocPCScanMUAwareScheduler", FEATURE_ENABLED_BY_DEFAULT};
 
+// In addition to heap, scan also the stack of the current mutator.
+const Feature kPartitionAllocPCScanStackScanning {
+  "PartitionAllocPCScanStackScanning",
+#if defined(PA_PCSCAN_STACK_SUPPORTED)
+      FEATURE_ENABLED_BY_DEFAULT
+#else
+      FEATURE_DISABLED_BY_DEFAULT
+#endif  // defined(PA_PCSCAN_STACK_SUPPORTED)
+};
+
 }  // namespace features
 }  // namespace base
diff --git a/base/allocator/partition_allocator/partition_alloc_features.h b/base/allocator/partition_allocator/partition_alloc_features.h
index 861542b..007acaa 100644
--- a/base/allocator/partition_allocator/partition_alloc_features.h
+++ b/base/allocator/partition_allocator/partition_alloc_features.h
@@ -25,6 +25,7 @@
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
 extern const BASE_EXPORT Feature kPartitionAllocPCScanMUAwareScheduler;
+extern const BASE_EXPORT Feature kPartitionAllocPCScanStackScanning;
 
 extern const BASE_EXPORT Feature kPartitionAllocLazyCommit;
 
diff --git a/base/allocator/partition_allocator/starscan/pcscan.cc b/base/allocator/partition_allocator/starscan/pcscan.cc
index aab7035..943551d5 100644
--- a/base/allocator/partition_allocator/starscan/pcscan.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan.cc
@@ -28,6 +28,7 @@
 #include "base/allocator/partition_allocator/starscan/metadata_allocator.h"
 #include "base/allocator/partition_allocator/starscan/pcscan_scheduling.h"
 #include "base/allocator/partition_allocator/starscan/raceful_worklist.h"
+#include "base/allocator/partition_allocator/starscan/stack/stack.h"
 #include "base/allocator/partition_allocator/thread_cache.h"
 #include "base/compiler_specific.h"
 #include "base/cpu.h"
@@ -210,6 +211,7 @@
 
 #define FOR_ALL_PCSCAN_MUTATOR_SCOPES(V) \
   V(Clear)                               \
+  V(ScanStack)                           \
   V(Scan)                                \
   V(Overall)
 
@@ -353,6 +355,8 @@
     switch (id) {
       case MutatorId::kClear:
         return "PCScan.Mutator.Clear";
+      case MutatorId::kScanStack:
+        return "PCScan.Mutator.ScanStack";
       case MutatorId::kScan:
         return "PCScan.Mutator.Scan";
       case MutatorId::kOverall:
@@ -385,6 +389,8 @@
     switch (id) {
       case MutatorId::kClear:
         return "PA.PCScan." + process_name + ".Mutator.Clear";
+      case MutatorId::kScanStack:
+        return "PA.PCScan." + process_name + ".Mutator.ScanStack";
       case MutatorId::kScan:
         return "PA.PCScan." + process_name + ".Mutator.Scan";
       case MutatorId::kOverall:
@@ -557,13 +563,21 @@
 
   SimdSupport simd_support() const { return simd_support_; }
 
+  void EnableStackScanning();
+  void DisableStackScanning();
+  bool IsStackScanningEnabled() const;
+
   void NotifyThreadCreated(void* stack_top);
   void NotifyThreadDestroyed();
 
+  void* GetCurrentThreadStackTop() const;
+
   void ClearRootsForTesting();  // IN-TEST
-  void ReinitForTesting();  // IN-TEST
+  void ReinitForTesting();      // IN-TEST
 
  private:
+  using StackTops = MetadataHashMap<PlatformThreadId, void*>;
+
   friend base::NoDestructor<PCScanInternal>;
 
   PCScanInternal();
@@ -574,6 +588,12 @@
   Roots scannable_roots_{};
   Roots nonscannable_roots_{};
 
+  bool stack_scanning_enabled_{false};
+  // TLS emulation of stack tops. Since this is guaranteed to go through
+  // non-quarantinable partition, using it from safepoints is safe.
+  StackTops stack_tops_;
+  mutable std::mutex stack_tops_mutex_;
+
   const char* process_name_ = nullptr;
   const SimdSupport simd_support_;
 };
@@ -662,12 +682,37 @@
                          0u, acc);
 }
 
+void PCScanInternal::EnableStackScanning() {
+  PA_DCHECK(!stack_scanning_enabled_);
+  stack_scanning_enabled_ = true;
+}
+void PCScanInternal::DisableStackScanning() {
+  PA_DCHECK(stack_scanning_enabled_);
+  stack_scanning_enabled_ = false;
+}
+bool PCScanInternal::IsStackScanningEnabled() const {
+  return stack_scanning_enabled_;
+}
+
 void PCScanInternal::NotifyThreadCreated(void* stack_top) {
-  // TODO(bikineev,1202644): Add implementation.
+  const auto tid = base::PlatformThread::CurrentId();
+  std::lock_guard<std::mutex> lock(stack_tops_mutex_);
+  const auto res = stack_tops_.insert({tid, stack_top});
+  PA_DCHECK(res.second);
 }
 
 void PCScanInternal::NotifyThreadDestroyed() {
-  // TODO(bikineev,1202644): Add implementation.
+  const auto tid = base::PlatformThread::CurrentId();
+  std::lock_guard<std::mutex> lock(stack_tops_mutex_);
+  PA_DCHECK(1 == stack_tops_.count(tid));
+  stack_tops_.erase(tid);
+}
+
+void* PCScanInternal::GetCurrentThreadStackTop() const {
+  const auto tid = base::PlatformThread::CurrentId();
+  std::lock_guard<std::mutex> lock(stack_tops_mutex_);
+  auto it = stack_tops_.find(tid);
+  return it != stack_tops_.end() ? it->second : nullptr;
 }
 
 void PCScanInternal::ClearRootsForTesting() {
@@ -845,6 +890,7 @@
 
  private:
   class ScanLoop;
+  class StackVisitor;
 
   using Root = PCScan::Root;
   using SlotSpan = SlotSpanMetadata<ThreadSafe>;
@@ -950,8 +996,10 @@
   template <typename LookupPolicy>
   ALWAYS_INLINE size_t TryMarkObjectInNormalBuckets(uintptr_t maybe_ptr) const;
 
-  // Scans all registeres partitions and marks reachable quarantined objects.
-  // Returns the size of marked objects.
+  // Scans stack, only called from safepoints.
+  void ScanStack();
+
+  // Scans all registered partitions and marks reachable quarantined objects.
   void ScanPartitions();
 
   // Clear quarantined objects and prepare card table for fast lookup
@@ -1269,11 +1317,52 @@
 #endif
 };
 
+class PCScanTask::StackVisitor final : public internal::StackVisitor {
+ public:
+  explicit StackVisitor(const PCScanTask& task) : task_(task) {}
+
+  void VisitStack(uintptr_t* stack_ptr, uintptr_t* stack_top) override {
+    static constexpr size_t kMinimalAlignment = 32;
+    stack_ptr = reinterpret_cast<uintptr_t*>(
+        reinterpret_cast<uintptr_t>(stack_ptr) & ~(kMinimalAlignment - 1));
+    stack_top = reinterpret_cast<uintptr_t*>(
+        (reinterpret_cast<uintptr_t>(stack_top) + kMinimalAlignment - 1) &
+        ~(kMinimalAlignment - 1));
+    PA_CHECK(stack_ptr < stack_top);
+    ScanLoop loop(task_);
+    quarantine_size_ += loop.Run(stack_ptr, stack_top);
+  }
+
+  // Returns size of quarantined objects that are reachable from the current
+  // stack.
+  size_t quarantine_size() const { return quarantine_size_; }
+
+ private:
+  const PCScanTask& task_;
+  size_t quarantine_size_ = 0;
+};
+
 PCScanTask::PCScanTask(PCScan& pcscan)
     : pcscan_epoch_(pcscan.epoch()),
       stats_(PCScanInternal::Instance().process_name()),
       pcscan_(pcscan) {}
 
+void PCScanTask::ScanStack() {
+  const auto& pcscan = PCScanInternal::Instance();
+  if (!pcscan.IsStackScanningEnabled())
+    return;
+  // Check if the stack top was registered. It may happen that it's not if the
+  // current allocation happens from pthread trampolines.
+  void* stack_top = pcscan.GetCurrentThreadStackTop();
+  if (UNLIKELY(!stack_top))
+    return;
+
+  Stack stack_scanner(stack_top);
+  StackVisitor visitor(*this);
+  stack_scanner.IteratePointers(&visitor);
+  stats_.IncreaseSurvivedQuarantineSize(visitor.quarantine_size());
+}
+
 void PCScanTask::ScanPartitions() {
   const ScanLoop scan_loop(*this);
   // For scanning large areas, it's worthwhile checking whether the range that
@@ -1397,6 +1486,12 @@
       ClearQuarantinedObjectsAndPrepareCardTable();
     }
     {
+      // Scan the thread's stack to find dangling references.
+      StatsCollector::MutatorScope scan_scope(
+          stats_, StatsCollector::MutatorId::kScanStack);
+      ScanStack();
+    }
+    {
       // Scan heap for dangling references.
       StatsCollector::MutatorScope scan_scope(stats_,
                                               StatsCollector::MutatorId::kScan);
@@ -1626,6 +1721,16 @@
   PCScanInternal::Instance().SetProcessName(process_name);
 }
 
+void PCScan::EnableStackScanning() {
+  PCScanInternal::Instance().EnableStackScanning();
+}
+void PCScan::DisableStackScanning() {
+  PCScanInternal::Instance().DisableStackScanning();
+}
+bool PCScan::IsStackScanningEnabled() {
+  return PCScanInternal::Instance().IsStackScanningEnabled();
+}
+
 void PCScan::NotifyThreadCreated(void* stack_top) {
   PCScanInternal::Instance().NotifyThreadCreated(stack_top);
 }
diff --git a/base/allocator/partition_allocator/starscan/pcscan.h b/base/allocator/partition_allocator/starscan/pcscan.h
index ca00572..52ec083 100644
--- a/base/allocator/partition_allocator/starscan/pcscan.h
+++ b/base/allocator/partition_allocator/starscan/pcscan.h
@@ -81,6 +81,10 @@
   // Sets process name (used for histograms). |name| must be a string literal.
   static void SetProcessName(const char* name);
 
+  static void EnableStackScanning();
+  static void DisableStackScanning();
+  static bool IsStackScanningEnabled();
+
   // Notify PCScan that a new thread was created/destroyed.
   static void NotifyThreadCreated(void* stack_top);
   static void NotifyThreadDestroyed();
diff --git a/base/allocator/partition_allocator/starscan/pcscan_unittest.cc b/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
index c8476b7..4687768 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
@@ -9,6 +9,7 @@
 
 #include "base/allocator/partition_allocator/partition_alloc.h"
 #include "base/allocator/partition_allocator/partition_alloc_features.h"
+#include "base/allocator/partition_allocator/starscan/stack/stack.h"
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,6 +19,24 @@
 namespace base {
 namespace internal {
 
+namespace {
+struct DisableStackScanningScope final {
+  DisableStackScanningScope() {
+    if (PCScan::IsStackScanningEnabled()) {
+      PCScan::DisableStackScanning();
+      changed_ = true;
+    }
+  }
+  ~DisableStackScanningScope() {
+    if (changed_)
+      PCScan::EnableStackScanning();
+  }
+
+ private:
+  bool changed_ = false;
+};
+}  // namespace
+
 class PCScanTest : public testing::Test {
  public:
   PCScanTest() {
@@ -477,6 +496,8 @@
   using SourceList = List<64>;
   using ValueList = SourceList;
 
+  DisableStackScanningScope no_stack_scanning;
+
   auto* source = SourceList::Create(root());
   auto* value = ValueList::Create(root());
   source->next = value;
@@ -485,6 +506,50 @@
 }
 #endif  // PCSCAN_DISABLE_SAFEPOINTS
 
+TEST_F(PCScanTest, StackScanning) {
+  using ValueList = List<8>;
+
+  PCScan::EnableStackScanning();
+
+  static void* dangling_reference = nullptr;
+  // Set to nullptr if the test is retried.
+  dangling_reference = nullptr;
+
+  // Create and set dangling reference in the global.
+  [this]() NOINLINE {
+    auto* value = ValueList::Create(root(), nullptr);
+    ValueList::Destroy(root(), value);
+    dangling_reference = value;
+  }();
+
+  [this]() NOINLINE {
+    // Register the top of the stack to be the current pointer.
+    PCScan::NotifyThreadCreated(GetStackPointer());
+    [this]() NOINLINE {
+      // This writes the pointer to the stack.
+      auto* volatile stack_ref = dangling_reference;
+      ALLOW_UNUSED_LOCAL(stack_ref);
+      [this]() NOINLINE {
+        // Schedule PCScan but don't scan.
+        SchedulePCScan();
+        // Enter safepoint and scan from mutator. This will scan the stack.
+        JoinPCScanAsMutator();
+        // Check that the object is still quarantined since it's referenced by
+        // |dangling_reference|.
+        EXPECT_TRUE(IsInQuarantine(dangling_reference));
+        // Check that value is not in the freelist.
+        EXPECT_FALSE(IsInFreeList(
+            root().AdjustPointerForExtrasSubtract(dangling_reference)));
+        // Run sweeper.
+        FinishPCScanAsScanner();
+        // Check that |dangling_reference| still exists.
+        EXPECT_FALSE(IsInFreeList(
+            root().AdjustPointerForExtrasSubtract(dangling_reference)));
+      }();
+    }();
+  }();
+}
+
 }  // namespace internal
 }  // namespace base
 
diff --git a/base/allocator/partition_allocator/starscan/stack/asm/arm/push_registers_asm.cc b/base/allocator/partition_allocator/starscan/stack/asm/arm/push_registers_asm.cc
new file mode 100644
index 0000000..d7fc960
--- /dev/null
+++ b/base/allocator/partition_allocator/starscan/stack/asm/arm/push_registers_asm.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 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.
+
+// Push all callee-saved registers to get them on the stack for conservative
+// stack scanning.
+//
+// See asm/x64/push_registers_clang.cc for why the function is not generated
+// using clang.
+//
+// We maintain 8-byte alignment at calls by pushing an additional
+// non-callee-saved register (r3).
+//
+// Calling convention source:
+// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A32)
+// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4127.html
+asm(".globl PAPushAllRegistersAndIterateStack             \n"
+    ".type PAPushAllRegistersAndIterateStack, %function   \n"
+    ".hidden PAPushAllRegistersAndIterateStack            \n"
+    "PAPushAllRegistersAndIterateStack:                   \n"
+    // Push all callee-saved registers and save return address.
+    // Only {r4-r11} are callee-saved registers. Push r3 in addition to align
+    // the stack back to 8 bytes.
+    "  push {r3-r11, lr}                                  \n"
+    // Pass 1st parameter (r0) unchanged (Stack*).
+    // Pass 2nd parameter (r1) unchanged (StackVisitor*).
+    // Save 3rd parameter (r2; IterateStackCallback).
+    "  mov r3, r2                                         \n"
+    // Pass 3rd parameter as sp (stack pointer).
+    "  mov r2, sp                                         \n"
+    // Call the callback.
+    "  blx r3                                             \n"
+    // Discard all the registers.
+    "  add sp, sp, #36                                    \n"
+    // Pop lr into pc which returns and switches mode if needed.
+    "  pop {pc}                                           \n");
diff --git a/base/allocator/partition_allocator/starscan/stack/asm/arm64/push_registers_asm.cc b/base/allocator/partition_allocator/starscan/stack/asm/arm64/push_registers_asm.cc
new file mode 100644
index 0000000..123f8846
--- /dev/null
+++ b/base/allocator/partition_allocator/starscan/stack/asm/arm64/push_registers_asm.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 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.
+
+// Push all callee-saved registers to get them on the stack for conservative
+// stack scanning.
+//
+// See asm/x64/push_registers_clang.cc for why the function is not generated
+// using clang.
+
+// We maintain 16-byte alignment.
+//
+// Calling convention source:
+// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64)
+
+asm(
+#if defined(__APPLE__)
+    ".globl _PAPushAllRegistersAndIterateStack            \n"
+    ".private_extern _PAPushAllRegistersAndIterateStack   \n"
+    ".p2align 2                                           \n"
+    "_PAPushAllRegistersAndIterateStack:                  \n"
+#else  // !defined(__APPLE__)
+    ".globl PAPushAllRegistersAndIterateStack             \n"
+#if !defined(_WIN64)
+    ".type PAPushAllRegistersAndIterateStack, %function   \n"
+    ".hidden PAPushAllRegistersAndIterateStack            \n"
+#endif  // !defined(_WIN64)
+    ".p2align 2                                           \n"
+    "PAPushAllRegistersAndIterateStack:                   \n"
+#endif  // !defined(__APPLE__)
+    // x19-x29 are callee-saved.
+    "  stp x19, x20, [sp, #-16]!                          \n"
+    "  stp x21, x22, [sp, #-16]!                          \n"
+    "  stp x23, x24, [sp, #-16]!                          \n"
+    "  stp x25, x26, [sp, #-16]!                          \n"
+    "  stp x27, x28, [sp, #-16]!                          \n"
+    "  stp fp, lr,   [sp, #-16]!                          \n"
+    // Maintain frame pointer.
+    "  mov fp, sp                                         \n"
+    // Pass 1st parameter (x0) unchanged (Stack*).
+    // Pass 2nd parameter (x1) unchanged (StackVisitor*).
+    // Save 3rd parameter (x2; IterateStackCallback)
+    "  mov x7, x2                                         \n"
+    // Pass 3rd parameter as sp (stack pointer).
+    "  mov x2, sp                                         \n"
+    "  blr x7                                             \n"
+    // Load return address and frame pointer.
+    "  ldp fp, lr, [sp], #16                              \n"
+    // Drop all callee-saved registers.
+    "  add sp, sp, #80                                    \n"
+    "  ret                                                \n");
diff --git a/base/allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc b/base/allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc
new file mode 100644
index 0000000..159f7363
--- /dev/null
+++ b/base/allocator/partition_allocator/starscan/stack/asm/x64/push_registers_asm.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 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.
+
+// Push all callee-saved registers to get them on the stack for conservative
+// stack scanning.
+//
+// We cannot rely on clang generating the function and right symbol mangling
+// as `__attribite__((naked))` does not prevent clang from generating TSAN
+// function entry stubs (`__tsan_func_entry`). Even with
+// `__attribute__((no_sanitize_thread)` annotation clang generates the entry
+// stub.
+// See https://bugs.llvm.org/show_bug.cgi?id=45400.
+
+// _WIN64 Defined as 1 when the compilation target is 64-bit ARM or x64.
+// Otherwise, undefined.
+#ifdef _WIN64
+
+// We maintain 16-byte alignment at calls. There is an 8-byte return address
+// on the stack and we push 72 bytes which maintains 16-byte stack alignment
+// at the call.
+// Source: https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
+asm(".globl PAPushAllRegistersAndIterateStack             \n"
+    "PAPushAllRegistersAndIterateStack:                   \n"
+    // rbp is callee-saved. Maintain proper frame pointer for debugging.
+    "  push %rbp                                          \n"
+    "  mov %rsp, %rbp                                     \n"
+    // Dummy for alignment.
+    "  push $0xCDCDCD                                     \n"
+    "  push %rsi                                          \n"
+    "  push %rdi                                          \n"
+    "  push %rbx                                          \n"
+    "  push %r12                                          \n"
+    "  push %r13                                          \n"
+    "  push %r14                                          \n"
+    "  push %r15                                          \n"
+    // Pass 1st parameter (rcx) unchanged (Stack*).
+    // Pass 2nd parameter (rdx) unchanged (StackVisitor*).
+    // Save 3rd parameter (r8; IterateStackCallback)
+    "  mov %r8, %r9                                       \n"
+    // Pass 3rd parameter as rsp (stack pointer).
+    "  mov %rsp, %r8                                      \n"
+    // Call the callback.
+    "  call *%r9                                          \n"
+    // Pop the callee-saved registers.
+    "  add $64, %rsp                                      \n"
+    // Restore rbp as it was used as frame pointer.
+    "  pop %rbp                                           \n"
+    "  ret                                                \n");
+
+#else  // !_WIN64
+
+// We maintain 16-byte alignment at calls. There is an 8-byte return address
+// on the stack and we push 56 bytes which maintains 16-byte stack alignment
+// at the call.
+// Source: https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-1.0.pdf
+asm(
+#ifdef __APPLE__
+    ".globl _PAPushAllRegistersAndIterateStack            \n"
+    ".private_extern _PAPushAllRegistersAndIterateStack   \n"
+    "_PAPushAllRegistersAndIterateStack:                  \n"
+#else   // !__APPLE__
+    ".globl PAPushAllRegistersAndIterateStack             \n"
+    ".type PAPushAllRegistersAndIterateStack, %function   \n"
+    ".hidden PAPushAllRegistersAndIterateStack            \n"
+    "PAPushAllRegistersAndIterateStack:                   \n"
+#endif  // !__APPLE__
+    // rbp is callee-saved. Maintain proper frame pointer for debugging.
+    "  push %rbp                                          \n"
+    "  mov %rsp, %rbp                                     \n"
+    // Dummy for alignment.
+    "  push $0xCDCDCD                                     \n"
+    "  push %rbx                                          \n"
+    "  push %r12                                          \n"
+    "  push %r13                                          \n"
+    "  push %r14                                          \n"
+    "  push %r15                                          \n"
+    // Pass 1st parameter (rdi) unchanged (Stack*).
+    // Pass 2nd parameter (rsi) unchanged (StackVisitor*).
+    // Save 3rd parameter (rdx; IterateStackCallback)
+    "  mov %rdx, %r8                                      \n"
+    // Pass 3rd parameter as rsp (stack pointer).
+    "  mov %rsp, %rdx                                     \n"
+    // Call the callback.
+    "  call *%r8                                          \n"
+    // Pop the callee-saved registers.
+    "  add $48, %rsp                                      \n"
+    // Restore rbp as it was used as frame pointer.
+    "  pop %rbp                                           \n"
+    "  ret                                                \n");
+
+#endif  // !_WIN64
diff --git a/base/allocator/partition_allocator/starscan/stack/asm/x86/push_registers_asm.cc b/base/allocator/partition_allocator/starscan/stack/asm/x86/push_registers_asm.cc
new file mode 100644
index 0000000..40fe200
--- /dev/null
+++ b/base/allocator/partition_allocator/starscan/stack/asm/x86/push_registers_asm.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 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.
+
+// Push all callee-saved registers to get them on the stack for conservative
+// stack scanning.
+//
+// See asm/x64/push_registers_clang.cc for why the function is not generated
+// using clang.
+
+// We maintain 16-byte alignment at calls. There is an 4-byte return address
+// on the stack and we push 28 bytes which maintains 16-byte stack alignment
+// at the call.
+//
+// The following assumes cdecl calling convention.
+// Source: https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
+asm(
+#ifdef _WIN32
+    ".globl _PAPushAllRegistersAndIterateStack            \n"
+    "_PAPushAllRegistersAndIterateStack:                  \n"
+#else   // !_WIN32
+    ".globl PAPushAllRegistersAndIterateStack             \n"
+    ".type PAPushAllRegistersAndIterateStack, %function   \n"
+    ".hidden PAPushAllRegistersAndIterateStack            \n"
+    "PAPushAllRegistersAndIterateStack:                   \n"
+#endif  // !_WIN32
+    // [ IterateStackCallback ]
+    // [ StackVisitor*        ]
+    // [ Stack*               ]
+    // [ ret                  ]
+    // ebp is callee-saved. Maintain proper frame pointer for debugging.
+    "  push %ebp                                          \n"
+    "  movl %esp, %ebp                                    \n"
+    "  push %ebx                                          \n"
+    "  push %esi                                          \n"
+    "  push %edi                                          \n"
+    // Save 3rd parameter (IterateStackCallback).
+    "  movl 28(%esp), %ecx                                \n"
+    // Pass 3rd parameter as esp (stack pointer).
+    "  push %esp                                          \n"
+    // Pass 2nd parameter (StackVisitor*).
+    "  push 28(%esp)                                      \n"
+    // Pass 1st parameter (Stack*).
+    "  push 28(%esp)                                      \n"
+    "  call *%ecx                                         \n"
+    // Pop the callee-saved registers.
+    "  addl $24, %esp                                     \n"
+    // Restore rbp as it was used as frame pointer.
+    "  pop %ebp                                           \n"
+    "  ret                                                \n");
diff --git a/base/allocator/partition_allocator/starscan/stack/stack.cc b/base/allocator/partition_allocator/starscan/stack/stack.cc
index fc0445e..6b1ce37 100644
--- a/base/allocator/partition_allocator/starscan/stack/stack.cc
+++ b/base/allocator/partition_allocator/starscan/stack/stack.cc
@@ -78,9 +78,71 @@
 #error "Unsupported GetStackTop"
 #endif  // defined(OS_WIN)
 
+using IterateStackCallback = void (*)(const Stack*, StackVisitor*, uintptr_t*);
+extern "C" void PAPushAllRegistersAndIterateStack(const Stack*,
+                                                  StackVisitor*,
+                                                  IterateStackCallback);
+
+Stack::Stack(void* stack_top) : stack_top_(stack_top) {
+  PA_DCHECK(stack_top);
+}
+
 NOINLINE uintptr_t* GetStackPointer() {
   return reinterpret_cast<uintptr_t*>(__builtin_frame_address(0));
 }
 
+namespace {
+
+ALLOW_UNUSED_TYPE
+void IterateSafeStackIfNecessary(StackVisitor* visitor) {
+#if defined(__has_feature)
+#if __has_feature(safe_stack)
+  // Source:
+  // https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/safestack/safestack.cpp
+  constexpr size_t kSafeStackAlignmentBytes = 16;
+  void* stack_ptr = __builtin___get_unsafe_stack_ptr();
+  void* stack_top = __builtin___get_unsafe_stack_top();
+  PA_CHECK(stack_top > stack_ptr);
+  PA_CHECK(0u == (reinterpret_cast<uintptr_t>(stack_ptr) &
+                  (kSafeStackAlignmentBytes - 1)));
+  PA_CHECK(0u == (reinterpret_cast<uintptr_t>(stack_top) &
+                  (kSafeStackAlignmentBytes - 1)));
+  visitor->VisitStack(reinterpret_cast<uintptr_t*>(stack_ptr),
+                      reinterpret_cast<uintptr_t*>(stack_top));
+#endif  // __has_feature(safe_stack)
+#endif  // defined(__has_feature)
+}
+
+// Called by the trampoline that pushes registers on the stack. This method
+// should never be inlined to ensure that a possible redzone cannot contain
+// any data that needs to be scanned.
+// No ASAN support as method accesses redzones while walking the stack.
+NOINLINE NO_SANITIZE("address") ALLOW_UNUSED_TYPE
+    void IteratePointersImpl(const Stack* stack,
+                             StackVisitor* visitor,
+                             uintptr_t* stack_ptr) {
+  PA_DCHECK(stack);
+  PA_DCHECK(visitor);
+  PA_CHECK(nullptr != stack->stack_top());
+  // All supported platforms should have their stack aligned to at least
+  // sizeof(void*).
+  constexpr size_t kMinStackAlignment = sizeof(void*);
+  PA_CHECK(0u ==
+           (reinterpret_cast<uintptr_t>(stack_ptr) & (kMinStackAlignment - 1)));
+  visitor->VisitStack(stack_ptr,
+                      reinterpret_cast<uintptr_t*>(stack->stack_top()));
+}
+
+}  // namespace
+
+void Stack::IteratePointers(StackVisitor* visitor) const {
+#if defined(PA_PCSCAN_STACK_SUPPORTED)
+  PAPushAllRegistersAndIterateStack(this, visitor, &IteratePointersImpl);
+  // No need to deal with callee-saved registers as they will be kept alive by
+  // the regular conservative stack iteration.
+  IterateSafeStackIfNecessary(visitor);
+#endif
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/allocator/partition_allocator/starscan/stack/stack.h b/base/allocator/partition_allocator/starscan/stack/stack.h
index 6badc4b..571bd2f 100644
--- a/base/allocator/partition_allocator/starscan/stack/stack.h
+++ b/base/allocator/partition_allocator/starscan/stack/stack.h
@@ -19,6 +19,31 @@
 // Returns the top of the stack using system API.
 BASE_EXPORT void* GetStackTop();
 
+// Interface for stack visitation.
+class StackVisitor {
+ public:
+  virtual void VisitStack(uintptr_t* stack_ptr, uintptr_t* stack_top) = 0;
+};
+
+// Abstraction over the stack. Supports handling of:
+// - native stack;
+// - SafeStack: https://releases.llvm.org/10.0.0/tools/clang/docs/SafeStack.html
+class BASE_EXPORT Stack final {
+ public:
+  // Sets start of the stack.
+  explicit Stack(void* stack_top);
+
+  // Word-aligned iteration of the stack. Flushes callee saved registers and
+  // passes the range of the stack on to |visitor|.
+  void IteratePointers(StackVisitor* visitor) const;
+
+  // Returns the top of the stack.
+  void* stack_top() const { return stack_top_; }
+
+ private:
+  void* stack_top_;
+};
+
 }  // namespace internal
 }  // namespace base
 
diff --git a/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc b/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc
new file mode 100644
index 0000000..3c8d1e57
--- /dev/null
+++ b/base/allocator/partition_allocator/starscan/stack/stack_unittest.cc
@@ -0,0 +1,346 @@
+// 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 "base/allocator/partition_allocator/starscan/stack/stack.h"
+
+#include <memory>
+#include <ostream>
+
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+
+#if defined(OS_LINUX) && (defined(ARCH_CPU_X86) || defined(ARCH_CPU_X86_64))
+#include <xmmintrin.h>
+#endif
+
+namespace base {
+namespace internal {
+
+namespace {
+
+class StackTest : public ::testing::Test {
+ protected:
+  StackTest() : stack_(std::make_unique<Stack>(GetStackTop())) {}
+
+  Stack* GetStack() const { return stack_.get(); }
+
+ private:
+  std::unique_ptr<Stack> stack_;
+};
+
+class StackScanner final : public StackVisitor {
+ public:
+  struct Container {
+    std::unique_ptr<int> value;
+  };
+
+  StackScanner() : container_(std::make_unique<Container>()) {
+    container_->value = std::make_unique<int>();
+  }
+
+  void VisitStack(uintptr_t* stack_ptr, uintptr_t* stack_top) final {
+    for (; stack_ptr != stack_top; ++stack_ptr) {
+      if (*stack_ptr == reinterpret_cast<uintptr_t>(container_->value.get()))
+        found_ = true;
+    }
+  }
+
+  void Reset() { found_ = false; }
+  bool found() const { return found_; }
+  int* needle() const { return container_->value.get(); }
+
+ private:
+  std::unique_ptr<Container> container_;
+  bool found_ = false;
+};
+
+}  // namespace
+
+TEST_F(StackTest, IteratePointersFindsOnStackValue) {
+  auto scanner = std::make_unique<StackScanner>();
+
+  // No check that the needle is initially not found as on some platforms it
+  // may be part of temporaries after setting it up through StackScanner.
+  {
+    int* volatile tmp = scanner->needle();
+    ALLOW_UNUSED_LOCAL(tmp);
+    GetStack()->IteratePointers(scanner.get());
+    EXPECT_TRUE(scanner->found());
+  }
+}
+
+TEST_F(StackTest, IteratePointersFindsOnStackValuePotentiallyUnaligned) {
+  auto scanner = std::make_unique<StackScanner>();
+
+  // No check that the needle is initially not found as on some platforms it
+  // may be part of  temporaries after setting it up through StackScanner.
+  {
+    char a = 'c';
+    ALLOW_UNUSED_LOCAL(a);
+    int* volatile tmp = scanner->needle();
+    ALLOW_UNUSED_LOCAL(tmp);
+    GetStack()->IteratePointers(scanner.get());
+    EXPECT_TRUE(scanner->found());
+  }
+}
+
+namespace {
+
+// Prevent inlining as that would allow the compiler to prove that the parameter
+// must not actually be materialized.
+//
+// Parameter positiosn are explicit to test various calling conventions.
+NOINLINE void* RecursivelyPassOnParameterImpl(void* p1,
+                                              void* p2,
+                                              void* p3,
+                                              void* p4,
+                                              void* p5,
+                                              void* p6,
+                                              void* p7,
+                                              void* p8,
+                                              Stack* stack,
+                                              StackVisitor* visitor) {
+  if (p1) {
+    return RecursivelyPassOnParameterImpl(nullptr, p1, nullptr, nullptr,
+                                          nullptr, nullptr, nullptr, nullptr,
+                                          stack, visitor);
+  } else if (p2) {
+    return RecursivelyPassOnParameterImpl(nullptr, nullptr, p2, nullptr,
+                                          nullptr, nullptr, nullptr, nullptr,
+                                          stack, visitor);
+  } else if (p3) {
+    return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, p3,
+                                          nullptr, nullptr, nullptr, nullptr,
+                                          stack, visitor);
+  } else if (p4) {
+    return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                          p4, nullptr, nullptr, nullptr, stack,
+                                          visitor);
+  } else if (p5) {
+    return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                          nullptr, p5, nullptr, nullptr, stack,
+                                          visitor);
+  } else if (p6) {
+    return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                          nullptr, nullptr, p6, nullptr, stack,
+                                          visitor);
+  } else if (p7) {
+    return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                          nullptr, nullptr, nullptr, p7, stack,
+                                          visitor);
+  } else if (p8) {
+    stack->IteratePointers(visitor);
+    return p8;
+  }
+  return nullptr;
+}
+
+NOINLINE void* RecursivelyPassOnParameter(size_t num,
+                                          void* parameter,
+                                          Stack* stack,
+                                          StackVisitor* visitor) {
+  switch (num) {
+    case 0:
+      stack->IteratePointers(visitor);
+      return parameter;
+    case 1:
+      return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                            nullptr, nullptr, nullptr,
+                                            parameter, stack, visitor);
+    case 2:
+      return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                            nullptr, nullptr, parameter,
+                                            nullptr, stack, visitor);
+    case 3:
+      return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                            nullptr, parameter, nullptr,
+                                            nullptr, stack, visitor);
+    case 4:
+      return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr, nullptr,
+                                            parameter, nullptr, nullptr,
+                                            nullptr, stack, visitor);
+    case 5:
+      return RecursivelyPassOnParameterImpl(nullptr, nullptr, nullptr,
+                                            parameter, nullptr, nullptr,
+                                            nullptr, nullptr, stack, visitor);
+    case 6:
+      return RecursivelyPassOnParameterImpl(nullptr, nullptr, parameter,
+                                            nullptr, nullptr, nullptr, nullptr,
+                                            nullptr, stack, visitor);
+    case 7:
+      return RecursivelyPassOnParameterImpl(nullptr, parameter, nullptr,
+                                            nullptr, nullptr, nullptr, nullptr,
+                                            nullptr, stack, visitor);
+    case 8:
+      return RecursivelyPassOnParameterImpl(parameter, nullptr, nullptr,
+                                            nullptr, nullptr, nullptr, nullptr,
+                                            nullptr, stack, visitor);
+    default:
+      __builtin_unreachable();
+  }
+  __builtin_unreachable();
+}
+
+}  // namespace
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting0) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(0, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting1) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(1, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting2) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(2, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting3) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(3, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting4) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(4, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting5) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(5, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting6) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(6, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting7) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(7, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+TEST_F(StackTest, IteratePointersFindsParameterNesting8) {
+  auto scanner = std::make_unique<StackScanner>();
+  void* needle = RecursivelyPassOnParameter(8, scanner->needle(), GetStack(),
+                                            scanner.get());
+  EXPECT_EQ(scanner->needle(), needle);
+  EXPECT_TRUE(scanner->found());
+}
+
+// The following test uses inline assembly and has been checked to work on clang
+// to verify that the stack-scanning trampoline pushes callee-saved registers.
+//
+// The test uses a macro loop as asm() can only be passed string literals.
+#if defined(__clang__) && defined(ARCH_CPU_X86_64) && !defined(OS_WIN)
+
+// Excluded from test: rbp
+#define FOR_ALL_CALLEE_SAVED_REGS(V) \
+  V("rbx")                           \
+  V("r12")                           \
+  V("r13")                           \
+  V("r14")                           \
+  V("r15")
+
+namespace {
+extern "C" void IteratePointersNoMangling(Stack* stack, StackVisitor* visitor) {
+  stack->IteratePointers(visitor);
+}
+}  // namespace
+
+TEST_F(StackTest, IteratePointersFindsCalleeSavedRegisters) {
+  auto scanner = std::make_unique<StackScanner>();
+
+  // No check that the needle is initially not found as on some platforms it
+  // may be part of  temporaries after setting it up through StackScanner.
+
+// First, clear all callee-saved registers.
+#define CLEAR_REGISTER(reg) asm("mov $0, %%" reg : : : reg);
+
+  FOR_ALL_CALLEE_SAVED_REGS(CLEAR_REGISTER)
+#undef CLEAR_REGISTER
+
+  // Keep local raw pointers to keep instruction sequences small below.
+  auto* local_stack = GetStack();
+  auto* local_scanner = scanner.get();
+
+// Moves |local_scanner->needle()| into a callee-saved register, leaving the
+// callee-saved register as the only register referencing the needle.
+// (Ignoring implementation-dependent dirty registers/stack.)
+#define KEEP_ALIVE_FROM_CALLEE_SAVED(reg)                                \
+  local_scanner->Reset();                                                \
+  [local_stack, local_scanner]() NOINLINE {                              \
+    asm volatile("   mov %0, %%" reg                                     \
+                 "\n mov %1, %%rdi"                                      \
+                 "\n mov %2, %%rsi"                                      \
+                 "\n call %P3"                                           \
+                 "\n mov $0, %%" reg                                     \
+                 :                                                       \
+                 : "r"(local_scanner->needle()), "r"(local_stack),       \
+                   "r"(local_scanner), "i"(IteratePointersNoMangling)    \
+                 : "memory", reg, "rdi", "rsi", "cc");                   \
+  }();                                                                   \
+  EXPECT_TRUE(local_scanner->found())                                    \
+      << "pointer in callee-saved register not found. register: " << reg \
+      << std::endl;
+
+  FOR_ALL_CALLEE_SAVED_REGS(KEEP_ALIVE_FROM_CALLEE_SAVED)
+#undef KEEP_ALIVE_FROM_CALLEE_SAVED
+#undef FOR_ALL_CALLEE_SAVED_REGS
+}
+
+#endif  // defined(__clang__) && defined(ARCH_CPU_X86_64) && !defined(OS_WIN)
+
+#if defined(OS_LINUX) && (defined(ARCH_CPU_X86) || defined(ARCH_CPU_X86_64))
+class CheckStackAlignmentVisitor final : public StackVisitor {
+ public:
+  void VisitStack(uintptr_t*, uintptr_t*) final {
+    // Check that the stack doesn't get misaligned by asm trampolines.
+    float f[4] = {0.};
+    volatile auto xmm = ::_mm_load_ps(f);
+    ALLOW_UNUSED_LOCAL(xmm);
+  }
+};
+
+TEST_F(StackTest, StackAlignment) {
+  auto checker = std::make_unique<CheckStackAlignmentVisitor>();
+  GetStack()->IteratePointers(checker.get());
+}
+#endif  // defined(OS_LINUX) && (defined(ARCH_CPU_X86) ||
+        // defined(ARCH_CPU_X86_64))
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/time/time_win_unittest.cc b/base/time/time_win_unittest.cc
index c60c98f..ebb1636 100644
--- a/base/time/time_win_unittest.cc
+++ b/base/time/time_win_unittest.cc
@@ -391,8 +391,6 @@
 }
 
 TEST(HighResolutionTimer, GetUsage) {
-  EXPECT_EQ(0.0, Time::GetHighResolutionTimerUsage());
-
   Time::ResetHighResolutionTimerUsage();
 
   // 0% usage since the timer isn't activated regardless of how much time has
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
index d0fbb8a..b095ad6 100644
--- a/base/trace_event/memory_allocator_dump.h
+++ b/base/trace_event/memory_allocator_dump.h
@@ -7,8 +7,8 @@
 
 #include <stdint.h>
 
+#include <iosfwd>
 #include <memory>
-#include <ostream>
 #include <string>
 #include <vector>
 
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index 4ddccea..739ebd8d 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -264,6 +264,11 @@
   return callback_id;
 }
 
+void OneCopyRasterBufferProvider::SetShutdownEvent(
+    base::WaitableEvent* shutdown_event) {
+  shutdown_event_ = shutdown_event;
+}
+
 void OneCopyRasterBufferProvider::Shutdown() {
   staging_pool_.Shutdown();
 }
@@ -316,7 +321,8 @@
     staging_buffer->gpu_memory_buffer =
         gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
             staging_buffer->size, BufferFormat(format),
-            gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gpu::kNullSurfaceHandle);
+            gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gpu::kNullSurfaceHandle,
+            shutdown_event_);
   }
 
   gfx::Rect playback_rect = raster_full_rect;
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index a5f91e1..9c38d691 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -16,6 +16,10 @@
 #include "components/viz/client/client_resource_provider.h"
 #include "gpu/command_buffer/common/sync_token.h"
 
+namespace base {
+class WaitableEvent;
+}
+
 namespace gpu {
 class GpuMemoryBufferManager;
 }
@@ -65,6 +69,7 @@
       const std::vector<const ResourcePool::InUsePoolResource*>& resources,
       base::OnceClosure callback,
       uint64_t pending_callback_id) const override;
+  void SetShutdownEvent(base::WaitableEvent* shutdown_event) override;
   void Shutdown() override;
 
   // Playback raster source and copy result into |resource|.
@@ -153,6 +158,7 @@
   viz::ContextProvider* const compositor_context_provider_;
   viz::RasterContextProvider* const worker_context_provider_;
   gpu::GpuMemoryBufferManager* const gpu_memory_buffer_manager_;
+  base::WaitableEvent* shutdown_event_ = nullptr;
   const int max_bytes_per_copy_operation_;
   const bool use_partial_raster_;
   const bool use_gpu_memory_buffer_resources_;
diff --git a/cc/raster/raster_buffer_provider.h b/cc/raster/raster_buffer_provider.h
index d0da146..8185f93 100644
--- a/cc/raster/raster_buffer_provider.h
+++ b/cc/raster/raster_buffer_provider.h
@@ -18,6 +18,10 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 
+namespace base {
+class WaitableEvent;
+}
+
 namespace cc {
 class Resource;
 
@@ -88,6 +92,12 @@
       base::OnceClosure callback,
       uint64_t pending_callback_id) const = 0;
 
+  // Sets an event, guaranteed to live past this object's lifetime, that is
+  // signalled when the TileManger is cancelling tasks. Subclasses can use
+  // this as an argument to GpuMemoryBufferManager::CreateGpuMemoryBuffer to
+  // avoid deadlocks when TileManager is cancelling tasks.
+  virtual void SetShutdownEvent(base::WaitableEvent* shutdown_event) {}
+
   // Shutdown for doing cleanup.
   virtual void Shutdown() = 0;
 };
diff --git a/cc/raster/zero_copy_raster_buffer_provider.cc b/cc/raster/zero_copy_raster_buffer_provider.cc
index cca625539..2184021 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.cc
+++ b/cc/raster/zero_copy_raster_buffer_provider.cc
@@ -67,10 +67,12 @@
  public:
   ZeroCopyRasterBufferImpl(
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager,
+      base::WaitableEvent* shutdown_event,
       const ResourcePool::InUsePoolResource& in_use_resource,
       ZeroCopyGpuBacking* backing)
       : backing_(backing),
         gpu_memory_buffer_manager_(gpu_memory_buffer_manager),
+        shutdown_event_(shutdown_event),
         resource_size_(in_use_resource.size()),
         resource_format_(in_use_resource.format()),
         resource_color_space_(in_use_resource.color_space()),
@@ -124,7 +126,7 @@
     if (!gpu_memory_buffer_) {
       gpu_memory_buffer_ = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
           resource_size_, viz::BufferFormat(resource_format_), kBufferUsage,
-          gpu::kNullSurfaceHandle);
+          gpu::kNullSurfaceHandle, shutdown_event_);
       // Note that GpuMemoryBuffer allocation can fail.
       // https://crbug.com/554541
       if (!gpu_memory_buffer_)
@@ -156,6 +158,7 @@
 
   // These fields are for use on the worker thread.
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+  base::WaitableEvent* shutdown_event_;
   gfx::Size resource_size_;
   viz::ResourceFormat resource_format_;
   gfx::ColorSpace resource_color_space_;
@@ -201,8 +204,8 @@
   ZeroCopyGpuBacking* backing =
       static_cast<ZeroCopyGpuBacking*>(resource.gpu_backing());
 
-  return std::make_unique<ZeroCopyRasterBufferImpl>(gpu_memory_buffer_manager_,
-                                                    resource, backing);
+  return std::make_unique<ZeroCopyRasterBufferImpl>(
+      gpu_memory_buffer_manager_, shutdown_event_, resource, backing);
 }
 
 void ZeroCopyRasterBufferProvider::Flush() {}
@@ -234,6 +237,11 @@
   return 0;
 }
 
+void ZeroCopyRasterBufferProvider::SetShutdownEvent(
+    base::WaitableEvent* shutdown_event) {
+  shutdown_event_ = shutdown_event;
+}
+
 void ZeroCopyRasterBufferProvider::Shutdown() {}
 
 }  // namespace cc
diff --git a/cc/raster/zero_copy_raster_buffer_provider.h b/cc/raster/zero_copy_raster_buffer_provider.h
index f2322a8..02dc9ad 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.h
+++ b/cc/raster/zero_copy_raster_buffer_provider.h
@@ -15,6 +15,7 @@
 #include "cc/raster/raster_buffer_provider.h"
 
 namespace base {
+class WaitableEvent;
 namespace trace_event {
 class ConvertableToTraceFormat;
 }
@@ -56,6 +57,7 @@
       const std::vector<const ResourcePool::InUsePoolResource*>& resources,
       base::OnceClosure callback,
       uint64_t pending_callback_id) const override;
+  void SetShutdownEvent(base::WaitableEvent* shutdown_event) override;
   void Shutdown() override;
 
  private:
@@ -63,6 +65,7 @@
       const;
 
   gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager_;
+  base::WaitableEvent* shutdown_event_ = nullptr;
   viz::ContextProvider* compositor_context_provider_;
   viz::ResourceFormat tile_format_;
 };
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 22bc4ac..91517ad 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -438,10 +438,18 @@
 
   global_state_ = GlobalStateThatImpactsTilePriority();
 
+  // This must be signalled before the Shutdown call below so that if there are
+  // any pending tasks on the worker thread that might be waiting on tasks
+  // posted to this thread they are cancelled.
+  shutdown_event_.Signal();
+
   // This cancels tasks if possible, finishes pending tasks, and release any
   // uninitialized resources.
   tile_task_manager_->Shutdown();
 
+  // Reset the signal since SetResources() might be called later.
+  shutdown_event_.Reset();
+
   raster_buffer_provider_->Shutdown();
 
   tile_task_manager_->CheckForCompletedTasks();
@@ -480,6 +488,8 @@
   image_controller_.SetImageDecodeCache(image_decode_cache);
   tile_task_manager_ = TileTaskManagerImpl::Create(task_graph_runner);
   raster_buffer_provider_ = raster_buffer_provider;
+
+  raster_buffer_provider_->SetShutdownEvent(&shutdown_event_);
 }
 
 void TileManager::Release(Tile* tile) {
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 39cf268..460c492e 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "base/sequenced_task_runner.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/values.h"
 #include "cc/base/unique_notifier.h"
 #include "cc/raster/raster_buffer_provider.h"
@@ -499,6 +500,14 @@
   bool has_pending_queries_ = false;
   base::CancelableOnceClosure check_pending_tile_queries_callback_;
 
+  // Signaled inside FinishTasksAndCleanUp() to avoid deadlock.
+  // FinishTasksAndCleanUp() may block waiting for worker thread tasks to finish
+  // and worker thread tasks may block on this thread causing deadlock. Worker
+  // thread tasks can use WaitableEvent::WaitMany() to wait on two events, one
+  // for the original task completion plus this event to cancel waiting on
+  // completion when FinishTasksAndCleanUp() runs.
+  base::WaitableEvent shutdown_event_;
+
   // We need two WeakPtrFactory objects as the invalidation pattern of each is
   // different. The |task_set_finished_weak_ptr_factory_| is invalidated any
   // time new tasks are scheduled, preventing a race when the callback has
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 502439c7..376e478 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -619,6 +619,7 @@
   "java/src/org/chromium/chrome/browser/firstrun/TosDialogBehaviorSharedPrefInvalidator.java",
   "java/src/org/chromium/chrome/browser/fonts/FontPreloader.java",
   "java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManager.java",
+  "java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java",
   "java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java",
   "java/src/org/chromium/chrome/browser/gcore/ChromeGoogleApiClient.java",
   "java/src/org/chromium/chrome/browser/gcore/ChromeGoogleApiClientImpl.java",
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
index cb005bbd..8abe515 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java
@@ -16,6 +16,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.Function;
+import org.chromium.base.Log;
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
@@ -85,12 +86,16 @@
 
     /**
      * Starts Autofill Assistant.
-     * @param activity {@link ChromeActivity} the activity on which the Autofill Assistant is being
+     * @param activity {@link Activity} the activity on which the Autofill Assistant is being
      *         started.
      * @param triggerContext {@link TriggerContext} the trigger context, containing startup
      *         parameters and information.
      */
-    public static void start(ChromeActivity activity, TriggerContext triggerContext) {
+    public static void start(@Nullable Activity activity, TriggerContext triggerContext) {
+        if (!(activity instanceof ChromeActivity)) {
+            Log.v(TAG, "Failed to retrieve ChromeActivity.");
+            return;
+        }
         // Register synthetic trial as soon as possible.
         UmaSessionStats.registerSyntheticFieldTrial(TRIGGERED_SYNTHETIC_TRIAL, ENABLED_GROUP);
         // Synthetic trial for experiments.
@@ -105,7 +110,8 @@
         String intent = triggerContext.getParameters().get("INTENT");
         // Have an "attempted starts" baseline for the drop out histogram.
         AutofillAssistantMetrics.recordDropOut(DropOutReason.AA_START, intent);
-        waitForTab(activity, tab -> { AutofillAssistantTabHelper.get(tab).start(triggerContext); });
+        waitForTab((ChromeActivity) activity,
+                tab -> { AutofillAssistantTabHelper.get(tab).start(triggerContext); });
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 7d6a0bc..599ec864 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -35,6 +35,9 @@
     "java/res/drawable/ic_add_alert_blue.xml",
     "java/res/drawable/ic_check_googblue_20dp_animated.xml",
     "java/res/drawable/ic_group_icon_16dp.xml",
+    "java/res/drawable/ic_rating_star_full.xml",
+    "java/res/drawable/ic_rating_star_half.xml",
+    "java/res/drawable/ic_rating_star_outline.xml",
     "java/res/drawable/ic_trending_down_blue.xml",
     "java/res/drawable/iph_drag_and_drop_animated_drawable.xml",
     "java/res/drawable/iph_drag_and_drop_drawable.xml",
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_full.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_full.xml
new file mode 100644
index 0000000..80ed536
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_full.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27z"
+      android:fillColor="@color/rating_star_yellow"/>
+</vector>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_half.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_half.xml
new file mode 100644
index 0000000..55d24e4
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_half.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4V6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"
+      android:fillColor="@color/rating_star_yellow"/>
+</vector>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_outline.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_outline.xml
new file mode 100644
index 0000000..8ddc6f5
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/drawable/ic_rating_star_outline.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="16dp"
+    android:height="16dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"
+      android:fillColor="@color/rating_star_yellow"/>
+</vector>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
index 7b74d286..f556256 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
@@ -434,8 +434,8 @@
       </message>
       <message name="IDS_MERCHANT_VIEWER_MESSAGE_DESCRIPTION_REVIEWS" desc="Second part of the message description. Shows the number of reviews.">
          {REVIEWS, plural,
-            =1 {(<ph name="REVIEWS_COUNT_ONE">%1$d<ex>1</ex></ph> review)}
-            other {(<ph name="REVIEWS_COUNT_MANY">%1$d<ex>8</ex></ph> reviews)}}
+            =1 {(<ph name="REVIEWS_COUNT_ONE">%1$s<ex>1</ex></ph> review)}
+            other {(<ph name="REVIEWS_COUNT_MANY">%1$s<ex>8</ex></ph> reviews)}}
       </message>
       <message name="IDS_MERCHANT_VIEWER_MESSAGE_RATING_NUMERICAL" desc="The rating of the merchant in numerical form.">
         <ph name="CURRENT_RATING">%1$.1f<ex>4.5</ex></ph>/<ph name="RATING_BASELINE">%2$d<ex>5</ex></ph>
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MERCHANT_VIEWER_MESSAGE_DESCRIPTION_REVIEWS.png.sha1 b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MERCHANT_VIEWER_MESSAGE_DESCRIPTION_REVIEWS.png.sha1
index 5500777e..938288c 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MERCHANT_VIEWER_MESSAGE_DESCRIPTION_REVIEWS.png.sha1
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MERCHANT_VIEWER_MESSAGE_DESCRIPTION_REVIEWS.png.sha1
@@ -1 +1 @@
-50348ef2b33b3e48032bc2d10e842f137e558d13
\ No newline at end of file
+416f0632e256c88ddf97ccc3e4bb36e2e39f8795
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index 2d49b90..4c4356f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -119,12 +119,6 @@
   "AutocompleteMediator\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "CredentialLeakDialogBridge\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
-  "PasswordChangeLauncher\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "AndroidPaymentApp\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
index 5a6a977..588b12a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -116,6 +116,7 @@
                 LensFeature.SEARCH_BOX_START_VARIANT_LENS_CAMERA_ASSISTED_SEARCH,
                 LensFeature.MIN_AGSA_VERSION_LENS_CAMERA_ASSISTED_SEARCH,
                 MerchantViewerConfig.DEFAULT_TRUST_SIGNALS_MESSAGE_DELAY,
+                MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_USE_RATING_BAR,
                 MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_WINDOW_DURATION_SECONDS,
                 MerchantViewerConfig.TRUST_SIGNALS_SHEET_USE_PAGE_TITLE,
                 PageAnnotationsServiceConfig.PAGE_ANNOTATIONS_BASE_URL,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java
new file mode 100644
index 0000000..c7f9fe9d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/BrowserControlsManagerSupplier.java
@@ -0,0 +1,40 @@
+// 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.
+
+package org.chromium.chrome.browser.fullscreen;
+
+import androidx.annotation.Nullable;
+
+import org.chromium.base.UnownedUserDataKey;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.UnownedUserDataSupplier;
+import org.chromium.ui.base.WindowAndroid;
+
+/**
+ * A {@link UnownedUserDataSupplier} which manages the supplier and UnownedUserData for a
+ * {@link BrowserControlsManager}.
+ */
+public class BrowserControlsManagerSupplier
+        extends UnownedUserDataSupplier<BrowserControlsManager> {
+    private static final UnownedUserDataKey<BrowserControlsManagerSupplier> KEY =
+            new UnownedUserDataKey<BrowserControlsManagerSupplier>(
+                    BrowserControlsManagerSupplier.class);
+
+    /**
+     * Retrieves an {@link ObservableSupplier} from the given host. Real implementations should
+     * use {@link WindowAndroid}.
+     */
+    public static @Nullable BrowserControlsManager getValueOrNullFrom(
+            @Nullable WindowAndroid windowAndroid) {
+        if (windowAndroid == null) return null;
+        BrowserControlsManagerSupplier supplier =
+                KEY.retrieveDataFromHost(windowAndroid.getUnownedUserDataHost());
+        return supplier == null ? null : supplier.get();
+    }
+
+    /** Constructs a BrowserControlsManagerSupplier and attaches it to the {@link WindowAndroid} */
+    public BrowserControlsManagerSupplier() {
+        super(KEY);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
index 5c23b378..dd72527 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/CredentialLeakDialogBridge.java
@@ -1,33 +1,39 @@
 // Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
 package org.chromium.chrome.browser.password_manager;
 
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
+import org.chromium.chrome.browser.fullscreen.BrowserControlsManagerSupplier;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModelSelectorSupplier;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
 
-import java.lang.ref.WeakReference;
-
 /** JNI call glue between the native password manager CredentialLeak class and Java objects. */
 public class CredentialLeakDialogBridge {
     private long mNativeCredentialLeakDialogViewAndroid;
     private final PasswordManagerDialogCoordinator mCredentialLeakDialog;
-    private final WeakReference<ChromeActivity> mActivity;
+    private final WindowAndroid mWindowAndroid;
 
     private CredentialLeakDialogBridge(
-            WindowAndroid windowAndroid, long nativeCredentialLeakDialogViewAndroid) {
+            @NonNull WindowAndroid windowAndroid, long nativeCredentialLeakDialogViewAndroid) {
         mNativeCredentialLeakDialogViewAndroid = nativeCredentialLeakDialogViewAndroid;
-        ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get();
-        mActivity = new WeakReference<>(activity);
-        mCredentialLeakDialog = new PasswordManagerDialogCoordinator(
-                activity.getModalDialogManager(), activity.findViewById(android.R.id.content),
-                activity.getBrowserControlsManager(), activity.getControlContainerHeightResource());
+        mWindowAndroid = windowAndroid;
+
+        mCredentialLeakDialog =
+                new PasswordManagerDialogCoordinator(windowAndroid.getModalDialogManager(),
+                        windowAndroid.getActivity().get().findViewById(android.R.id.content),
+                        BrowserControlsManagerSupplier.getValueOrNullFrom(windowAndroid));
     }
 
     @CalledByNative
@@ -39,14 +45,15 @@
     @CalledByNative
     public void showDialog(String credentialLeakTitle, String credentialLeakDetails,
             String positiveButton, String negativeButton) {
-        if (mActivity.get() == null) return;
+        Activity activity = mWindowAndroid.getActivity().get();
+        if (activity == null) return;
 
         PasswordManagerDialogContents contents = createDialogContents(
                 credentialLeakTitle, credentialLeakDetails, positiveButton, negativeButton);
         contents.setPrimaryButtonFilled(negativeButton != null);
         contents.setHelpButtonCallback(this::showHelpArticle);
 
-        mCredentialLeakDialog.initialize(mActivity.get(), contents);
+        mCredentialLeakDialog.initialize(activity, contents);
         mCredentialLeakDialog.showDialog();
     }
 
@@ -80,13 +87,15 @@
     }
 
     private void showHelpArticle() {
-        if (mActivity.get() == null) return;
+        Activity activity = mWindowAndroid.getActivity().get();
+        if (activity == null) return;
 
-        Profile profile = Profile.fromWebContents(
-                mActivity.get().getActivityTabProvider().get().getWebContents());
-        HelpAndFeedbackLauncherImpl.getInstance().show(mActivity.get(),
-                mActivity.get().getString(R.string.help_context_password_leak_detection), profile,
-                null);
+        Tab currentTab = TabModelSelectorSupplier.getCurrentTabFrom(mWindowAndroid);
+        if (currentTab == null) return;
+
+        Profile profile = Profile.fromWebContents(currentTab.getWebContents());
+        HelpAndFeedbackLauncherImpl.getInstance().show(activity,
+                activity.getString(R.string.help_context_password_leak_detection), profile, null);
     }
 
     @NativeMethods
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java
index a4a738a..a7eb32db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordChangeLauncher.java
@@ -4,9 +4,7 @@
 
 package org.chromium.chrome.browser.password_manager;
 
-import org.chromium.base.Log;
 import org.chromium.base.annotations.CalledByNative;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
 import org.chromium.chrome.browser.autofill_assistant.TriggerContext;
 import org.chromium.ui.base.WindowAndroid;
@@ -14,8 +12,6 @@
 
 /** Class for starting a password change flow in Autofill Assistant. */
 public class PasswordChangeLauncher {
-    private static final String TAG = "AutofillAssistant";
-
     /**
      * Name for the parameter that stores session username. Should be synced with
      * |kSessionUsernameParameterName| from
@@ -35,12 +31,7 @@
 
     public static void start(WindowAndroid windowAndroid, GURL origin, String username,
             String debugBundleId, String debutSocketId) {
-        ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get();
-        if (activity == null) {
-            Log.v(TAG, "Failed to retrieve ChromeActivity.");
-            return;
-        }
-        AutofillAssistantFacade.start(activity,
+        AutofillAssistantFacade.start(windowAndroid.getActivity().get(),
                 TriggerContext.newBuilder()
                         .withInitialUrl(origin.getSpec())
                         .addParameter(DEBUG_BUNDLE_ID, debugBundleId)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java
index 32135470..53e1a8a4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogCoordinator.java
@@ -37,11 +37,10 @@
     private PropertyModel mModel;
 
     public PasswordManagerDialogCoordinator(ModalDialogManager modalDialogManager,
-            View androidContentView, BrowserControlsStateProvider browserControlsStateProvider,
-            int containerHeightResource) {
+            View androidContentView, BrowserControlsStateProvider browserControlsStateProvider) {
         mMediator = new PasswordManagerDialogMediator(
                 new PropertyModel.Builder(ModalDialogProperties.ALL_KEYS), modalDialogManager,
-                androidContentView, browserControlsStateProvider, containerHeightResource);
+                androidContentView, browserControlsStateProvider);
     }
 
     public void initialize(Context context, PasswordManagerDialogContents contents) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
index 0f81d603..d703831 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogMediator.java
@@ -29,7 +29,6 @@
     private final ModalDialogManager mDialogManager;
     private final View mAndroidContentView;
     private final BrowserControlsStateProvider mBrowserControlsStateProvider;
-    private final int mContainerHeightResource;
 
     private PropertyModel.Builder mHostDialogModelBuilder;
     private PropertyModel mHostDialogModel;
@@ -66,12 +65,11 @@
 
     PasswordManagerDialogMediator(PropertyModel.Builder hostDialogModelBuilder,
             ModalDialogManager manager, View androidContentView,
-            BrowserControlsStateProvider controlsStateProvider, int containerHeightResource) {
+            BrowserControlsStateProvider controlsStateProvider) {
         mDialogManager = manager;
         mHostDialogModelBuilder = hostDialogModelBuilder;
         mAndroidContentView = androidContentView;
         mBrowserControlsStateProvider = controlsStateProvider;
-        mContainerHeightResource = containerHeightResource;
         mAndroidContentView.addOnLayoutChangeListener(this);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java
index e8321ffb..66a0c879 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java
@@ -32,8 +32,7 @@
         ChromeActivity activity = (ChromeActivity) windowAndroid.getActivity().get();
         mActivity = new WeakReference<>(activity);
         mDialogCoordinator = new PasswordManagerDialogCoordinator(activity.getModalDialogManager(),
-                activity.findViewById(android.R.id.content), activity.getBrowserControlsManager(),
-                activity.getControlContainerHeightResource());
+                activity.findViewById(android.R.id.content), activity.getBrowserControlsManager());
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
index 41d709e0..2c63897 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
@@ -67,7 +67,6 @@
     private final IdentityManager mIdentityManager;
     private final IdentityMutator mIdentityMutator;
     private final AndroidSyncSettings mAndroidSyncSettings;
-    private final ExternalAuthUtils mExternalAuthUtils;
     private final ObserverList<SignInStateObserver> mSignInStateObservers = new ObserverList<>();
     private final ObserverList<SignInAllowedObserver> mSignInAllowedObservers =
             new ObserverList<>();
@@ -109,8 +108,7 @@
         assert identityManager != null;
         assert identityMutator != null;
         final SigninManagerImpl signinManager = new SigninManagerImpl(nativeSigninManagerAndroid,
-                accountTrackerService, identityManager, identityMutator, AndroidSyncSettings.get(),
-                ExternalAuthUtils.getInstance());
+                accountTrackerService, identityManager, identityMutator, AndroidSyncSettings.get());
 
         identityManager.addObserver(signinManager);
         AccountInfoService.init(identityManager, accountTrackerService);
@@ -125,8 +123,7 @@
 
     private SigninManagerImpl(long nativeSigninManagerAndroid,
             AccountTrackerService accountTrackerService, IdentityManager identityManager,
-            IdentityMutator identityMutator, AndroidSyncSettings androidSyncSettings,
-            ExternalAuthUtils externalAuthUtils) {
+            IdentityMutator identityMutator, AndroidSyncSettings androidSyncSettings) {
         ThreadUtils.assertOnUiThread();
         assert androidSyncSettings != null;
         mNativeSigninManagerAndroid = nativeSigninManagerAndroid;
@@ -134,7 +131,6 @@
         mIdentityManager = identityManager;
         mIdentityMutator = identityMutator;
         mAndroidSyncSettings = androidSyncSettings;
-        mExternalAuthUtils = externalAuthUtils;
 
         mSigninAllowedByPolicy =
                 SigninManagerImplJni.get().isSigninAllowedByPolicy(mNativeSigninManagerAndroid);
@@ -634,7 +630,7 @@
     }
 
     private boolean isGooglePlayServicesPresent() {
-        return !mExternalAuthUtils.isGooglePlayServicesMissing(
+        return !ExternalAuthUtils.getInstance().isGooglePlayServicesMissing(
                 ContextUtils.getApplicationContext());
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncConsentFragmentBase.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncConsentFragmentBase.java
index 02fbfd5..c9a3043c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncConsentFragmentBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SyncConsentFragmentBase.java
@@ -42,7 +42,6 @@
 import org.chromium.components.externalauth.UserRecoverableErrorHandler;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
-import org.chromium.components.signin.AccountManagerResult;
 import org.chromium.components.signin.AccountUtils;
 import org.chromium.components.signin.AccountsChangeObserver;
 import org.chromium.components.signin.ChildAccountStatus;
@@ -279,8 +278,9 @@
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         boolean cancelable = !ChildAccountStatus.isChild(mChildAccountStatus);
-        ExternalAuthUtils.getInstance().canUseGooglePlayServices(
+        mHasGmsError = !ExternalAuthUtils.getInstance().canUseGooglePlayServices(
                 new UserRecoverableErrorHandler.ModalDialog(requireActivity(), cancelable));
+        mView.getAcceptButton().setEnabled(!mHasGmsError);
     }
 
     /**
@@ -478,11 +478,8 @@
                 mAccountPickerDialogCoordinator = null;
             }
             // Wait for the account cache to be updated and select newly-added account.
-            mAccountManagerFacade.waitForPendingUpdates(() -> {
-                mAccountSelectionPending = true;
-                mRequestedAccountName = addedAccountName;
-                triggerUpdateAccounts();
-            });
+            mAccountSelectionPending = true;
+            mRequestedAccountName = addedAccountName;
         }
     }
 
@@ -510,21 +507,15 @@
     }
 
     private void triggerUpdateAccounts() {
-        mAccountManagerFacade.getGoogleAccounts(this::updateAccounts);
+        mAccountManagerFacade.tryGetGoogleAccounts(accounts -> {
+            if (isResumed() && !mHasGmsError) {
+                updateAccounts(accounts);
+            }
+        });
     }
 
-    private void updateAccounts(AccountManagerResult<List<Account>> accounts) {
-        if (!isResumed()) {
-            return;
-        }
-
-        List<String> accountNames =
-                accounts.hasValue() ? AccountUtils.toAccountNames(accounts.getValue()) : null;
-        mHasGmsError = accountNames == null;
-        mView.getAcceptButton().setEnabled(accountNames != null);
-        if (accountNames == null) {
-            return;
-        } else if (accountNames.isEmpty()) {
+    private void updateAccounts(List<Account> accounts) {
+        if (accounts.isEmpty()) {
             mSelectedAccountName = null;
             mAccountSelectionPending = false;
             setHasAccounts(false);
@@ -532,9 +523,8 @@
         } else {
             setHasAccounts(true);
         }
-
         if (mAccountSelectionPending) {
-            String defaultAccount = accountNames.get(0);
+            String defaultAccount = accounts.get(0).name;
             String accountToSelect =
                     mRequestedAccountName != null ? mRequestedAccountName : defaultAccount;
             selectAccount(accountToSelect, accountToSelect.equals(defaultAccount));
@@ -542,7 +532,10 @@
             mRequestedAccountName = null;
         }
 
-        if (mSelectedAccountName != null && accountNames.contains(mSelectedAccountName)) return;
+        if (mSelectedAccountName != null
+                && AccountUtils.findAccountByName(accounts, mSelectedAccountName) != null) {
+            return;
+        }
 
         if (mConfirmSyncDataStateMachine != null) {
             // Any dialogs that may have been showing are now invalid (they were created
@@ -557,7 +550,7 @@
             return;
         }
 
-        selectAccount(accountNames.get(0), true);
+        selectAccount(accounts.get(0).name, true);
         // Show account picker to user to confirm the account selection
         mAccountPickerDialogCoordinator =
                 new AccountPickerDialogCoordinator(requireContext(), this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenMostVisitedTileClickObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenMostVisitedTileClickObserver.java
index a43f0a7..d0c1c98 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenMostVisitedTileClickObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenMostVisitedTileClickObserver.java
@@ -13,7 +13,6 @@
 import org.chromium.chrome.browser.tab.CurrentTabObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.embedder_support.util.UrlUtilities;
 import org.chromium.url.GURL;
 
 /**
@@ -38,7 +37,7 @@
         mCurrentTabObserver = new CurrentTabObserver(tabSupplier, new EmptyTabObserver() {
             @Override
             public void onPageLoadFinished(Tab tab, GURL url) {
-                if (UrlUtilities.isNTPUrl(url)) {
+                if (isNTP(tab)) {
                     // If we are on NTP, add ourselves as an observer for most visited tiles.
                     NewTabPage ntp = (NewTabPage) tab.getNativePage();
                     ntp.addMostVisitedTileClickObserver(
@@ -67,10 +66,13 @@
     }
 
     private void removeObserver(Tab tab) {
-        if (tab.getNativePage() == null) return;
-        if (!UrlUtilities.isNTPUrl(tab.getNativePage().getUrl())) return;
+        if (!isNTP(tab)) return;
 
         NewTabPage ntp = (NewTabPage) tab.getNativePage();
         ntp.removeMostVisitedTileClickObserver(this);
     }
+
+    private boolean isNTP(Tab tab) {
+        return tab != null && tab.getNativePage() instanceof NewTabPage;
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index d9c2fbc..b2d27c9f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -209,6 +209,8 @@
             ChromeFeatureList.RELATED_SEARCHES, true, ChromeFeatureList.RELATED_SEARCHES_UI, false);
     private static final ImmutableMap<String, Boolean> ENABLE_RELATED_SEARCHES_UI = ImmutableMap.of(
             ChromeFeatureList.RELATED_SEARCHES, true, ChromeFeatureList.RELATED_SEARCHES_UI, true);
+    private static final ImmutableMap<String, Boolean> DISABLE_FORCE_CAPTION =
+            ImmutableMap.of(ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, false);
     private static final ImmutableMap<String, Boolean> ENABLE_FORCE_CAPTION =
             ImmutableMap.of(ChromeFeatureList.CONTEXTUAL_SEARCH_FORCE_CAPTION, true);
 
@@ -3797,6 +3799,7 @@
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     public void testNonResolveCaption() throws Exception {
         // Simulate a non-resolve search and make sure no Caption is shown.
+        FeatureList.setTestFeatures(DISABLE_FORCE_CAPTION);
         simulateNonResolveSearch("search");
         Assert.assertFalse(mPanel.getSearchBarControl().getCaptionVisible());
         closePanel();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java
index 2361d5d..559a03e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java
@@ -71,8 +71,7 @@
                 TestThreadUtils.runOnUiThreadBlockingNoException(activity::getModalDialogManager);
 
         mCoordinator = new PasswordManagerDialogCoordinator(dialogManager,
-                activity.findViewById(android.R.id.content), activity.getBrowserControlsManager(),
-                activity.getControlContainerHeightResource());
+                activity.findViewById(android.R.id.content), activity.getBrowserControlsManager());
         PasswordManagerDialogContents contents = new PasswordManagerDialogContents(TITLE, DETAILS,
                 R.drawable.data_reduction_illustration, OK_BUTTON, CANCEL_BUTTON, mOnClick);
         contents.setDialogType(ModalDialogManager.ModalDialogType.TAB);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java
index cffaa5e5..9a8ae3c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/SigninSignoutIntegrationTest.java
@@ -12,8 +12,10 @@
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.support.test.InstrumentationRegistry;
 
@@ -50,6 +52,7 @@
 import org.chromium.chrome.test.util.ActivityTestUtils;
 import org.chromium.chrome.test.util.BookmarkTestUtil;
 import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
+import org.chromium.components.externalauth.ExternalAuthUtils;
 import org.chromium.components.signin.GAIAServiceType;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.ConsentLevel;
@@ -89,6 +92,9 @@
     public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
     @Mock
+    private ExternalAuthUtils mExternalAuthUtilsMock;
+
+    @Mock
     private SigninMetricsUtils.Natives mSigninMetricsUtilsNativeMock;
 
     @Mock
@@ -117,6 +123,8 @@
     @Test
     @LargeTest
     public void testSignIn() {
+        when(mExternalAuthUtilsMock.canUseGooglePlayServices(any())).thenReturn(true);
+        ExternalAuthUtils.setInstanceForTesting(mExternalAuthUtilsMock);
         CoreAccountInfo coreAccountInfo = mAccountManagerTestRule.addAccountAndWaitForSeeding(
                 AccountManagerTestRule.TEST_ACCOUNT_EMAIL);
         SyncConsentActivity syncConsentActivity = ActivityTestUtils.waitForActivity(
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 6ef74f4..0eb84ee 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2343,6 +2343,18 @@
   <message name="IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_CANCEL" desc="Settings > Internet > Network details >Rename profile dialog: The Label for the dialog cancel button to rename an eSIM cellular network">
     Cancel
   </message>
+  <message name="IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_SUBTITLE" desc="Settings > Internet > Network details >Rename profile dialog: The Label for the dialog input that renames an eSIM cellular network, informing the user that letters, numbers and special characters are allowed.">
+    Name can use letters, numbers, and special characters
+  </message>
+  <message name="IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_CHARACTER_COUNT" desc="Settings > Internet > Network details >Rename profile dialog: The Label for the dialog input that renames an eSIM cellular network, informing the user the number of characters they have inputted compared to the maximum number of characters allowed.">
+    <ph name="CURRENT_CHARACTER_COUNT">$1<ex>15</ex></ph>/<ph name="MAX_CHARACTER_COUNT">$2<ex>20</ex></ph>
+  </message>
+  <message name="IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_A11Y_LABEL" desc="Settings > Internet > Network details >Rename profile dialog: The a11y label for the dialog input that renames an eSIM cellular network, informing the user that letters, numbers and special characters are allowed, as well as the maximum number of characters permitted.">
+    Name can use letters, numbers, and special characters, and must be <ph name="MAX_CHARACTER_COUNT">$1<ex>20</ex></ph> characters or fewer
+  </message>
+  <message name="IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_ERROR_TOAST" desc="Settings > Internet > Network details > Rename eSIM cellular profile dialog: Message in toast shown when rename fails">
+    Profile could not be renamed. Please try again or contact your carrier for technical support.
+  </message>
   <message name="IDS_SETTINGS_INTERNET_NETWORK_REMOVE_PROFILE_DIALOG_TITLE" desc="Settings > Internet > Network details >Remove profile dialog: Title remove esim profile dialog">
     Remove "<ph name="ESIM_PROFILE_NAME">$1<ex>Profile 1</ex></ph>"?
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_ERROR_TOAST.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_ERROR_TOAST.png.sha1
new file mode 100644
index 0000000..adcd85f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_ERROR_TOAST.png.sha1
@@ -0,0 +1 @@
+2cc2b5f780da11731548606394bec65fd564223d
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_A11Y_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_A11Y_LABEL.png.sha1
new file mode 100644
index 0000000..6a8518b
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@
+4ba2fa3c3213f86df35cc425a0a6747fda320a77
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_CHARACTER_COUNT.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_CHARACTER_COUNT.png.sha1
new file mode 100644
index 0000000..6a8518b
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_CHARACTER_COUNT.png.sha1
@@ -0,0 +1 @@
+4ba2fa3c3213f86df35cc425a0a6747fda320a77
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_SUBTITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_SUBTITLE.png.sha1
new file mode 100644
index 0000000..6a8518b
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+4ba2fa3c3213f86df35cc425a0a6747fda320a77
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 7e8e8607..5b9b02e 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5672,6 +5672,10 @@
     {"enable-unsafe-webgpu", flag_descriptions::kUnsafeWebGPUName,
      flag_descriptions::kUnsafeWebGPUDescription, kOsMac | kOsLinux | kOsWin,
      SINGLE_VALUE_TYPE(switches::kEnableUnsafeWebGPU)},
+    {"enable-unsafe-webgpu-service",
+     flag_descriptions::kUnsafeWebGPUServiceName,
+     flag_descriptions::kUnsafeWebGPUServiceDescription,
+     kOsMac | kOsLinux | kOsWin, FEATURE_VALUE_TYPE(features::kWebGPUService)},
 
     {"enable-unsafe-fast-js-calls", flag_descriptions::kUnsafeFastJSCallsName,
      flag_descriptions::kUnsafeFastJSCallsDescription, kOsAll,
diff --git a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
index f982a61c..43b3e69 100644
--- a/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/ash/accessibility/spoken_feedback_browsertest.cc
@@ -40,7 +40,7 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
diff --git a/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc b/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc
index 1c8bedbe..c49bbcd 100644
--- a/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc
+++ b/chrome/browser/ash/apps/apk_web_app_installer_browsertest.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/app_registrar_observer.h"
diff --git a/chrome/browser/ash/apps/apk_web_app_service.cc b/chrome/browser/ash/apps/apk_web_app_service.cc
index e8de9c7..7556a0e 100644
--- a/chrome/browser/ash/apps/apk_web_app_service.cc
+++ b/chrome/browser/ash/apps/apk_web_app_service.cc
@@ -12,7 +12,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/ash/apps/apk_web_app_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
 #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
diff --git a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
index e08adff..de07fab5 100644
--- a/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
+++ b/chrome/browser/ash/arc/accessibility/arc_accessibility_helper_bridge_browsertest.cc
@@ -11,7 +11,7 @@
 #include "base/feature_list.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.h"
+#include "chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/arc/arc_service_manager.h"
diff --git a/chrome/browser/ash/arc/arc_util.cc b/chrome/browser/ash/arc/arc_util.cc
index 036ab8e0..33a3f20 100644
--- a/chrome/browser/ash/arc/arc_util.cc
+++ b/chrome/browser/ash/arc/arc_util.cc
@@ -42,7 +42,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profiles_state.h"
 #include "chrome/browser/tab_contents/tab_util.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/simple_message_box.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/arc/arc_features.h"
diff --git a/chrome/browser/ash/arc/fileapi/arc_select_files_handler.cc b/chrome/browser/ash/arc/fileapi/arc_select_files_handler.cc
index 28e22f4..542a8d63 100644
--- a/chrome/browser/ash/arc/fileapi/arc_select_files_handler.cc
+++ b/chrome/browser/ash/arc/fileapi/arc_select_files_handler.cc
@@ -20,7 +20,7 @@
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
 #include "chrome/common/chrome_isolated_world_ids.h"
diff --git a/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.cc b/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.cc
index 3cefc8b5..e0cead0b 100644
--- a/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.cc
+++ b/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.cc
@@ -31,7 +31,8 @@
   // Should be a no-op on ARC. This is managed on the Android side.
 }
 
-void ArcPictureInPictureWindowControllerImpl::OnWindowDestroyed() {
+void ArcPictureInPictureWindowControllerImpl::OnWindowDestroyed(
+    bool should_pause_video) {
   // Should be a no-op on ARC. This is managed on the Android side.
 }
 
diff --git a/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.h b/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.h
index da4f290b..4dcf1ef 100644
--- a/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.h
+++ b/chrome/browser/ash/arc/pip/arc_picture_in_picture_window_controller_impl.h
@@ -33,7 +33,7 @@
   void Show() override;
   void Close(bool should_pause_video) override;
   void CloseAndFocusInitiator() override;
-  void OnWindowDestroyed() override;
+  void OnWindowDestroyed(bool should_pause_video) override;
   content::OverlayWindow* GetWindowForTesting() override;
   void UpdateLayerBounds() override;
   bool IsPlayerActive() override;
diff --git a/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler.cc b/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler.cc
index 1fa189b1..d62ffd27 100644
--- a/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler.cc
+++ b/chrome/browser/ash/arc/session/arc_play_store_enabled_preference_handler.cc
@@ -17,7 +17,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_util.h"
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index 922e5af..9191e199 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -49,7 +49,7 @@
 #include "chrome/browser/component_updater/cros_component_manager.h"
 #include "chrome/browser/notifications/system_notification_helper.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "chromeos/startup/startup_switches.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/login/chrome_restart_request.cc b/chrome/browser/ash/login/chrome_restart_request.cc
index b0b5fbe..d9f73ac 100644
--- a/chrome/browser/ash/login/chrome_restart_request.cc
+++ b/chrome/browser/ash/login/chrome_restart_request.cc
@@ -152,6 +152,7 @@
     ::switches::kDisableWebGLImageChromium,
     ::switches::kEnableWebGLImageChromium,
     ::switches::kEnableUnsafeWebGPU,
+    ::switches::kEnableUnsafeWebGPUService,
     ::switches::kDisableWebRtcHWDecoding,
     ::switches::kDisableWebRtcHWEncoding,
     ::switches::kOzonePlatform,
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_files.cc b/chrome/browser/ash/plugin_vm/plugin_vm_files.cc
index 6b715f6..dc5eca5 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_files.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_files.cc
@@ -23,9 +23,9 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chromeos/dbus/cicerone/cicerone_client.h"
 #include "chromeos/dbus/cicerone/cicerone_service.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
index c18d639..13de937 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_files_unittest.cc
@@ -17,10 +17,10 @@
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/cicerone/fake_cicerone_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
index 7c2db00..e00e894 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl.cc
@@ -17,9 +17,9 @@
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h"
 #include "chrome/browser/ui/simple_message_box.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
index e2676a89..1a3da623 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_manager_impl_unittest.cc
@@ -19,9 +19,9 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/fake_dlcservice_client.h"
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_test_helper.cc b/chrome/browser/ash/plugin_vm/plugin_vm_test_helper.cc
index 880b37e4..75565ee 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_test_helper.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_test_helper.cc
@@ -14,7 +14,7 @@
 #include "chrome/browser/ash/plugin_vm/plugin_vm_pref_names.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_util.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
index 0a3b1e8..a2194594 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_util.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/ash/settings/cros_settings.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc b/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc
index 3bd3c5cc..e1cb1b5 100644
--- a/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc
+++ b/chrome/browser/ash/remote_apps/remote_apps_impl_browsertest.cc
@@ -28,7 +28,7 @@
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_paths.h"
 #include "components/policy/proto/chrome_device_policy.pb.h"
 #include "components/user_manager/user.h"
diff --git a/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc b/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc
index 55fd0f4..2ff2e2a 100644
--- a/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc
+++ b/chrome/browser/ash/remote_apps/remote_apps_manager_browsertest.cc
@@ -30,7 +30,7 @@
 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chromeos/login/auth/user_context.h"
 #include "components/account_id/account_id.h"
diff --git a/chrome/browser/bluetooth/web_bluetooth_browsertest.cc b/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
index cd07296..0c254d9 100644
--- a/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
+++ b/chrome/browser/bluetooth/web_bluetooth_browsertest.cc
@@ -234,11 +234,15 @@
   TestBluetoothDelegate(const TestBluetoothDelegate&) = delete;
   TestBluetoothDelegate& operator=(const TestBluetoothDelegate&) = delete;
 
-  void SetDeviceToSelect(const std::string& device_address) {
-    device_to_select_ = device_address;
+  void UseRealChooser() {
+    EXPECT_FALSE(device_to_select_.has_value());
+    use_real_chooser_ = true;
   }
 
-  void UseRealChooser() { use_real_chooser_ = true; }
+  void SetDeviceToSelect(const std::string& device_address) {
+    EXPECT_FALSE(use_real_chooser_);
+    device_to_select_ = device_address;
+  }
 
  protected:
   // content::BluetoothDelegate implementation:
@@ -502,6 +506,31 @@
   EXPECT_EQ(GURL("https://google.com"), web_contents_->GetLastCommittedURL());
 }
 
+IN_PROC_BROWSER_TEST_F(WebBluetoothTest, ShowChooserInBackgroundTab) {
+  UseRealChooser();
+
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Create a new foreground tab that covers |web_contents|.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("https://example.com"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  // Try to show the chooser in the background tab.
+  EXPECT_EQ("NotFoundError: User cancelled the requestDevice() chooser.",
+            content::EvalJs(web_contents,
+                            R"((async () => {
+      try {
+        await navigator.bluetooth.requestDevice({ filters: [{name: 'Hello'}] });
+        return "Expected error, got success.";
+      } catch (e) {
+        return `${e.name}: ${e.message}`;
+      }
+    })())"));
+}
+
 // The new Web Bluetooth permissions backend is currently implemented behind a
 // feature flag.
 // TODO(https://crbug.com/589228): Delete this class and convert all the tests
diff --git a/chrome/browser/cart/cart_db_content.proto b/chrome/browser/cart/cart_db_content.proto
index 5b528cfe..de9da9fe2 100644
--- a/chrome/browser/cart/cart_db_content.proto
+++ b/chrome/browser/cart/cart_db_content.proto
@@ -8,11 +8,19 @@
 
 package cart_db;
 
+// Used for storing the discount information of this cart.
 message ChromeCartDiscountProto {
   // String indicating the content of the discount on this cart (e.g. 15% off).
   string discount_text = 1;
 }
 
+// Used for storing information of products within the cart.
+message ChromeCartProductProto {
+  // String indicating the ID of the product. The format of IDs may vary among
+  // different merchants.
+  string product_id = 1;
+}
+
 // Used for storing ChromeCart Content.
 message ChromeCartContentProto {
   // Original key for data.
@@ -40,4 +48,7 @@
 
   // Information about current discount on the cart.
   ChromeCartDiscountProto discount_info = 8;
+
+  // Information of products within the cart.
+  repeated ChromeCartProductProto product_infos = 9;
 }
diff --git a/chrome/browser/cart/cart_service.cc b/chrome/browser/cart/cart_service.cc
index 37f48fa..3e0734d1 100644
--- a/chrome/browser/cart/cart_service.cc
+++ b/chrome/browser/cart/cart_service.cc
@@ -460,20 +460,40 @@
   if (existing_proto.is_removed()) {
     return;
   }
-  // If the new proto has no product images, keep the existing proto while
-  // update timestamp and hidden status; otherwise add the new proto.
-  if (proto.product_image_urls().size() == 0) {
-    existing_proto.set_is_hidden(false);
-    existing_proto.set_timestamp(proto.timestamp());
-    if (cart_url) {
-      existing_proto.set_merchant_cart_url(cart_url->spec());
-    }
-    cart_db_->AddCart(domain, std::move(existing_proto),
-                      base::BindOnce(&CartService::OnOperationFinished,
-                                     weak_ptr_factory_.GetWeakPtr()));
-  } else {
+  // If the new proto has product images, we can add it to the database without
+  // worrying about overwriting as it reflects the latest state; if not, we keep
+  // the existing proto while updating timestamp, hidden status and product
+  // information if any.
+  if (proto.product_image_urls().size()) {
     cart_db_->AddCart(domain, std::move(proto),
                       base::BindOnce(&CartService::OnOperationFinished,
                                      weak_ptr_factory_.GetWeakPtr()));
+    return;
   }
+  existing_proto.set_is_hidden(false);
+  existing_proto.set_timestamp(proto.timestamp());
+  if (cart_url) {
+    existing_proto.set_merchant_cart_url(cart_url->spec());
+  }
+  // If no product images, this addition comes from AddToCart detection and
+  // should have only one product (if any). Add this product to the existing
+  // cart if not included already.
+  if (proto.product_infos().size()) {
+    DCHECK_EQ(1, proto.product_infos().size());
+    auto new_product_info = std::move(proto.product_infos().at(0));
+    bool is_included = false;
+    for (auto product_proto : existing_proto.product_infos()) {
+      is_included |=
+          (product_proto.product_id() == new_product_info.product_id());
+      if (is_included)
+        break;
+    }
+    if (!is_included) {
+      auto* added_product = existing_proto.add_product_infos();
+      *added_product = std::move(new_product_info);
+    }
+  }
+  cart_db_->AddCart(domain, std::move(existing_proto),
+                    base::BindOnce(&CartService::OnOperationFinished,
+                                   weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/cart/cart_service_unittest.cc b/chrome/browser/cart/cart_service_unittest.cc
index 490c8ae..2530754 100644
--- a/chrome/browser/cart/cart_service_unittest.cc
+++ b/chrome/browser/cart/cart_service_unittest.cc
@@ -27,6 +27,13 @@
   return proto;
 }
 
+cart_db::ChromeCartProductProto BuildProductProto(
+    const std::string& product_id) {
+  cart_db::ChromeCartProductProto proto;
+  proto.set_product_id(product_id);
+  return proto;
+}
+
 constexpr char kFakeDataPrefix[] = "Fake:";
 const char kMockMerchantA[] = "foo.com";
 const char kMockMerchantURLA[] = "https://www.foo.com";
@@ -40,6 +47,7 @@
     BuildProto(kMockMerchantB, kMockMerchantURLB);
 using ShoppingCarts =
     std::vector<ProfileProtoDB<cart_db::ChromeCartContentProto>::KeyAndValue>;
+using ProductInfos = std::vector<cart_db::ChromeCartProductProto>;
 const ShoppingCarts kExpectedA = {{kMockMerchantA, kMockProtoA}};
 const ShoppingCarts kExpectedB = {{kMockMerchantB, kMockProtoB}};
 const ShoppingCarts kExpectedAB = {
@@ -140,6 +148,20 @@
     std::move(closure).Run();
   }
 
+  void GetEvaluationProductInfo(base::OnceClosure closure,
+                                ProductInfos expected_products,
+                                bool result,
+                                ShoppingCarts found_carts) {
+    EXPECT_EQ(1U, found_carts.size());
+    auto found_products = found_carts[0].second.product_infos();
+    EXPECT_EQ((size_t)found_products.size(), expected_products.size());
+    for (size_t i = 0; i < expected_products.size(); i++) {
+      EXPECT_EQ(found_products.at(i).product_id(),
+                expected_products[i].product_id());
+    }
+    std::move(closure).Run();
+  }
+
   std::string getDomainName(base::StringPiece domain) {
     std::string* res = service_->domain_name_mapping_->FindStringKey(domain);
     if (!res)
@@ -312,6 +334,64 @@
   run_loop[2].Run();
 }
 
+TEST_F(CartServiceTest, TestAddCartWithProductInfo) {
+  base::RunLoop run_loop[6];
+  CartDB* cart_db_ = service_->GetDB();
+  cart_db::ChromeCartContentProto merchant_proto =
+      BuildProto(kMockMerchantA, kMockMerchantURLA);
+  merchant_proto.set_timestamp(0);
+  merchant_proto.add_product_image_urls("https://image1.com");
+  service_->AddCart(kMockMerchantA, base::nullopt, merchant_proto);
+  task_environment_.RunUntilIdle();
+
+  // Adding a new proto with new product infos should reflect in storage.
+  cart_db::ChromeCartContentProto new_proto =
+      BuildProto(kMockMerchantA, kMockMerchantURLA);
+  const auto& product_info = BuildProductProto("id_foo");
+  auto* added_product = new_proto.add_product_infos();
+  *added_product = product_info;
+  new_proto.set_timestamp(1);
+  service_->AddCart(kMockMerchantA, base::nullopt, new_proto);
+  task_environment_.RunUntilIdle();
+
+  cart_db_->LoadCart(
+      kMockMerchantA,
+      base::BindOnce(&CartServiceTest::GetEvaluationCartTimeStamp,
+                     base::Unretained(this), run_loop[1].QuitClosure(), 1));
+  run_loop[1].Run();
+  const ShoppingCarts& expected_carts = {{kMockMerchantA, merchant_proto}};
+  cart_db_->LoadCart(
+      kMockMerchantA,
+      base::BindOnce(&CartServiceTest::GetEvaluationURL, base::Unretained(this),
+                     run_loop[2].QuitClosure(), expected_carts));
+  run_loop[2].Run();
+  const ProductInfos& expected_products = {product_info};
+  cart_db_->LoadCart(
+      kMockMerchantA,
+      base::BindOnce(&CartServiceTest::GetEvaluationProductInfo,
+                     base::Unretained(this), run_loop[3].QuitClosure(),
+                     expected_products));
+  run_loop[3].Run();
+
+  // Adding a new proto with same product infos shouldn't change the current
+  // storage about product infos.
+  new_proto.set_timestamp(2);
+  service_->AddCart(kMockMerchantA, base::nullopt, new_proto);
+  task_environment_.RunUntilIdle();
+
+  cart_db_->LoadCart(
+      kMockMerchantA,
+      base::BindOnce(&CartServiceTest::GetEvaluationCartTimeStamp,
+                     base::Unretained(this), run_loop[4].QuitClosure(), 2));
+  run_loop[4].Run();
+  cart_db_->LoadCart(
+      kMockMerchantA,
+      base::BindOnce(&CartServiceTest::GetEvaluationProductInfo,
+                     base::Unretained(this), run_loop[5].QuitClosure(),
+                     expected_products));
+  run_loop[5].Run();
+}
+
 // Tests deleting one cart from the service.
 TEST_F(CartServiceTest, TestDeleteCart) {
   CartDB* cart_db_ = service_->GetDB();
diff --git a/chrome/browser/cart/commerce_hint_service.cc b/chrome/browser/cart/commerce_hint_service.cc
index 748e503..bd3a7b6b 100644
--- a/chrome/browser/cart/commerce_hint_service.cc
+++ b/chrome/browser/cart/commerce_hint_service.cc
@@ -32,6 +32,28 @@
       url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
 }
 
+void ConstructCartProto(cart_db::ChromeCartContentProto* proto,
+                        const GURL& navigation_url,
+                        std::vector<mojom::ProductPtr> products) {
+  const std::string& domain = GetDomain(navigation_url);
+  proto->set_key(domain);
+  proto->set_merchant(domain);
+  proto->set_merchant_cart_url(navigation_url.spec());
+  proto->set_timestamp(base::Time::Now().ToDoubleT());
+  for (auto& product : products) {
+    if (product->image_url.spec().size() != 0) {
+      proto->add_product_image_urls(product->image_url.spec());
+    }
+    if (product->product_id.size() != 0) {
+      cart_db::ChromeCartProductProto product_proto;
+      product_proto.set_product_id(std::move(product->product_id));
+      cart_db::ChromeCartProductProto* added_product =
+          proto->add_product_infos();
+      *added_product = std::move(product_proto);
+    }
+  }
+}
+
 }  // namespace
 
 // Implementation of the Mojo CommerceHintObserver. This is called by the
@@ -49,12 +71,13 @@
 
   ~CommerceHintObserverImpl() override = default;
 
-  void OnAddToCart(const base::Optional<GURL>& cart_url) override {
+  void OnAddToCart(const base::Optional<GURL>& cart_url,
+                   const std::string& product_id) override {
     DVLOG(1) << "Received OnAddToCart in the browser process on "
              << binding_url_;
     if (!service_ || !binding_url_.SchemeIsHTTPOrHTTPS())
       return;
-    service_->OnAddToCart(binding_url_, cart_url);
+    service_->OnAddToCart(binding_url_, cart_url, product_id);
   }
 
   void OnVisitCart() override {
@@ -138,7 +161,8 @@
 }
 
 void CommerceHintService::OnAddToCart(const GURL& navigation_url,
-                                      const base::Optional<GURL>& cart_url) {
+                                      const base::Optional<GURL>& cart_url,
+                                      const std::string& product_id) {
   if (ShouldSkip(navigation_url))
     return;
   base::Optional<GURL> validated_cart = cart_url;
@@ -148,6 +172,11 @@
   }
   cart_db::ChromeCartContentProto proto;
   std::vector<mojom::ProductPtr> products;
+  if (!product_id.empty()) {
+    mojom::ProductPtr product_ptr(mojom::Product::New());
+    product_ptr->product_id = product_id;
+    products.push_back(std::move(product_ptr));
+  }
   ConstructCartProto(&proto, navigation_url, std::move(products));
   service_->AddCart(GetDomain(navigation_url), validated_cart,
                     std::move(proto));
@@ -167,20 +196,6 @@
   service_->AddCart(proto.key(), cart_url, std::move(proto));
 }
 
-void CommerceHintService::ConstructCartProto(
-    cart_db::ChromeCartContentProto* proto,
-    const GURL& navigation_url,
-    std::vector<mojom::ProductPtr> products) {
-  const std::string& domain = GetDomain(navigation_url);
-  proto->set_key(domain);
-  proto->set_merchant(domain);
-  proto->set_merchant_cart_url(navigation_url.spec());
-  proto->set_timestamp(base::Time::Now().ToDoubleT());
-  for (auto& product : products) {
-    proto->add_product_image_urls(product->image_url.spec());
-  }
-}
-
 WEB_CONTENTS_USER_DATA_KEY_IMPL(CommerceHintService)
 
 }  // namespace cart
diff --git a/chrome/browser/cart/commerce_hint_service.h b/chrome/browser/cart/commerce_hint_service.h
index 3001b127..c7f6535 100644
--- a/chrome/browser/cart/commerce_hint_service.h
+++ b/chrome/browser/cart/commerce_hint_service.h
@@ -25,7 +25,8 @@
       mojo::PendingReceiver<mojom::CommerceHintObserver> receiver);
   content::WebContents* WebContents();
   void OnAddToCart(const GURL& navigation_url,
-                   const base::Optional<GURL>& cart_url);
+                   const base::Optional<GURL>& cart_url,
+                   const std::string& product_id = std::string());
   void OnRemoveCart(const GURL& url);
   void OnCartUpdated(const GURL& cart_url,
                      std::vector<mojom::ProductPtr> products);
@@ -39,9 +40,6 @@
                    bool success,
                    std::vector<CartDB::KeyAndValue> proto_pairs);
   void OnOperationFinished(const std::string& operation, bool success);
-  void ConstructCartProto(cart_db::ChromeCartContentProto* proto,
-                          const GURL& navigation_url,
-                          std::vector<mojom::ProductPtr> products);
 
   content::WebContents* web_contents_;
   CartService* service_;
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index 6ff86f21..ca77c5a5 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -32,11 +32,11 @@
 #include "chrome/browser/chromeos/crostini/crostini_terminal.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/webui/chromeos/crostini_upgrader/crostini_upgrader_dialog.h"
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 6286ce89..4ec8135d 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -97,8 +97,8 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
index aa51dc32..ea9326a 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
@@ -26,7 +26,7 @@
 #include "chrome/browser/ash/web_applications/system_web_app_integration_test.h"
 #include "chrome/browser/chromeos/full_restore/full_restore_service.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.h"
+#include "chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/chromeos/full_restore/arc_ghost_window_shell_surface.cc b/chrome/browser/chromeos/full_restore/arc_ghost_window_shell_surface.cc
index 7c63dc83..0f98106 100644
--- a/chrome/browser/chromeos/full_restore/arc_ghost_window_shell_surface.cc
+++ b/chrome/browser/chromeos/full_restore/arc_ghost_window_shell_surface.cc
@@ -78,7 +78,7 @@
           ->GetGpuMemoryBufferManager()
           ->CreateGpuMemoryBuffer({1, 1}, gfx::BufferFormat::RGBA_8888,
                                   gfx::BufferUsage::GPU_READ,
-                                  gpu::kNullSurfaceHandle));
+                                  gpu::kNullSurfaceHandle, nullptr));
   controller_surface_->Attach(buffer_.get());
   controller_surface_->SetFrame(exo::SurfaceFrameType::NORMAL);
   controller_surface_->Commit();
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
index cb9a437..e98792e 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
@@ -12,7 +12,6 @@
 #include "chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h
index 340c8fb..115f2c1c 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_RULES_MANAGER_H_
 #define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_RULES_MANAGER_H_
 
+#include <string>
+
 #include "components/keyed_service/core/keyed_service.h"
 
 class GURL;
@@ -93,6 +95,13 @@
   // serverside. Should always return a nullptr if reporting is disabled (see
   // IsReportingEnabled).
   virtual DlpReportingManager* GetReportingManager() const = 0;
+
+  // Returns the URL pattern that `source_url` is matched against. The returned
+  // URL pattern should be configured in a policy rule with the same
+  // `restriction` and `level`.
+  virtual std::string GetSourceUrlPattern(const GURL& source_url,
+                                          Restriction restriction,
+                                          Level level) const = 0;
 };
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
index ce89c73..ddb68c3 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.cc
@@ -255,6 +255,36 @@
   return reporting_manager_.get();
 }
 
+std::string DlpRulesManagerImpl::GetSourceUrlPattern(const GURL& source_url,
+                                                     Restriction restriction,
+                                                     Level level) const {
+  const std::set<UrlConditionId> url_conditions_ids =
+      src_url_matcher_->MatchURL(source_url);
+
+  std::map<RuleId, UrlConditionId> rules_conditions_map;
+  for (const auto& condition_id : url_conditions_ids) {
+    rules_conditions_map.insert(
+        std::make_pair(src_url_rules_mapping_.at(condition_id), condition_id));
+  }
+  auto restriction_itr = restrictions_map_.find(restriction);
+  if (restriction_itr == restrictions_map_.end())
+    return std::string();
+
+  const auto rules_levels_map = restriction_itr->second;
+  for (const auto& rule_level_entry : rules_levels_map) {
+    auto rule_id = rule_level_entry.first;
+    auto lvl = rule_level_entry.second;
+    auto rule_condition_itr = rules_conditions_map.find(rule_id);
+    if (lvl == level && rule_condition_itr != rules_conditions_map.end()) {
+      auto condition_id = rule_condition_itr->second;
+      auto condition_pattern_itr = src_pattterns_mapping_.find(condition_id);
+      if (condition_pattern_itr != src_pattterns_mapping_.end())
+        return condition_pattern_itr->second;
+    }
+  }
+  return std::string();
+}
+
 void DlpRulesManagerImpl::OnPolicyUpdate() {
   components_rules_.clear();
   restrictions_map_.clear();
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h
index 7cc809f7..6705a1fb9 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl.h
@@ -42,6 +42,9 @@
                               Restriction restriction) const override;
   bool IsReportingEnabled() const override;
   DlpReportingManager* GetReportingManager() const override;
+  std::string GetSourceUrlPattern(const GURL& source_url,
+                                  Restriction restriction,
+                                  Level level) const override;
 
  protected:
   friend class DlpRulesManagerFactory;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl_unittest.cc
index 7df20651..97ef7f04 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_rules_manager_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_test_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/scoped_testing_local_state.h"
@@ -509,4 +510,73 @@
                    ->GetSetDlpFilesPolicyCount());
 }
 
+TEST_F(DlpRulesManagerImplTest, GetSourceUrlPattern) {
+  base::Value rules(base::Value::Type::LIST);
+
+  base::Value src_urls_1(base::Value::Type::LIST);
+  src_urls_1.Append(kChatPattern);
+  src_urls_1.Append(kSalesforcePattern);
+  src_urls_1.Append(kDocsPattern);
+  src_urls_1.Append(kDrivePattern);
+  src_urls_1.Append(kCompanyPattern);
+
+  base::Value restrictions_1(base::Value::Type::LIST);
+  restrictions_1.Append(dlp_test_util::CreateRestrictionWithLevel(
+      dlp::kScreenshotRestriction, dlp::kBlockLevel));
+
+  rules.Append(dlp_test_util::CreateRule(
+      "Block screenshots", "Block screenshots of work urls",
+      std::move(src_urls_1),
+      /*dst_urls=*/base::Value(base::Value::Type::LIST),
+      /*dst_components=*/base::Value(base::Value::Type::LIST),
+      std::move(restrictions_1)));
+
+  base::Value src_urls_2(base::Value::Type::LIST);
+  src_urls_1.Append(kWildCardMatching);
+
+  base::Value restrictions_2(base::Value::Type::LIST);
+  restrictions_2.Append(dlp_test_util::CreateRestrictionWithLevel(
+      dlp::kPrintingRestriction, dlp::kBlockLevel));
+
+  rules.Append(dlp_test_util::CreateRule(
+      "Block any printing", "Block printing any docs", std::move(src_urls_1),
+      /*dst_urls=*/base::Value(base::Value::Type::LIST),
+      /*dst_components=*/base::Value(base::Value::Type::LIST),
+      std::move(restrictions_2)));
+
+  UpdatePolicyPref(std::move(rules));
+
+  EXPECT_EQ(std::string(kChatPattern),
+            dlp_rules_manager_.GetSourceUrlPattern(
+                GURL(std::string("https://") + std::string(kChatPattern)),
+                DlpRulesManager::Restriction::kScreenshot,
+                DlpRulesManager::Level::kBlock));
+  EXPECT_EQ(std::string(kSalesforcePattern),
+            dlp_rules_manager_.GetSourceUrlPattern(
+                GURL(std::string("https://") + std::string(kSalesforcePattern) +
+                     std::string("/xyz")),
+                DlpRulesManager::Restriction::kScreenshot,
+                DlpRulesManager::Level::kBlock));
+  EXPECT_EQ(std::string(kDocsPattern),
+            dlp_rules_manager_.GetSourceUrlPattern(
+                GURL(std::string("https://") + std::string(kDocsPattern) +
+                     std::string("/path?v=1")),
+                DlpRulesManager::Restriction::kScreenshot,
+                DlpRulesManager::Level::kBlock));
+  EXPECT_EQ(std::string(""),
+            dlp_rules_manager_.GetSourceUrlPattern(
+                GURL(std::string("https://") + std::string(kDrivePattern)),
+                DlpRulesManager::Restriction::kScreenshot,
+                DlpRulesManager::Level::kAllow));
+  EXPECT_EQ(std::string(""),
+            dlp_rules_manager_.GetSourceUrlPattern(
+                GURL(std::string("https://") + std::string(kCompanyPattern)),
+                DlpRulesManager::Restriction::kPrivacyScreen,
+                DlpRulesManager::Level::kBlock));
+  EXPECT_EQ(std::string(kWildCardMatching),
+            dlp_rules_manager_.GetSourceUrlPattern(
+                GURL(kGoogleUrl), DlpRulesManager::Restriction::kPrinting,
+                DlpRulesManager::Level::kBlock));
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h b/chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h
index 29f7ca4..762490c 100644
--- a/chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_POLICY_DLP_MOCK_DLP_RULES_MANAGER_H_
 
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
+#include "chrome/browser/chromeos/policy/dlp/mock_dlp_rules_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -36,6 +37,11 @@
   MOCK_CONST_METHOD0(IsReportingEnabled, bool());
 
   MOCK_CONST_METHOD0(GetReportingManager, DlpReportingManager*());
+
+  MOCK_CONST_METHOD3(GetSourceUrlPattern,
+                     std::string(const GURL& source_url,
+                                 Restriction restriction,
+                                 Level level));
 };
 
 }  // namespace policy
diff --git a/chrome/browser/commerce/merchant_viewer/android/BUILD.gn b/chrome/browser/commerce/merchant_viewer/android/BUILD.gn
index 9a4c2e71..2e17e98 100644
--- a/chrome/browser/commerce/merchant_viewer/android/BUILD.gn
+++ b/chrome/browser/commerce/merchant_viewer/android/BUILD.gn
@@ -12,6 +12,7 @@
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustDetailsTabMediator.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageContext.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageScheduler.java",
+    "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewModel.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMetrics.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsCoordinator.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsDataProvider.java",
@@ -19,6 +20,7 @@
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsEventStorage.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsMediator.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/MerchantViewerConfig.java",
+    "java/src/org/chromium/chrome/browser/merchant_viewer/RatingStarSpan.java",
     "java/src/org/chromium/chrome/browser/merchant_viewer/WebContentsHelpers.java",
   ]
 
@@ -120,6 +122,7 @@
   testonly = true
 
   sources = [
+    "javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java",
     "javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsCoordinatorTest.java",
     "javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsEventLoadCallbackHelper.java",
     "javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsEventStorageTest.java",
@@ -136,6 +139,7 @@
     "//chrome/test/android:chrome_java_test_support",
     "//components/browser_ui/bottomsheet/android:java",
     "//components/messages/android:java",
+    "//components/messages/android/internal:java",
     "//content/public/android:content_full_java",
     "//content/public/test/android:content_java_test_support",
     "//third_party/android_support_test_runner:runner_java",
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewModel.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewModel.java
new file mode 100644
index 0000000..72ec588d
--- /dev/null
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewModel.java
@@ -0,0 +1,97 @@
+// 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.
+
+package org.chromium.chrome.browser.merchant_viewer;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+
+import androidx.core.content.res.ResourcesCompat;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.browser.merchant_viewer.RatingStarSpan.RatingStarType;
+import org.chromium.chrome.browser.merchant_viewer.proto.MerchantTrustSignalsOuterClass.MerchantTrustSignals;
+import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.messages.MessageBannerProperties;
+import org.chromium.ui.modelutil.PropertyModel;
+
+import java.text.NumberFormat;
+
+/**
+ * This is a util class for creating the property model of the MerchantTrustMessage.
+ */
+class MerchantTrustMessageViewModel {
+    private static final int BASELINE_RATING = 5;
+
+    public static PropertyModel create(Context context, MerchantTrustSignals trustSignals,
+            Callback<Integer> onDismissed, Callback<MerchantTrustSignals> onPrimaryAction) {
+        return new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
+                .with(MessageBannerProperties.ICON,
+                        ResourcesCompat.getDrawable(context.getResources(),
+                                R.drawable.ic_logo_googleg_24dp, context.getTheme()))
+                .with(MessageBannerProperties.ICON_TINT_COLOR, MessageBannerProperties.TINT_NONE)
+                .with(MessageBannerProperties.TITLE,
+                        context.getResources().getString(R.string.merchant_viewer_message_title))
+                .with(MessageBannerProperties.DESCRIPTION,
+                        getMessageDescription(context, trustSignals))
+                .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT,
+                        context.getResources().getString(R.string.merchant_viewer_message_action))
+                .with(MessageBannerProperties.ON_DISMISSED, onDismissed)
+                .with(MessageBannerProperties.ON_PRIMARY_ACTION,
+                        () -> { onPrimaryAction.onResult(trustSignals); })
+                .build();
+    }
+
+    private static Spannable getMessageDescription(
+            Context context, MerchantTrustSignals trustSignals) {
+        SpannableStringBuilder builder = new SpannableStringBuilder();
+        NumberFormat numberFormatter = NumberFormat.getIntegerInstance();
+        numberFormatter.setMaximumFractionDigits(1);
+        if (MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_USE_RATING_BAR.getValue()) {
+            builder.append(numberFormatter.format(trustSignals.getMerchantStarRating()));
+            builder.append(" ");
+            builder.append(getRatingBarSpan(context, trustSignals.getMerchantStarRating()));
+        } else {
+            builder.append(context.getResources().getString(
+                    R.string.merchant_viewer_message_description_rating,
+                    numberFormatter.format(trustSignals.getMerchantStarRating()),
+                    numberFormatter.format(BASELINE_RATING)));
+            builder.setSpan(new StyleSpan(Typeface.BOLD), 0, builder.length(),
+                    Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+        builder.append(" ");
+        builder.append(context.getResources().getQuantityString(
+                R.plurals.merchant_viewer_message_description_reviews,
+                trustSignals.getMerchantCountRating(),
+                numberFormatter.format(trustSignals.getMerchantCountRating())));
+        return builder;
+    }
+
+    private static SpannableStringBuilder getRatingBarSpan(Context context, double ratingValue) {
+        assert ratingValue >= 0 && ratingValue <= BASELINE_RATING;
+        SpannableStringBuilder ratingBarSpan = new SpannableStringBuilder();
+        int floorRatingValue = (int) Math.floor(ratingValue);
+        int ceilRatingValue = (int) Math.ceil(ratingValue);
+        for (int i = 0; i < floorRatingValue; i++) {
+            ratingBarSpan.append(" ");
+            ratingBarSpan.setSpan(new RatingStarSpan(context, RatingStarType.FULL), i, i + 1,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        if (ratingValue - floorRatingValue > 0) {
+            ratingBarSpan.append(" ");
+            ratingBarSpan.setSpan(new RatingStarSpan(context, RatingStarType.HALF),
+                    floorRatingValue, floorRatingValue + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        for (int i = ceilRatingValue; i < BASELINE_RATING; i++) {
+            ratingBarSpan.append(" ");
+            ratingBarSpan.setSpan(new RatingStarSpan(context, RatingStarType.OUTLINE), i, i + 1,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        return ratingBarSpan;
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsCoordinator.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsCoordinator.java
index 20b07259..481a82f 100644
--- a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsCoordinator.java
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustSignalsCoordinator.java
@@ -5,14 +5,9 @@
 package org.chromium.chrome.browser.merchant_viewer;
 
 import android.content.Context;
-import android.graphics.Typeface;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.style.StyleSpan;
 import android.view.View;
 
 import androidx.annotation.VisibleForTesting;
-import androidx.core.content.res.ResourcesCompat;
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.Supplier;
@@ -24,21 +19,16 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.messages.DismissReason;
-import org.chromium.components.messages.MessageBannerProperties;
 import org.chromium.components.messages.MessageDispatcher;
 import org.chromium.ui.base.WindowAndroid;
-import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.url.GURL;
 
-import java.text.NumberFormat;
 import java.util.concurrent.TimeUnit;
 
 /**
  * Coordinator for managing merchant trust signals experience.
  */
 public class MerchantTrustSignalsCoordinator {
-    private static final int BASELINE_RATING = 5;
-
     private final MerchantTrustSignalsMediator mMediator;
     private final MerchantTrustMessageScheduler mMessageScheduler;
     private final MerchantTrustDetailsTabCoordinator mDetailsTabCoordinator;
@@ -102,8 +92,10 @@
                     return;
                 }
 
-                mMessageScheduler.schedule(getMessagePropertyModel(item, trustSignals), item,
-                        MerchantViewerConfig.DEFAULT_TRUST_SIGNALS_MESSAGE_DELAY.getValue(),
+                mMessageScheduler.schedule(
+                        MerchantTrustMessageViewModel.create(mContext, trustSignals,
+                                this::onMessageDismissed, this::onMessagePrimaryAction),
+                        item, MerchantViewerConfig.DEFAULT_TRUST_SIGNALS_MESSAGE_DELAY.getValue(),
                         this::onMessageEnqueued);
             });
         }
@@ -133,51 +125,19 @@
         });
     }
 
-    private void launchDetailsPage(GURL url) {
-        mDetailsTabCoordinator.requestOpenSheet(url,
-                mContext.getResources().getString(R.string.merchant_viewer_preview_sheet_title));
-    }
-
-    private PropertyModel getMessagePropertyModel(
-            MerchantTrustMessageContext messageContext, MerchantTrustSignals trustSignals) {
-        return new PropertyModel.Builder(MessageBannerProperties.ALL_KEYS)
-                .with(MessageBannerProperties.ICON,
-                        ResourcesCompat.getDrawable(mContext.getResources(),
-                                R.drawable.ic_logo_googleg_24dp, mContext.getTheme()))
-                .with(MessageBannerProperties.ICON_TINT_COLOR, MessageBannerProperties.TINT_NONE)
-                .with(MessageBannerProperties.TITLE,
-                        mContext.getResources().getString(R.string.merchant_viewer_message_title))
-                .with(MessageBannerProperties.DESCRIPTION, getMessageDescription(trustSignals))
-                .with(MessageBannerProperties.PRIMARY_BUTTON_TEXT,
-                        mContext.getResources().getString(R.string.merchant_viewer_message_action))
-                .with(MessageBannerProperties.ON_DISMISSED, this::onMessageDismissed)
-                .with(MessageBannerProperties.ON_PRIMARY_ACTION,
-                        () -> {
-                            mMetrics.recordMetricsForMessageTapped();
-                            launchDetailsPage(new GURL(trustSignals.getMerchantDetailsPageUrl()));
-                        })
-                .build();
-    }
-
     @VisibleForTesting
     void onMessageDismissed(@DismissReason int dismissReason) {
         mMetrics.recordMetricsForMessageDismissed(dismissReason);
     }
 
-    private Spannable getMessageDescription(MerchantTrustSignals trustSignals) {
-        SpannableStringBuilder builder = new SpannableStringBuilder();
-        NumberFormat numberFormatter = NumberFormat.getIntegerInstance();
-        numberFormatter.setMaximumFractionDigits(1);
-        builder.append(mContext.getResources().getString(
-                R.string.merchant_viewer_message_description_rating,
-                numberFormatter.format(trustSignals.getMerchantStarRating()),
-                numberFormatter.format(BASELINE_RATING)));
-        builder.append(" ");
-        builder.setSpan(new StyleSpan(Typeface.BOLD), 0, builder.length(),
-                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
-        builder.append(mContext.getResources().getQuantityString(
-                R.plurals.merchant_viewer_message_description_reviews,
-                trustSignals.getMerchantCountRating(), trustSignals.getMerchantCountRating()));
-        return builder;
+    @VisibleForTesting
+    void onMessagePrimaryAction(MerchantTrustSignals trustSignals) {
+        mMetrics.recordMetricsForMessageTapped();
+        launchDetailsPage(new GURL(trustSignals.getMerchantDetailsPageUrl()));
+    }
+
+    private void launchDetailsPage(GURL url) {
+        mDetailsTabCoordinator.requestOpenSheet(url,
+                mContext.getResources().getString(R.string.merchant_viewer_preview_sheet_title));
     }
 }
\ No newline at end of file
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantViewerConfig.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantViewerConfig.java
index 9c16b3c..4e4e2684 100644
--- a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantViewerConfig.java
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantViewerConfig.java
@@ -17,6 +17,8 @@
             "trust_signals_message_window_duration_ms";
     private static final String TRUST_SIGNALS_SHEET_USE_PAGE_TITLE_PARAM =
             "trust_signals_sheet_use_page_title";
+    private static final String TRUST_SIGNALS_MESSAGE_USE_RATING_BAR_PARAM =
+            "trust_signals_message_use_rating_bar";
 
     public static final IntCachedFieldTrialParameter DEFAULT_TRUST_SIGNALS_MESSAGE_DELAY =
             new IntCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_MERCHANT_VIEWER,
@@ -29,4 +31,8 @@
     public static final BooleanCachedFieldTrialParameter TRUST_SIGNALS_SHEET_USE_PAGE_TITLE =
             new BooleanCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_MERCHANT_VIEWER,
                     TRUST_SIGNALS_SHEET_USE_PAGE_TITLE_PARAM, true);
+
+    public static final BooleanCachedFieldTrialParameter TRUST_SIGNALS_MESSAGE_USE_RATING_BAR =
+            new BooleanCachedFieldTrialParameter(ChromeFeatureList.COMMERCE_MERCHANT_VIEWER,
+                    TRUST_SIGNALS_MESSAGE_USE_RATING_BAR_PARAM, true);
 }
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/RatingStarSpan.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/RatingStarSpan.java
new file mode 100644
index 0000000..1e72066
--- /dev/null
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/RatingStarSpan.java
@@ -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.
+package org.chromium.chrome.browser.merchant_viewer;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.style.DynamicDrawableSpan;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
+import androidx.core.content.res.ResourcesCompat;
+
+import org.chromium.chrome.tab_ui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An implementation of {@link DynamicDrawableSpan} for displaying rating stars in a {@link
+ * Spannable}.
+ */
+public class RatingStarSpan extends DynamicDrawableSpan {
+    @IntDef({RatingStarType.OUTLINE, RatingStarType.HALF, RatingStarType.FULL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RatingStarType {
+        int OUTLINE = 0;
+        int HALF = 1;
+        int FULL = 2;
+    }
+
+    private final Context mContext;
+    private final @RatingStarType int mType;
+
+    public RatingStarSpan(Context context, @RatingStarType int type) {
+        mContext = context;
+        mType = type;
+    }
+
+    @Override
+    public Drawable getDrawable() {
+        Drawable drawable = ResourcesCompat.getDrawable(
+                mContext.getResources(), getResourceId(mType), mContext.getTheme());
+        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+        return drawable;
+    }
+
+    @DrawableRes
+    private int getResourceId(@RatingStarType int type) {
+        switch (type) {
+            case RatingStarType.OUTLINE:
+                return R.drawable.ic_rating_star_outline;
+            case RatingStarType.HALF:
+                return R.drawable.ic_rating_star_half;
+            case RatingStarType.FULL:
+                return R.drawable.ic_rating_star_full;
+        }
+        throw new IllegalArgumentException("RatingStarType value is invalid.");
+    }
+}
diff --git a/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java
new file mode 100644
index 0000000..051dfa53
--- /dev/null
+++ b/chrome/browser/commerce/merchant_viewer/android/javatests/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustMessageViewTest.java
@@ -0,0 +1,122 @@
+// 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.
+
+package org.chromium.chrome.browser.merchant_viewer;
+
+import android.app.Activity;
+import android.view.LayoutInflater;
+import android.view.ViewGroup.LayoutParams;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.Callback;
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterAnnotations.ClassParameter;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.merchant_viewer.proto.MerchantTrustSignalsOuterClass.MerchantTrustSignals;
+import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.util.ChromeRenderTestRule;
+import org.chromium.components.messages.MessageBannerView;
+import org.chromium.components.messages.MessageBannerViewBinder;
+import org.chromium.components.messages.R;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.test.util.DummyUiActivityTestCase;
+import org.chromium.ui.test.util.NightModeTestUtils;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Tests for MerchantTrustMessageView.
+ */
+@RunWith(ParameterizedRunner.class)
+@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
+public class MerchantTrustMessageViewTest extends DummyUiActivityTestCase {
+    @ClassParameter
+    private static List<ParameterSet> sClassParams =
+            new NightModeTestUtils.NightModeParams().getParameters();
+
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus().build();
+
+    public MerchantTrustMessageViewTest(boolean nightModeEnabled) {
+        NightModeTestUtils.setUpNightModeForDummyUiActivity(nightModeEnabled);
+        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
+    }
+
+    @Mock
+    private Callback<Integer> mMockOnDismissed;
+
+    @Mock
+    private Callback<MerchantTrustSignals> mMockOnPrimaryAction;
+
+    private Activity mActivity;
+    private MessageBannerView mMessageBannerView;
+    private MerchantTrustSignals mMerchantTrustSignals;
+    private LayoutParams mParams;
+
+    @Override
+    public void setUpTest() throws Exception {
+        super.setUpTest();
+        mActivity = getActivity();
+        mMessageBannerView = (MessageBannerView) LayoutInflater.from(mActivity).inflate(
+                R.layout.message_banner_view, null, false);
+        mMerchantTrustSignals = MerchantTrustSignals.newBuilder()
+                                        .setMerchantStarRating(3.51234f)
+                                        .setMerchantCountRating(1640)
+                                        .setMerchantDetailsPageUrl("http://dummy/url")
+                                        .build();
+        mParams = new LayoutParams(LayoutParams.MATCH_PARENT,
+                mActivity.getResources().getDimensionPixelSize(R.dimen.message_banner_height));
+    }
+
+    @Override
+    public void tearDownTest() throws Exception {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { NightModeTestUtils.tearDownNightModeForDummyUiActivity(); });
+        super.tearDownTest();
+    }
+
+    private void createModelAndSetView() {
+        PropertyModel propertyModel = MerchantTrustMessageViewModel.create(
+                mActivity, mMerchantTrustSignals, mMockOnDismissed, mMockOnPrimaryAction);
+        PropertyModelChangeProcessor.create(
+                propertyModel, mMessageBannerView, MessageBannerViewBinder::bind);
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { mActivity.setContentView(mMessageBannerView, mParams); });
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testRenderMessage_UseRatingBar() throws IOException {
+        MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_USE_RATING_BAR.setForTesting(true);
+        createModelAndSetView();
+        mRenderTestRule.render(mMessageBannerView, "merchant_trust_message_use_rating_bar");
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testRenderMessage_NotUseRatingBar() throws IOException {
+        MerchantViewerConfig.TRUST_SIGNALS_MESSAGE_USE_RATING_BAR.setForTesting(false);
+        createModelAndSetView();
+        mRenderTestRule.render(mMessageBannerView, "merchant_trust_message_not_use_rating_bar");
+    }
+}
\ No newline at end of file
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 332a4edf..033e89e 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -1301,6 +1301,13 @@
                                          int* active_index,
                                          int index,
                                          std::string* error) {
+  // Cannot change tab highlight. This may for instance be due to user dragging
+  // in progress.
+  if (!tabstrip->delegate()->CanHighlightTabs()) {
+    *error = tabs_constants::kCannotHighlightTabs;
+    return false;
+  }
+
   // Make sure the index is in range.
   if (!tabstrip->ContainsIndex(index)) {
     *error = ErrorUtils::FormatErrorMessage(
@@ -1386,8 +1393,10 @@
 
   if (params->update_properties.highlighted.get()) {
     bool highlighted = *params->update_properties.highlighted;
-    if (highlighted != tab_strip->IsTabSelected(tab_index))
-      tab_strip->ToggleSelectionAt(tab_index);
+    if (highlighted != tab_strip->IsTabSelected(tab_index)) {
+      if (!tab_strip->ToggleSelectionAt(tab_index))
+        return RespondNow(Error(tabs_constants::kCannotHighlightTabs));
+    }
   }
 
   if (params->update_properties.pinned.get()) {
diff --git a/chrome/browser/extensions/api/tabs/tabs_constants.cc b/chrome/browser/extensions/api/tabs/tabs_constants.cc
index c3bd865..5484321 100644
--- a/chrome/browser/extensions/api/tabs/tabs_constants.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_constants.cc
@@ -127,6 +127,9 @@
 const char kCannotNavigateToDevtools[] =
     "Cannot navigate to a devtools:// page without either the devtools or "
     "debugger permission.";
+const char kCannotHighlightTabs[] =
+    "Cannot change tab highlight. This may for instance be due to user "
+    "dragging in progress.";
 
 }  // namespace tabs_constants
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/tabs/tabs_constants.h b/chrome/browser/extensions/api/tabs/tabs_constants.h
index a6365048..0f2c9605 100644
--- a/chrome/browser/extensions/api/tabs/tabs_constants.h
+++ b/chrome/browser/extensions/api/tabs/tabs_constants.h
@@ -111,6 +111,7 @@
 extern const char kLockedFullscreenModeNewTabError[];
 extern const char kGroupParamsError[];
 extern const char kCannotNavigateToDevtools[];
+extern const char kCannotHighlightTabs[];
 
 }  // namespace tabs_constants
 }  // namespace extensions
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 0d3798e..db6af8b 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -601,4 +601,10 @@
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
+IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, Xslt) {
+  content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  ASSERT_TRUE(RunExtensionTest("webnavigation/xslt")) << message_;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_app_icon_service.h b/chrome/browser/extensions/chrome_app_icon_service.h
index f04cf81..4069208 100644
--- a/chrome/browser/extensions/chrome_app_icon_service.h
+++ b/chrome/browser/extensions/chrome_app_icon_service.h
@@ -19,7 +19,7 @@
 #include "extensions/browser/extension_registry_observer.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ui/ash/launcher/shelf_extension_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/shelf_extension_app_updater.h"
 #endif
 
 namespace content {
diff --git a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
index fdc0c6e..cb7b4a7e 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
@@ -29,6 +29,8 @@
 #include "chrome/browser/extensions/suspicious_extension_bubble_delegate.h"
 #include "chrome/browser/extensions/test_extension_message_bubble_delegate.h"
 #include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/profiles/profile_keep_alive_types.h"
+#include "chrome/browser/profiles/scoped_profile_keep_alive.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model.h"
 #include "chrome/browser/ui/toolbar/toolbar_actions_model_factory.h"
 #include "chrome/common/chrome_features.h"
@@ -362,6 +364,11 @@
         std::make_unique<base::CommandLine>(base::CommandLine::NO_PROGRAM);
     ExtensionMessageBubbleController::set_should_ignore_learn_more_for_testing(
         true);
+    // Prevent the Profile from getting deleted before TearDown() is complete,
+    // since WaitForStorageCleanup() relies on an active Profile. See the
+    // DestroyProfileOnBrowserClose flag.
+    profile_keep_alive_ = std::make_unique<ScopedProfileKeepAlive>(
+        profile(), ProfileKeepAliveOrigin::kBrowserWindow);
   }
 
   void TearDown() override {
@@ -378,6 +385,7 @@
       SettingsApiBubbleDelegate(profile(), type).ClearProfileSetForTesting();
     }
     SuspiciousExtensionBubbleDelegate(profile()).ClearProfileSetForTesting();
+    profile_keep_alive_.reset();
     BrowserWithTestWindowTest::TearDown();
   }
 
@@ -422,6 +430,7 @@
 
  private:
   std::unique_ptr<base::CommandLine> command_line_;
+  std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
 
   DISALLOW_COPY_AND_ASSIGN(ExtensionMessageBubbleTest);
 };
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index f9d3bd23..ed10425 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2576,6 +2576,11 @@
     "expiry_milestone": 98
   },
   {
+    "name": "enable-unsafe-webgpu-service",
+    "owners": [ "//third_party/blink/renderer/modules/webgpu/OWNERS" ],
+    "expiry_milestone": 98
+  },
+  {
     "name": "enable-use-aaudio-driver",
     "owners": [ "tguilbert" ],
     // This feature has launched and is on by default. We are still debugging a
@@ -4809,7 +4814,9 @@
   {
     "name": "show-overdraw-feedback",
     "owners": [ "andrescj", "chromeos-gfx@google.com" ],
-    "expiry_milestone": 88
+    // This is a debug flag to visualize compositing and rendering issues in
+    // the field.
+    "expiry_milestone": -1
   },
   {
     "name": "show-performance-metrics-hud",
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json
index ae2b606..3e191a09 100644
--- a/chrome/browser/flag-never-expire-list.json
+++ b/chrome/browser/flag-never-expire-list.json
@@ -87,6 +87,7 @@
   "set-market-url-for-testing",
   "show-autofill-type-predictions",
   "show-bluetooth-debug-log-toggle",
+  "show-overdraw-feedback",
   "show-performance-metrics-hud",
   "show-taps",
   "show-touch-hud",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 087ef56..f41ad02 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2555,6 +2555,12 @@
     "Enables access to the experimental WebGPU API. Warning: As GPU sandboxing "
     "isn't implemented yet for the WebGPU API, it is possible to read GPU data "
     "for other processes.";
+const char kUnsafeWebGPUServiceName[] = "Unsafe WebGPU Service";
+const char kUnsafeWebGPUServiceDescription[] =
+    "Enables access to the experimental WebGPU API on service side but not "
+    "blink side."
+    "This is used to test origin trial code integrations before WebGPU is "
+    "enabled globally.";
 
 const char kUnsafeFastJSCallsName[] = "Unsafe fast JS calls";
 const char kUnsafeFastJSCallsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index db14b30a..42ea06e6 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1479,6 +1479,9 @@
 extern const char kUnsafeWebGPUName[];
 extern const char kUnsafeWebGPUDescription[];
 
+extern const char kUnsafeWebGPUServiceName[];
+extern const char kUnsafeWebGPUServiceDescription[];
+
 extern const char kUnsafeFastJSCallsName[];
 extern const char kUnsafeFastJSCallsDescription[];
 
diff --git a/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.cc b/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.cc
index c6d3ee4..b86473a 100644
--- a/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.cc
+++ b/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.cc
@@ -20,6 +20,7 @@
                             blink::mojom::PermissionsPolicyFeature::kNotFound) {
   host_content_settings_map_ =
       permissions::PermissionsClient::Get()->GetSettingsMap(browser_context);
+  content_setting_observer_registered_by_subclass_ = true;
   host_content_settings_map_->AddObserver(this);
 }
 
@@ -73,6 +74,9 @@
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
     ContentSettingsType content_type) {
+  PermissionContextBase::OnContentSettingChanged(
+      primary_pattern, secondary_pattern, content_type);
+
   if (content_type != ContentSettingsType::MEDIASTREAM_CAMERA &&
       content_type != ContentSettingsType::CAMERA_PAN_TILT_ZOOM) {
     return;
diff --git a/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.h b/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.h
index 96ad4743..456054d3 100644
--- a/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.h
+++ b/chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.h
@@ -7,7 +7,6 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
-#include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/permissions/permission_context_base.h"
 
@@ -15,8 +14,7 @@
 // zoom). Those permissions are automatically reset when the "regular" camera
 // permission is blocked or reset.
 class CameraPanTiltZoomPermissionContext
-    : public permissions::PermissionContextBase,
-      public content_settings::Observer {
+    : public permissions::PermissionContextBase {
  public:
   explicit CameraPanTiltZoomPermissionContext(
       content::BrowserContext* browser_context);
diff --git a/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc b/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc
index 65b6b0d..a9ade527 100644
--- a/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_metrics_logger.cc
@@ -400,12 +400,6 @@
   }
   base::UmaHistogramEnumeration(
       "Nearby.Share.Connection.EstablishOutgoingConnectionStatus", status);
-
-  // Log a high-level success/failure metric, ignoring cancellations.
-  if (!cancelled) {
-    base::UmaHistogramBoolean(
-        "Nearby.Share.Connection.EstablishOutgoingConnection.Success", success);
-  }
 }
 
 void RecordNearbyShareTimeFromInitiateSendToRemoteDeviceNotificationMetric(
@@ -568,38 +562,12 @@
   base::UmaHistogramEnumeration("Nearby.Share.DeviceType" + send_or_receive,
                                 type);
 
-  // Log the detailed transfer final status enum.
-  {
-    TransferFinalStatus final_status =
-        TransferMetadataStatusToTransferFinalStatus(status);
-    const std::string prefix = "Nearby.Share.Transfer.FinalStatus";
-    base::UmaHistogramEnumeration(prefix, final_status);
-    base::UmaHistogramEnumeration(prefix + send_or_receive, final_status);
-    base::UmaHistogramEnumeration(prefix + share_target_type, final_status);
-    base::UmaHistogramEnumeration(prefix + contact_or_not, final_status);
-  }
+  TransferFinalStatus final_status =
+      TransferMetadataStatusToTransferFinalStatus(status);
 
-  // Log the transfer success/failure for high-level success and Critical User
-  // Journey (CUJ) metrics.
-  {
-    base::Optional<bool> success;
-    switch (TransferMetadata::ToResult(status)) {
-      case TransferMetadata::Result::kSuccess:
-        success = true;
-        break;
-      case TransferMetadata::Result::kFailure:
-        success = false;
-        break;
-      case TransferMetadata::Result::kIndeterminate:
-        success.reset();
-        break;
-    }
-    if (success.has_value()) {
-      const std::string prefix = "Nearby.Share.Transfer.Success";
-      base::UmaHistogramBoolean(prefix, *success);
-      base::UmaHistogramBoolean(
-          prefix + send_or_receive + share_target_type + contact_or_not,
-          *success);
-    }
-  }
+  const std::string prefix = "Nearby.Share.Transfer.FinalStatus";
+  base::UmaHistogramEnumeration(prefix, final_status);
+  base::UmaHistogramEnumeration(prefix + send_or_receive, final_status);
+  base::UmaHistogramEnumeration(prefix + share_target_type, final_status);
+  base::UmaHistogramEnumeration(prefix + contact_or_not, final_status);
 }
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
index aa9c8790..f351e756 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
@@ -88,8 +88,6 @@
 KeyedService* NearbySharingServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   if (!IsNearbyShareSupportedForBrowserContext(context)) {
-    NS_LOG(WARNING) << __func__
-                    << ": Nearby Share not supported for browser context.";
     return nullptr;
   }
 
diff --git a/chrome/browser/nearby_sharing/transfer_metadata.cc b/chrome/browser/nearby_sharing/transfer_metadata.cc
index 1eaf4c0..53a4c8b 100644
--- a/chrome/browser/nearby_sharing/transfer_metadata.cc
+++ b/chrome/browser/nearby_sharing/transfer_metadata.cc
@@ -44,43 +44,6 @@
 }
 
 // static
-TransferMetadata::Result TransferMetadata::ToResult(Status status) {
-  switch (status) {
-    case Status::kComplete:
-      return Result::kSuccess;
-    case Status::kUnknown:
-    case Status::kAwaitingRemoteAcceptanceFailed:
-    case Status::kFailed:
-    case Status::kDecodeAdvertisementFailed:
-    case Status::kMissingTransferUpdateCallback:
-    case Status::kMissingShareTarget:
-    case Status::kMissingEndpointId:
-    case Status::kMissingPayloads:
-    case Status::kPairedKeyVerificationFailed:
-    case Status::kInvalidIntroductionFrame:
-    case Status::kIncompletePayloads:
-    case Status::kFailedToCreateShareTarget:
-    case Status::kFailedToInitiateOutgoingConnection:
-    case Status::kFailedToReadOutgoingConnectionResponse:
-    case Status::kUnexpectedDisconnection:
-      return Result::kFailure;
-    case Status::kConnecting:
-    case Status::kAwaitingLocalConfirmation:
-    case Status::kAwaitingRemoteAcceptance:
-    case Status::kInProgress:
-    case Status::kRejected:
-    case Status::kCancelled:
-    case Status::kTimedOut:
-    case Status::kMediaUnavailable:
-    case Status::kMediaDownloading:
-    case Status::kNotEnoughSpace:
-    case Status::kUnsupportedAttachmentType:
-    case Status::kExternalProviderLaunched:
-      return Result::kIndeterminate;
-  }
-}
-
-// static
 std::string TransferMetadata::StatusToString(Status status) {
   switch (status) {
     case Status::kConnecting:
diff --git a/chrome/browser/nearby_sharing/transfer_metadata.h b/chrome/browser/nearby_sharing/transfer_metadata.h
index cbad074d..43a2c89a 100644
--- a/chrome/browser/nearby_sharing/transfer_metadata.h
+++ b/chrome/browser/nearby_sharing/transfer_metadata.h
@@ -48,17 +48,8 @@
     kMaxValue = kUnexpectedDisconnection
   };
 
-  enum class Result {
-    kIndeterminate,
-    kSuccess,
-    kFailure,
-    kMaxValue = kFailure
-  };
-
   static bool IsFinalStatus(Status status);
 
-  static Result ToResult(Status status);
-
   static std::string StatusToString(TransferMetadata::Status status);
 
   static nearby_share::mojom::TransferStatus StatusToMojo(Status status);
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/DemoPaintPreview.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/DemoPaintPreview.java
index 31b869a1..dd67ffa 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/DemoPaintPreview.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/DemoPaintPreview.java
@@ -111,6 +111,15 @@
         return ChromeAccessibilityUtil.get().isAccessibilityEnabled();
     }
 
+    @Override
+    public void onAccessibilityNotSupported() {
+        if (isAccessibilityEnabled()) {
+            Toast.makeText(mTab.getContext(), R.string.paint_preview_demo_no_accessibility,
+                         Toast.LENGTH_LONG)
+                    .show();
+        }
+    }
+
     private class DemoPaintPreviewTabObserver extends EmptyTabObserver {
         @Override
         public void onDidStartNavigation(Tab tab, NavigationHandle navigationHandle) {
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java
index 910241ce..b2a486f 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreview.java
@@ -290,6 +290,14 @@
         return ChromeAccessibilityUtil.get().isAccessibilityEnabled();
     }
 
+    @Override
+    public void onAccessibilityNotSupported() {
+        // Ignore accessibility failures if accessibility is not enabled.
+        if (!isAccessibilityEnabled()) return;
+
+        remove(ExitCause.ACCESSIBILITY_NOT_SUPPORTED);
+    }
+
     @VisibleForTesting
     TabObserver getTabObserverForTesting() {
         return mStartupTabObserver;
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewMetrics.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewMetrics.java
index 25346c44..09f38a7 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewMetrics.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewMetrics.java
@@ -22,7 +22,8 @@
     /** Used for recording the cause for exiting the Paint Preview player. */
     @IntDef({ExitCause.PULL_TO_REFRESH, ExitCause.SNACK_BAR_ACTION, ExitCause.COMPOSITOR_FAILURE,
             ExitCause.TAB_FINISHED_LOADING, ExitCause.LINK_CLICKED, ExitCause.NAVIGATION_STARTED,
-            ExitCause.TAB_DESTROYED, ExitCause.TAB_HIDDEN, ExitCause.OFFLINE_AVAILABLE})
+            ExitCause.TAB_DESTROYED, ExitCause.TAB_HIDDEN, ExitCause.OFFLINE_AVAILABLE,
+            ExitCause.ACCESSIBILITY_NOT_SUPPORTED})
     @interface ExitCause {
         int PULL_TO_REFRESH = 0;
         int SNACK_BAR_ACTION = 1;
@@ -33,7 +34,8 @@
         int TAB_DESTROYED = 6;
         int TAB_HIDDEN = 7;
         int OFFLINE_AVAILABLE = 8;
-        int COUNT = 9;
+        int ACCESSIBILITY_NOT_SUPPORTED = 9;
+        int COUNT = 10;
     }
 
     private static final Map<Integer, String> UPTIME_HISTOGRAM_MAP = new HashMap<>();
@@ -56,6 +58,8 @@
                 "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnTabHidden");
         UPTIME_HISTOGRAM_MAP.put(ExitCause.OFFLINE_AVAILABLE,
                 "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnOfflineAvailable");
+        UPTIME_HISTOGRAM_MAP.put(ExitCause.ACCESSIBILITY_NOT_SUPPORTED,
+                "Browser.PaintPreview.TabbedPlayer.UpTime.RemovedOnAccessibilityNotSupported");
     }
 
     private long mShownTime;
diff --git a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewAccessibilityTest.java b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewAccessibilityTest.java
index 14e1039..3d83a860 100644
--- a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewAccessibilityTest.java
+++ b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewAccessibilityTest.java
@@ -15,6 +15,7 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabService;
 import org.chromium.chrome.browser.tab.Tab;
@@ -90,8 +91,11 @@
         viewReadyCallback.waitForFirst("Paint preview view ready never happened.");
 
         // Assert accessibility support is initialized.
-        Assert.assertNotNull("PlayerManager doesn't have a valid WebContentsAccessibility.",
-                tabbedPaintPreview.getPlayerManagerForTesting()
-                        .getWebContentsAccessibilityForTesting());
+        CriteriaHelper.pollInstrumentationThread(
+                ()
+                        -> tabbedPaintPreview.getPlayerManagerForTesting()
+                                   .getWebContentsAccessibilityForTesting()
+                        != null,
+                "PlayerManager doesn't have a valid WebContentsAccessibility.");
     }
 }
diff --git a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewTest.java b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewTest.java
index 1eccfe15..fc84a75 100644
--- a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewTest.java
+++ b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewTest.java
@@ -413,5 +413,8 @@
         public boolean isAccessibilityEnabled() {
             return false;
         }
+
+        @Override
+        public void onAccessibilityNotSupported() {}
     }
 }
diff --git a/chrome/browser/performance_monitor/process_metrics_recorder_util.cc b/chrome/browser/performance_monitor/process_metrics_recorder_util.cc
index 560b324..76edadb 100644
--- a/chrome/browser/performance_monitor/process_metrics_recorder_util.cc
+++ b/chrome/browser/performance_monitor/process_metrics_recorder_util.cc
@@ -37,14 +37,14 @@
 #if defined(OS_MAC) || defined(OS_LINUX) || defined(OS_CHROMEOS) || \
     defined(OS_AIX)
   base::UmaHistogramCounts10000(
-      base::JoinString({"PerformanceMonitor.IdleWakeUps.", histogram_suffix},
+      base::JoinString({"PerformanceMonitor.IdleWakeups.", histogram_suffix},
                        ""),
       metrics.idle_wakeups);
 #endif
 #if defined(OS_MAC)
   base::UmaHistogramCounts1000(
       base::JoinString(
-          {"PerformanceMonitor.PackageExitIdleWakeUps.", histogram_suffix}, ""),
+          {"PerformanceMonitor.PackageExitIdleWakeups.", histogram_suffix}, ""),
       metrics.package_idle_wakeups);
   base::UmaHistogramCounts100000(
       base::JoinString({"PerformanceMonitor.EnergyImpact.", histogram_suffix},
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index 170ca5c..9020d1c 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -77,7 +77,7 @@
   MOCK_METHOD0(Show, void());
   MOCK_METHOD1(Close, void(bool));
   MOCK_METHOD0(CloseAndFocusInitiator, void());
-  MOCK_METHOD0(OnWindowDestroyed, void());
+  MOCK_METHOD1(OnWindowDestroyed, void(bool));
   MOCK_METHOD0(GetWindowForTesting, content::OverlayWindow*());
   MOCK_METHOD0(UpdateLayerBounds, void());
   MOCK_METHOD0(IsPlayerActive, bool());
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
index 9f77af65..23297a78 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -578,12 +578,32 @@
         return;
       }
 
-      // Some cases (e.g. in overview mode), require overriding the assumption
-      // that focus is an ancestor of a selection target.
-      const override = AutomationPredicate.menuItem(evt.target) ||
+      let override = false;
+      const isDesktop =
           (evt.target.root === focus.root &&
-           focus.root.role === RoleType.DESKTOP) ||
-          evt.target.role === RoleType.IME_CANDIDATE;
+           focus.root.role === RoleType.DESKTOP);
+
+      // Menu items and IME candidates always announce on selection events,
+      // independent of focus.
+      if (AutomationPredicate.menuItem(evt.target) ||
+          evt.target.role === RoleType.IME_CANDIDATE) {
+        override = true;
+      }
+
+      // Selection events that happen in native UI (the desktop tree) should
+      // generally announce as long as focus isn't in some other tree; this is
+      // all first-party code that's firing the event for a good reason.
+      if (isDesktop) {
+        // TableView is an exception; it fires selection events on rows/cells
+        // and we want to ignore those because it also fires focus events.
+        if (evt.target.role === RoleType.CELL ||
+            evt.target.role === RoleType.ROW) {
+          return;
+        }
+
+        override = true;
+      }
+
       if (override || AutomationUtil.isDescendantOf(evt.target, focus)) {
         this.onEventDefault(evt);
       }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
index 6e0ea0a..8c3efc3d 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler_test.js
@@ -68,3 +68,28 @@
             .replay();
       });
     });
+
+TEST_F(
+    'ChromeVoxDesktopAutomationHandlerTest', 'TaskManagerTableView',
+    function() {
+      const mockFeedback = this.createMockFeedback();
+      this.runWithLoadedDesktop((desktop) => {
+        mockFeedback
+            .call(() => {
+              EventGenerator.sendKeyPress(KeyCode.ESCAPE, {search: true});
+            })
+            .expectSpeech('Task Manager, window')
+            .call(() => {
+              EventGenerator.sendKeyPress(KeyCode.DOWN);
+            })
+            .expectSpeech('Browser', 'row 2 column 1', 'Task')
+            .call(() => {
+              EventGenerator.sendKeyPress(KeyCode.DOWN);
+            })
+            // Make sure it doesn't repeat the previous line!
+            .expectNextSpeechUtteranceIsNot('Browser')
+            .expectSpeech('row 3 column 1');
+
+        mockFeedback.replay();
+      });
+    });
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
index 0bcbfdb..f8ba1790 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
@@ -4,15 +4,21 @@
 <link rel="import" href="chrome://resources/cr_components/chromeos/cellular_setup/esim_manager_utils.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
+<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 
 <dom-module id="esim-rename-dialog">
   <template>
-    <style>
+    <style include="iron-positioning">
       :host {
         --cr-dialog-width: 324px;
       }
 
+      #body {
+        overflow: hidden;
+        padding: 0 20px 2px 20px;
+      }
+
       #warningMessage {
         --iron-icon-fill-color: #5F6368;
         --iron-icon-height: 16px;
@@ -25,22 +31,68 @@
         padding-inline-end: 4px;
       }
 
+      #inputInfo {
+        background-color: white;
+        font-size: var(--cr-form-field-label-font-size);
+        font-weight: 500;
+        height: 30px;
+        line-height: var(--cr-form-field-label-line-height);
+        position: absolute;
+        top: 40px;
+        width: 100%;
+      }
+
+      #inputInfo.error {
+        color: var(--google-red-600);
+        font-weight: 400;
+      }
+
+      #inputSubtitle {
+        display: block;
+        width: 260px;
+      }
+
+      #inputCount {
+        position: absolute;
+        right: 0;
+        top: 0;
+      }
+
       #cancel {
         margin-inline-end: 8px;
       }
     </style>
     <cr-dialog id="profileRenameDialog" show-on-attach>
       <div slot="title">$i18n{eSimRenameProfileDialogLabel}</div>
-      <div slot="body">
+      <div id="body" slot="body">
         <div id="warningMessage" hidden$="[[!showCellularDisconnectWarning]]">
           <iron-icon icon="cellular-setup:warning"></iron-icon>
           $i18n{eSimDialogConnectionWarning}
         </div>
         <template is="dom-if" if="[[!errorMessage_]]" restamp>
-          <cr-input id="eSimprofileName"
-              value="{{esimProfileName_}}"
-              spellcheck="false">
-          </cr-input>
+          <div class="relative">
+            <!-- Set error-message so then it is read out by ChromeVox
+              when cr-input is invalid. Since we already display the error
+              message in #inputInfo, this is visually hidden by #inputInfo. -->
+            <cr-input id="eSimprofileName"
+                value="{{esimProfileName_}}"
+                spellcheck="false"
+                disabled="[[isRenameInProgress_]]"
+                invalid="[[isInputInvalid_]]"
+                aria-description="[[i18n('eSimRenameProfileInputA11yLabel',
+                    MAX_INPUT_LENGTH)]]"
+                error-message="[[i18n('eSimRenameProfileInputA11yLabel',
+                    MAX_INPUT_LENGTH)]]">
+            </cr-input>
+            <div id="inputInfo"
+                class$="[[getInputInfoClass_(isInputInvalid_)]]"
+                aria-hidden="true">
+              <span id="inputSubtitle">$i18n{eSimRenameProfileInputSubtitle}</span>
+              <span id="inputCount">
+                [[getInputCountString_(esimProfileName_)]]
+              </span>
+            </div>
+          </div>
         </template>
         <div id="errorMessage" hidden$="[[!errorMessage_]]">
           [[errorMessage_]]
@@ -50,6 +102,7 @@
         <template is="dom-if" if="[[!errorMessage_]]" restamp>
           <cr-button id="cancel"
               on-click="onCancelTap_"
+              disabled="[[isRenameInProgress_]]"
               class="cancel-button">
             $i18n{eSimRenameProfileDialogCancel}
           </cr-button>
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js
index 4a73ff0..72cce6bd 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js
@@ -2,6 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+/** @type {number} */
+const MAX_INPUT_LENGTH = 20;
+
+/** @type {RegExp} */
+const EMOJI_REGEX_EXP =
+    /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/gi;
+
 /**
  * @fileoverview Polymer element to rename eSIM profile name
  */
@@ -14,6 +21,12 @@
   ],
 
   properties: {
+    /** Used to reference the MAX_INPUT_LENGTH constant in HTML. */
+    MAX_INPUT_LENGTH: {
+      type: Number,
+      value: MAX_INPUT_LENGTH,
+    },
+
     /** @type {?OncMojo.NetworkStateProperties} */
     networkState: {
       type: Object,
@@ -30,6 +43,7 @@
     esimProfileName_: {
       type: String,
       value: '',
+      observer: 'onEsimProfileNameChanged_',
     },
 
     /** @private {string} */
@@ -42,7 +56,13 @@
     isRenameInProgress_: {
       type: Boolean,
       value: false,
-    }
+    },
+
+    /** @private {boolean} */
+    isInputInvalid_: {
+      type: Boolean,
+      value: false,
+    },
   },
 
   /** @private {?chromeos.cellularSetup.mojom.ESimProfileRemote} */
@@ -107,8 +127,8 @@
   handleSetProfileNicknameResponse_(result) {
     this.isRenameInProgress_ = false;
     if (result === chromeos.cellularSetup.mojom.ESimOperationResult.kFailure) {
-      this.errorMessage_ = this.i18n('eSimRenameProfileDialogError');
-      return;
+      this.fire(
+          'show-error-toast', this.i18n('eSimRenameProfileDialogErrorToast'));
     }
     this.$.profileRenameDialog.close();
   },
@@ -120,4 +140,57 @@
   onCancelTap_(event) {
     this.$.profileRenameDialog.close();
   },
+
+  /**
+   * Observer for esimProfileName_ that sanitizes its value by removing any
+   * Emojis and truncating it to MAX_INPUT_LENGTH. This method will be
+   * recursively called until esimProfileName_ is fully sanitized.
+   * @param {string} newValue
+   * @param {string} oldValue
+   * @private
+   */
+  onEsimProfileNameChanged_(newValue, oldValue) {
+    if (oldValue) {
+      const sanitizedOldValue = oldValue.replace(EMOJI_REGEX_EXP, '');
+      // If sanitizedOldValue.length > MAX_INPUT_LENGTH, the user attempted to
+      // enter more than the max limit, this method was called and it was
+      // truncated, and then this method was called one more time.
+      this.isInputInvalid_ = sanitizedOldValue.length > MAX_INPUT_LENGTH;
+    } else {
+      this.isInputInvalid_ = false;
+    }
+
+    // Remove all Emojis from the name.
+    const sanitizedProfileName =
+        this.esimProfileName_.replace(EMOJI_REGEX_EXP, '');
+
+    // Truncate the name to MAX_INPUT_LENGTH.
+    this.esimProfileName_ = sanitizedProfileName.substring(0, MAX_INPUT_LENGTH);
+  },
+
+  /**
+   * @param {boolean} isInputInvalid
+   * @return {string}
+   * @private
+   */
+  getInputInfoClass_(isInputInvalid) {
+    return isInputInvalid ? 'error' : '';
+  },
+
+  /**
+   * Returns a formatted string containing the current number of characters
+   * entered in the input compared to the maximum number of characters allowed.
+   * @param {string} esimProfileName
+   * @return {string}
+   * @private
+   */
+  getInputCountString_(esimProfileName) {
+    // minimumIntegerDigits is 2 because we want to show a leading zero if
+    // length is less than 10.
+    return this.i18n(
+        'eSimRenameProfileInputCharacterCount',
+        esimProfileName.length.toLocaleString(
+            /*locales=*/ undefined, {minimumIntegerDigits: 2}),
+        MAX_INPUT_LENGTH.toLocaleString());
+  },
 });
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f085469a..1d603c7f 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1296,6 +1296,12 @@
       "user_education/feature_promo_snooze_service.h",
       "user_education/feature_promo_text_replacements.cc",
       "user_education/feature_promo_text_replacements.h",
+      "user_education/feature_tutorial_service.cc",
+      "user_education/feature_tutorial_service.h",
+      "user_education/feature_tutorial_service_factory.cc",
+      "user_education/feature_tutorial_service_factory.h",
+      "user_education/feature_tutorials.cc",
+      "user_education/feature_tutorials.h",
       "user_education/reopen_tab_in_product_help.cc",
       "user_education/reopen_tab_in_product_help.h",
       "user_education/reopen_tab_in_product_help_factory.cc",
@@ -1961,75 +1967,6 @@
       "ash/keyboard/chrome_keyboard_ui_factory.h",
       "ash/keyboard/chrome_keyboard_web_contents.cc",
       "ash/keyboard/chrome_keyboard_web_contents.h",
-      "ash/launcher/app_service/app_service_app_window_arc_tracker.cc",
-      "ash/launcher/app_service/app_service_app_window_arc_tracker.h",
-      "ash/launcher/app_service/app_service_app_window_crostini_tracker.cc",
-      "ash/launcher/app_service/app_service_app_window_crostini_tracker.h",
-      "ash/launcher/app_service/app_service_app_window_shelf_controller.cc",
-      "ash/launcher/app_service/app_service_app_window_shelf_controller.h",
-      "ash/launcher/app_service/app_service_app_window_shelf_item_controller.cc",
-      "ash/launcher/app_service/app_service_app_window_shelf_item_controller.h",
-      "ash/launcher/app_service/app_service_instance_registry_helper.cc",
-      "ash/launcher/app_service/app_service_instance_registry_helper.h",
-      "ash/launcher/app_service/app_service_shelf_context_menu.cc",
-      "ash/launcher/app_service/app_service_shelf_context_menu.h",
-      "ash/launcher/app_service/exo_app_type_resolver.cc",
-      "ash/launcher/app_service/exo_app_type_resolver.h",
-      "ash/launcher/app_service/shelf_app_service_app_updater.cc",
-      "ash/launcher/app_service/shelf_app_service_app_updater.h",
-      "ash/launcher/app_shortcut_shelf_item_controller.cc",
-      "ash/launcher/app_shortcut_shelf_item_controller.h",
-      "ash/launcher/app_window_base.cc",
-      "ash/launcher/app_window_base.h",
-      "ash/launcher/app_window_shelf_controller.cc",
-      "ash/launcher/app_window_shelf_controller.h",
-      "ash/launcher/app_window_shelf_item_controller.cc",
-      "ash/launcher/app_window_shelf_item_controller.h",
-      "ash/launcher/arc_app_shelf_id.cc",
-      "ash/launcher/arc_app_shelf_id.h",
-      "ash/launcher/arc_app_window.cc",
-      "ash/launcher/arc_app_window.h",
-      "ash/launcher/arc_app_window_delegate.h",
-      "ash/launcher/arc_app_window_info.cc",
-      "ash/launcher/arc_app_window_info.h",
-      "ash/launcher/arc_playstore_shortcut_shelf_item_controller.cc",
-      "ash/launcher/arc_playstore_shortcut_shelf_item_controller.h",
-      "ash/launcher/arc_shelf_spinner_item_controller.cc",
-      "ash/launcher/arc_shelf_spinner_item_controller.h",
-      "ash/launcher/browser_shortcut_shelf_item_controller.cc",
-      "ash/launcher/browser_shortcut_shelf_item_controller.h",
-      "ash/launcher/browser_status_monitor.cc",
-      "ash/launcher/browser_status_monitor.h",
-      "ash/launcher/chrome_shelf_controller.cc",
-      "ash/launcher/chrome_shelf_controller.h",
-      "ash/launcher/chrome_shelf_controller_util.cc",
-      "ash/launcher/chrome_shelf_controller_util.h",
-      "ash/launcher/crostini_app_display.cc",
-      "ash/launcher/crostini_app_display.h",
-      "ash/launcher/crostini_app_window.cc",
-      "ash/launcher/crostini_app_window.h",
-      "ash/launcher/extension_shelf_context_menu.cc",
-      "ash/launcher/extension_shelf_context_menu.h",
-      "ash/launcher/extension_uninstaller.cc",
-      "ash/launcher/extension_uninstaller.h",
-      "ash/launcher/lacros_app_window.cc",
-      "ash/launcher/lacros_app_window.h",
-      "ash/launcher/multi_profile_browser_status_monitor.cc",
-      "ash/launcher/multi_profile_browser_status_monitor.h",
-      "ash/launcher/settings_window_observer.cc",
-      "ash/launcher/settings_window_observer.h",
-      "ash/launcher/shelf_app_updater.cc",
-      "ash/launcher/shelf_app_updater.h",
-      "ash/launcher/shelf_context_menu.cc",
-      "ash/launcher/shelf_context_menu.h",
-      "ash/launcher/shelf_controller_helper.cc",
-      "ash/launcher/shelf_controller_helper.h",
-      "ash/launcher/shelf_extension_app_updater.cc",
-      "ash/launcher/shelf_extension_app_updater.h",
-      "ash/launcher/shelf_spinner_controller.cc",
-      "ash/launcher/shelf_spinner_controller.h",
-      "ash/launcher/shelf_spinner_item_controller.cc",
-      "ash/launcher/shelf_spinner_item_controller.h",
       "ash/login_screen_client_impl.cc",
       "ash/login_screen_client_impl.h",
       "ash/login_screen_shown_observer.h",
@@ -2084,6 +2021,75 @@
       "ash/sharesheet/sharesheet_expand_button.h",
       "ash/sharesheet/sharesheet_target_button.cc",
       "ash/sharesheet/sharesheet_target_button.h",
+      "ash/shelf/app_service/app_service_app_window_arc_tracker.cc",
+      "ash/shelf/app_service/app_service_app_window_arc_tracker.h",
+      "ash/shelf/app_service/app_service_app_window_crostini_tracker.cc",
+      "ash/shelf/app_service/app_service_app_window_crostini_tracker.h",
+      "ash/shelf/app_service/app_service_app_window_shelf_controller.cc",
+      "ash/shelf/app_service/app_service_app_window_shelf_controller.h",
+      "ash/shelf/app_service/app_service_app_window_shelf_item_controller.cc",
+      "ash/shelf/app_service/app_service_app_window_shelf_item_controller.h",
+      "ash/shelf/app_service/app_service_instance_registry_helper.cc",
+      "ash/shelf/app_service/app_service_instance_registry_helper.h",
+      "ash/shelf/app_service/app_service_shelf_context_menu.cc",
+      "ash/shelf/app_service/app_service_shelf_context_menu.h",
+      "ash/shelf/app_service/exo_app_type_resolver.cc",
+      "ash/shelf/app_service/exo_app_type_resolver.h",
+      "ash/shelf/app_service/shelf_app_service_app_updater.cc",
+      "ash/shelf/app_service/shelf_app_service_app_updater.h",
+      "ash/shelf/app_shortcut_shelf_item_controller.cc",
+      "ash/shelf/app_shortcut_shelf_item_controller.h",
+      "ash/shelf/app_window_base.cc",
+      "ash/shelf/app_window_base.h",
+      "ash/shelf/app_window_shelf_controller.cc",
+      "ash/shelf/app_window_shelf_controller.h",
+      "ash/shelf/app_window_shelf_item_controller.cc",
+      "ash/shelf/app_window_shelf_item_controller.h",
+      "ash/shelf/arc_app_shelf_id.cc",
+      "ash/shelf/arc_app_shelf_id.h",
+      "ash/shelf/arc_app_window.cc",
+      "ash/shelf/arc_app_window.h",
+      "ash/shelf/arc_app_window_delegate.h",
+      "ash/shelf/arc_app_window_info.cc",
+      "ash/shelf/arc_app_window_info.h",
+      "ash/shelf/arc_playstore_shortcut_shelf_item_controller.cc",
+      "ash/shelf/arc_playstore_shortcut_shelf_item_controller.h",
+      "ash/shelf/arc_shelf_spinner_item_controller.cc",
+      "ash/shelf/arc_shelf_spinner_item_controller.h",
+      "ash/shelf/browser_shortcut_shelf_item_controller.cc",
+      "ash/shelf/browser_shortcut_shelf_item_controller.h",
+      "ash/shelf/browser_status_monitor.cc",
+      "ash/shelf/browser_status_monitor.h",
+      "ash/shelf/chrome_shelf_controller.cc",
+      "ash/shelf/chrome_shelf_controller.h",
+      "ash/shelf/chrome_shelf_controller_util.cc",
+      "ash/shelf/chrome_shelf_controller_util.h",
+      "ash/shelf/crostini_app_display.cc",
+      "ash/shelf/crostini_app_display.h",
+      "ash/shelf/crostini_app_window.cc",
+      "ash/shelf/crostini_app_window.h",
+      "ash/shelf/extension_shelf_context_menu.cc",
+      "ash/shelf/extension_shelf_context_menu.h",
+      "ash/shelf/extension_uninstaller.cc",
+      "ash/shelf/extension_uninstaller.h",
+      "ash/shelf/lacros_app_window.cc",
+      "ash/shelf/lacros_app_window.h",
+      "ash/shelf/multi_profile_browser_status_monitor.cc",
+      "ash/shelf/multi_profile_browser_status_monitor.h",
+      "ash/shelf/settings_window_observer.cc",
+      "ash/shelf/settings_window_observer.h",
+      "ash/shelf/shelf_app_updater.cc",
+      "ash/shelf/shelf_app_updater.h",
+      "ash/shelf/shelf_context_menu.cc",
+      "ash/shelf/shelf_context_menu.h",
+      "ash/shelf/shelf_controller_helper.cc",
+      "ash/shelf/shelf_controller_helper.h",
+      "ash/shelf/shelf_extension_app_updater.cc",
+      "ash/shelf/shelf_extension_app_updater.h",
+      "ash/shelf/shelf_spinner_controller.cc",
+      "ash/shelf/shelf_spinner_controller.h",
+      "ash/shelf/shelf_spinner_item_controller.cc",
+      "ash/shelf/shelf_spinner_item_controller.h",
       "ash/system_tray_client.cc",
       "ash/system_tray_client.h",
       "ash/tab_scrubber.cc",
@@ -4267,6 +4273,8 @@
       "views/user_education/feature_promo_controller_views.h",
       "views/user_education/feature_promo_registry.cc",
       "views/user_education/feature_promo_registry.h",
+      "views/user_education/feature_tutorial_service_views.cc",
+      "views/user_education/feature_tutorial_service_views.h",
       "views/user_education/new_badge_label.cc",
       "views/user_education/new_badge_label.h",
       "views/user_education/tip_marquee_view.cc",
diff --git a/chrome/browser/ui/android/overlay/overlay_window_android.cc b/chrome/browser/ui/android/overlay/overlay_window_android.cc
index c4a53b5..b1e61393 100644
--- a/chrome/browser/ui/android/overlay/overlay_window_android.cc
+++ b/chrome/browser/ui/android/overlay/overlay_window_android.cc
@@ -83,7 +83,7 @@
   }
 
   controller_->CloseAndFocusInitiator();
-  controller_->OnWindowDestroyed();
+  controller_->OnWindowDestroyed(/*should_pause_video=*/true);
 }
 
 void OverlayWindowAndroid::Play(JNIEnv* env) {
@@ -113,6 +113,16 @@
 }
 
 void OverlayWindowAndroid::Close() {
+  CloseInternal();
+  controller_->OnWindowDestroyed(/*should_pause_video=*/true);
+}
+
+void OverlayWindowAndroid::Hide() {
+  CloseInternal();
+  controller_->OnWindowDestroyed(/*should_pause_video=*/false);
+}
+
+void OverlayWindowAndroid::CloseInternal() {
   if (java_ref_.is_uninitialized())
     return;
 
@@ -121,11 +131,6 @@
   window_android_ = nullptr;
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_PictureInPictureActivity_close(env, java_ref_.get(env));
-  controller_->OnWindowDestroyed();
-}
-
-void OverlayWindowAndroid::Hide() {
-  Close();
 }
 
 bool OverlayWindowAndroid::IsActive() {
diff --git a/chrome/browser/ui/android/overlay/overlay_window_android.h b/chrome/browser/ui/android/overlay/overlay_window_android.h
index af11366..fcb64910 100644
--- a/chrome/browser/ui/android/overlay/overlay_window_android.h
+++ b/chrome/browser/ui/android/overlay/overlay_window_android.h
@@ -72,6 +72,8 @@
   cc::Layer* GetLayerForTesting() override;
 
  private:
+  void CloseInternal();
+
   // A weak reference to Java PictureInPictureActivity object.
   JavaObjectWeakGlobalRef java_ref_;
   ui::WindowAndroid* window_android_;
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index c6b3247f..4d40895 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -4519,11 +4519,14 @@
         Failed to capture Paint Preview.
       </message>
       <message name="IDS_PAINT_PREVIEW_DEMO_PLAYBACK_START" desc="Toast message displayed when paint preview starts displaying. Used in paint preview demo mode." translateable="false">
-        Displaying Paint Preview. Press back to exit the Paint Preview demo.
+        Displaying Paint Preview.
       </message>
       <message name="IDS_PAINT_PREVIEW_DEMO_PLAYBACK_FAILURE" desc="Toast message displayed when there is a failure in playing back a paint preview for the demo. Used in paint preview demo mode." translateable="false">
         Paint Preview playback failed.
       </message>
+      <message name="IDS_PAINT_PREVIEW_DEMO_NO_ACCESSIBILITY" desc="Toast message displayed when there is not accessibility support while playing back a paint preview for the demo. Used in paint preview demo mode." translateable="false">
+        Paint Preview does not have accessibility support.
+      </message>
 
       <!-- Paint Preview Startup Experiment -->
       <message name="IDS_PAINT_PREVIEW_STARTUP_UPGRADE_SNACKBAR_MESSAGE" desc="Message displayed on a snackbar when a paint preview is shown on startup, telling the user that this is a preview of the page">
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index 32dc029..f875fdd 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -33,8 +33,8 @@
 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc b/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
index 0c4c834..6abff06 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 #include "chrome/common/chrome_features.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/app_update.h"
diff --git a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
index 4084a3b..1f8f4d0 100644
--- a/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
+++ b/chrome/browser/ui/app_list/app_service/app_service_app_item.cc
@@ -19,7 +19,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/app_service/app_service_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_features.h"
 
 // static
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.cc b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
index 0427e2e..fbb48290 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
@@ -23,7 +23,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 #include "chrome/grit/component_extension_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/grit/extensions_browser_resources.h"
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index e28cb1a..517a5f5 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -37,7 +37,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_default_app_list.h"
 #include "chrome/browser/ui/app_list/arc/arc_package_syncable_service.h"
 #include "chrome/browser/ui/app_list/arc/arc_pai_starter.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/arc/arc_prefs.h"
diff --git a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
index 08e359d9..e6ce775 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_unittest.cc
@@ -63,9 +63,9 @@
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils.cc b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
index 8ad7f99..d39d26bc 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_utils.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_utils.cc
@@ -38,10 +38,10 @@
 #include "chrome/browser/ui/app_list/search/search_controller.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/app_launch_data.h"
 #include "chrome/browser/ui/app_list/search/search_result_ranker/ranking_item_util.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
-#include "chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/common/pref_names.h"
 #include "components/arc/arc_prefs.h"
 #include "components/arc/arc_service_manager.h"
diff --git a/chrome/browser/ui/app_list/arc/arc_app_utils_unittest.cc b/chrome/browser/ui/app_list/arc/arc_app_utils_unittest.cc
index a44d0a3..d7bda4a 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_utils_unittest.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_utils_unittest.cc
@@ -8,7 +8,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/arc/test/fake_app_instance.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
index efe71d47..c0c1ff6a 100644
--- a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
+++ b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
@@ -24,7 +24,7 @@
 #include "chrome/browser/sync/session_sync_service_factory.h"
 #include "chrome/browser/ui/app_list/app_list_client_impl.h"
 #include "chrome/browser/ui/app_list/extension_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
diff --git a/chrome/browser/ui/app_list/search/app_service_app_result.cc b/chrome/browser/ui/app_list/search/app_service_app_result.cc
index b8bf5c0..3d31429f3f 100644
--- a/chrome/browser/ui/app_list/search/app_service_app_result.cc
+++ b/chrome/browser/ui/app_list/search/app_service_app_result.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/ui/app_list/app_service/app_service_app_item.h"
 #include "chrome/browser/ui/app_list/app_service/app_service_context_menu.h"
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
 #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/ash/DEPS b/chrome/browser/ui/ash/DEPS
index eff67c0..4526bf60 100644
--- a/chrome/browser/ui/ash/DEPS
+++ b/chrome/browser/ui/ash/DEPS
@@ -17,4 +17,7 @@
   "assistant_timers_browsertest\.cc": [
     "+ui/message_center",
   ],
+  "chrome_shell_delegate\.cc": [
+    "+cc/input/touch_action.h",
+  ],
 }
diff --git a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
index 3d83d0f..522bbf8 100644
--- a/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/chrome_browser_main_extra_parts_ash.cc
@@ -35,8 +35,6 @@
 #include "chrome/browser/ui/ash/crosapi_new_window_delegate.h"
 #include "chrome/browser/ui/ash/ime_controller_client.h"
 #include "chrome/browser/ui/ash/in_session_auth_dialog_client.h"
-#include "chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/login_screen_client_impl.h"
 #include "chrome/browser/ui/ash/media_client_impl.h"
 #include "chrome/browser/ui/ash/media_notification_provider_impl.h"
@@ -47,6 +45,8 @@
 #include "chrome/browser/ui/ash/quick_answers/quick_answers_browser_client_impl.h"
 #include "chrome/browser/ui/ash/screen_orientation_delegate_chromeos.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
+#include "chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/system_tray_client.h"
 #include "chrome/browser/ui/ash/tab_scrubber.h"
 #include "chrome/browser/ui/ash/tablet_mode_page_behavior.h"
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 6ad82829..9f5112f 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -33,10 +33,10 @@
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/sessions/tab_restore_service_factory.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
diff --git a/chrome/browser/ui/ash/chrome_shelf_prefs.cc b/chrome/browser/ui/ash/chrome_shelf_prefs.cc
index 54438b18..00e8159 100644
--- a/chrome/browser/ui/ash/chrome_shelf_prefs.cc
+++ b/chrome/browser/ui/ash/chrome_shelf_prefs.cc
@@ -25,8 +25,8 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/default_pinned_apps.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.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/web_app_provider.h"
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 384dd00..6c5b0ad5 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -10,6 +10,7 @@
 #include "ash/public/cpp/ash_features.h"
 #include "ash/screenshot_delegate.h"
 #include "base/bind.h"
+#include "cc/input/touch_action.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part_chromeos.h"
 #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_service_factory.h"
diff --git a/chrome/browser/ui/ash/multi_user/multi_profile_support.cc b/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
index 9469ae6..cc5c0409 100644
--- a/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
+++ b/chrome/browser/ui/ash/multi_user/multi_profile_support.cc
@@ -15,10 +15,10 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
 #include "chrome/browser/ui/ash/session_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
diff --git a/chrome/browser/ui/ash/launcher/DEPS b/chrome/browser/ui/ash/shelf/DEPS
similarity index 100%
rename from chrome/browser/ui/ash/launcher/DEPS
rename to chrome/browser/ui/ash/shelf/DEPS
diff --git a/chrome/browser/ui/ash/launcher/DIR_METADATA b/chrome/browser/ui/ash/shelf/DIR_METADATA
similarity index 100%
rename from chrome/browser/ui/ash/launcher/DIR_METADATA
rename to chrome/browser/ui/ash/shelf/DIR_METADATA
diff --git a/chrome/browser/ui/ash/launcher/OWNERS b/chrome/browser/ui/ash/shelf/OWNERS
similarity index 100%
rename from chrome/browser/ui/ash/launcher/OWNERS
rename to chrome/browser/ui/ash/shelf/OWNERS
diff --git a/chrome/browser/ui/ash/launcher/app_service/OWNERS b/chrome/browser/ui/ash/shelf/app_service/OWNERS
similarity index 100%
rename from chrome/browser/ui/ash/launcher/app_service/OWNERS
rename to chrome/browser/ui/ash/shelf/app_service/OWNERS
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.cc
index 482a2a6..5a9917c 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.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/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.h"
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_types.h"
@@ -23,14 +23,14 @@
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window_info.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window_info.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "components/arc/compat_mode/arc_splash_screen_dialog_view.h"
 #include "components/exo/shell_surface_base.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.h b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.h
similarity index 96%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.h
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.h
index 5288264..0628266 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.h
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.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_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_ARC_TRACKER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_ARC_TRACKER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_ARC_TRACKER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_ARC_TRACKER_H_
 
 #include <map>
 #include <memory>
@@ -172,4 +172,4 @@
   base::WeakPtrFactory<AppServiceAppWindowArcTracker> weak_ptr_factory_{this};
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_ARC_TRACKER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_ARC_TRACKER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_browsertest.cc
similarity index 99%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_browsertest.cc
index 1acb5078..7d9bc320 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_browsertest.cc
@@ -25,8 +25,8 @@
 #include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
 #include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc
similarity index 95%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.cc
index e7a41dd..417389d 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.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/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.h"
 
 #include "ash/public/cpp/multi_user_window_manager.h"
 #include "ash/public/cpp/shelf_model.h"
@@ -21,12 +21,12 @@
 #include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/common/chrome_features.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.h b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.h
similarity index 85%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.h
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.h
index f2d8d22..bf03e92 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.h
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_CROSTINI_TRACKER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_CROSTINI_TRACKER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_CROSTINI_TRACKER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_CROSTINI_TRACKER_H_
 
 #include "ash/public/cpp/shelf_types.h"
 #include "base/containers/flat_set.h"
-#include "chrome/browser/ui/ash/launcher/crostini_app_display.h"
+#include "chrome/browser/ui/ash/shelf/crostini_app_display.h"
 
 class AppServiceAppWindowShelfController;
 
@@ -58,4 +58,4 @@
   base::flat_set<aura::Window*> activation_permissions_;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_CROSTINI_TRACKER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_CROSTINI_TRACKER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.cc
index 6c4aad82..156b03a 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.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/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
 
 #include <memory>
 
@@ -24,17 +24,17 @@
 #include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_crostini_tracker.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/crostini_app_window.h"
-#include "chrome/browser/ui/ash/launcher/lacros_app_window.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_crostini_tracker.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/crostini_app_window.h"
+#include "chrome/browser/ui/ash/shelf/lacros_app_window.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h
similarity index 91%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h
index 7f2d869..be0e179 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.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_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_CONTROLLER_H_
 
 #include <map>
 #include <memory>
@@ -13,9 +13,9 @@
 #include "base/macros.h"
 #include "base/scoped_multi_source_observation.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window_delegate.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window_delegate.h"
 #include "components/services/app_service/public/cpp/instance_registry.h"
 #include "ui/aura/env_observer.h"
 #include "ui/aura/window.h"
@@ -158,4 +158,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppServiceAppWindowShelfController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.cc
similarity index 95%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.cc
index 32eb502..dfa1410 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h"
 
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/ash/arc/pip/arc_pip_bridge.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/favicon/content/content_favicon_driver.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h
similarity index 81%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h
rename to chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h
index 5321845..abc07c6b 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
 
 #include <set>
 
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
 
 class AppServiceAppWindowShelfController;
 
@@ -51,4 +51,4 @@
   std::set<int> task_ids_;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.cc
index dac5924b..4297812 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.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/ui/ash/launcher/app_service/app_service_instance_registry_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.h"
 
 #include <set>
 #include <string>
@@ -15,8 +15,8 @@
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.h
similarity index 94%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
rename to chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.h
index b3bc5453..176456a 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
 
 #include <map>
 #include <memory>
 #include <set>
 
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "components/services/app_service/public/cpp/instance.h"
 
 namespace apps {
@@ -137,4 +137,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppServiceInstanceRegistryHelper);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_INSTANCE_REGISTRY_HELPER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.cc
index 2d0705d..fe542682 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.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/ui/ash/launcher/app_service/app_service_shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.h"
 
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/new_window_delegate.h"
@@ -32,9 +32,9 @@
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/extension_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/views/crostini/crostini_app_restart_dialog.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.h b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.h
similarity index 91%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.h
rename to chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.h
index 33d376d..90f2669 100644
--- a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.h
+++ b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.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_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_SHELF_CONTEXT_MENU_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_SHELF_CONTEXT_MENU_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_SHELF_CONTEXT_MENU_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_SHELF_CONTEXT_MENU_H_
 
 #include <memory>
 #include <string>
@@ -11,7 +11,7 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/apps/app_service/app_shortcut_item.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "extensions/common/constants.h"
 
@@ -92,4 +92,4 @@
   base::WeakPtrFactory<AppServiceShelfContextMenu> weak_ptr_factory_{this};
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_APP_SERVICE_SHELF_CONTEXT_MENU_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_APP_SERVICE_SHELF_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu_browsertest.cc b/chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc
similarity index 100%
rename from chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu_browsertest.cc
rename to chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc
diff --git a/chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.cc b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.cc
rename to chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
index 705e9d5a..17def913 100644
--- a/chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.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/ui/ash/launcher/app_service/exo_app_type_resolver.h"
+#include "chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h"
 
 #include "ash/public/cpp/app_types.h"
 #include "base/strings/string_piece.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.h b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h
similarity index 78%
rename from chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.h
rename to chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.h
index 2f68da2..4934bab 100644
--- a/chrome/browser/ui/ash/launcher/app_service/exo_app_type_resolver.h
+++ b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.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_UI_ASH_LAUNCHER_APP_SERVICE_EXO_APP_TYPE_RESOLVER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_EXO_APP_TYPE_RESOLVER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_EXO_APP_TYPE_RESOLVER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_EXO_APP_TYPE_RESOLVER_H_
 
 #include "components/exo/wm_helper.h"
 
@@ -22,4 +22,4 @@
       ui::PropertyHandler& out_properties_container) override;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_EXO_APP_TYPE_RESOLVER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_EXO_APP_TYPE_RESOLVER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.cc b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.cc
rename to chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.cc
index 7af538c..0b2dd4a 100644
--- a/chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.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/ui/ash/launcher/app_service/shelf_app_service_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h"
 
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
diff --git a/chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.h b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h
similarity index 79%
rename from chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.h
rename to chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h
index f28d0d5..90666f4 100644
--- a/chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.h
+++ b/chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_SHELF_APP_SERVICE_APP_UPDATER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_SHELF_APP_SERVICE_APP_UPDATER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_SHELF_APP_SERVICE_APP_UPDATER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_SHELF_APP_SERVICE_APP_UPDATER_H_
 
 #include <set>
 #include <string>
 
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/shelf_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/shelf_app_updater.h"
 #include "components/services/app_service/public/cpp/app_registry_cache.h"
 
 namespace apps {
@@ -36,4 +36,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfAppServiceAppUpdater);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SERVICE_SHELF_APP_SERVICE_APP_UPDATER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SERVICE_SHELF_APP_SERVICE_APP_UPDATER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.cc
rename to chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.cc
index e1729589..c946be7 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.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/ui/ash/launcher/app_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h"
 
 #include <stddef.h>
 
@@ -16,13 +16,13 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
+#include "chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.h b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h
similarity index 94%
rename from chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.h
rename to chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h
index 1420829..1e90ab2 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.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_UI_ASH_LAUNCHER_APP_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
 
 #include <memory>
 #include <string>
@@ -116,4 +116,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppShortcutShelfItemController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller_browsertest.cc
rename to chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc
index 9335d22c..cfdbe546 100644
--- a/chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h"
 
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "base/callback_helpers.h"
 #include "chrome/browser/chromeos/crostini/crostini_terminal.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
diff --git a/chrome/browser/ui/ash/launcher/app_window_base.cc b/chrome/browser/ui/ash/shelf/app_window_base.cc
similarity index 94%
rename from chrome/browser/ui/ash/launcher/app_window_base.cc
rename to chrome/browser/ui/ash/shelf/app_window_base.cc
index cd410d5..3f14b73d 100644
--- a/chrome/browser/ui/ash/launcher/app_window_base.cc
+++ b/chrome/browser/ui/ash/shelf/app_window_base.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
 
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
 #include "ui/views/widget/widget.h"
 
 AppWindowBase::AppWindowBase(const ash::ShelfID& shelf_id,
diff --git a/chrome/browser/ui/ash/launcher/app_window_base.h b/chrome/browser/ui/ash/shelf/app_window_base.h
similarity index 93%
rename from chrome/browser/ui/ash/launcher/app_window_base.h
rename to chrome/browser/ui/ash/shelf/app_window_base.h
index 88b18f7..a8b7d71 100644
--- a/chrome/browser/ui/ash/launcher/app_window_base.h
+++ b/chrome/browser/ui/ash/shelf/app_window_base.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_BASE_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_BASE_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_BASE_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_BASE_H_
 
 #include <string>
 
@@ -84,4 +84,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppWindowBase);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_BASE_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_BASE_H_
diff --git a/chrome/browser/ui/ash/launcher/app_window_shelf_controller.cc b/chrome/browser/ui/ash/shelf/app_window_shelf_controller.cc
similarity index 89%
rename from chrome/browser/ui/ash/launcher/app_window_shelf_controller.cc
rename to chrome/browser/ui/ash/shelf/app_window_shelf_controller.cc
index 63a3d17e..eec13f25 100644
--- a/chrome/browser/ui/ash/launcher/app_window_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/app_window_shelf_controller.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_controller.h"
 
 #include "ash/public/cpp/shelf_model.h"
 #include "ash/shell.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "ui/wm/public/activation_client.h"
 
 AppWindowShelfController::AppWindowShelfController(ChromeShelfController* owner)
diff --git a/chrome/browser/ui/ash/launcher/app_window_shelf_controller.h b/chrome/browser/ui/ash/shelf/app_window_shelf_controller.h
similarity index 90%
rename from chrome/browser/ui/ash/launcher/app_window_shelf_controller.h
rename to chrome/browser/ui/ash/shelf/app_window_shelf_controller.h
index 80b29126..a7c6881 100644
--- a/chrome/browser/ui/ash/launcher/app_window_shelf_controller.h
+++ b/chrome/browser/ui/ash/shelf/app_window_shelf_controller.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_UI_ASH_LAUNCHER_APP_WINDOW_SHELF_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_SHELF_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_SHELF_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_SHELF_CONTROLLER_H_
 
 #include <string>
 
@@ -66,4 +66,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppWindowShelfController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_SHELF_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_SHELF_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.cc
rename to chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.cc
index c170a7b..285351b 100644
--- a/chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.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/ui/ash/launcher/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
 
 #include <algorithm>
 #include <iterator>
@@ -11,10 +11,10 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/window_properties.h"
 #include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/wm/core/window_util.h"
 
diff --git a/chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h b/chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h
similarity index 94%
rename from chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h
rename to chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h
index 4dbc3bb3..72622b4 100644
--- a/chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.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_UI_ASH_LAUNCHER_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
 
 #include <list>
 #include <memory>
@@ -108,4 +108,4 @@
   DISALLOW_COPY_AND_ASSIGN(AppWindowShelfItemController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_APP_WINDOW_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_app_shelf_browsertest.cc b/chrome/browser/ui/ash/shelf/arc_app_shelf_browsertest.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/arc_app_shelf_browsertest.cc
rename to chrome/browser/ui/ash/shelf/arc_app_shelf_browsertest.cc
index e2bff06..8ecd6da 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_shelf_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/arc_app_shelf_browsertest.cc
@@ -30,10 +30,10 @@
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "components/arc/arc_service_manager.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
diff --git a/chrome/browser/ui/ash/launcher/arc_app_shelf_id.cc b/chrome/browser/ui/ash/shelf/arc_app_shelf_id.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/arc_app_shelf_id.cc
rename to chrome/browser/ui/ash/shelf/arc_app_shelf_id.cc
index 32382ab..92e224e 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_shelf_id.cc
+++ b/chrome/browser/ui/ash/shelf/arc_app_shelf_id.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/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
diff --git a/chrome/browser/ui/ash/launcher/arc_app_shelf_id.h b/chrome/browser/ui/ash/shelf/arc_app_shelf_id.h
similarity index 89%
rename from chrome/browser/ui/ash/launcher/arc_app_shelf_id.h
rename to chrome/browser/ui/ash/shelf/arc_app_shelf_id.h
index 333dc47..25aee0e 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_shelf_id.h
+++ b/chrome/browser/ui/ash/shelf/arc_app_shelf_id.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_UI_ASH_LAUNCHER_ARC_APP_SHELF_ID_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_SHELF_ID_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_SHELF_ID_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_SHELF_ID_H_
 
 #include <string>
 
@@ -49,4 +49,4 @@
 
 }  // namespace arc
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_SHELF_ID_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_SHELF_ID_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_app_shelf_id_unittest.cc b/chrome/browser/ui/ash/shelf/arc_app_shelf_id_unittest.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/arc_app_shelf_id_unittest.cc
rename to chrome/browser/ui/ash/shelf/arc_app_shelf_id_unittest.cc
index 6284e48..2d0c20f7 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_shelf_id_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/arc_app_shelf_id_unittest.cc
@@ -5,7 +5,7 @@
 #include <string>
 
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window.cc b/chrome/browser/ui/ash/shelf/arc_app_window.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/arc_app_window.cc
rename to chrome/browser/ui/ash/shelf/arc_app_window.cc
index 0ba630a3..22f19cf1 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window.cc
+++ b/chrome/browser/ui/ash/shelf/arc_app_window.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/ui/ash/launcher/arc_app_window.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window.h"
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
@@ -11,7 +11,7 @@
 #include "chrome/browser/ui/app_list/app_service/app_service_app_icon_loader.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_icon.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window_delegate.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window_delegate.h"
 #include "components/arc/arc_util.h"
 #include "components/exo/shell_surface_base.h"
 #include "components/exo/shell_surface_util.h"
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window.h b/chrome/browser/ui/ash/shelf/arc_app_window.h
similarity index 89%
rename from chrome/browser/ui/ash/launcher/arc_app_window.h
rename to chrome/browser/ui/ash/shelf/arc_app_window.h
index f48f5d1..fde4f10 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window.h
+++ b/chrome/browser/ui/ash/shelf/arc_app_window.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_UI_ASH_LAUNCHER_ARC_APP_WINDOW_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_H_
 
 #include <string>
 #include <vector>
@@ -13,8 +13,8 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/image_decoder/image_decoder.h"
 #include "chrome/browser/ui/app_icon_loader.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 
 class AppServiceAppIconLoader;
 class ArcAppWindowDelegate;
@@ -90,4 +90,4 @@
   DISALLOW_COPY_AND_ASSIGN(ArcAppWindow);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_delegate.h b/chrome/browser/ui/ash/shelf/arc_app_window_delegate.h
similarity index 72%
rename from chrome/browser/ui/ash/launcher/arc_app_window_delegate.h
rename to chrome/browser/ui/ash/shelf/arc_app_window_delegate.h
index 92855e3..2d97390b 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_delegate.h
+++ b/chrome/browser/ui/ash/shelf/arc_app_window_delegate.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_UI_ASH_LAUNCHER_ARC_APP_WINDOW_DELEGATE_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_DELEGATE_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_DELEGATE_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_DELEGATE_H_
 
 // Delegate interface for ArcAppWindow.
 class ArcAppWindowDelegate {
@@ -18,4 +18,4 @@
   virtual int GetActiveTaskId() const = 0;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_DELEGATE_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_info.cc b/chrome/browser/ui/ash/shelf/arc_app_window_info.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/arc_app_window_info.cc
rename to chrome/browser/ui/ash/shelf/arc_app_window_info.cc
index ea0bc70f..1102fcc 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_info.cc
+++ b/chrome/browser/ui/ash/shelf/arc_app_window_info.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/ui/ash/launcher/arc_app_window_info.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window_info.h"
 
 #include "ash/public/cpp/window_properties.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_info.h b/chrome/browser/ui/ash/shelf/arc_app_window_info.h
similarity index 86%
rename from chrome/browser/ui/ash/launcher/arc_app_window_info.h
rename to chrome/browser/ui/ash/shelf/arc_app_window_info.h
index 8c966fb6..815dd89 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_info.h
+++ b/chrome/browser/ui/ash/shelf/arc_app_window_info.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_INFO_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_INFO_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_INFO_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_INFO_H_
 
 #include <string>
 #include <vector>
 
 #include "ash/public/cpp/shelf_types.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 #include "ui/aura/window.h"
 
 // The information about the ARC application window which has to be kept
@@ -63,4 +63,4 @@
   aura::Window* window_ = nullptr;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_APP_WINDOW_INFO_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_ARC_APP_WINDOW_INFO_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.cc
similarity index 92%
rename from chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.cc
rename to chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.cc
index e0feed4d..d7890dd 100644
--- a/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.h"
 
 #include <utility>
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_launcher.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 #include "ui/events/event_constants.h"
 #include "ui/gfx/image/image_skia.h"
diff --git a/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.h b/chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.h
similarity index 74%
rename from chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.h
rename to chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.h
index eb36aa1ce..bd764ff 100644
--- a/chrome/browser/ui/ash/launcher/arc_playstore_shortcut_shelf_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/arc_playstore_shortcut_shelf_item_controller.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_PLAYSTORE_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_PLAYSTORE_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_ARC_PLAYSTORE_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_ARC_PLAYSTORE_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h"
 
 class ArcAppLauncher;
 
@@ -31,4 +31,4 @@
   DISALLOW_COPY_AND_ASSIGN(ArcPlaystoreShortcutShelfItemController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_PLAYSTORE_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_ARC_PLAYSTORE_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc b/chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.cc
similarity index 94%
rename from chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc
rename to chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.cc
index 02106e0..72ead27 100644
--- a/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.h"
 
 #include "chrome/browser/ash/arc/session/arc_session_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 
 ArcShelfSpinnerItemController::ArcShelfSpinnerItemController(
diff --git a/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.h b/chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.h
similarity index 87%
rename from chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.h
rename to chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.h
index 4a012788..8a7425f 100644
--- a/chrome/browser/ui/ash/launcher/arc_shelf_spinner_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/arc_shelf_spinner_item_controller.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_UI_ASH_LAUNCHER_ARC_SHELF_SPINNER_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_SHELF_SPINNER_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_ARC_SHELF_SPINNER_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_ARC_SHELF_SPINNER_ITEM_CONTROLLER_H_
 
 #include <stdint.h>
 
@@ -15,7 +15,7 @@
 #include "chrome/browser/ash/arc/session/arc_session_manager_observer.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h"
 #include "components/arc/mojom/app.mojom.h"
 
 // ArcShelfSpinnerItemController displays the icon of the ARC app that
@@ -59,4 +59,4 @@
   DISALLOW_COPY_AND_ASSIGN(ArcShelfSpinnerItemController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_ARC_SHELF_SPINNER_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_ARC_SHELF_SPINNER_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.cc b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.cc
rename to chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.cc
index ac346f2..7ae9d4a 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.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/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
 
 #include <limits>
 #include <utility>
@@ -13,10 +13,10 @@
 #include "ash/wm/desks/desks_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/ash_util.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h
similarity index 91%
rename from chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h
rename to chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h
index 9a4cd18..3042849 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.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_UI_ASH_LAUNCHER_BROWSER_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_BROWSER_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_BROWSER_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
 
 #include <memory>
 
@@ -66,4 +66,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserShortcutShelfItemController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_BROWSER_SHORTCUT_SHELF_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller_browsertest.cc
rename to chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc
index ee046641..35a31d17 100644
--- a/chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
 
 #include "ash/public/cpp/shelf_model.h"
 #include "base/callback_helpers.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/ui/ash/launcher/browser_status_monitor.cc b/chrome/browser/ui/ash/shelf/browser_status_monitor.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/browser_status_monitor.cc
rename to chrome/browser/ui/ash/shelf/browser_status_monitor.cc
index 91e5d0b..3d8650d 100644
--- a/chrome/browser/ui/ash/launcher/browser_status_monitor.cc
+++ b/chrome/browser/ui/ash/shelf/browser_status_monitor.cc
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
+#include "chrome/browser/ui/ash/shelf/browser_status_monitor.h"
 
 #include <memory>
 
 #include "ash/public/cpp/shelf_types.h"
 #include "base/containers/contains.h"
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
diff --git a/chrome/browser/ui/ash/launcher/browser_status_monitor.h b/chrome/browser/ui/ash/shelf/browser_status_monitor.h
similarity index 92%
rename from chrome/browser/ui/ash/launcher/browser_status_monitor.h
rename to chrome/browser/ui/ash/shelf/browser_status_monitor.h
index a150f30..446603e 100644
--- a/chrome/browser/ui/ash/launcher/browser_status_monitor.h
+++ b/chrome/browser/ui/ash/shelf/browser_status_monitor.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_UI_ASH_LAUNCHER_BROWSER_STATUS_MONITOR_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_STATUS_MONITOR_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_BROWSER_STATUS_MONITOR_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_BROWSER_STATUS_MONITOR_H_
 
 #include <stdint.h>
 
@@ -14,8 +14,8 @@
 
 #include "base/check_op.h"
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_instance_registry_helper.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_instance_registry_helper.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/browser_tab_strip_tracker.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
@@ -121,4 +121,4 @@
   DISALLOW_COPY_AND_ASSIGN(BrowserStatusMonitor);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_BROWSER_STATUS_MONITOR_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_BROWSER_STATUS_MONITOR_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller.cc
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller.cc
index 992aa06..188858c 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.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/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 
 #include <algorithm>
 #include <memory>
@@ -48,23 +48,23 @@
 #include "chrome/browser/ui/apps/app_info_dialog.h"
 #include "chrome/browser/ui/ash/chrome_shelf_prefs.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_arc_tracker.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_service/shelf_app_service_app_updater.h"
-#include "chrome/browser/ui/ash/launcher/app_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
-#include "chrome/browser/ui/ash/launcher/shelf_extension_app_updater.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
 #include "chrome/browser/ui/ash/notification_badge_color_cache.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_arc_tracker.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/shelf_app_service_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/app_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/browser_status_monitor.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/shelf_extension_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller.h b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
similarity index 97%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller.h
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller.h
index 27139901..171ba7a 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller.h
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller.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_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_H_
 
 #include <map>
 #include <memory>
@@ -18,8 +18,8 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/app_icon_loader_delegate.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
-#include "chrome/browser/ui/ash/launcher/settings_window_observer.h"
-#include "chrome/browser/ui/ash/launcher/shelf_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/settings_window_observer.h"
+#include "chrome/browser/ui/ash/shelf/shelf_app_updater.h"
 #include "components/account_id/account_id.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/sync_preferences/pref_service_syncable_observer.h"
@@ -451,4 +451,4 @@
   DISALLOW_COPY_AND_ASSIGN(ChromeShelfController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
similarity index 99%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller_browsertest.cc
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index 57cdd01..660f145 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.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/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 
 #include <stddef.h>
 #include <memory>
@@ -59,10 +59,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 #include "chrome/browser/ui/ash/chrome_shelf_prefs.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_dialogs.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.cc
similarity index 92%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.cc
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.cc
index 63744d2..c87cdfb4 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.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/ui/ash/launcher/chrome_shelf_controller_test_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h"
 
 #include <memory>
 
@@ -12,7 +12,7 @@
 #include "base/callback_helpers.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
 
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.h b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h
similarity index 75%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.h
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.h
index 6df6bc7..b299eeb 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_test_util.h
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_test_util.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_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_TEST_UTIL_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_TEST_UTIL_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_TEST_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_TEST_UTIL_H_
 
 #include "ash/public/cpp/shelf_types.h"
 #include "ui/events/types/event_type.h"
@@ -17,4 +17,4 @@
     int64_t display_id,
     ash::ShelfLaunchSource source = ash::LAUNCH_FROM_UNKNOWN);
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_TEST_UTIL_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_TEST_UTIL_H_
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_unittest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
similarity index 99%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller_unittest.cc
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc
index d4c91ae..0bcae544 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_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/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 
 #include <stddef.h>
 
@@ -67,19 +67,19 @@
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
 #include "chrome/browser/ui/ash/chrome_shelf_prefs.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/app_window_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_window.h"
-#include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_profile_support.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_helper.h"
 #include "chrome/browser/ui/ash/session_controller_client_impl.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/app_window_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_window.h"
+#include "chrome/browser/ui/ash/shelf/browser_status_monitor.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h"
 #include "chrome/browser/ui/ash/test_wallpaper_controller.h"
 #include "chrome/browser/ui/ash/wallpaper_controller_client_impl.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.cc
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.cc
index 1a4346c5..b533d54 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.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/ui/ash/launcher/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 
 #include "ash/public/cpp/shelf_model.h"
 #include "base/containers/contains.h"
diff --git a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h
similarity index 81%
rename from chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h
rename to chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h
index e3d4a93..8b7d95d3 100644
--- a/chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.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_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_UTIL_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_UTIL_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_UTIL_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_UTIL_H_
 
 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
 
@@ -32,4 +32,4 @@
 bool IsBrowserRepresentedInBrowserList(Browser* browser,
                                        const ash::ShelfModel* model);
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CHROME_SHELF_CONTROLLER_UTIL_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_CHROME_SHELF_CONTROLLER_UTIL_H_
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_display.cc b/chrome/browser/ui/ash/shelf/crostini_app_display.cc
similarity index 94%
rename from chrome/browser/ui/ash/launcher/crostini_app_display.cc
rename to chrome/browser/ui/ash/shelf/crostini_app_display.cc
index c639667..dd31e19 100644
--- a/chrome/browser/ui/ash/launcher/crostini_app_display.cc
+++ b/chrome/browser/ui/ash/shelf/crostini_app_display.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/ui/ash/launcher/crostini_app_display.h"
+#include "chrome/browser/ui/ash/shelf/crostini_app_display.h"
 
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_display.h b/chrome/browser/ui/ash/shelf/crostini_app_display.h
similarity index 83%
rename from chrome/browser/ui/ash/launcher/crostini_app_display.h
rename to chrome/browser/ui/ash/shelf/crostini_app_display.h
index e39358f..c14b487 100644
--- a/chrome/browser/ui/ash/launcher/crostini_app_display.h
+++ b/chrome/browser/ui/ash/shelf/crostini_app_display.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_UI_ASH_LAUNCHER_CROSTINI_APP_DISPLAY_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_DISPLAY_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_DISPLAY_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_DISPLAY_H_
 
 #include <deque>
 #include <map>
@@ -33,4 +33,4 @@
   DISALLOW_COPY_AND_ASSIGN(CrostiniAppDisplay);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_DISPLAY_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_DISPLAY_H_
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window.cc b/chrome/browser/ui/ash/shelf/crostini_app_window.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/crostini_app_window.cc
rename to chrome/browser/ui/ash/shelf/crostini_app_window.cc
index 061b07a..6842902 100644
--- a/chrome/browser/ui/ash/launcher/crostini_app_window.cc
+++ b/chrome/browser/ui/ash/shelf/crostini_app_window.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/ui/ash/launcher/crostini_app_window.h"
+#include "chrome/browser/ui/ash/shelf/crostini_app_window.h"
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_icon_loader_delegate.h"
diff --git a/chrome/browser/ui/ash/launcher/crostini_app_window.h b/chrome/browser/ui/ash/shelf/crostini_app_window.h
similarity index 82%
rename from chrome/browser/ui/ash/launcher/crostini_app_window.h
rename to chrome/browser/ui/ash/shelf/crostini_app_window.h
index b9efc56..cf899fe 100644
--- a/chrome/browser/ui/ash/launcher/crostini_app_window.h
+++ b/chrome/browser/ui/ash/shelf/crostini_app_window.h
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_WINDOW_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_WINDOW_H_
 
 #include <memory>
 #include <string>
 
 #include "ash/public/cpp/shelf_types.h"
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
 
 namespace views {
 class Widget;
@@ -42,4 +42,4 @@
   std::unique_ptr<IconLoader> window_icon_loader_;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_CROSTINI_APP_WINDOW_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_CROSTINI_APP_WINDOW_H_
diff --git a/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc
rename to chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
index cc2b9ba..0e55cfd 100644
--- a/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.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/ui/ash/launcher/extension_shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h"
 
 #include <utility>
 
@@ -15,9 +15,9 @@
 #include "chrome/browser/prefs/incognito_mode_prefs.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/extension_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
diff --git a/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.h b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h
similarity index 85%
rename from chrome/browser/ui/ash/launcher/extension_shelf_context_menu.h
rename to chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h
index bcee91e..0c59409 100644
--- a/chrome/browser/ui/ash/launcher/extension_shelf_context_menu.h
+++ b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_EXTENSION_SHELF_CONTEXT_MENU_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_EXTENSION_SHELF_CONTEXT_MENU_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_EXTENSION_SHELF_CONTEXT_MENU_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_EXTENSION_SHELF_CONTEXT_MENU_H_
 
 #include <memory>
 
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 #include "extensions/common/constants.h"
 
 namespace extensions {
@@ -51,4 +51,4 @@
   DISALLOW_COPY_AND_ASSIGN(ExtensionShelfContextMenu);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_EXTENSION_SHELF_CONTEXT_MENU_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_EXTENSION_SHELF_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/ash/launcher/extension_uninstaller.cc b/chrome/browser/ui/ash/shelf/extension_uninstaller.cc
similarity index 95%
rename from chrome/browser/ui/ash/launcher/extension_uninstaller.cc
rename to chrome/browser/ui/ash/shelf/extension_uninstaller.cc
index 536d1e7..8b3430f 100644
--- a/chrome/browser/ui/ash/launcher/extension_uninstaller.cc
+++ b/chrome/browser/ui/ash/shelf/extension_uninstaller.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/ui/ash/launcher/extension_uninstaller.h"
+#include "chrome/browser/ui/ash/shelf/extension_uninstaller.h"
 
 #include <string>
 
diff --git a/chrome/browser/ui/ash/launcher/extension_uninstaller.h b/chrome/browser/ui/ash/shelf/extension_uninstaller.h
similarity index 87%
rename from chrome/browser/ui/ash/launcher/extension_uninstaller.h
rename to chrome/browser/ui/ash/shelf/extension_uninstaller.h
index b617d8d6..c5baaea 100644
--- a/chrome/browser/ui/ash/launcher/extension_uninstaller.h
+++ b/chrome/browser/ui/ash/shelf/extension_uninstaller.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_UI_ASH_LAUNCHER_EXTENSION_UNINSTALLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_EXTENSION_UNINSTALLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_EXTENSION_UNINSTALLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_EXTENSION_UNINSTALLER_H_
 
 #include "base/macros.h"
 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
@@ -42,4 +42,4 @@
   DISALLOW_COPY_AND_ASSIGN(ExtensionUninstaller);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_EXTENSION_UNINSTALLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_EXTENSION_UNINSTALLER_H_
diff --git a/chrome/browser/ui/ash/launcher/lacros_app_window.cc b/chrome/browser/ui/ash/shelf/lacros_app_window.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/lacros_app_window.cc
rename to chrome/browser/ui/ash/shelf/lacros_app_window.cc
index b46e344..721c2bd 100644
--- a/chrome/browser/ui/ash/launcher/lacros_app_window.cc
+++ b/chrome/browser/ui/ash/shelf/lacros_app_window.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/ui/ash/launcher/lacros_app_window.h"
+#include "chrome/browser/ui/ash/shelf/lacros_app_window.h"
 
 #include "build/branding_buildflags.h"
 #include "chrome/grit/chrome_unscaled_resources.h"
diff --git a/chrome/browser/ui/ash/launcher/lacros_app_window.h b/chrome/browser/ui/ash/shelf/lacros_app_window.h
similarity index 72%
rename from chrome/browser/ui/ash/launcher/lacros_app_window.h
rename to chrome/browser/ui/ash/shelf/lacros_app_window.h
index 02245d3..1aae47e5 100644
--- a/chrome/browser/ui/ash/launcher/lacros_app_window.h
+++ b/chrome/browser/ui/ash/shelf/lacros_app_window.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_LACROS_APP_WINDOW_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_LACROS_APP_WINDOW_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_LACROS_APP_WINDOW_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_LACROS_APP_WINDOW_H_
 
-#include "chrome/browser/ui/ash/launcher/app_window_base.h"
+#include "chrome/browser/ui/ash/shelf/app_window_base.h"
 
 namespace ash {
 struct ShelfID;
@@ -25,4 +25,4 @@
   ~LacrosAppWindow() override;
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_LACROS_APP_WINDOW_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_LACROS_APP_WINDOW_H_
diff --git a/chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.cc b/chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.cc
rename to chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.cc
index 94dc1e0..32443b48 100644
--- a/chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.cc
+++ b/chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.cc
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h"
+#include "chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.h"
 
 #include "ash/public/cpp/shelf_item.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/containers/contains.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
diff --git a/chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h b/chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.h
similarity index 81%
rename from chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h
rename to chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.h
index 64d9b04..5c468954 100644
--- a/chrome/browser/ui/ash/launcher/multi_profile_browser_status_monitor.h
+++ b/chrome/browser/ui/ash/shelf/multi_profile_browser_status_monitor.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_MULTI_PROFILE_BROWSER_STATUS_MONITOR_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_MULTI_PROFILE_BROWSER_STATUS_MONITOR_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_MULTI_PROFILE_BROWSER_STATUS_MONITOR_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_MULTI_PROFILE_BROWSER_STATUS_MONITOR_H_
 
 #include "base/macros.h"
-#include "chrome/browser/ui/ash/launcher/browser_status_monitor.h"
+#include "chrome/browser/ui/ash/shelf/browser_status_monitor.h"
 
 // MultiProfileBrowserStatusMonitor uses mainly the BrowserStatusMonitor
 // with the addition that it creates and destroys launcher items for windowed
@@ -38,4 +38,4 @@
   DISALLOW_COPY_AND_ASSIGN(MultiProfileBrowserStatusMonitor);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_MULTI_PROFILE_BROWSER_STATUS_MONITOR_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_MULTI_PROFILE_BROWSER_STATUS_MONITOR_H_
diff --git a/chrome/browser/ui/ash/launcher/settings_window_observer.cc b/chrome/browser/ui/ash/shelf/settings_window_observer.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/settings_window_observer.cc
rename to chrome/browser/ui/ash/shelf/settings_window_observer.cc
index 2c6f846cb..5c00f6d4 100644
--- a/chrome/browser/ui/ash/launcher/settings_window_observer.cc
+++ b/chrome/browser/ui/ash/shelf/settings_window_observer.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/ui/ash/launcher/settings_window_observer.h"
+#include "chrome/browser/ui/ash/shelf/settings_window_observer.h"
 
 #include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "ash/public/cpp/shelf_item.h"
diff --git a/chrome/browser/ui/ash/launcher/settings_window_observer.h b/chrome/browser/ui/ash/shelf/settings_window_observer.h
similarity index 81%
rename from chrome/browser/ui/ash/launcher/settings_window_observer.h
rename to chrome/browser/ui/ash/shelf/settings_window_observer.h
index fa1c02a..366c7844 100644
--- a/chrome/browser/ui/ash/launcher/settings_window_observer.h
+++ b/chrome/browser/ui/ash/shelf/settings_window_observer.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_UI_ASH_LAUNCHER_SETTINGS_WINDOW_OBSERVER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SETTINGS_WINDOW_OBSERVER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SETTINGS_WINDOW_OBSERVER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SETTINGS_WINDOW_OBSERVER_H_
 
 #include <memory>
 
@@ -28,4 +28,4 @@
   DISALLOW_COPY_AND_ASSIGN(SettingsWindowObserver);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SETTINGS_WINDOW_OBSERVER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SETTINGS_WINDOW_OBSERVER_H_
diff --git a/chrome/browser/ui/ash/launcher/shelf_app_updater.cc b/chrome/browser/ui/ash/shelf/shelf_app_updater.cc
similarity index 86%
rename from chrome/browser/ui/ash/launcher/shelf_app_updater.cc
rename to chrome/browser/ui/ash/shelf/shelf_app_updater.cc
index f90ec6c..3d924ab 100644
--- a/chrome/browser/ui/ash/launcher/shelf_app_updater.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_app_updater.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/ui/ash/launcher/shelf_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/shelf_app_updater.h"
 
 ShelfAppUpdater::ShelfAppUpdater(Delegate* delegate,
                                  content::BrowserContext* browser_context)
diff --git a/chrome/browser/ui/ash/launcher/shelf_app_updater.h b/chrome/browser/ui/ash/shelf/shelf_app_updater.h
similarity index 87%
rename from chrome/browser/ui/ash/launcher/shelf_app_updater.h
rename to chrome/browser/ui/ash/shelf/shelf_app_updater.h
index 048a58b..56d66db 100644
--- a/chrome/browser/ui/ash/launcher/shelf_app_updater.h
+++ b/chrome/browser/ui/ash/shelf/shelf_app_updater.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_UI_ASH_LAUNCHER_SHELF_APP_UPDATER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_APP_UPDATER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SHELF_APP_UPDATER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SHELF_APP_UPDATER_H_
 
 #include <string>
 
@@ -49,4 +49,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfAppUpdater);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_APP_UPDATER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SHELF_APP_UPDATER_H_
diff --git a/chrome/browser/ui/ash/launcher/shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/shelf_context_menu.cc
similarity index 96%
rename from chrome/browser/ui/ash/launcher/shelf_context_menu.cc
rename to chrome/browser/ui/ash/shelf/shelf_context_menu.cc
index 5162101..e6b79fd9 100644
--- a/chrome/browser/ui/ash/launcher/shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_context_menu.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/ui/ash/launcher/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 
 #include <memory>
 #include <string>
@@ -20,11 +20,11 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
-#include "chrome/browser/ui/ash/launcher/app_service/app_service_shelf_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/extension_shelf_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/extension_uninstaller.h"
+#include "chrome/browser/ui/ash/shelf/app_service/app_service_shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/extension_uninstaller.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/vector_icons/vector_icons.h"
diff --git a/chrome/browser/ui/ash/launcher/shelf_context_menu.h b/chrome/browser/ui/ash/shelf/shelf_context_menu.h
similarity index 92%
rename from chrome/browser/ui/ash/launcher/shelf_context_menu.h
rename to chrome/browser/ui/ash/shelf/shelf_context_menu.h
index fa287c7..a88b860 100644
--- a/chrome/browser/ui/ash/launcher/shelf_context_menu.h
+++ b/chrome/browser/ui/ash/shelf/shelf_context_menu.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_UI_ASH_LAUNCHER_SHELF_CONTEXT_MENU_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_CONTEXT_MENU_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SHELF_CONTEXT_MENU_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SHELF_CONTEXT_MENU_H_
 
 #include "ash/public/cpp/app_menu_constants.h"
 #include "ash/public/cpp/shelf_item.h"
@@ -70,4 +70,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfContextMenu);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_CONTEXT_MENU_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SHELF_CONTEXT_MENU_H_
diff --git a/chrome/browser/ui/ash/launcher/shelf_context_menu_unittest.cc b/chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/shelf_context_menu_unittest.cc
rename to chrome/browser/ui/ash/shelf/shelf_context_menu_unittest.cc
index ecc36bd..fe7b67a 100644
--- a/chrome/browser/ui/ash/launcher/shelf_context_menu_unittest.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_context_menu_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/ui/ash/launcher/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
 
 #include <memory>
 #include <utility>
@@ -35,11 +35,11 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
-#include "chrome/browser/ui/ash/launcher/browser_shortcut_shelf_item_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/extension_shelf_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/browser_shortcut_shelf_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/extension_shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/system_web_apps/test/test_system_web_app_manager.h"
 #include "chrome/browser/web_applications/test/test_web_app_provider.h"
diff --git a/chrome/browser/ui/ash/launcher/shelf_controller_helper.cc b/chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/shelf_controller_helper.cc
rename to chrome/browser/ui/ash/shelf/shelf_controller_helper.cc
index c8e5e975..f226d0a 100644
--- a/chrome/browser/ui/ash/launcher/shelf_controller_helper.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_controller_helper.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/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 
 #include <vector>
 
@@ -25,7 +25,7 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h"
-#include "chrome/browser/ui/ash/launcher/arc_app_shelf_id.h"
+#include "chrome/browser/ui/ash/shelf/arc_app_shelf_id.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/extensions/app_launch_params.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
diff --git a/chrome/browser/ui/ash/launcher/shelf_controller_helper.h b/chrome/browser/ui/ash/shelf/shelf_controller_helper.h
similarity index 93%
rename from chrome/browser/ui/ash/launcher/shelf_controller_helper.h
rename to chrome/browser/ui/ash/shelf/shelf_controller_helper.h
index 3f757762..6739ca6 100644
--- a/chrome/browser/ui/ash/launcher/shelf_controller_helper.h
+++ b/chrome/browser/ui/ash/shelf/shelf_controller_helper.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_UI_ASH_LAUNCHER_SHELF_CONTROLLER_HELPER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_CONTROLLER_HELPER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SHELF_CONTROLLER_HELPER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SHELF_CONTROLLER_HELPER_H_
 
 #include <memory>
 #include <string>
@@ -76,4 +76,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfControllerHelper);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_CONTROLLER_HELPER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SHELF_CONTROLLER_HELPER_H_
diff --git a/chrome/browser/ui/ash/launcher/shelf_extension_app_updater.cc b/chrome/browser/ui/ash/shelf/shelf_extension_app_updater.cc
similarity index 97%
rename from chrome/browser/ui/ash/launcher/shelf_extension_app_updater.cc
rename to chrome/browser/ui/ash/shelf/shelf_extension_app_updater.cc
index 518f9f7..3b86e29 100644
--- a/chrome/browser/ui/ash/launcher/shelf_extension_app_updater.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_extension_app_updater.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/ui/ash/launcher/shelf_extension_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/shelf_extension_app_updater.h"
 
 #include "chrome/browser/chromeos/extensions/gfx_utils.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/ui/ash/launcher/shelf_extension_app_updater.h b/chrome/browser/ui/ash/shelf/shelf_extension_app_updater.h
similarity index 89%
rename from chrome/browser/ui/ash/launcher/shelf_extension_app_updater.h
rename to chrome/browser/ui/ash/shelf/shelf_extension_app_updater.h
index 762bd767..18a4f98f 100644
--- a/chrome/browser/ui/ash/launcher/shelf_extension_app_updater.h
+++ b/chrome/browser/ui/ash/shelf/shelf_extension_app_updater.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_EXTENSION_APP_UPDATER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_EXTENSION_APP_UPDATER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SHELF_EXTENSION_APP_UPDATER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SHELF_EXTENSION_APP_UPDATER_H_
 
 #include "base/macros.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
-#include "chrome/browser/ui/ash/launcher/shelf_app_updater.h"
+#include "chrome/browser/ui/ash/shelf/shelf_app_updater.h"
 #include "extensions/browser/extension_registry_observer.h"
 
 class ShelfExtensionAppUpdater : public ShelfAppUpdater,
@@ -55,4 +55,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfExtensionAppUpdater);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_EXTENSION_APP_UPDATER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SHELF_EXTENSION_APP_UPDATER_H_
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc b/chrome/browser/ui/ash/shelf/shelf_spinner_controller.cc
similarity index 98%
rename from chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc
rename to chrome/browser/ui/ash/shelf/shelf_spinner_controller.cc
index ddeea0d..1552e843 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_spinner_controller.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/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 
 #include <vector>
 
@@ -14,8 +14,8 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/image/canvas_image_source.h"
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h b/chrome/browser/ui/ash/shelf/shelf_spinner_controller.h
similarity index 94%
rename from chrome/browser/ui/ash/launcher/shelf_spinner_controller.h
rename to chrome/browser/ui/ash/shelf/shelf_spinner_controller.h
index aecd6f47..bd82f650 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_controller.h
+++ b/chrome/browser/ui/ash/shelf/shelf_spinner_controller.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_UI_ASH_LAUNCHER_SHELF_SPINNER_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_SPINNER_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SHELF_SPINNER_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SHELF_SPINNER_CONTROLLER_H_
 
 #include <stdint.h>
 
@@ -112,4 +112,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfSpinnerController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_SPINNER_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SHELF_SPINNER_CONTROLLER_H_
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc b/chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.cc
similarity index 85%
rename from chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc
rename to chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.cc
index 63351c1..9cde2332 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.cc
+++ b/chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h"
 
 #include <utility>
 
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_context_menu.h"
-#include "chrome/browser/ui/ash/launcher/shelf_spinner_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_context_menu.h"
+#include "chrome/browser/ui/ash/shelf/shelf_spinner_controller.h"
 
 ShelfSpinnerItemController::ShelfSpinnerItemController(
     const std::string& app_id)
diff --git a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h b/chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h
similarity index 87%
rename from chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h
rename to chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.h
index 905f08a..c6eba487 100644
--- a/chrome/browser/ui/ash/launcher/shelf_spinner_item_controller.h
+++ b/chrome/browser/ui/ash/shelf/shelf_spinner_item_controller.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_UI_ASH_LAUNCHER_SHELF_SPINNER_ITEM_CONTROLLER_H_
-#define CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_SPINNER_ITEM_CONTROLLER_H_
+#ifndef CHROME_BROWSER_UI_ASH_SHELF_SHELF_SPINNER_ITEM_CONTROLLER_H_
+#define CHROME_BROWSER_UI_ASH_SHELF_SHELF_SPINNER_ITEM_CONTROLLER_H_
 
 #include <stdint.h>
 
@@ -49,4 +49,4 @@
   DISALLOW_COPY_AND_ASSIGN(ShelfSpinnerItemController);
 };
 
-#endif  // CHROME_BROWSER_UI_ASH_LAUNCHER_SHELF_SPINNER_ITEM_CONTROLLER_H_
+#endif  // CHROME_BROWSER_UI_ASH_SHELF_SHELF_SPINNER_ITEM_CONTROLLER_H_
diff --git a/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc b/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc
index 31c216a..7ee4bd0 100644
--- a/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc
+++ b/chrome/browser/ui/bluetooth/bluetooth_chooser_controller.cc
@@ -66,7 +66,12 @@
   }
 }
 
-BluetoothChooserController::~BluetoothChooserController() {}
+BluetoothChooserController::~BluetoothChooserController() {
+  if (event_handler_) {
+    event_handler_.Run(content::BluetoothChooserEvent::CANCELLED,
+                       std::string());
+  }
+}
 
 bool BluetoothChooserController::ShouldShowIconBeforeText() const {
   return true;
@@ -170,6 +175,7 @@
   DCHECK_LT(index, devices_.size());
   event_handler_.Run(content::BluetoothChooserEvent::SELECTED,
                      devices_[index].id);
+  event_handler_.Reset();
 }
 
 void BluetoothChooserController::Cancel() {
@@ -177,6 +183,7 @@
   if (event_handler_.is_null())
     return;
   event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, std::string());
+  event_handler_.Reset();
 }
 
 void BluetoothChooserController::Close() {
@@ -184,6 +191,7 @@
   if (event_handler_.is_null())
     return;
   event_handler_.Run(content::BluetoothChooserEvent::CANCELLED, std::string());
+  event_handler_.Reset();
 }
 
 void BluetoothChooserController::OpenHelpCenterUrl() const {
diff --git a/chrome/browser/ui/browser_tab_strip_model_delegate.cc b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
index 4b0950e..17da976d 100644
--- a/chrome/browser/ui/browser_tab_strip_model_delegate.cc
+++ b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
@@ -108,6 +108,10 @@
   return CanDuplicateTabAt(browser_, index);
 }
 
+bool BrowserTabStripModelDelegate::CanHighlightTabs() {
+  return browser_->window()->IsTabStripEditable();
+}
+
 void BrowserTabStripModelDelegate::DuplicateContentsAt(int index) {
   DuplicateTabAt(browser_, index);
 }
diff --git a/chrome/browser/ui/browser_tab_strip_model_delegate.h b/chrome/browser/ui/browser_tab_strip_model_delegate.h
index b7b6d4e..b9a00c55 100644
--- a/chrome/browser/ui/browser_tab_strip_model_delegate.h
+++ b/chrome/browser/ui/browser_tab_strip_model_delegate.h
@@ -35,6 +35,7 @@
   void WillAddWebContents(content::WebContents* contents) override;
   int GetDragActions() const override;
   bool CanDuplicateContentsAt(int index) override;
+  bool CanHighlightTabs() override;
   void DuplicateContentsAt(int index) override;
   void MoveToExistingWindow(const std::vector<int>& indices,
                             int browser_index) override;
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc
index c8d18ff..805425a 100644
--- a/chrome/browser/ui/startup/bad_flags_prompt.cc
+++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -140,6 +140,7 @@
     // GPU sanboxing isn't implemented for the Web GPU API yet meaning it would
     // be possible to read GPU data for other Chromium processes.
     switches::kEnableUnsafeWebGPU,
+    switches::kEnableUnsafeWebGPUService,
 
     // A flag to support local file based WebBundle loading, only for testing
     // purpose.
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index f55bdcf..38b8bd04 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -903,14 +903,16 @@
                /*triggered_by_other_operation=*/false);
 }
 
-void TabStripModel::ToggleSelectionAt(int index) {
+bool TabStripModel::ToggleSelectionAt(int index) {
+  if (!delegate()->CanHighlightTabs())
+    return false;
   DCHECK(ContainsIndex(index));
   ui::ListSelectionModel new_model = selection_model();
   if (selection_model_.IsSelected(index)) {
     if (selection_model_.size() == 1) {
       // One tab must be selected and this tab is currently selected so we can't
       // unselect it.
-      return;
+      return false;
     }
     new_model.RemoveIndexFromSelection(index);
     new_model.set_anchor(index);
@@ -924,6 +926,7 @@
   }
   SetSelection(std::move(new_model), TabStripModelObserver::CHANGE_REASON_NONE,
                /*triggered_by_other_operation=*/false);
+  return true;
 }
 
 void TabStripModel::AddSelectionFromAnchorTo(int index) {
diff --git a/chrome/browser/ui/tabs/tab_strip_model.h b/chrome/browser/ui/tabs/tab_strip_model.h
index 47cc667..de83d5f 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.h
+++ b/chrome/browser/ui/tabs/tab_strip_model.h
@@ -379,9 +379,9 @@
   // Extends the selection from the anchor to |index|.
   void ExtendSelectionTo(int index);
 
-  // Toggles the selection at |index|. This does nothing if |index| is selected
-  // and there are no other selected tabs.
-  void ToggleSelectionAt(int index);
+  // Returns true if the selection was toggled; this can fail if the tabstrip
+  // is not editable.
+  bool ToggleSelectionAt(int index);
 
   // Makes sure the tabs from the anchor to |index| are selected. This only
   // adds to the selection.
diff --git a/chrome/browser/ui/tabs/tab_strip_model_delegate.h b/chrome/browser/ui/tabs/tab_strip_model_delegate.h
index a2697fcb..fa71b49c 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_delegate.h
+++ b/chrome/browser/ui/tabs/tab_strip_model_delegate.h
@@ -92,6 +92,10 @@
   // Returns whether some contents can be duplicated.
   virtual bool CanDuplicateContentsAt(int index) = 0;
 
+  // Returns whether tabs can be highlighted. This may return false due to tab
+  // dragging in process for instance
+  virtual bool CanHighlightTabs() = 0;
+
   // Duplicates the contents at the provided index and places it into a new tab.
   virtual void DuplicateContentsAt(int index) = 0;
 
diff --git a/chrome/browser/ui/tabs/test_tab_strip_model_delegate.cc b/chrome/browser/ui/tabs/test_tab_strip_model_delegate.cc
index fde9a2ee..0b00a543 100644
--- a/chrome/browser/ui/tabs/test_tab_strip_model_delegate.cc
+++ b/chrome/browser/ui/tabs/test_tab_strip_model_delegate.cc
@@ -43,6 +43,10 @@
   return false;
 }
 
+bool TestTabStripModelDelegate::CanHighlightTabs() {
+  return true;
+}
+
 void TestTabStripModelDelegate::DuplicateContentsAt(int index) {
 }
 
diff --git a/chrome/browser/ui/tabs/test_tab_strip_model_delegate.h b/chrome/browser/ui/tabs/test_tab_strip_model_delegate.h
index 5e8a99df..77d743a 100644
--- a/chrome/browser/ui/tabs/test_tab_strip_model_delegate.h
+++ b/chrome/browser/ui/tabs/test_tab_strip_model_delegate.h
@@ -34,6 +34,7 @@
   void WillAddWebContents(content::WebContents* contents) override;
   int GetDragActions() const override;
   bool CanDuplicateContentsAt(int index) override;
+  bool CanHighlightTabs() override;
   void DuplicateContentsAt(int index) override;
   void MoveToExistingWindow(const std::vector<int>& indices,
                             int browser_index) override;
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model_browsertest.cc b/chrome/browser/ui/toolbar/toolbar_actions_model_browsertest.cc
index a3fd80c7..a1cb1e0 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model_browsertest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_browsertest.cc
@@ -7,15 +7,38 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "content/public/test/browser_test.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/common/extension_set.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace {
+
+// We use some arbitrary extensions. Use constants for more clarity.
+constexpr char kExtension1Path[] = "simple_with_file";
+constexpr char kExtension1Name[] = "foo";
+constexpr char kExtension2Path[] = "simple_with_icon";
+constexpr char kExtension2Name[] = "Simple Extension with icon";
+constexpr char kExtension3Path[] = "simple_with_host";
+constexpr char kExtension3Name[] = "bar";
+
+}  // namespace
 
 class ToolbarActionsModelBrowserTest : public extensions::ExtensionBrowserTest {
  public:
   ToolbarActionsModelBrowserTest() = default;
   ~ToolbarActionsModelBrowserTest() override = default;
 
+  void SetUpOnMainThread() override {
+    extensions::ExtensionBrowserTest::SetUpOnMainThread();
+    toolbar_model_ = ToolbarActionsModel::Get(profile());
+    ASSERT_TRUE(toolbar_model_);
+  }
+
+  ToolbarActionsModel* toolbar_model() { return toolbar_model_; }
   base::HistogramTester* histogram_tester() { return &histogram_tester_; }
 
  private:
+  ToolbarActionsModel* toolbar_model_ = nullptr;
   base::HistogramTester histogram_tester_;
 };
 
@@ -28,23 +51,92 @@
       LoadExtension(test_data_dir_.AppendASCII("simple_with_icon"));
   ASSERT_TRUE(extension2);
 
-  ToolbarActionsModel* model = ToolbarActionsModel::Get(profile());
-  EXPECT_EQ(2u, model->action_ids().size());
+  EXPECT_EQ(2u, toolbar_model()->action_ids().size());
 
-  EXPECT_FALSE(model->IsActionPinned(extension1->id()));
-  EXPECT_FALSE(model->IsActionPinned(extension2->id()));
-  model->SetActionVisibility(extension1->id(), true);
-  EXPECT_TRUE(model->IsActionPinned(extension1->id()));
-  EXPECT_FALSE(model->IsActionPinned(extension2->id()));
+  EXPECT_FALSE(toolbar_model()->IsActionPinned(extension1->id()));
+  EXPECT_FALSE(toolbar_model()->IsActionPinned(extension2->id()));
+  toolbar_model()->SetActionVisibility(extension1->id(), true);
+  EXPECT_TRUE(toolbar_model()->IsActionPinned(extension1->id()));
+  EXPECT_FALSE(toolbar_model()->IsActionPinned(extension2->id()));
 }
 
 IN_PROC_BROWSER_TEST_F(ToolbarActionsModelBrowserTest, PinnedStateMetrics) {
-  ToolbarActionsModel* model = ToolbarActionsModel::Get(profile());
-  EXPECT_EQ(2u, model->action_ids().size());
-  EXPECT_EQ(1u, model->pinned_action_ids().size());
+  EXPECT_EQ(2u, toolbar_model()->action_ids().size());
+  EXPECT_EQ(1u, toolbar_model()->pinned_action_ids().size());
 
   histogram_tester()->ExpectUniqueSample(
       "Extensions.Toolbar.PinnedExtensionPercentage3", 50, 1);
   histogram_tester()->ExpectUniqueSample(
       "Extensions.Toolbar.PinnedExtensionCount2", 1, 1);
 }
+
+// Test that a user's pinned extensions and ordering persist across sessions. We
+// exercise this in a two-step browser test, which most closely reflects a
+// "real world" multi-session scenario.
+IN_PROC_BROWSER_TEST_F(ToolbarActionsModelBrowserTest,
+                       PRE_PinnedStatePersistence) {
+  const extensions::Extension* const extension1 =
+      LoadExtension(test_data_dir_.AppendASCII(kExtension1Path));
+  ASSERT_TRUE(extension1);
+
+  const extensions::Extension* const extension2 =
+      LoadExtension(test_data_dir_.AppendASCII(kExtension2Path));
+  ASSERT_TRUE(extension2);
+
+  const extensions::Extension* const extension3 =
+      LoadExtension(test_data_dir_.AppendASCII(kExtension3Path));
+  ASSERT_TRUE(extension3);
+
+  EXPECT_THAT(toolbar_model()->action_ids(),
+              ::testing::UnorderedElementsAre(
+                  extension1->id(), extension2->id(), extension3->id()));
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(), ::testing::IsEmpty());
+
+  // Pin extension 3, followed by 2. The pinned extensions should be in the
+  // order 3, 2.
+  // TODO(devlin): The order of ToolbarActionsModel::action_ids() is not updated
+  // as a result of pinning, but is updated as a result of moving extensions
+  // around. Since pinned extensions are now the sorted order (and the rest are
+  // displayed in alphabetical order in the menu), we can likely get rid of
+  // sorting in action_ids() entirely. We should at least be consistent.
+  toolbar_model()->SetActionVisibility(extension3->id(), true);
+  toolbar_model()->SetActionVisibility(extension2->id(), true);
+
+  EXPECT_THAT(toolbar_model()->action_ids(),
+              ::testing::UnorderedElementsAre(
+                  extension1->id(), extension2->id(), extension3->id()));
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(extension3->id(), extension2->id()));
+}
+
+IN_PROC_BROWSER_TEST_F(ToolbarActionsModelBrowserTest, PinnedStatePersistence) {
+  // Re-look-up the extensions. (Note that we can't use ID, since unpacked
+  // extensions' ids are based on their absolute file path.)
+  extensions::ExtensionRegistry* const registry =
+      extensions::ExtensionRegistry::Get(profile());
+  auto get_extension_by_name =
+      [registry](const char* name) -> const extensions::Extension* {
+    for (const auto& extension : registry->enabled_extensions()) {
+      if (extension->name() == name)
+        return extension.get();
+    }
+    return nullptr;
+  };
+
+  const extensions::Extension* const extension1 =
+      get_extension_by_name(kExtension1Name);
+  ASSERT_TRUE(extension1);
+  const extensions::Extension* const extension2 =
+      get_extension_by_name(kExtension2Name);
+  ASSERT_TRUE(extension2);
+  const extensions::Extension* const extension3 =
+      get_extension_by_name(kExtension3Name);
+  ASSERT_TRUE(extension3);
+
+  // Pin state should have been persisted across sessions.
+  EXPECT_THAT(toolbar_model()->action_ids(),
+              ::testing::UnorderedElementsAre(
+                  extension1->id(), extension2->id(), extension3->id()));
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(extension3->id(), extension2->id()));
+}
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
index a8609d0..275adb6 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_model_unittest.cc
@@ -549,7 +549,7 @@
 
 // Test that new extension actions are always visible on installation and
 // inserted at the "end" of the visible section.
-TEST_F(ToolbarActionsModelUnitTest, NewToolbarExtensionsAreVisible) {
+TEST_F(ToolbarActionsModelUnitTest, NewToolbarExtensionsAreUnpinned) {
   Init();
 
   // Three extensions with actions.
@@ -568,81 +568,33 @@
           .SetAction(ActionType::BROWSER_ACTION)
           .SetLocation(ManifestLocation::kInternal)
           .Build();
-  scoped_refptr<const extensions::Extension> extension_d =
-      extensions::ExtensionBuilder("d")
-          .SetAction(ActionType::BROWSER_ACTION)
-          .SetLocation(ManifestLocation::kInternal)
-          .Build();
 
   // We should start off without any actions.
   EXPECT_EQ(0u, num_actions());
-  EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
 
-  // Add one action. It should be visible.
+  // Add one action. It should be unpinned.
   service()->AddExtension(extension_a.get());
   EXPECT_EQ(1u, num_actions());
-  EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
-  EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(0u));
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(), ::testing::IsEmpty());
 
-  // Hide all actions.
-  toolbar_model()->SetVisibleIconCount(0);
-  EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
-
-  // Add a new action - it should be visible, so it should be in the first
-  // index. The other action should remain hidden.
+  // Add a second. It should also be unpinned (even with existing extensions,
+  // default state is unpinned).
   service()->AddExtension(extension_b.get());
   EXPECT_EQ(2u, num_actions());
-  EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
-  EXPECT_EQ(extension_b.get()->id(), GetActionIdAtIndex(0u));
-  EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(1u));
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(), ::testing::IsEmpty());
 
-  // Show all actions.
-  toolbar_model()->SetVisibleIconCount(2);
-  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-  EXPECT_TRUE(toolbar_model()->all_icons_visible());
+  // Pin the second. It should now be the only pinned icon.
+  toolbar_model()->SetActionVisibility(extension_b->id(), true);
+  EXPECT_EQ(2u, num_actions());
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(extension_b->id()));
 
-  // Add the third action. Since all action are visible, it should go in the
-  // last index.
+  // Add a third extension. It should be unpinned (pin state should not carry
+  // to new extensions).
   service()->AddExtension(extension_c.get());
   EXPECT_EQ(3u, num_actions());
-  EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
-  EXPECT_TRUE(toolbar_model()->all_icons_visible());
-  EXPECT_EQ(extension_b.get()->id(), GetActionIdAtIndex(0u));
-  EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(1u));
-  EXPECT_EQ(extension_c.get()->id(), GetActionIdAtIndex(2u));
-
-  // Hide one action (two remaining visible).
-  toolbar_model()->SetVisibleIconCount(2);
-  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-
-  // Add a fourth action. It should go at the end of the visible section and
-  // be visible, so it increases visible count by 1, and goes into the fourth
-  // index. The hidden action should remain hidden.
-  service()->AddExtension(extension_d.get());
-  EXPECT_EQ(4u, num_actions());
-  EXPECT_EQ(3u, toolbar_model()->visible_icon_count());
-  EXPECT_EQ(extension_b.get()->id(), GetActionIdAtIndex(0u));
-  EXPECT_EQ(extension_a.get()->id(), GetActionIdAtIndex(1u));
-  EXPECT_EQ(extension_d.get()->id(), GetActionIdAtIndex(2u));
-  EXPECT_EQ(extension_c.get()->id(), GetActionIdAtIndex(3u));
-}
-
-// Test that the action toolbar maintains the proper size, even after a pref
-// change.
-TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarSizeAfterPrefChange) {
-  Init();
-
-  // Add the three browser action extensions.
-  ASSERT_TRUE(AddBrowserActionExtensions());
-  EXPECT_EQ(3u, num_actions());
-
-  // Should be at max size.
-  EXPECT_TRUE(toolbar_model()->all_icons_visible());
-  EXPECT_EQ(num_actions(), toolbar_model()->visible_icon_count());
-  toolbar_model()->OnActionToolbarPrefChange();
-  // Should still be at max size.
-  EXPECT_TRUE(toolbar_model()->all_icons_visible());
-  EXPECT_EQ(num_actions(), toolbar_model()->visible_icon_count());
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(extension_b->id()));
 }
 
 // Test that, with the extension-action-redesign switch, the model contains
@@ -697,7 +649,7 @@
   EXPECT_TRUE(ModelHasActionForId(internal_extension_no_action->id()));
 }
 
-TEST_F(ToolbarActionsModelUnitTest, ActionsToolbarIncognitoModeTest) {
+TEST_F(ToolbarActionsModelUnitTest, PinnedStateIsTransferredToIncognito) {
   Init();
   ASSERT_TRUE(AddBrowserActionExtensions());
 
@@ -711,69 +663,85 @@
   extension_prefs->SetIsIncognitoEnabled(browser_action_b()->id(), true);
   extension_prefs->SetIsIncognitoEnabled(browser_action_c()->id(), true);
 
-  extensions::util::SetIsIncognitoEnabled(browser_action_b()->id(), profile(),
-                                          true);
-  extensions::util::SetIsIncognitoEnabled(browser_action_c()->id(), profile(),
-                                          true);
-
-  // Move C to the second index.
-  toolbar_model()->MoveActionIcon(browser_action_c()->id(), 1u);
-  // Set visible count to 3 so that C is overflowed. State is A, C, [B].
-  toolbar_model()->SetVisibleIconCount(2);
-  EXPECT_EQ(1u, observer()->moved_count());
+  // Pin extensions A and C. State is A, C, [B].
+  toolbar_model()->SetActionVisibility(browser_action_a()->id(), true);
+  toolbar_model()->SetActionVisibility(browser_action_c()->id(), true);
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(browser_action_a()->id(),
+                                     browser_action_c()->id()));
 
   // Get an incognito profile and toolbar.
   ToolbarActionsModel* incognito_model =
       extensions::extension_action_test_util::CreateToolbarModelForProfile(
           profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
-  ToolbarActionsModelTestObserver incognito_observer(incognito_model);
-  EXPECT_EQ(0u, incognito_observer.moved_count());
+  // We should have two actions in the incognito bar, C and B. The pinned state
+  // should be preserved, so C should be pinned.
+  EXPECT_THAT(incognito_model->action_ids(),
+              ::testing::ElementsAre(browser_action_b()->id(),
+                                     browser_action_c()->id()));
+  EXPECT_THAT(incognito_model->pinned_action_ids(),
+              ::testing::ElementsAre(browser_action_c()->id()));
 
-  // We should have two actions: C, B, and the order should be preserved from
-  // the original model.
-  EXPECT_EQ(2u, incognito_model->action_ids().size());
-  EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(0u, incognito_model));
-  EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u, incognito_model));
+  // 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(),
+                                     browser_action_b()->id()));
+  // Similarly, unpinning C transfers to the incognito profile.
+  toolbar_model()->SetActionVisibility(browser_action_c()->id(), false);
+  EXPECT_THAT(incognito_model->pinned_action_ids(),
+              ::testing::ElementsAre(browser_action_b()->id()));
+}
 
-  // Actions in the overflow menu in the regular toolbar should remain in
-  // overflow in the incognito toolbar. So, we should have C, [B].
-  EXPECT_EQ(1u, incognito_model->visible_icon_count());
-  // The regular model should still have two icons visible.
-  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+TEST_F(ToolbarActionsModelUnitTest,
+       MovingPinnedActionsTransfersBetweenIncognito) {
+  Init();
+  ASSERT_TRUE(AddBrowserActionExtensions());
 
-  // Changing the incognito model size should not affect the regular model.
-  incognito_model->SetVisibleIconCount(0);
-  EXPECT_EQ(0u, incognito_model->visible_icon_count());
-  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
+  // Give all extensions incognito access.
+  // Note: We use ExtensionPrefs::SetIsIncognitoEnabled instead of
+  // util::SetIsIncognitoEnabled because the latter tries to reload the
+  // extension, which requires a filepath associated with the extension (and,
+  // for this test, reloading the extension is irrelevant to us).
+  extensions::ExtensionPrefs* extension_prefs =
+      extensions::ExtensionPrefs::Get(profile());
+  extension_prefs->SetIsIncognitoEnabled(browser_action_a()->id(), true);
+  extension_prefs->SetIsIncognitoEnabled(browser_action_b()->id(), true);
+  extension_prefs->SetIsIncognitoEnabled(browser_action_c()->id(), true);
 
-  // Expanding the incognito model to 3 should register as "all icons"
-  // since it is all of the incognito-enabled extensions.
-  incognito_model->SetVisibleIconCount(2u);
-  EXPECT_EQ(2u, incognito_model->visible_icon_count());
-  EXPECT_TRUE(incognito_model->all_icons_visible());
+  // Pin all extensions, to allow moving them around.
+  toolbar_model()->SetActionVisibility(browser_action_a()->id(), true);
+  toolbar_model()->SetActionVisibility(browser_action_b()->id(), true);
+  toolbar_model()->SetActionVisibility(browser_action_c()->id(), true);
 
-  // Moving icons in the incognito toolbar should not affect the regular
-  // toolbar. Incognito currently has C, B...
-  incognito_model->MoveActionIcon(browser_action_b()->id(), 0u);
-  // So now it should be B, C...
-  EXPECT_EQ(1u, incognito_observer.moved_count());
-  EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(0u, incognito_model));
-  EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u, incognito_model));
-  // ... and the regular toolbar should be unaffected.
-  EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
-  EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
-  EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(2u));
+  // Get an incognito profile and toolbar.
+  ToolbarActionsModel* incognito_model =
+      extensions::extension_action_test_util::CreateToolbarModelForProfile(
+          profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
 
-  // Similarly, the observer for the regular model should not have received
-  // any updates.
-  EXPECT_EQ(1u, observer()->moved_count());
+  // The incognito pinned actions should be A, B, C (matching the order from
+  // the on-the-record profile).
+  EXPECT_THAT(
+      incognito_model->pinned_action_ids(),
+      ::testing::ElementsAre(browser_action_a()->id(), browser_action_b()->id(),
+                             browser_action_c()->id()));
 
-  // And performing moves on the regular model should have no effect on the
-  // incognito model or its observers.
-  toolbar_model()->MoveActionIcon(browser_action_c()->id(), 2u);
-  EXPECT_EQ(2u, observer()->moved_count());
-  EXPECT_EQ(1u, incognito_observer.moved_count());
+  // 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(),
+      ::testing::ElementsAre(browser_action_c()->id(), browser_action_a()->id(),
+                             browser_action_b()->id()));
+  EXPECT_THAT(
+      incognito_model->pinned_action_ids(),
+      ::testing::ElementsAre(browser_action_c()->id(), browser_action_a()->id(),
+                             browser_action_b()->id()));
 }
 
 // Test that enabling extensions incognito with an active incognito profile
@@ -820,13 +788,10 @@
   std::string extension_a = extensions[0]->id();
   std::string extension_b = extensions[1]->id();
 
-  // The first model should have both extensions visible.
-  EXPECT_EQ(2u, toolbar_model()->action_ids().size());
-  EXPECT_EQ(extension_a, GetActionIdAtIndex(0u));
-  EXPECT_EQ(extension_b, GetActionIdAtIndex(1u));
-
-  // Set the model to only show one extension, so the order is A, [B].
-  toolbar_model()->SetVisibleIconCount(1u);
+  // Pin Extension A in the on-the-record profile.
+  toolbar_model()->SetActionVisibility(extension_a, true);
+  EXPECT_THAT(toolbar_model()->pinned_action_ids(),
+              ::testing::ElementsAre(extension_a));
 
   // Get an incognito profile and toolbar.
   ToolbarActionsModel* incognito_model =
@@ -834,10 +799,10 @@
           profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
   ToolbarActionsModelTestObserver incognito_observer(incognito_model);
 
-  // Right now, no actions are enabled in incognito mode.
-  EXPECT_EQ(0u, incognito_model->action_ids().size());
+  // Right now, no extensions are enabled in incognito mode.
+  EXPECT_THAT(incognito_model->action_ids(), ::testing::IsEmpty());
 
-  // Set extension B (which is overflowed) to be enabled in incognito. This
+  // Set extension B (which is unpinned) to be enabled in incognito. This
   // results in b reloading, so wait for it.
   {
     extensions::TestExtensionRegistryObserver observer(registry(), extension_b);
@@ -846,25 +811,24 @@
   }
 
   // Now, we should have one icon in the incognito bar. But, since B is
-  // overflowed in the main bar, it shouldn't be visible.
-  EXPECT_EQ(1u, incognito_model->action_ids().size());
-  EXPECT_EQ(extension_b, GetActionIdAtIndex(0u, incognito_model));
-  EXPECT_EQ(0u, incognito_model->visible_icon_count());
+  // unpinned in the main bar, it shouldn't be visible.
+  EXPECT_THAT(incognito_model->action_ids(),
+              ::testing::ElementsAre(extension_b));
+  EXPECT_THAT(incognito_model->pinned_action_ids(), ::testing::IsEmpty());
 
-  // Also enable extension a for incognito (again, wait for the reload).
+  // Also enable extension A for incognito (again, wait for the reload).
   {
     extensions::TestExtensionRegistryObserver observer(registry(), extension_a);
     extensions::util::SetIsIncognitoEnabled(extension_a, profile(), true);
     observer.WaitForExtensionLoaded();
   }
 
-  // Now, both extensions should be enabled in incognito mode. In addition, the
-  // incognito toolbar should have expanded to show extension A (since it isn't
-  // overflowed in the main bar).
-  EXPECT_EQ(2u, incognito_model->action_ids().size());
-  EXPECT_EQ(extension_a, GetActionIdAtIndex(0u, incognito_model));
-  EXPECT_EQ(extension_b, GetActionIdAtIndex(1u, incognito_model));
-  EXPECT_EQ(1u, incognito_model->visible_icon_count());
+  // Now, both extensions should be enabled in incognito mode. Extension A
+  // should be pinned (since it's pinned in the main bar).
+  EXPECT_THAT(incognito_model->action_ids(),
+              ::testing::ElementsAre(extension_a, extension_b));
+  EXPECT_THAT(incognito_model->pinned_action_ids(),
+              ::testing::ElementsAre(extension_a));
 }
 
 // Test that observers receive no Added notifications until after the
@@ -932,28 +896,6 @@
             observer()->inserted_count() - observer()->removed_count());
 }
 
-TEST_F(ToolbarActionsModelUnitTest,
-       TestUninstallVisibleExtensionDoesntBringOutOther) {
-  Init();
-  ASSERT_TRUE(AddBrowserActionExtensions());
-  toolbar_model()->SetVisibleIconCount(2u);
-  EXPECT_EQ(3u, num_actions());
-  EXPECT_EQ(2u, toolbar_model()->visible_icon_count());
-  EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
-  EXPECT_EQ(browser_action_b()->id(), GetActionIdAtIndex(1u));
-  EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(2u));
-
-  service()->UninstallExtension(browser_action_b()->id(),
-                                extensions::UNINSTALL_REASON_FOR_TESTING,
-                                nullptr);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(2u, num_actions());
-  EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
-  EXPECT_EQ(browser_action_a()->id(), GetActionIdAtIndex(0u));
-  EXPECT_EQ(browser_action_c()->id(), GetActionIdAtIndex(1u));
-}
-
 // Test that user-script extensions show up on the toolbar.
 TEST_F(ToolbarActionsModelUnitTest, AddUserScriptExtension) {
   Init();
@@ -968,13 +910,11 @@
 
   // We should start off without any actions.
   EXPECT_EQ(0u, num_actions());
-  EXPECT_EQ(0u, toolbar_model()->visible_icon_count());
 
-  // Add the extension. It should be visible.
+  // Add the extension and verify it gets an icon.
   service()->AddExtension(extension.get());
   EXPECT_EQ(1u, num_actions());
-  EXPECT_EQ(1u, toolbar_model()->visible_icon_count());
-  EXPECT_EQ(extension.get()->id(), GetActionIdAtIndex(0u));
+  EXPECT_EQ(extension->id(), GetActionIdAtIndex(0u));
 }
 
 TEST_F(ToolbarActionsModelUnitTest, IsActionPinnedCorrespondsToPinningState) {
diff --git a/chrome/browser/ui/user_education/feature_tutorial_service.cc b/chrome/browser/ui/user_education/feature_tutorial_service.cc
new file mode 100644
index 0000000..1f31e10
--- /dev/null
+++ b/chrome/browser/ui/user_education/feature_tutorial_service.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 "chrome/browser/ui/user_education/feature_tutorial_service.h"
+
+#include "base/check.h"
+#include "build/build_config.h"
+
+FeatureTutorialService::FeatureTutorialService() = default;
+FeatureTutorialService::~FeatureTutorialService() = default;
+
+// For Views, this method is defined elsewhere. For non-Views it should never be
+// called.
+#if !defined(TOOLKIT_VIEWS)
+// static
+std::unique_ptr<FeatureTutorialService> FeatureTutorialService::MakeInstance(
+    Profile* profile) {
+  CHECK(0);
+}
+#endif  // !defined(TOOLKIT_VIEWS)
diff --git a/chrome/browser/ui/user_education/feature_tutorial_service.h b/chrome/browser/ui/user_education/feature_tutorial_service.h
new file mode 100644
index 0000000..d062320
--- /dev/null
+++ b/chrome/browser/ui/user_education/feature_tutorial_service.h
@@ -0,0 +1,28 @@
+// 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 CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_H_
+#define CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_H_
+
+#include <memory>
+
+#include "chrome/browser/ui/user_education/feature_tutorials.h"
+#include "components/keyed_service/core/keyed_service.h"
+
+class Profile;
+
+class FeatureTutorialService : public KeyedService {
+ public:
+  FeatureTutorialService();
+  ~FeatureTutorialService() override;
+
+  virtual bool StartTutorial(FeatureTutorial tutorial) = 0;
+
+ private:
+  friend class FeatureTutorialServiceFactory;
+
+  static std::unique_ptr<FeatureTutorialService> MakeInstance(Profile* profile);
+};
+
+#endif  // CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_H_
diff --git a/chrome/browser/ui/user_education/feature_tutorial_service_factory.cc b/chrome/browser/ui/user_education/feature_tutorial_service_factory.cc
new file mode 100644
index 0000000..b6185b1
--- /dev/null
+++ b/chrome/browser/ui/user_education/feature_tutorial_service_factory.cc
@@ -0,0 +1,34 @@
+// 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 "chrome/browser/ui/user_education/feature_tutorial_service_factory.h"
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/user_education/feature_tutorial_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+FeatureTutorialServiceFactory::FeatureTutorialServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "FeatureTutorialService",
+          BrowserContextDependencyManager::GetInstance()) {}
+
+// static
+FeatureTutorialServiceFactory* FeatureTutorialServiceFactory::GetInstance() {
+  static base::NoDestructor<FeatureTutorialServiceFactory> instance;
+  return instance.get();
+}
+
+// static
+FeatureTutorialService* FeatureTutorialServiceFactory::GetForProfile(
+    Profile* profile) {
+  return static_cast<FeatureTutorialService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+KeyedService* FeatureTutorialServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return FeatureTutorialService::MakeInstance(profile).release();
+}
diff --git a/chrome/browser/ui/user_education/feature_tutorial_service_factory.h b/chrome/browser/ui/user_education/feature_tutorial_service_factory.h
new file mode 100644
index 0000000..545f2e3a
--- /dev/null
+++ b/chrome/browser/ui/user_education/feature_tutorial_service_factory.h
@@ -0,0 +1,34 @@
+// 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 CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_FACTORY_H_
+
+#include "chrome/browser/ui/user_education/feature_tutorial_service.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace content {
+class BrowserContext;
+}
+
+class Profile;
+
+class FeatureTutorialServiceFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  FeatureTutorialServiceFactory();
+  FeatureTutorialServiceFactory(const FeatureTutorialServiceFactory&) = delete;
+  FeatureTutorialServiceFactory& operator=(
+      const FeatureTutorialServiceFactory&) = delete;
+
+  static FeatureTutorialServiceFactory* GetInstance();
+
+  static FeatureTutorialService* GetForProfile(Profile* profile);
+
+ private:
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+};
+
+#endif  // CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_FACTORY_H_
diff --git a/chrome/browser/ui/user_education/feature_tutorials.cc b/chrome/browser/ui/user_education/feature_tutorials.cc
new file mode 100644
index 0000000..741f5c8
--- /dev/null
+++ b/chrome/browser/ui/user_education/feature_tutorials.cc
@@ -0,0 +1,46 @@
+// 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 "chrome/browser/ui/user_education/feature_tutorials.h"
+
+#include <utility>
+
+#include "base/notreached.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+
+namespace {
+
+const std::pair<FeatureTutorial, const char*> kTutorialIds[] = {
+    {FeatureTutorial::kTabGroups, "tab_groups"},
+};
+
+}  // namespace
+
+base::StringPiece GetStringIdForFeatureTutorial(FeatureTutorial tutorial) {
+  for (const auto& p : kTutorialIds) {
+    if (p.first == tutorial)
+      return p.second;
+  }
+
+  NOTREACHED();
+  return "";
+}
+
+base::Optional<FeatureTutorial> GetFeatureTutorialFromStringId(
+    base::StringPiece id) {
+  for (const auto& p : kTutorialIds) {
+    if (p.second == id)
+      return p.first;
+  }
+
+  return base::nullopt;
+}
+
+std::vector<base::StringPiece> GetAllFeatureTutorialStringIds() {
+  std::vector<base::StringPiece> result;
+  for (const auto& p : kTutorialIds)
+    result.push_back(p.second);
+  return result;
+}
diff --git a/chrome/browser/ui/user_education/feature_tutorials.h b/chrome/browser/ui/user_education/feature_tutorials.h
new file mode 100644
index 0000000..c2af9ae
--- /dev/null
+++ b/chrome/browser/ui/user_education/feature_tutorials.h
@@ -0,0 +1,36 @@
+// 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 CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIALS_H_
+#define CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIALS_H_
+
+#include <vector>
+
+#include "base/strings/string_piece_forward.h"
+
+namespace base {
+template <typename T>
+class Optional;
+}
+
+// A tutorial's identifier. Each defined tutorial has a FeatureTutorial enum
+// value.
+enum class FeatureTutorial {
+  kTabGroups,
+};
+
+// Get a string identifier for a FeatureTutorial value. This string cannot be
+// used on its own, but may eventually be translated back to a FeatureTutorial
+// value. It may be used for identifying a tutorial from a WebUI, for example.
+base::StringPiece GetStringIdForFeatureTutorial(FeatureTutorial tutorial);
+
+// Translate a string ID GetStringIdForFeatureTutorial() back to a
+// FeatureTutorial.
+base::Optional<FeatureTutorial> GetFeatureTutorialFromStringId(
+    base::StringPiece id);
+
+// Get the string IDs of all defined tutorials.
+std::vector<base::StringPiece> GetAllFeatureTutorialStringIds();
+
+#endif  // CHROME_BROWSER_UI_USER_EDUCATION_FEATURE_TUTORIALS_H_
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
index 002cbc21..269f71d 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
@@ -34,8 +34,8 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
index 620ee83..765ff4e 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_footer_panel.cc
@@ -28,8 +28,8 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // gn check complains on Linux Ozone.
 #include "ash/public/cpp/shelf_model.h"  // nogncheck
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #endif
 
 AppInfoFooterPanel::AppInfoFooterPanel(Profile* profile,
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index d729163f..068035b 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -161,13 +161,11 @@
   METADATA_HEADER(BookmarkButtonBase);
   BookmarkButtonBase(PressedCallback callback, const std::u16string& title)
       : LabelButton(std::move(callback), title) {
+    ConfigureInkDropForToolbar(this);
     SetImageLabelSpacing(ChromeLayoutProvider::Get()->GetDistanceMetric(
         DISTANCE_RELATED_LABEL_HORIZONTAL_LIST));
 
     views::InstallPillHighlightPathGenerator(this);
-    SetInkDropMode(InkDropMode::ON);
-    SetHasInkDropActionOnClick(true);
-    SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
 
     SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
 
@@ -304,11 +302,10 @@
       PressedCallback callback,
       const std::u16string& title = std::u16string())
       : MenuButton(std::move(callback), title) {
+    ConfigureInkDropForToolbar(this);
     SetImageLabelSpacing(ChromeLayoutProvider::Get()->GetDistanceMetric(
         DISTANCE_RELATED_LABEL_HORIZONTAL_LIST));
     views::InstallPillHighlightPathGenerator(this);
-    SetInkDropMode(InkDropMode::ON);
-    SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
   }
   BookmarkMenuButtonBase(const BookmarkMenuButtonBase&) = delete;
   BookmarkMenuButtonBase& operator=(const BookmarkMenuButtonBase&) = delete;
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 5fa109767..c22d84ee 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -989,7 +989,7 @@
 }
 
 void OverlayWindowViews::OnNativeWidgetDestroyed() {
-  controller_->OnWindowDestroyed();
+  controller_->OnWindowDestroyed(/*should_pause_video=*/true);
 }
 
 gfx::Size OverlayWindowViews::GetMinimumSize() const {
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc b/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
index b79dc874..463bb81 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
@@ -29,7 +29,7 @@
   void Show() override {}
   void Close(bool) override {}
   void CloseAndFocusInitiator() override {}
-  void OnWindowDestroyed() override {}
+  void OnWindowDestroyed(bool) override {}
   content::OverlayWindow* GetWindowForTesting() override { return nullptr; }
   void UpdateLayerBounds() override {}
   bool IsPlayerActive() override { return false; }
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index 33a7aa7..cf86e32 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -178,15 +178,21 @@
 class ScopedWebContentsTestHelper {
  public:
   ScopedWebContentsTestHelper() {
-    web_contents_ = factory_.CreateWebContents(&profile_);
+    TestingProfile::Builder profile_builder;
+    profile_builder.AddTestingFactory(
+        HistoryServiceFactory::GetInstance(),
+        HistoryServiceFactory::GetDefaultFactory());
+    profile_ = profile_builder.Build();
+
+    web_contents_ = factory_.CreateWebContents(profile_.get());
   }
 
-  Profile* profile() { return &profile_; }
+  Profile* profile() { return profile_.get(); }
   content::WebContents* web_contents() { return web_contents_; }
 
  private:
   content::BrowserTaskEnvironment task_environment_;
-  TestingProfile profile_;
+  std::unique_ptr<TestingProfile> profile_;
   content::TestWebContentsFactory factory_;
   content::WebContents* web_contents_;  // Weak. Owned by factory_.
 
@@ -247,7 +253,6 @@
   TestingProfile* profile =
       static_cast<TestingProfile*>(web_contents_helper_.profile());
   ukm::TestAutoSetUkmRecorder ukm_recorder;
-  ASSERT_TRUE(profile->CreateHistoryService());
   auto* history_service = HistoryServiceFactory::GetForProfile(
       profile, ServiceAccessType::EXPLICIT_ACCESS);
   history_service->AddPage(origin_url, base::Time::Now(),
@@ -292,10 +297,6 @@
   // This test creates settings that are left at their defaults, leading to zero
   // checked options, and checks that the text on the MenuButtons is right.
 
-  TestingProfile* profile =
-      static_cast<TestingProfile*>(web_contents_helper_.profile());
-  ASSERT_TRUE(profile->CreateHistoryService());
-
   PermissionInfoList list(1);
   list.back().type = ContentSettingsType::GEOLOCATION;
   list.back().source = content_settings::SETTING_SOURCE_USER;
diff --git a/chrome/browser/ui/views/read_later/read_later_button.cc b/chrome/browser/ui/views/read_later/read_later_button.cc
index 631d70a..d866eac 100644
--- a/chrome/browser/ui/views/read_later/read_later_button.cc
+++ b/chrome/browser/ui/views/read_later/read_later_button.cc
@@ -112,6 +112,7 @@
       })),
       highlight_color_animation_(
           std::make_unique<HighlightColorAnimation>(this)) {
+  ConfigureInkDropForToolbar(this);
   // Note: BrowserView may not exist during tests.
   if (BrowserView::GetBrowserViewForBrowser(browser_))
     DCHECK(!BrowserView::GetBrowserViewForBrowser(browser_)
@@ -128,9 +129,6 @@
       DISTANCE_RELATED_LABEL_HORIZONTAL_LIST));
 
   views::InstallPillHighlightPathGenerator(this);
-  SetInkDropMode(InkDropMode::ON);
-  SetHasInkDropActionOnClick(true);
-  SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
   SetTooltipText(l10n_util::GetStringUTF16(IDS_READ_LATER_TITLE));
   GetViewAccessibility().OverrideHasPopup(ax::mojom::HasPopup::kMenu);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index 7c42c3b2..59a3f25 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/tabs/tab_style.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -4539,6 +4540,48 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+namespace {
+
+class SelectTabDuringDragObserver : public TabStripModelObserver {
+ public:
+  SelectTabDuringDragObserver() = default;
+  ~SelectTabDuringDragObserver() override = default;
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override {
+    if (change.type() != TabStripModelChange::kMoved)
+      return;
+    const TabStripModelChange::Move* move = change.GetMove();
+    int index_to_select = move->to_index == 0 ? 1 : 0;
+    tab_strip_model->ToggleSelectionAt(index_to_select);
+  }
+};
+
+}  // namespace
+
+// Bug fix for crbug.com/1196309. Don't change tab selection while dragging.
+IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest,
+                       SelectTabDuringDrag) {
+  TabStripModel* model = browser()->tab_strip_model();
+  TabStrip* tab_strip = GetTabStripForBrowser(browser());
+
+  SelectTabDuringDragObserver observer;
+  model->AddObserver(&observer);
+
+  AddTabsAndResetBrowser(browser(), 1);
+  ASSERT_EQ(2, model->count());
+
+  ASSERT_TRUE(PressInput(GetCenterInScreenCoordinates(tab_strip->tab_at(0))));
+  ASSERT_TRUE(DragInputTo(GetCenterInScreenCoordinates(tab_strip->tab_at(1))));
+  {
+    gfx::Rect tab_bounds = tab_strip->tab_at(1)->GetLocalBounds();
+    views::View::ConvertRectToScreen(tab_strip->tab_at(1), &tab_bounds);
+    ASSERT_TRUE(DragInputTo(tab_bounds.right_center()));
+  }
+  ASSERT_TRUE(ReleaseInput());
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 INSTANTIATE_TEST_SUITE_P(TabDragging,
                          DetachToBrowserTabDragControllerTest,
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 6e8cddb..82bec01 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1229,6 +1229,10 @@
          !drag_context_->IsActiveDropTarget();
 }
 
+bool TabStrip::CanHighlightTabs() const {
+  return !drag_context_->IsDragSessionActive();
+}
+
 bool TabStrip::IsTabCrashed(int tab_index) const {
   return tab_at(tab_index)->data().IsCrashed();
 }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index dffb175..857350d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -125,6 +125,9 @@
   // while that's happening.
   bool IsTabStripEditable() const;
 
+  // Returns whether tab dragging is in progress.
+  bool CanHighlightTabs() const;
+
   // Returns information about tabs at given indices.
   bool IsTabCrashed(int tab_index) const;
   bool TabHasNetworkError(int tab_index) const;
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 45936b53..867b7d09 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.cc
@@ -54,10 +54,7 @@
     : AppMenuButton(base::BindRepeating(&BrowserAppMenuButton::ButtonPressed,
                                         base::Unretained(this))),
       toolbar_view_(toolbar_view) {
-  SetInkDropMode(InkDropMode::ON);
   SetHorizontalAlignment(gfx::ALIGN_RIGHT);
-
-  SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
 }
 
 BrowserAppMenuButton::~BrowserAppMenuButton() {}
@@ -171,11 +168,6 @@
   SetHighlight(text, color);
 }
 
-std::unique_ptr<views::InkDropHighlight>
-BrowserAppMenuButton::CreateInkDropHighlight() const {
-  return CreateToolbarInkDropHighlight(this);
-}
-
 void BrowserAppMenuButton::OnTouchUiChanged() {
   UpdateColorsAndInsets();
   PreferredSizeChanged();
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 31965b19..13f8c50 100644
--- a/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
+++ b/chrome/browser/ui/views/toolbar/browser_app_menu_button.h
@@ -42,8 +42,6 @@
   static bool g_open_app_immediately_for_testing;
 
   // AppMenuButton:
-  std::unique_ptr<views::InkDropHighlight> CreateInkDropHighlight()
-      const override;
   void OnThemeChanged() override;
   // Updates the presentation according to |severity_| and the theme provider.
   void UpdateIcon() override;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index b236789..3d9ce13 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -59,8 +59,7 @@
                                      base::Unretained(this))),
       view_controller_(view_controller),
       delegate_(delegate) {
-  SetInkDropMode(InkDropMode::ON);
-  SetHasInkDropActionOnClick(true);
+  ConfigureInkDropForToolbar(this);
   SetHideInkDropWhenShowingContextMenu(false);
   SetShowInkDropWhenHotTracked(true);
   SetID(VIEW_ID_BROWSER_ACTION);
@@ -72,10 +71,6 @@
       std::make_unique<ExtensionContextMenuController>(view_controller);
   set_context_menu_controller(context_menu_controller_.get());
 
-  InstallToolbarButtonHighlightPathGenerator(this);
-
-  SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
-
   UpdateState();
 }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index 412e099..0108f7e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -158,7 +158,7 @@
       tab_strip_model_(tab_strip_model),
       trigger_menu_on_long_press_(trigger_menu_on_long_press),
       highlight_color_animation_(this) {
-  SetHasInkDropActionOnClick(true);
+  ConfigureInkDropForToolbar(this);
   set_context_menu_controller(this);
 
   if (base::FeatureList::IsEnabled(views::kInstallableInkDropFeature)) {
@@ -166,16 +166,10 @@
     installable_ink_drop_->SetConfig(GetToolbarInstallableInkDropConfig(this));
   }
 
-  InstallToolbarButtonHighlightPathGenerator(this);
-
-  SetInkDropMode(InkDropMode::ON);
-
   // Make sure icons are flipped by default so that back, forward, etc. follows
   // UI direction.
   SetFlipCanvasOnPaintForRTLUI(true);
 
-  SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
-
   SetImageLabelSpacing(ChromeLayoutProvider::Get()->GetDistanceMetric(
       DISTANCE_RELATED_LABEL_HORIZONTAL_LIST));
   SetHorizontalAlignment(gfx::ALIGN_RIGHT);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
index b97d9ba..2dc2176 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
@@ -83,16 +83,11 @@
   return config;
 }
 
-void InstallToolbarButtonHighlightPathGenerator(views::View* host) {
-  views::HighlightPathGenerator::Install(
-      host, std::make_unique<ToolbarButtonHighlightPathGenerator>());
-}
-
 void ConfigureInkDropForToolbar(views::Button* host) {
   host->SetHasInkDropActionOnClick(true);
-  InstallToolbarButtonHighlightPathGenerator(host);
+  views::HighlightPathGenerator::Install(
+      host, std::make_unique<ToolbarButtonHighlightPathGenerator>());
   host->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
   host->SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
   host->SetInkDropHighlightOpacity(kToolbarInkDropHighlightVisibleOpacity);
-  host->SetInkDropBaseColor(GetToolbarInkDropBaseColor(host));
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
index 6ced920..cfaa5c35 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
@@ -37,9 +37,6 @@
 views::InstallableInkDropConfig GetToolbarInstallableInkDropConfig(
     const views::View* host_view);
 
-// Installs a highlight path generator that matches the toolbar button style.
-void InstallToolbarButtonHighlightPathGenerator(views::View* host);
-
 void ConfigureInkDropForToolbar(views::Button* host);
 
 #endif  // CHROME_BROWSER_UI_VIEWS_TOOLBAR_TOOLBAR_INK_DROP_UTIL_H_
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 8ab3032c..0b200c69 100644
--- a/chrome/browser/ui/views/toolbar/webui_tab_counter_button.cc
+++ b/chrome/browser/ui/views/toolbar/webui_tab_counter_button.cc
@@ -495,6 +495,7 @@
     : Button(std::move(pressed_callback)),
       tab_strip_model_(browser_view->browser()->tab_strip_model()),
       browser_view_(browser_view) {
+  ConfigureInkDropForToolbar(this);
   // Not focusable by default, only for accessibility.
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
 }
@@ -611,7 +612,7 @@
 void WebUITabCounterButton::OnThemeChanged() {
   views::Button::OnThemeChanged();
   UpdateColors();
-  ConfigureInkDropForToolbar(this);
+  SetInkDropBaseColor(GetToolbarInkDropBaseColor(this));
 }
 
 void WebUITabCounterButton::Layout() {
diff --git a/chrome/browser/ui/views/user_education/feature_tutorial_service_views.cc b/chrome/browser/ui/views/user_education/feature_tutorial_service_views.cc
new file mode 100644
index 0000000..31eac82
--- /dev/null
+++ b/chrome/browser/ui/views/user_education/feature_tutorial_service_views.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 "chrome/browser/ui/views/user_education/feature_tutorial_service_views.h"
+
+#include <memory>
+
+FeatureTutorialServiceViews::FeatureTutorialServiceViews(Profile* profile) {}
+FeatureTutorialServiceViews::~FeatureTutorialServiceViews() = default;
+
+bool FeatureTutorialServiceViews::StartTutorial(FeatureTutorial tutorial) {
+  return true;
+}
+
+// Here we define the base class's MakeInstance function.
+// static
+std::unique_ptr<FeatureTutorialService> FeatureTutorialService::MakeInstance(
+    Profile* profile) {
+  return std::make_unique<FeatureTutorialServiceViews>(profile);
+}
diff --git a/chrome/browser/ui/views/user_education/feature_tutorial_service_views.h b/chrome/browser/ui/views/user_education/feature_tutorial_service_views.h
new file mode 100644
index 0000000..0f832fd
--- /dev/null
+++ b/chrome/browser/ui/views/user_education/feature_tutorial_service_views.h
@@ -0,0 +1,22 @@
+// 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 CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_VIEWS_H_
+#define CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_VIEWS_H_
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/user_education/feature_tutorial_service.h"
+
+class FeatureTutorialServiceViews : public FeatureTutorialService {
+ public:
+  explicit FeatureTutorialServiceViews(Profile* profile);
+  ~FeatureTutorialServiceViews() override;
+
+  bool StartTutorial(FeatureTutorial tutorial) override;
+
+ private:
+  // Profile* const profile_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_USER_EDUCATION_FEATURE_TUTORIAL_SERVICE_VIEWS_H_
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 3e4feb4f..278a1168 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -75,7 +75,7 @@
 #include "ui/gfx/geometry/size.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #endif
 
 #if defined(OS_MAC)
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
index 4db641dc..a7411d3 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
@@ -36,7 +36,7 @@
 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/extension_app_utils.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #endif
 
 #if defined(OS_WIN)
diff --git a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
index cd04417..462f5cf 100644
--- a/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_shelf_delegate_chromeos.cc
@@ -11,9 +11,9 @@
 #include "ash/public/cpp/shelf_types.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/chrome_shelf_prefs.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller.h"
-#include "chrome/browser/ui/ash/launcher/chrome_shelf_controller_util.h"
-#include "chrome/browser/ui/ash/launcher/shelf_controller_helper.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
+#include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
+#include "chrome/browser/ui/ash/shelf/shelf_controller_helper.h"
 #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
index 867cac38..a77a56e 100644
--- a/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/internet_section.cc
@@ -743,8 +743,16 @@
        IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_DONE},
       {"eSimRenameProfileDialogCancel",
        IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_CANCEL},
+      {"eSimRenameProfileInputSubtitle",
+       IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_SUBTITLE},
+      {"eSimRenameProfileInputCharacterCount",
+       IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_CHARACTER_COUNT},
+      {"eSimRenameProfileInputA11yLabel",
+       IDS_SETTINGS_INTERNET_NETWORK_RENAME_INPUT_A11Y_LABEL},
       {"eSimRenameProfileDialogError",
        IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_ERROR_MESSAGE},
+      {"eSimRenameProfileDialogErrorToast",
+       IDS_SETTINGS_INTERNET_NETWORK_RENAME_DIALOG_ERROR_TOAST},
       {"eSimRemoveProfileDialogCancel",
        IDS_SETTINGS_INTERNET_NETWORK_REMOVE_PROFILE_DIALOG_CANCEL},
       {"esimRemoveProfileDialogTitle",
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index 6697d28e..05b12129 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -83,7 +83,6 @@
 
 #if defined(OS_WIN)
 #include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
 #include "chrome/credential_provider/common/gcp_strings.h"
 #endif  // defined(OS_WIN)
 
diff --git a/chrome/browser/vr/win/graphics_delegate_win.cc b/chrome/browser/vr/win/graphics_delegate_win.cc
index bbff917..372f4b0 100644
--- a/chrome/browser/vr/win/graphics_delegate_win.cc
+++ b/chrome/browser/vr/win/graphics_delegate_win.cc
@@ -162,7 +162,7 @@
 
     gpu_memory_buffer_ = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
         gfx::Size(width, height), gfx::BufferFormat::RGBA_8888,
-        gfx::BufferUsage::SCANOUT, gpu::kNullSurfaceHandle);
+        gfx::BufferUsage::SCANOUT, gpu::kNullSurfaceHandle, nullptr);
     if (!gpu_memory_buffer_)
       return false;
 
diff --git a/chrome/browser/webapps/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenIPHController.java b/chrome/browser/webapps/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenIPHController.java
index 027f73e8..7ba55f4e 100644
--- a/chrome/browser/webapps/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenIPHController.java
+++ b/chrome/browser/webapps/android/java/src/org/chromium/chrome/browser/webapps/AddToHomescreenIPHController.java
@@ -150,6 +150,10 @@
     }
 
     private void showMessageIPH(Tab tab) {
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.MESSAGES_FOR_ANDROID_INFRASTRUCTURE)) {
+            return;
+        }
+
         if (!mTracker.shouldTriggerHelpUI(FeatureConstants.ADD_TO_HOMESCREEN_MESSAGE_FEATURE)) {
             return;
         }
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 7d43bdb..cf74015 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1619697388-9f31f86f9595af924e1ca1d2577fe4a30c9d0604.profdata
+chrome-mac-master-1619718818-415f3a4ea7bf6a28542c409acffc59b97bf0784c.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5721025..b763ad8 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1619697388-2e39591d1d1f0989422cf9650fbb22981b2d456d.profdata
+chrome-win32-master-1619718818-3403de47ee619a498d87e1054053c34f0d3e72e3.profdata
diff --git a/chrome/common/cart/commerce_hints.mojom b/chrome/common/cart/commerce_hints.mojom
index 19ab1f9..49e9a23 100644
--- a/chrome/common/cart/commerce_hints.mojom
+++ b/chrome/common/cart/commerce_hints.mojom
@@ -11,13 +11,16 @@
   url.mojom.Url image_url;
   // The product name.
   string name;
+  // The product ID.
+  string product_id;
 };
 
 // This service is implemented in the browser process and is used by the
 // renderer to notify the CommerceHint events.
 interface CommerceHintObserver {
-  // Add-to-cart action is detected. |cart_url| is provided if found.
-  OnAddToCart(url.mojom.Url? cart_url);
+  // Add-to-cart action is detected. |cart_url| and |product_id|
+  // provided if found.
+  OnAddToCart(url.mojom.Url? cart_url, string product_id);
 
   // Shopping cart page is visited.
   OnVisitCart();
diff --git a/chrome/renderer/cart/commerce_hint_agent.cc b/chrome/renderer/cart/commerce_hint_agent.cc
index 6e7c2d8..88dc30b 100644
--- a/chrome/renderer/cart/commerce_hint_agent.cc
+++ b/chrome/renderer/cart/commerce_hint_agent.cc
@@ -48,6 +48,11 @@
     // This regex does not match anything.
     "\\b\\B"};
 
+constexpr base::FeatureParam<std::string> kPartnerMerchantPattern{
+    &ntp_features::kNtpChromeCartModule, "partner-merchant-pattern",
+    // This regex does not match anything.
+    "\\b\\B"};
+
 std::string eTLDPlusOne(const GURL& url) {
   return net::registry_controlled_domains::GetDomainAndRegistry(
       url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
@@ -121,10 +126,11 @@
   return best;
 }
 
-void OnAddToCart(content::RenderFrame* render_frame) {
+void OnAddToCart(content::RenderFrame* render_frame,
+                 const std::string& product_id = std::string()) {
   mojo::Remote<mojom::CommerceHintObserver> observer =
       GetObserver(render_frame);
-  observer->OnAddToCart(ScanCartURL(render_frame));
+  observer->OnAddToCart(ScanCartURL(render_frame), product_id);
 }
 
 void OnVisitCart(content::RenderFrame* render_frame) {
@@ -156,6 +162,12 @@
   return RE2::PartialMatch(re2::StringPiece(str.data(), str.size()), re);
 }
 
+bool GetPartialMatchString(base::StringPiece str,
+                           const re2::RE2& re,
+                           std::string* res) {
+  return RE2::PartialMatch(re2::StringPiece(str.data(), str.size()), re, res);
+}
+
 // This is based on top 30 US shopping sites.
 // TODO(crbug/1164236): cover more shopping sites.
 const re2::RE2& GetAddToCartPattern() {
@@ -282,6 +294,32 @@
   return *instance;
 }
 
+const re2::RE2& GetProductIdPatternFromRequest() {
+  re2::RE2::Options options;
+  options.set_case_sensitive(false);
+  static base::NoDestructor<re2::RE2> instance("product_id=(\\w+)", options);
+  return *instance;
+}
+
+const re2::RE2& GetProductIdPatternFromURL() {
+  re2::RE2::Options options;
+  options.set_case_sensitive(false);
+  static base::NoDestructor<re2::RE2> instance("(\\w+)-\\d+-medium", options);
+  return *instance;
+}
+
+const re2::RE2& GetPartnerMerchantPattern() {
+  re2::RE2::Options options;
+  options.set_case_sensitive(false);
+  static base::NoDestructor<re2::RE2> instance(kPartnerMerchantPattern.Get(),
+                                               options);
+  return *instance;
+}
+
+bool IsPartnerMerchant(const GURL& url) {
+  return PartialMatch(url.spec(), GetPartnerMerchantPattern());
+}
+
 bool IsSameDomainXHR(const std::string& host,
                      const blink::WebURLRequest& request) {
   // Only handle XHR POST requests here.
@@ -354,11 +392,16 @@
       return;
 
     if (CommerceHintAgent::IsAddToCart(str)) {
+      std::string product_id;
+      if (IsPartnerMerchant(url)) {
+        GetPartialMatchString(str.substr(0, kLengthLimit),
+                              GetProductIdPatternFromRequest(), &product_id);
+      }
       RecordCommerceEvent(CommerceEvent::kAddToCartByForm);
       DVLOG(2) << "Matched add-to-cart. Request from \"" << navigation_url
                << "\" to \"" << url << "\" with payload (size = " << str.size()
                << ") \"" << str << "\"";
-      OnAddToCart(render_frame);
+      OnAddToCart(render_frame, std::move(product_id));
       return;
     }
   }
@@ -482,6 +525,8 @@
   // that the cart is not loaded.
   if (!results->is_list())
     return;
+  bool is_partner = IsPartnerMerchant(
+      GURL(render_frame()->GetWebFrame()->GetDocument().Url()));
   std::vector<mojom::ProductPtr> products;
   for (const auto& product : results->GetList()) {
     if (!product.is_dict())
@@ -497,6 +542,13 @@
       DVLOG(1) << "skipped";
       continue;
     }
+    std::string product_id;
+    if (is_partner &&
+        GetPartialMatchString(image_url->GetString().substr(0, kLengthLimit),
+                              GetProductIdPatternFromURL(), &product_id)) {
+      DVLOG(1) << "product_id = " << product_id;
+      product_ptr->product_id = std::move(product_id);
+    }
     products.push_back(std::move(product_ptr));
   }
   OnCartProductUpdated(render_frame(), std::move(products));
diff --git a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
index fee316c..df1b3e1 100644
--- a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
+++ b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
@@ -42,6 +42,12 @@
   return proto;
 }
 
+cart_db::ChromeCartProductProto BuildProductInfoProto(const char* product_id) {
+  cart_db::ChromeCartProductProto proto;
+  proto.set_product_id(product_id);
+  return proto;
+}
+
 cart_db::ChromeCartContentProto BuildProtoWithProducts(
     const char* domain,
     const char* cart_url,
@@ -55,6 +61,24 @@
   return proto;
 }
 
+cart_db::ChromeCartContentProto BuildProtoWithProducts(
+    const char* domain,
+    const char* cart_url,
+    const std::vector<const char*>& product_urls,
+    const std::vector<const char*>& product_ids) {
+  cart_db::ChromeCartContentProto proto;
+  proto.set_key(domain);
+  proto.set_merchant_cart_url(cart_url);
+  for (const auto* const url : product_urls) {
+    proto.add_product_image_urls(url);
+  }
+  for (const auto* const id : product_ids) {
+    auto* added_product = proto.add_product_infos();
+    *added_product = BuildProductInfoProto(id);
+  }
+  return proto;
+}
+
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 void UnblockOnProfileCreation(base::RunLoop* run_loop,
                               Profile* profile,
@@ -80,8 +104,8 @@
     BuildProtoWithProducts(
         kMockExample,
         kMockExampleURL,
-        {"https://static.guitarcenter.com/product-image/123.png",
-         "https://static.guitarcenter.com/product-image/456.png"});
+        {"https://static.guitarcenter.com/product-image/foo_123-0-medium",
+         "https://static.guitarcenter.com/product-image/bar_456-0-medium"});
 
 const char kMockAmazon[] = "amazon.com";
 const char kMockAmazonURL[] = "https://www.amazon.com/gp/cart/view.html";
@@ -280,14 +304,24 @@
                 expected[i].second.merchant_cart_url());
       bool same_size = found[i].second.product_image_urls_size() ==
                        expected[i].second.product_image_urls_size();
-      if (!same_size)
+      if (!same_size) {
         fail = true;
-      if (same_size) {
+      } else {
         for (int j = 0; j < found[i].second.product_image_urls_size(); j++) {
           EXPECT_EQ(found[i].second.product_image_urls(j),
                     expected[i].second.product_image_urls(j));
         }
       }
+      same_size = found[i].second.product_infos_size() ==
+                  expected[i].second.product_infos_size();
+      if (!same_size) {
+        fail = true;
+      } else {
+        for (int j = 0; j < found[i].second.product_infos_size(); j++) {
+          EXPECT_EQ(found[i].second.product_infos(j).product_id(),
+                    expected[i].second.product_infos(j).product_id());
+        }
+      }
     }
     satisfied_ = !fail;
     std::move(closure).Run();
@@ -483,4 +517,41 @@
   WaitForCartCount(kEmptyExpected);
 }
 
+class CommerceHintProductInfoTest : public CommerceHintAgentTest {
+ public:
+  void SetUpInProcessBrowserTestFixture() override {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        ntp_features::kNtpChromeCartModule,
+        {{"partner-merchant-pattern", "(guitarcenter.com)"},
+         {"product-skip-pattern", "(^|\\W)(?i)(skipped)(\\W|$)"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(CommerceHintProductInfoTest, AddToCartByForm_CaptureId) {
+  NavigateToURL("https://www.guitarcenter.com/product.html");
+  SendXHR("/cart/update", "product_id=id_foo&add_to_cart=true");
+
+  const cart_db::ChromeCartContentProto expected_cart_protos =
+      BuildProtoWithProducts(kMockExample, kMockExampleLinkURL, {}, {"id_foo"});
+  const ShoppingCarts expected_carts = {{kMockExample, expected_cart_protos}};
+  WaitForProductCount(expected_carts);
+}
+
+IN_PROC_BROWSER_TEST_F(CommerceHintProductInfoTest, ExtractCart_CaptureId) {
+  // This page has two products.
+  NavigateToURL("https://www.guitarcenter.com/cart.html");
+
+  const cart_db::ChromeCartContentProto expected_cart_protos =
+      BuildProtoWithProducts(
+          kMockExample, kMockExampleURL,
+          {"https://static.guitarcenter.com/product-image/foo_123-0-medium",
+           "https://static.guitarcenter.com/product-image/bar_456-0-medium"},
+          {"foo_123", "bar_456"});
+  const ShoppingCarts expected_carts = {{kMockExample, expected_cart_protos}};
+  WaitForProductCount(expected_carts);
+}
+
 }  // namespace
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 49a5874..d9c8061 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2861,19 +2861,19 @@
         "../browser/ui/ash/clipboard_history_browsertest.cc",
         "../browser/ui/ash/keyboard/keyboard_controller_browsertest.cc",
         "../browser/ui/ash/keyboard/keyboard_end_to_end_browsertest.cc",
-        "../browser/ui/ash/launcher/app_service/app_service_app_window_browsertest.cc",
-        "../browser/ui/ash/launcher/app_service/app_service_shelf_context_menu_browsertest.cc",
-        "../browser/ui/ash/launcher/app_shortcut_shelf_item_controller_browsertest.cc",
-        "../browser/ui/ash/launcher/arc_app_shelf_browsertest.cc",
-        "../browser/ui/ash/launcher/browser_shortcut_shelf_item_controller_browsertest.cc",
-        "../browser/ui/ash/launcher/chrome_shelf_controller_browsertest.cc",
-        "../browser/ui/ash/launcher/chrome_shelf_controller_test_util.cc",
-        "../browser/ui/ash/launcher/chrome_shelf_controller_test_util.h",
         "../browser/ui/ash/multi_user/test_multi_user_window_manager.cc",
         "../browser/ui/ash/multi_user/test_multi_user_window_manager.h",
         "../browser/ui/ash/recording_service_browsertest.cc",
         "../browser/ui/ash/screen_orientation_delegate_chromeos_browsertest.cc",
         "../browser/ui/ash/sharesheet/sharesheet_bubble_view_browsertest.cc",
+        "../browser/ui/ash/shelf/app_service/app_service_app_window_browsertest.cc",
+        "../browser/ui/ash/shelf/app_service/app_service_shelf_context_menu_browsertest.cc",
+        "../browser/ui/ash/shelf/app_shortcut_shelf_item_controller_browsertest.cc",
+        "../browser/ui/ash/shelf/arc_app_shelf_browsertest.cc",
+        "../browser/ui/ash/shelf/browser_shortcut_shelf_item_controller_browsertest.cc",
+        "../browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc",
+        "../browser/ui/ash/shelf/chrome_shelf_controller_test_util.cc",
+        "../browser/ui/ash/shelf/chrome_shelf_controller_test_util.h",
         "../browser/ui/ash/shelf_browsertest.cc",
         "../browser/ui/ash/system_tray_client_browsertest.cc",
         "../browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc",
@@ -5175,9 +5175,6 @@
       "../browser/ui/ash/keyboard/chrome_keyboard_ui_unittest.cc",
       "../browser/ui/ash/keyboard/chrome_keyboard_web_contents_unittest.cc",
       "../browser/ui/ash/keyboard_shortcut_viewer_metadata_unittest.cc",
-      "../browser/ui/ash/launcher/arc_app_shelf_id_unittest.cc",
-      "../browser/ui/ash/launcher/chrome_shelf_controller_unittest.cc",
-      "../browser/ui/ash/launcher/shelf_context_menu_unittest.cc",
       "../browser/ui/ash/media_client_impl_unittest.cc",
       "../browser/ui/ash/media_notification_provider_impl_unittest.cc",
       "../browser/ui/ash/multi_user/multi_profile_support_unittest.cc",
@@ -5189,6 +5186,9 @@
       "../browser/ui/ash/network/tether_notification_presenter_unittest.cc",
       "../browser/ui/ash/notification_badge_color_cache_unittest.cc",
       "../browser/ui/ash/session_controller_client_impl_unittest.cc",
+      "../browser/ui/ash/shelf/arc_app_shelf_id_unittest.cc",
+      "../browser/ui/ash/shelf/chrome_shelf_controller_unittest.cc",
+      "../browser/ui/ash/shelf/shelf_context_menu_unittest.cc",
       "../browser/ui/ash/wallpaper_controller_client_impl_unittest.cc",
       "../browser/ui/webui/chromeos/login/fake_update_required_screen_handler.cc",
       "../browser/ui/webui/chromeos/login/fake_update_required_screen_handler.h",
diff --git a/chrome/test/data/cart/cart.html b/chrome/test/data/cart/cart.html
index 07b4a1e..5bf9451e 100644
--- a/chrome/test/data/cart/cart.html
+++ b/chrome/test/data/cart/cart.html
@@ -2,27 +2,27 @@
 <body>
 <div>
     <a href="/products/123/">Product one
-        <img src="https://static.guitarcenter.com/product-image/123.png" height=300 width=300></a>
+        <img src="https://static.guitarcenter.com/product-image/foo_123-0-medium" height=300 width=300></a>
     <span>$12.34</span><a>remove</a>
 </div>
 <div>
     <a href="/products/456/">Product two
-        <img src="//static.guitarcenter.com/product-image/456.png" height=300 width=300></a>
+        <img src="//static.guitarcenter.com/product-image/bar_456-0-medium" height=300 width=300></a>
     <span>$56.78</span><a>remove</a>
 </div>
 <div>
     <a href="/products/789/">Skipped
-        <img src="https://static.guitarcenter.com/product-image/789.png" height=300 width=300></a>
+        <img src="https://static.guitarcenter.com/product-image/baz_789-0-medium" height=300 width=300></a>
     <span>$56.78</span><a>remove</a>
 </div>
 <div>
     <a href="/products/abc/">A skipped product
-        <img src="https://static.guitarcenter.com/product-image/abc.png" height=300 width=300></a>
+        <img src="https://static.guitarcenter.com/product-image/qux_abc-0-medium" height=300 width=300></a>
     <span>$56.78</span><a>remove</a>
 </div>
 <div>
     <a href="/products/12/">Product three
-        <img src="https://static.walmart.com/product-image/12.png" height=300 width=300></a>
+        <img src="https://static.walmart.com/product-image/12-0-medium" height=300 width=300></a>
     <span>$1.2</span><a>remove</a><a>Move to cart</a>
 </div>
 </body>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/xslt/main.xml b/chrome/test/data/extensions/api_test/webnavigation/xslt/main.xml
new file mode 100644
index 0000000..909fe61
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webnavigation/xslt/main.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet href="transform.xsl" type="text/xsl"?>
+<MyData>
+
+</MyData>
\ No newline at end of file
diff --git a/chrome/test/data/extensions/api_test/webnavigation/xslt/manifest.json b/chrome/test/data/extensions/api_test/webnavigation/xslt/manifest.json
new file mode 100644
index 0000000..d3530e4
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webnavigation/xslt/manifest.json
@@ -0,0 +1,10 @@
+{
+  "name": "webNavigation",
+  "version": "1.0",
+  "manifest_version": 2,
+  "description": "Tests the webNavigation API events - xslt",
+  "background": {
+    "page": "test_xslt.html"
+  },
+  "permissions": ["webNavigation", "tabs"]
+}
diff --git a/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.html b/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.html
new file mode 100644
index 0000000..b8a7c25
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.html
@@ -0,0 +1,2 @@
+<script src="_test_resources/api_test/webnavigation/framework.js"></script>
+<script src="test_xslt.js"></script>
diff --git a/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js b/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js
new file mode 100644
index 0000000..2c740c4
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webnavigation/xslt/test_xslt.js
@@ -0,0 +1,77 @@
+// 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.
+
+onload = async function() {
+  debug = true;
+  let getURL = chrome.extension.getURL;
+  let tab = await promise(chrome.tabs.create, {"url": "about:blank"});
+  let config = await promise(chrome.test.getConfig);
+  let port = config.testServer.port;
+  let URL_MAIN = getURL('main.xml');
+  let PATH_FRAME =
+    '/extensions/api_test/webnavigation/xslt/frame.xml';
+  chrome.test.runTests([
+    // Navigate to an XML document with an XSLT stylesheet.
+    // For the original XML document the extension should
+    // receive onBeforeNavigate, onCommitted, and onDOMContentLoaded.
+    // Then the XSLT is processed and the initial document is
+    // replaced with a document that is the result of the XSLT
+    // transformation. For this document the extension receives
+    // a second onDOMContentLoaded followed by onCompleted.
+    function crossProcessIframe() {
+      expect([
+        { label: 'main-onBeforeNavigate',
+          event: 'onBeforeNavigate',
+          details: { frameId: 0,
+                     parentFrameId: -1,
+                     processId: -1,
+                     tabId: 0,
+                     timeStamp: 0,
+                     url: URL_MAIN }},
+        { label: 'main-onCommitted',
+          event: 'onCommitted',
+          details: { frameId: 0,
+                     parentFrameId: -1,
+                     processId: 0,
+                     tabId: 0,
+                     timeStamp: 0,
+                     transitionQualifiers: [],
+                     transitionType: 'link',
+                     url: URL_MAIN }},
+        { label: 'main-onDOMContentLoaded',
+          event: 'onDOMContentLoaded',
+          details: { frameId: 0,
+                     parentFrameId: -1,
+                     processId: 0,
+                     tabId: 0,
+                     timeStamp: 0,
+                     url: URL_MAIN }},
+        { label: 'main-onDOMContentLoaded',
+        event: 'onDOMContentLoaded',
+        details: { frameId: 0,
+                  parentFrameId: -1,
+                  processId: 0,
+                  tabId: 0,
+                  timeStamp: 0,
+                  url: URL_MAIN }},
+      { label: 'main-onCompleted',
+        event: 'onCompleted',
+        details: { frameId: 0,
+                  parentFrameId: -1,
+                  processId: 0,
+                  tabId: 0,
+                  timeStamp: 0,
+                  url: URL_MAIN }}],
+        [
+          [ "main-onBeforeNavigate",
+            "main-onCommitted",
+           "main-onDOMContentLoaded"],
+           ["main-onDOMContentLoaded",
+           "main-onCompleted"],
+        ]);
+
+      chrome.tabs.update(tab.id, {url: URL_MAIN + '?' + port});
+    },
+  ]);
+};
diff --git a/chrome/test/data/extensions/api_test/webnavigation/xslt/transform.xsl b/chrome/test/data/extensions/api_test/webnavigation/xslt/transform.xsl
new file mode 100644
index 0000000..6f719902
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/webnavigation/xslt/transform.xsl
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<xsl:output method="html" xmlns:xalan="http://xml.apache.org/xslt"/>
+	<xsl:template match="text()">
+		Let's put some content here
+	</xsl:template>
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js b/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
index a5bb0bc..241fe2d3 100644
--- a/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
+++ b/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
@@ -12,6 +12,7 @@
 // #import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {assertEquals, assertTrue} from '../../chai_assert.js';
+// #import {eventToPromise} from 'chrome://test/test_util.m.js';
 // clang-format on
 
 suite('EsimRenameDialog', function() {
@@ -64,6 +65,28 @@
     mojoApi_.setManagedPropertiesForTest(cellular);
   }
 
+  /**
+   * @param {string} value The value of the input
+   * @param {boolean} invalid If the input is invalid or not
+   * @param {string} inputCount The length of value in string
+   *     format, with 2 digits
+   */
+  function assertInput(value, invalid, valueLength) {
+    const inputBox = esimRenameDialog.$$('#eSimprofileName');
+    const inputCount = esimRenameDialog.$$('#inputCount');
+    assertTrue(!!inputBox);
+    assertTrue(!!inputCount);
+
+    assertEquals(inputBox.value, value);
+    assertEquals(inputBox.invalid, invalid);
+    const characterCountText = esimRenameDialog.i18n(
+        'eSimRenameProfileInputCharacterCount', valueLength, 20);
+    assertEquals(inputCount.textContent.trim(), characterCountText);
+    assertEquals(
+        inputBox.ariaDescription,
+        esimRenameDialog.i18n('eSimRenameProfileInputA11yLabel', 20));
+  }
+
   test('Rename esim profile', async function() {
     eSimManagerRemote.addEuiccForTest(1);
     addEsimCellularNetwork(TEST_CELLULAR_GUID, '1');
@@ -149,23 +172,35 @@
       inputBox.value = 'new profile nickname';
       await flushAsync();
 
+      const showErrorToastPromise =
+          test_util.eventToPromise('show-error-toast', esimRenameDialog);
+
       const doneBtn = esimRenameDialog.$$('#done');
+      const cancelBtn = esimRenameDialog.$$('#cancel');
       assertTrue(!!doneBtn);
+      assertTrue(!!cancelBtn);
       assertFalse(doneBtn.disabled);
+      assertFalse(cancelBtn.disabled);
+      assertFalse(inputBox.disabled);
       doneBtn.click();
       await flushAsync();
+
       assertTrue(doneBtn.disabled);
+      assertTrue(cancelBtn.disabled);
+      assertTrue(inputBox.disabled);
 
       profile.resolveSetProfileNicknamePromise_();
       await flushAsync();
       assertFalse(doneBtn.disabled);
+      assertFalse(cancelBtn.disabled);
+      assertFalse(inputBox.disabled);
 
       const profileProperties = (await profile.getProperties()).properties;
 
+      const showErrorToastEvent = await showErrorToastPromise;
       assertEquals(
-          'block',
-          window.getComputedStyle(esimRenameDialog.$$('#errorMessage'))
-              .display);
+          showErrorToastEvent.detail,
+          esimRenameDialog.i18n('eSimRenameProfileDialogErrorToast'));
       assertNotEquals(
           convertString16ToJSString_(profileProperties.nickname),
           'new profile nickname');
@@ -182,4 +217,96 @@
     esimRenameDialog.showCellularDisconnectWarning = true;
     assertFalse(warningMessage.hidden);
   });
+
+  test('Input is sanitized', async function() {
+    eSimManagerRemote.addEuiccForTest(1);
+    addEsimCellularNetwork(TEST_CELLULAR_GUID, '1');
+    await flushAsync();
+    init();
+
+    await flushAsync();
+    const inputBox = esimRenameDialog.$$('#eSimprofileName');
+    assertTrue(!!inputBox);
+    const profileName = inputBox.value;
+    assertEquals(profileName, 'profile1');
+
+    // Test empty name.
+    inputBox.value = '';
+    assertInput(
+        /*value=*/ '', /*invalid=*/ false, /*valueLength=*/ '00');
+
+    // Test name with no emojis, under character limit.
+    inputBox.value = '1234567890123456789';
+    assertInput(
+        /*value=*/ '1234567890123456789', /*invalid=*/ false,
+        /*valueLength=*/ '19');
+
+    // Test name with emojis, under character limit.
+    inputBox.value = '1234😀5678901234🧟';
+    assertInput(
+        /*value=*/ '12345678901234', /*invalid=*/ false,
+        /*valueLength=*/ '14');
+
+    // Test name with only emojis, under character limit.
+    inputBox.value = '😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀';
+    assertInput(
+        /*value=*/ '', /*invalid=*/ false, /*valueLength=*/ '00');
+
+    // Test name with no emojis, at character limit.
+    inputBox.value = '12345678901234567890';
+    assertInput(
+        /*value=*/ '12345678901234567890', /*invalid=*/ false,
+        /*valueLength=*/ '20');
+
+    // Test name with emojis, at character limit.
+    inputBox.value = '1234567890123456789🧟';
+    assertInput(
+        /*value=*/ '1234567890123456789', /*invalid=*/ false,
+        /*valueLength=*/ '19');
+
+    // Test name with only emojis, at character limit.
+    inputBox.value = '😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀';
+    assertInput(
+        /*value=*/ '', /*invalid=*/ false, /*valueLength=*/ '00');
+
+    // Test name with no emojis, above character limit.
+    inputBox.value = '123456789012345678901';
+    assertInput(
+        /*value=*/ '12345678901234567890', /*invalid=*/ true,
+        /*valueLength=*/ '20');
+
+    // Make sure input is not invalid once its value changes to a string below
+    // the character limit. (Simulates the user pressing backspace once they've
+    // reached the limit).
+    inputBox.value = '1234567890123456789';
+    assertInput(
+        /*value=*/ '1234567890123456789', /*invalid=*/ false,
+        /*valueLength=*/ '19');
+
+    // Test name with emojis, above character limit.
+    inputBox.value = '12345678901234567890🧟';
+    assertInput(
+        /*value=*/ '12345678901234567890', /*invalid=*/ false,
+        /*valueLength=*/ '20');
+
+    // Test name with only emojis, above character limit.
+    inputBox.value = '😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀';
+    assertInput(
+        /*value=*/ '', /*invalid=*/ false, /*valueLength=*/ '00');
+
+    // Set name with emojis, above character limit
+    inputBox.value = '12345678901234567890🧟';
+    const doneBtn = esimRenameDialog.$$('#done');
+    assertTrue(!!doneBtn);
+    doneBtn.click();
+    await flushAsync();
+
+    const euicc = (await eSimManagerRemote.getAvailableEuiccs()).euiccs[0];
+    const profile = (await euicc.getProfileList()).profiles[0];
+    const profileProperties = (await profile.getProperties()).properties;
+
+    assertEquals(
+        convertString16ToJSString_(profileProperties.nickname),
+        '12345678901234567890');
+  });
 });
\ No newline at end of file
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 2da5e8f..3c6e799 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -895,6 +895,9 @@
       <message name="IDS_QUICK_ANSWERS_UNIT_CONVERSION_RESULT_TEXT" desc="The result text format string used for Quick Answers unit conversion result card. The first placeholder contains the value of result unit and the second placeholder contains the display name of the result unit.">
         <ph name="VALUE">$1<ex>220.462</ex></ph> <ph name="DISPLAY_NAME">$2<ex>pounds</ex></ph>
       </message>
+      <message name="IDS_UNIT_CONVERSION_POUND_DISPLAY_TEXT" desc="The display text of pound unit type.">
+        pounds
+      </message>
 
     <!-- Common Network type strings -->
       <message name="IDS_NETWORK_TYPE" desc="Label for network types">
diff --git a/chromeos/chromeos_strings_grd/IDS_UNIT_CONVERSION_POUND_DISPLAY_TEXT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_UNIT_CONVERSION_POUND_DISPLAY_TEXT.png.sha1
new file mode 100644
index 0000000..f8b4269
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_UNIT_CONVERSION_POUND_DISPLAY_TEXT.png.sha1
@@ -0,0 +1 @@
+d2b01997a81b55eb91dcf5ffa9715834d048edb2
\ No newline at end of file
diff --git a/chromeos/components/quick_answers/search_result_parsers/unit_conversion_result_parser_unittest.cc b/chromeos/components/quick_answers/search_result_parsers/unit_conversion_result_parser_unittest.cc
index f277d4a..3b23103 100644
--- a/chromeos/components/quick_answers/search_result_parsers/unit_conversion_result_parser_unittest.cc
+++ b/chromeos/components/quick_answers/search_result_parsers/unit_conversion_result_parser_unittest.cc
@@ -205,7 +205,7 @@
   auto expected_result = BuildUnitConversionResultText(
       base::StringPrintf(kResultValueTemplate, (kKilogramRateA / kPoundRateA) *
                                                    kSourceAmountKilogram),
-      kPoundName);
+      GetUnitDisplayText(kPoundName));
 
   EXPECT_EQ(1u, quick_answer.first_answer_row.size());
   EXPECT_EQ(0u, quick_answer.title.size());
diff --git a/chromeos/components/quick_answers/utils/unit_conversion_constants.cc b/chromeos/components/quick_answers/utils/unit_conversion_constants.cc
index 99110ed..827c7989 100644
--- a/chromeos/components/quick_answers/utils/unit_conversion_constants.cc
+++ b/chromeos/components/quick_answers/utils/unit_conversion_constants.cc
@@ -4,6 +4,11 @@
 
 #include "chromeos/components/quick_answers/utils/unit_conversion_constants.h"
 
+#include "base/containers/fixed_flat_map.h"
+#include "base/strings/string_piece.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
 namespace chromeos {
 namespace quick_answers {
 
@@ -20,5 +25,20 @@
 const char kNamePath[] = "name";
 const char kUnitsPath[] = "units";
 
+constexpr char kPoundName[] = "Pound";
+
+// TODO(b/182389513): Handle the proper plural case for the display text.
+std::string GetUnitDisplayText(const std::string& name) {
+  constexpr auto kUnitDisplayTextMap =
+      base::MakeFixedFlatMap<base::StringPiece, int>(
+          {{kPoundName, IDS_UNIT_CONVERSION_POUND_DISPLAY_TEXT}});
+
+  const auto* it = kUnitDisplayTextMap.find(name);
+  if (it == kUnitDisplayTextMap.end())
+    return name;
+
+  return l10n_util::GetStringUTF8(it->second);
+}
+
 }  // namespace quick_answers
 }  // namespace chromeos
diff --git a/chromeos/components/quick_answers/utils/unit_conversion_constants.h b/chromeos/components/quick_answers/utils/unit_conversion_constants.h
index ffa3316..e026dcd 100644
--- a/chromeos/components/quick_answers/utils/unit_conversion_constants.h
+++ b/chromeos/components/quick_answers/utils/unit_conversion_constants.h
@@ -5,6 +5,8 @@
 #ifndef CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_UNIT_CONVERSION_CONSTANTS_H_
 #define CHROMEOS_COMPONENTS_QUICK_ANSWERS_UTILS_UNIT_CONVERSION_CONSTANTS_H_
 
+#include <string>
+
 namespace chromeos {
 namespace quick_answers {
 
@@ -20,6 +22,8 @@
 extern const char kNamePath[];
 extern const char kUnitsPath[];
 
+std::string GetUnitDisplayText(const std::string& name);
+
 }  // namespace quick_answers
 }  // namespace chromeos
 
diff --git a/chromeos/components/quick_answers/utils/unit_converter.cc b/chromeos/components/quick_answers/utils/unit_converter.cc
index 676b35a9..7faa832 100644
--- a/chromeos/components/quick_answers/utils/unit_converter.cc
+++ b/chromeos/components/quick_answers/utils/unit_converter.cc
@@ -46,7 +46,8 @@
       (src_rate_a.value() / dst_rate_a.value()) * src_value;
 
   return BuildUnitConversionResultText(
-      base::StringPrintf(kResultValueTemplate, result_value), *dst_name);
+      base::StringPrintf(kResultValueTemplate, result_value),
+      GetUnitDisplayText(*dst_name));
 }
 
 const Value* UnitConverter::FindProperDestinationUnit(
diff --git a/chromeos/components/quick_answers/utils/unit_converter_unittest.cc b/chromeos/components/quick_answers/utils/unit_converter_unittest.cc
index f7679a0..20780f2 100644
--- a/chromeos/components/quick_answers/utils/unit_converter_unittest.cc
+++ b/chromeos/components/quick_answers/utils/unit_converter_unittest.cc
@@ -218,7 +218,7 @@
   auto expected_result = BuildUnitConversionResultText(
       base::StringPrintf(kResultValueTemplate,
                          (kKilogramRateA / kPoundRateA) * kConvertSouceValue),
-      kPoundName);
+      GetUnitDisplayText(kPoundName));
   EXPECT_EQ(result, expected_result);
 }
 
diff --git a/components/arc/session/arc_vm_client_adapter.cc b/components/arc/session/arc_vm_client_adapter.cc
index b35015a7..239e24c 100644
--- a/components/arc/session/arc_vm_client_adapter.cc
+++ b/components/arc/session/arc_vm_client_adapter.cc
@@ -580,9 +580,21 @@
   }
 
   void TrimVmMemory(TrimVmMemoryCallback callback) override {
-    // TODO(yusukes): Implement this.
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback), true, std::string()));
+    VLOG(2) << "Start trimming VM memory";
+    if (user_id_hash_.empty()) {
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE,
+          base::BindOnce(std::move(callback), /*success=*/false,
+                         /*failure_reason=*/"user_id_hash_ is not set"));
+      return;
+    }
+    vm_tools::concierge::ReclaimVmMemoryRequest request;
+    request.set_name(kArcVmName);
+    request.set_owner_id(user_id_hash_);
+    GetConciergeClient()->ReclaimVmMemory(
+        request,
+        base::BindOnce(&ArcVmClientAdapter::OnTrimVmMemory,
+                       weak_factory_.GetWeakPtr(), std::move(callback)));
   }
 
   // chromeos::ConciergeClient::Observer overrides:
@@ -970,6 +982,27 @@
     OnArcInstanceStopped();
   }
 
+  void OnTrimVmMemory(
+      TrimVmMemoryCallback callback,
+      base::Optional<vm_tools::concierge::ReclaimVmMemoryResponse> reply) {
+    bool success = false;
+    std::string failure_reason;
+
+    if (!reply.has_value()) {
+      failure_reason = "Empty response";
+    } else {
+      const vm_tools::concierge::ReclaimVmMemoryResponse& response =
+          reply.value();
+      success = response.success();
+      if (!success)
+        failure_reason = response.failure_reason();
+    }
+
+    VLOG(2) << "Finished trimming memory: success=" << success
+            << (failure_reason.empty() ? "" : " reason=") << failure_reason;
+    std::move(callback).Run(success, failure_reason);
+  }
+
   base::Optional<bool> is_dev_mode_;
   // True when the *host* is running on a VM.
   const bool is_host_on_vm_;
diff --git a/components/arc/session/arc_vm_client_adapter_unittest.cc b/components/arc/session/arc_vm_client_adapter_unittest.cc
index 47eb3ef6..08ad2cf 100644
--- a/components/arc/session/arc_vm_client_adapter_unittest.cc
+++ b/components/arc/session/arc_vm_client_adapter_unittest.cc
@@ -1732,15 +1732,86 @@
                      "androidboot.disable_download_provider=1"));
 }
 
-// TODO(yusukes): Improve the test once the real implementation is in.
-TEST_F(ArcVmClientAdapterTest, TrimVmMemory) {
+TEST_F(ArcVmClientAdapterTest, TrimVmMemory_Success) {
+  SetValidUserInfo();
+
+  vm_tools::concierge::ReclaimVmMemoryResponse response;
+  response.set_success(true);
+  GetTestConciergeClient()->set_reclaim_vm_memory_response(response);
+
   bool result = false;
+  std::string reason("non empty");
   adapter()->TrimVmMemory(base::BindLambdaForTesting(
-      [&result](bool success, const std::string& failure_reason) {
+      [&result, &reason](bool success, const std::string& failure_reason) {
         result = success;
+        reason = failure_reason;
       }));
   run_loop()->RunUntilIdle();
   EXPECT_TRUE(result);
+  EXPECT_TRUE(reason.empty());
+}
+
+TEST_F(ArcVmClientAdapterTest, TrimVmMemory_Failure) {
+  SetValidUserInfo();
+
+  constexpr const char kReason[] = "This is the reason";
+  vm_tools::concierge::ReclaimVmMemoryResponse response;
+  response.set_success(false);
+  response.set_failure_reason(kReason);
+  GetTestConciergeClient()->set_reclaim_vm_memory_response(response);
+
+  bool result = true;
+  std::string reason;
+  adapter()->TrimVmMemory(base::BindLambdaForTesting(
+      [&result, &reason](bool success, const std::string& failure_reason) {
+        result = success;
+        reason = failure_reason;
+      }));
+  run_loop()->RunUntilIdle();
+  EXPECT_FALSE(result);
+  EXPECT_EQ(kReason, reason);
+}
+
+TEST_F(ArcVmClientAdapterTest, TrimVmMemory_EmptyResponse) {
+  SetValidUserInfo();
+
+  // By default, the fake concierge client returns an empty response.
+  // This is to make sure TrimMemoty() can handle such a response.
+  bool result = true;
+  std::string reason;
+  adapter()->TrimVmMemory(base::BindLambdaForTesting(
+      [&result, &reason](bool success, const std::string& failure_reason) {
+        result = success;
+        reason = failure_reason;
+      }));
+  run_loop()->RunUntilIdle();
+  EXPECT_FALSE(result);
+  EXPECT_FALSE(reason.empty());
+}
+
+TEST_F(ArcVmClientAdapterTest, TrimVmMemory_EmptyUserIdHash) {
+  adapter()->SetUserInfo(cryptohome::Identification(), std::string(),
+                         std::string());
+
+  constexpr const char kReason[] = "This is the reason";
+  vm_tools::concierge::ReclaimVmMemoryResponse response;
+  response.set_success(false);
+  response.set_failure_reason(kReason);
+  GetTestConciergeClient()->set_reclaim_vm_memory_response(response);
+
+  bool result = true;
+  std::string reason;
+  adapter()->TrimVmMemory(base::BindLambdaForTesting(
+      [&result, &reason](bool success, const std::string& failure_reason) {
+        result = success;
+        reason = failure_reason;
+      }));
+  run_loop()->RunUntilIdle();
+  EXPECT_FALSE(result);
+  // When |user_id_hash_| is empty, the call will fail without talking to
+  // Concierge.
+  EXPECT_NE(kReason, reason);
+  EXPECT_FALSE(reason.empty());
 }
 
 struct DalvikMemoryProfileTestParam {
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index 3fc99c7b2..4ba1528 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -8,11 +8,14 @@
 #include <set>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/values.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
@@ -83,7 +86,7 @@
     bool offer_fido_opt_in = false;
     // Public Key Credential Request Options required for authentication.
     // https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrequestoptions
-    base::Optional<base::Value> fido_request_options = base::nullopt;
+    base::Optional<base::Value> fido_request_options;
     // Set of credit cards ids that are eligible for FIDO Authentication.
     std::set<std::string> fido_eligible_card_ids;
   };
@@ -100,7 +103,7 @@
     CreditCard card;
     std::string risk_data;
     CardUnmaskDelegate::UserProvidedUnmaskDetails user_response;
-    base::Optional<base::Value> fido_assertion_info = base::nullopt;
+    base::Optional<base::Value> fido_assertion_info;
   };
 
   // Information retrieved from an UnmaskRequest.
@@ -124,10 +127,10 @@
     std::string dcvv;
     // Challenge required for enrolling user into FIDO authentication for future
     // card unmasking.
-    base::Optional<base::Value> fido_creation_options = base::nullopt;
+    base::Optional<base::Value> fido_creation_options;
     // Challenge required for authorizing user for FIDO authentication for
     // future card unmasking.
-    base::Optional<base::Value> fido_request_options = base::nullopt;
+    base::Optional<base::Value> fido_request_options;
     // An opaque token used to logically chain consecutive UnmaskCard and
     // OptChange calls together.
     std::string card_authorization_token = std::string();
@@ -159,7 +162,7 @@
     Reason reason;
     // Signature required for enrolling user into FIDO authentication for future
     // card unmasking.
-    base::Optional<base::Value> fido_authenticator_response = base::nullopt;
+    base::Optional<base::Value> fido_authenticator_response;
     // An opaque token used to logically chain consecutive UnmaskCard and
     // OptChange calls together.
     std::string card_authorization_token = std::string();
@@ -176,10 +179,10 @@
     base::Optional<bool> user_is_opted_in;
     // Challenge required for enrolling user into FIDO authentication for future
     // card unmasking.
-    base::Optional<base::Value> fido_creation_options = base::nullopt;
+    base::Optional<base::Value> fido_creation_options;
     // Challenge required for authorizing user for FIDO authentication for
     // future card unmasking.
-    base::Optional<base::Value> fido_request_options = base::nullopt;
+    base::Optional<base::Value> fido_request_options;
   };
 
   // A collection of the information required to make a credit card upload
diff --git a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
index e425ff6f..bee4fef 100644
--- a/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
+++ b/components/chromeos_camera/jpeg_encode_accelerator_unittest.cc
@@ -662,7 +662,7 @@
   auto input_buffer = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
       test_image->visible_size, gfx::BufferFormat::YUV_420_BIPLANAR,
       gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE,
-      gpu::kNullSurfaceHandle);
+      gpu::kNullSurfaceHandle, nullptr);
   ASSERT_EQ(input_buffer->Map(), true);
 
   uint8_t* plane_buf[2] = {static_cast<uint8_t*>(input_buffer->memory(0)),
@@ -682,7 +682,8 @@
 
   auto output_buffer = gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
       gfx::Size(kJpegMaxSize, 1), gfx::BufferFormat::R_8,
-      gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE, gpu::kNullSurfaceHandle);
+      gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE, gpu::kNullSurfaceHandle,
+      nullptr);
   ASSERT_EQ(output_buffer->Map(), true);
   hw_out_frame_ = GetVideoFrameFromGpuMemoryBuffer(
       output_buffer.get(), test_image->visible_size, media::PIXEL_FORMAT_MJPEG);
diff --git a/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc b/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
index c0d010f..729b0e03f 100644
--- a/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
+++ b/components/chromeos_camera/mjpeg_decode_accelerator_unittest.cc
@@ -342,7 +342,8 @@
   }
   std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
       gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
-          coded_size, *gfx_format, kBufferUsage, gpu::kNullSurfaceHandle);
+          coded_size, *gfx_format, kBufferUsage, gpu::kNullSurfaceHandle,
+          nullptr);
   if (!gmb) {
     LOG(ERROR) << "Failed to create GpuMemoryBuffer";
     return nullptr;
@@ -436,7 +437,7 @@
   std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
       gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
           gfx::Size(base::checked_cast<int>(size), 1), gfx::BufferFormat::R_8,
-          kBufferUsage, gpu::kNullSurfaceHandle);
+          kBufferUsage, gpu::kNullSurfaceHandle, nullptr);
   if (!gmb) {
     LOG(ERROR) << "Failed to create GpuMemoryBuffer";
     return base::ScopedFD();
diff --git a/components/exo/fullscreen_shell_surface_unittest.cc b/components/exo/fullscreen_shell_surface_unittest.cc
index eaac185d..b5104d4 100644
--- a/components/exo/fullscreen_shell_surface_unittest.cc
+++ b/components/exo/fullscreen_shell_surface_unittest.cc
@@ -35,7 +35,7 @@
       ->context_factory()
       ->GetGpuMemoryBufferManager()
       ->CreateGpuMemoryBuffer(size, format, gfx::BufferUsage::GPU_READ,
-                              gpu::kNullSurfaceHandle);
+                              gpu::kNullSurfaceHandle, nullptr);
 }
 
 void DestroyFullscreenShellSurface(
diff --git a/components/exo/test/exo_test_helper.cc b/components/exo/test/exo_test_helper.cc
index df2f044a..d9a8506 100644
--- a/components/exo/test/exo_test_helper.cc
+++ b/components/exo/test/exo_test_helper.cc
@@ -133,7 +133,7 @@
       ->context_factory()
       ->GetGpuMemoryBufferManager()
       ->CreateGpuMemoryBuffer(size, format, gfx::BufferUsage::GPU_READ,
-                              gpu::kNullSurfaceHandle);
+                              gpu::kNullSurfaceHandle, nullptr);
 }
 
 std::unique_ptr<ClientControlledShellSurface>
diff --git a/components/exo/test/shell_surface_builder.cc b/components/exo/test/shell_surface_builder.cc
index 268cece..17261e5 100644
--- a/components/exo/test/shell_surface_builder.cc
+++ b/components/exo/test/shell_surface_builder.cc
@@ -36,7 +36,7 @@
             ->GetGpuMemoryBufferManager()
             ->CreateGpuMemoryBuffer(size, buffer_format,
                                     gfx::BufferUsage::GPU_READ,
-                                    gpu::kNullSurfaceHandle));
+                                    gpu::kNullSurfaceHandle, nullptr));
     auto surface = std::make_unique<exo::Surface>();
     surface->Attach(buffer.get());
     root_surface = surface.get();
@@ -51,7 +51,7 @@
             ->GetGpuMemoryBufferManager()
             ->CreateGpuMemoryBuffer(bounds.size(), gfx::BufferFormat::RGBA_8888,
                                     gfx::BufferUsage::GPU_READ,
-                                    gpu::kNullSurfaceHandle));
+                                    gpu::kNullSurfaceHandle, nullptr));
 
     auto surface = std::make_unique<exo::Surface>();
     surface->Attach(buffer.get());
diff --git a/components/offline_pages/core/model/offline_page_test_utils.h b/components/offline_pages/core/model/offline_page_test_utils.h
index 2cd45614..b1d7544 100644
--- a/components/offline_pages/core/model/offline_page_test_utils.h
+++ b/components/offline_pages/core/model/offline_page_test_utils.h
@@ -5,9 +5,7 @@
 #ifndef COMPONENTS_OFFLINE_PAGES_CORE_MODEL_OFFLINE_PAGE_TEST_UTILS_H_
 #define COMPONENTS_OFFLINE_PAGES_CORE_MODEL_OFFLINE_PAGE_TEST_UTILS_H_
 
-#include <stdint.h>
-
-#include <string>
+#include <stddef.h>
 
 namespace base {
 
diff --git a/components/os_crypt/os_crypt_unittest.cc b/components/os_crypt/os_crypt_unittest.cc
index a7d2b35..dd03eed 100644
--- a/components/os_crypt/os_crypt_unittest.cc
+++ b/components/os_crypt/os_crypt_unittest.cc
@@ -26,7 +26,6 @@
 #endif
 
 #if defined(OS_WIN)
-#include "base/strings/string_util.h"
 #include "components/prefs/testing_pref_service.h"
 #include "crypto/random.h"
 #endif
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java
index 84bd866..7344c201 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java
@@ -88,6 +88,30 @@
         return mSubFrameClips;
     }
 
+    /**
+     *
+     * @param checkDirectChildren Should direct children of this frame be considered.
+     * @return Whether this frame has any scrollable descendants.
+     */
+    boolean hasScrollableDescendants(boolean checkDirectChildren) {
+        if (mSubFrameClips == null || mSubFrames == null) {
+            return false;
+        }
+
+        for (int i = 0; i < mSubFrames.length; i++) {
+            PaintPreviewFrame subFrame = mSubFrames[i];
+            Rect subFrameClip = mSubFrameClips[i];
+            if (checkDirectChildren) {
+                if (subFrame.mContentWidth > subFrameClip.width()
+                        || subFrame.mContentHeight > subFrameClip.height()) {
+                    return true;
+                }
+            }
+            if (subFrame.hasScrollableDescendants(true)) return true;
+        }
+        return false;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj == null || getClass() != obj.getClass()) return false;
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
index 3a0d5ee..723cfcd 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.util.Size;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -80,6 +81,11 @@
          * @return Whether accessibility is currently enabled.
          */
         boolean isAccessibilityEnabled();
+
+        /**
+         * Called when accessibility for paint preview cannot be provided.
+         */
+        void onAccessibilityNotSupported();
     }
 
     private static PlayerCompositorDelegate.Factory sCompositorDelegateFactory =
@@ -87,6 +93,7 @@
 
     private Context mContext;
     private PlayerCompositorDelegate mDelegate;
+    private PaintPreviewFrame mRootFrameData;
     private PlayerFrameCoordinator mRootFrameCoordinator;
     private FrameLayout mHostView;
     private static final String sInitEvent = "paint_preview PlayerManager init";
@@ -94,9 +101,17 @@
     private PlayerGestureListener mPlayerGestureListener;
     private boolean mIgnoreInitialScrollOffset;
     private Listener mListener;
+    private long mNativeAxTree;
     private PlayerAccessibilityDelegate mAccessibilityDelegate;
     private WebContentsAccessibilityImpl mWebContentsAccessibility;
 
+    // The minimum ratio value of a sub-frame's area to its parent, for the sub-frame to be
+    // considered 'large'.
+    private static final float LARGE_SUB_FRAME_RATIO = .8f;
+    // The maximum scroll extent value that is allowed for a frame to be considered non-scrollable,
+    // in pixels.
+    private static final float SCROLLABLE_FRAME_LENIENCY_THRESHOLD = 50;
+
     /**
      * Creates a new {@link PlayerManager}.
      *
@@ -152,16 +167,17 @@
     private void onCompositorReady(UnguessableToken rootFrameGuid, UnguessableToken[] frameGuids,
             int[] frameContentSize, int[] scrollOffsets, int[] subFramesCount,
             UnguessableToken[] subFrameGuids, int[] subFrameClipRects, long nativeAxTree) {
-        PaintPreviewFrame rootFrame = buildFrameTreeHierarchy(rootFrameGuid, frameGuids,
-                frameContentSize, scrollOffsets, subFramesCount, subFrameGuids, subFrameClipRects,
+        mRootFrameData = buildFrameTreeHierarchy(rootFrameGuid, frameGuids, frameContentSize,
+                scrollOffsets, subFramesCount, subFrameGuids, subFrameClipRects,
                 mIgnoreInitialScrollOffset);
 
-        mRootFrameCoordinator = new PlayerFrameCoordinator(mContext, mDelegate, rootFrame.getGuid(),
-                rootFrame.getContentWidth(), rootFrame.getContentHeight(),
-                rootFrame.getInitialScrollX(), rootFrame.getInitialScrollY(), true,
-                mPlayerSwipeRefreshHandler, mPlayerGestureListener, mListener::onFirstPaint,
-                mListener::isAccessibilityEnabled);
-        buildSubFrameCoordinators(mRootFrameCoordinator, rootFrame);
+        mRootFrameCoordinator = new PlayerFrameCoordinator(mContext, mDelegate,
+                mRootFrameData.getGuid(), mRootFrameData.getContentWidth(),
+                mRootFrameData.getContentHeight(), mRootFrameData.getInitialScrollX(),
+                mRootFrameData.getInitialScrollY(), true, mPlayerSwipeRefreshHandler,
+                mPlayerGestureListener, mListener::onFirstPaint, mListener::isAccessibilityEnabled,
+                this::initializeAccessibility);
+        buildSubFrameCoordinators(mRootFrameCoordinator, mRootFrameData);
         mHostView.addView(mRootFrameCoordinator.getView(),
                 new FrameLayout.LayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
@@ -169,15 +185,115 @@
             mHostView.addView(mPlayerSwipeRefreshHandler.getView());
         }
 
-        if (nativeAxTree != 0) {
+        mNativeAxTree = nativeAxTree;
+        TraceEvent.finishAsync(sInitEvent, hashCode());
+        mListener.onViewReady();
+    }
+
+    /**
+     * Attempts to initialize accessibility support for the player. The conditional logic exists
+     * because of the lack of accessibility support for paint previews with multiple scrollable
+     * sub-frames. The following cases can happen for a given paint preview:
+     * - If the root frame has no scrollable sub-frames, accessibility support will be added to it.
+     * - If the root frame has any scrollable sub-frames that are not its direct children, we can't
+     * add accessibility support.
+     * - If the root frame is scrollable and has any direct or indirect scrollable sub-frames,
+     * we can't have accessibility support.
+     * - If the root frame is not scrollable and has one large direct scrollable sub-frame (which
+     * is the case for AMP), it adds accessibility support for that sub-frame.
+     * - In any other case, we can't add accessibility support.
+     */
+    private void initializeAccessibility() {
+        if (mNativeAxTree == 0) {
+            mListener.onAccessibilityNotSupported();
+            return;
+        }
+
+        if (mRootFrameData.hasScrollableDescendants(false)) {
+            // If there are any scrollable sub-frames that are not direct children of root frame,
+            // we can't add accessibility support regardless of root frame's scrollability.
+            mListener.onAccessibilityNotSupported();
+            return;
+        }
+
+        if (!mRootFrameData.hasScrollableDescendants(true)) {
+            // In the absence of scrollable sub-frames, we can add accessibility support to the root
+            // frame.
             mAccessibilityDelegate =
-                    new PlayerAccessibilityDelegate(mRootFrameCoordinator, nativeAxTree);
+                    new PlayerAccessibilityDelegate(mRootFrameCoordinator, mNativeAxTree, null);
             mWebContentsAccessibility =
                     WebContentsAccessibilityImpl.fromDelegate(mAccessibilityDelegate);
             mRootFrameCoordinator.getView().setWebContentsAccessibility(mWebContentsAccessibility);
+            return;
         }
-        TraceEvent.finishAsync(sInitEvent, hashCode());
-        mListener.onViewReady();
+
+        float mainFrameScale = mRootFrameCoordinator.getViewportForAccessibility().getScale();
+        int mainFrameViewportHeight =
+                mRootFrameCoordinator.getViewportForAccessibility().getHeight();
+        boolean isMainFrameScrollable =
+                (mainFrameScale * mRootFrameData.getContentHeight()) - mainFrameViewportHeight
+                < SCROLLABLE_FRAME_LENIENCY_THRESHOLD;
+        if (isMainFrameScrollable) {
+            // We cannot have accessibility support if we have scrollable sub-frames as well as a
+            // scrollable main frame.
+            mListener.onAccessibilityNotSupported();
+            return;
+        }
+
+        // If the main frame is not scrollable and we have exactly 1 large scrollable sub-frame
+        // (which is the case in AMPs), we can add accessibility support.
+        int scrollableSubFrameIndex = indexOfLargeScrollableSubFrame();
+        if (scrollableSubFrameIndex == -1) {
+            // There were either more than 1 scrollable sub-frames, or the scrollable sub-frame
+            // was not large enough.
+            mListener.onAccessibilityNotSupported();
+            return;
+        }
+
+        PlayerFrameCoordinator scrollableSubFrame =
+                mRootFrameCoordinator.getSubFrameForAccessibility(scrollableSubFrameIndex);
+        if (scrollableSubFrame == null) {
+            mListener.onAccessibilityNotSupported();
+            return;
+        }
+
+        Size subFrameOffset =
+                new Size(mRootFrameData.getSubFrameClips()[scrollableSubFrameIndex].left,
+                        mRootFrameData.getSubFrameClips()[scrollableSubFrameIndex].top);
+        mAccessibilityDelegate =
+                new PlayerAccessibilityDelegate(scrollableSubFrame, mNativeAxTree, subFrameOffset);
+        mWebContentsAccessibility =
+                WebContentsAccessibilityImpl.fromDelegate(mAccessibilityDelegate);
+        scrollableSubFrame.getView().setWebContentsAccessibility(mWebContentsAccessibility);
+    }
+
+    /**
+     * Searches for a large scrollable sub-frame. Only returns a valid index if there is only one
+     * scrollable direct sub-frame, and that sub-frame is sufficiently large (80% of main frame).
+     */
+    private int indexOfLargeScrollableSubFrame() {
+        Rect mainFrameViewPort = mRootFrameCoordinator.getViewportForAccessibility().asRect();
+        int scrollableSubFrameIndex = -1;
+        boolean hasLargeScrollableSubFrame = false;
+        for (int i = 0; i < mRootFrameData.getSubFrames().length; i++) {
+            PaintPreviewFrame subFrame = mRootFrameData.getSubFrames()[i];
+            Rect subFrameClip = mRootFrameData.getSubFrameClips()[i];
+            if (subFrame.getContentWidth() > subFrameClip.width()
+                    || subFrame.getContentHeight() > subFrameClip.width()) {
+                if (scrollableSubFrameIndex != -1) {
+                    // This is the second scrollable sub-frame. We can't have accessibility support.
+                    scrollableSubFrameIndex = -1;
+                    break;
+                }
+                scrollableSubFrameIndex = i;
+                float subFrameArea = subFrameClip.width() * subFrameClip.height();
+                float mainFrameArea = mainFrameViewPort.width() * mainFrameViewPort.height();
+                if (subFrameArea / mainFrameArea > LARGE_SUB_FRAME_RATIO) {
+                    hasLargeScrollableSubFrame = true;
+                }
+            }
+        }
+        return hasLargeScrollableSubFrame ? scrollableSubFrameIndex : -1;
     }
 
     /**
@@ -238,7 +354,7 @@
                     new PlayerFrameCoordinator(mContext, mDelegate, childFrame.getGuid(),
                             childFrame.getContentWidth(), childFrame.getContentHeight(),
                             childFrame.getInitialScrollX(), childFrame.getInitialScrollY(), false,
-                            null, mPlayerGestureListener, null, null);
+                            null, mPlayerGestureListener, null, null, null);
             buildSubFrameCoordinators(childCoordinator, childFrame);
             frameCoordinator.addSubFrame(childCoordinator, frame.getSubFrameClips()[i]);
         }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/accessibility/PlayerAccessibilityDelegate.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/accessibility/PlayerAccessibilityDelegate.java
index a5ee1c75..0456f6e 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/accessibility/PlayerAccessibilityDelegate.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/accessibility/PlayerAccessibilityDelegate.java
@@ -24,12 +24,13 @@
     private final long mNativeAxTree;
     private final PlayerAccessibilityCoordinatesImpl mPlayerAccessibilityCoordinates;
 
-    public PlayerAccessibilityDelegate(PlayerFrameCoordinator coordinator, long nativeAxTree) {
+    public PlayerAccessibilityDelegate(
+            PlayerFrameCoordinator coordinator, long nativeAxTree, Size constantOffset) {
         mRootCoordinator = coordinator;
         mNativeAxTree = nativeAxTree;
         mPlayerAccessibilityCoordinates = new PlayerAccessibilityCoordinatesImpl(
                 mRootCoordinator.getViewportForAccessibility(),
-                mRootCoordinator.getContentSizeForAccessibility());
+                mRootCoordinator.getContentSizeForAccessibility(), constantOffset);
     }
 
     @Override
@@ -92,10 +93,13 @@
     static class PlayerAccessibilityCoordinatesImpl implements AccessibilityCoordinates {
         private final PlayerFrameViewport mViewport;
         private final Size mContentSize;
+        private final Size mConstantOffset;
 
-        PlayerAccessibilityCoordinatesImpl(PlayerFrameViewport viewport, Size contentSize) {
+        PlayerAccessibilityCoordinatesImpl(
+                PlayerFrameViewport viewport, Size contentSize, Size constantOffset) {
             mViewport = viewport;
             mContentSize = contentSize;
+            mConstantOffset = constantOffset;
         }
 
         @Override
@@ -137,13 +141,13 @@
         @Override
         public float getScrollX() {
             Rect rect = mViewport.asRect();
-            return rect.left;
+            return rect.left + (mConstantOffset == null ? 0 : mConstantOffset.getWidth());
         }
 
         @Override
         public float getScrollY() {
             Rect rect = mViewport.asRect();
-            return rect.top;
+            return rect.top + (mConstantOffset == null ? 0 : mConstantOffset.getHeight());
         }
     }
 }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
index 412c5efb..ca6b890 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinator.java
@@ -44,13 +44,15 @@
             int initialScrollY, boolean canDetectZoom,
             @Nullable OverscrollHandler overscrollHandler, PlayerGestureListener gestureHandler,
             @Nullable Runnable firstPaintListener,
-            @Nullable Supplier<Boolean> isAccessibilityEnabled) {
+            @Nullable Supplier<Boolean> isAccessibilityEnabled,
+            @Nullable Runnable initialViewportSizeAvailable) {
         PropertyModel model = new PropertyModel.Builder(PlayerFrameProperties.ALL_KEYS).build();
         OverScroller scroller = new OverScroller(context);
         scroller.setFriction(ViewConfiguration.getScrollFriction() / 2);
 
         mMediator = new PlayerFrameMediator(model, compositorDelegate, gestureHandler, frameGuid,
-                new Size(contentWidth, contentHeight), initialScrollX, initialScrollY);
+                new Size(contentWidth, contentHeight), initialScrollX, initialScrollY,
+                initialViewportSizeAvailable);
 
         if (canDetectZoom) {
             mScaleController =
@@ -108,6 +110,12 @@
         return mMediator.getViewport();
     }
 
+    public PlayerFrameCoordinator getSubFrameForAccessibility(int index) {
+        if (index > mSubFrames.size()) return null;
+
+        return mSubFrames.get(index);
+    }
+
     public Size getContentSizeForAccessibility() {
         return mMediator.getContentSize();
     }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
index 5361bd90..18972745 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediator.java
@@ -72,10 +72,11 @@
     private final PlayerFrameBitmapStateController mBitmapStateController;
 
     private PlayerGestureListener mGestureListener;
+    private Runnable mInitialViewportSizeAvailable;
 
     PlayerFrameMediator(PropertyModel model, PlayerCompositorDelegate compositorDelegate,
             PlayerGestureListener gestureListener, UnguessableToken frameGuid, Size contentSize,
-            int initialScrollX, int initialScrollY) {
+            int initialScrollX, int initialScrollY, Runnable initialViewportSizeAvailable) {
         mBitmapScaleMatrix = new Matrix();
         mModel = model;
         mModel.set(PlayerFrameProperties.SCALE_MATRIX, mBitmapScaleMatrix);
@@ -93,6 +94,7 @@
                 mGuid, mViewport, mContentSize, mCompositorDelegate, this, taskRunner);
         mViewport.offset(initialScrollX, initialScrollY);
         mViewport.setScale(0f);
+        mInitialViewportSizeAvailable = initialViewportSizeAvailable;
     }
 
     void destroy() {
@@ -164,6 +166,11 @@
         final float scaleFactor = mViewport.getScale();
         updateViewportSize(
                 width, height, (scaleFactor == 0f) ? getInitialScaleFactor() : scaleFactor);
+
+        if (mInitialViewportSizeAvailable != null) {
+            mInitialViewportSizeAvailable.run();
+            mInitialViewportSizeAvailable = null;
+        }
     }
 
     @Override
diff --git a/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java b/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
index 3d372e64a..8b753b2 100644
--- a/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
+++ b/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
@@ -217,6 +217,9 @@
                         public boolean isAccessibilityEnabled() {
                             return false;
                         }
+
+                        @Override
+                        public void onAccessibilityNotSupported() {}
                     }, 0xffffffff, false);
             mPlayerManager.setCompressOnClose(false);
         });
@@ -427,6 +430,9 @@
                         public boolean isAccessibilityEnabled() {
                             return false;
                         }
+
+                        @Override
+                        public void onAccessibilityNotSupported() {}
                     }, 0xffffffff, false);
             mPlayerManager.setCompressOnClose(false);
             getActivity().setContentView(mPlayerManager.getView());
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java
index b798019..702e336e 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameCoordinatorTest.java
@@ -30,11 +30,11 @@
         PlayerFrameCoordinator rootCoordinator = new PlayerFrameCoordinator(
                 RuntimeEnvironment.systemContext, Mockito.mock(PlayerCompositorDelegate.class),
                 Mockito.mock(UnguessableToken.class), 100, 2000, 0, 0, true, null,
-                Mockito.mock(PlayerGestureListener.class), null, null);
+                Mockito.mock(PlayerGestureListener.class), null, null, null);
         PlayerFrameCoordinator childCoordinator = new PlayerFrameCoordinator(
                 RuntimeEnvironment.systemContext, Mockito.mock(PlayerCompositorDelegate.class),
                 Mockito.mock(UnguessableToken.class), 100, 200, 0, 0, true, null,
-                Mockito.mock(PlayerGestureListener.class), null, null);
+                Mockito.mock(PlayerGestureListener.class), null, null, null);
         rootCoordinator.addSubFrame(childCoordinator, new Rect(10, 20, 35, 40));
 
         rootCoordinator.getMediator().setLayoutDimensions(100, 200);
diff --git a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
index 200d694..2b5e670 100644
--- a/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
+++ b/components/paint_preview/player/android/junit/src/org/chromium/components/paintpreview/player/frame/PlayerFrameMediatorTest.java
@@ -237,8 +237,8 @@
         mScroller = new OverScroller(ContextUtils.getApplicationContext());
         mGestureListener = new PlayerGestureListener(null, () -> mHasUserInteraction = true, null);
         Size contentSize = new Size(CONTENT_WIDTH, CONTENT_HEIGHT);
-        mMediator = new PlayerFrameMediator(mModel, mCompositorDelegate, mGestureListener,
-                mFrameGuid, contentSize, 0, 0);
+        mMediator = new PlayerFrameMediator(
+                mModel, mCompositorDelegate, mGestureListener, mFrameGuid, contentSize, 0, 0, null);
         mScaleController =
                 new PlayerFrameScaleController(mModel.get(PlayerFrameProperties.SCALE_MATRIX),
                         mMediator, null, mGestureListener::onScale);
diff --git a/components/permissions/permission_context_base.cc b/components/permissions/permission_context_base.cc
index 8a762896..2d8b6a5 100644
--- a/components/permissions/permission_context_base.cc
+++ b/components/permissions/permission_context_base.cc
@@ -109,6 +109,7 @@
 }
 
 PermissionContextBase::~PermissionContextBase() {
+  DCHECK(permission_observers_.empty());
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 }
 
@@ -423,6 +424,39 @@
   return browser_context_;
 }
 
+void PermissionContextBase::OnContentSettingChanged(
+    const ContentSettingsPattern& primary_pattern,
+    const ContentSettingsPattern& secondary_pattern,
+    ContentSettingsType content_type) {
+  if (content_type != content_settings_type_)
+    return;
+
+  for (permissions::Observer& obs : permission_observers_)
+    obs.OnPermissionChanged(primary_pattern, secondary_pattern, content_type);
+}
+
+void PermissionContextBase::AddObserver(
+    permissions::Observer* permission_observer) {
+  if (permission_observers_.empty() &&
+      !content_setting_observer_registered_by_subclass_) {
+    PermissionsClient::Get()
+        ->GetSettingsMap(browser_context_)
+        ->AddObserver(this);
+  }
+  permission_observers_.AddObserver(permission_observer);
+}
+
+void PermissionContextBase::RemoveObserver(
+    permissions::Observer* permission_observer) {
+  permission_observers_.RemoveObserver(permission_observer);
+  if (permission_observers_.empty() &&
+      !content_setting_observer_registered_by_subclass_) {
+    PermissionsClient::Get()
+        ->GetSettingsMap(browser_context_)
+        ->RemoveObserver(this);
+  }
+}
+
 void PermissionContextBase::NotifyPermissionSet(
     const PermissionRequestID& id,
     const GURL& requesting_origin,
diff --git a/components/permissions/permission_context_base.h b/components/permissions/permission_context_base.h
index f53b712..d310912 100644
--- a/components/permissions/permission_context_base.h
+++ b/components/permissions/permission_context_base.h
@@ -10,7 +10,9 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
 #include "build/build_config.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -32,6 +34,14 @@
 
 namespace permissions {
 
+class Observer : public base::CheckedObserver {
+ public:
+  virtual void OnPermissionChanged(
+      const ContentSettingsPattern& primary_pattern,
+      const ContentSettingsPattern& secondary_pattern,
+      ContentSettingsType content_type) = 0;
+};
+
 using BrowserPermissionCallback = base::OnceCallback<void(ContentSetting)>;
 
 // This base class contains common operations for granting permissions.
@@ -56,7 +66,8 @@
 // See midi_permission_context.h/cc or push_permission_context.cc/h for some
 // examples.
 
-class PermissionContextBase : public KeyedService {
+class PermissionContextBase : public KeyedService,
+                              public content_settings::Observer {
  public:
   PermissionContextBase(
       content::BrowserContext* browser_context,
@@ -111,6 +122,9 @@
   // camera and microphone, and for testing.
   bool IsPermissionKillSwitchOn() const;
 
+  void AddObserver(permissions::Observer* permission_observer);
+  void RemoveObserver(permissions::Observer* permission_observer);
+
  protected:
   virtual ContentSetting GetPermissionStatusInternal(
       content::RenderFrameHost* render_frame_host,
@@ -164,10 +178,21 @@
                                           const GURL& embedding_origin,
                                           ContentSetting content_setting);
 
+  // content_settings::Observer:
+  void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+                               const ContentSettingsPattern& secondary_pattern,
+                               ContentSettingsType content_type) override;
+
   ContentSettingsType content_settings_type() const {
     return content_settings_type_;
   }
 
+  base::ObserverList<permissions::Observer> permission_observers_;
+
+  // Set by subclasses to inform the base class that they will handle adding
+  // and removing themselves as observers to the HostContentSettingsMap.
+  bool content_setting_observer_registered_by_subclass_ = false;
+
  private:
   friend class PermissionContextBaseTests;
 
diff --git a/components/permissions/permission_manager.cc b/components/permissions/permission_manager.cc
index 6b418ae..df89981f 100644
--- a/components/permissions/permission_manager.cc
+++ b/components/permissions/permission_manager.cc
@@ -276,10 +276,15 @@
   is_shutting_down_ = true;
 
   if (!subscriptions_.IsEmpty()) {
-    PermissionsClient::Get()
-        ->GetSettingsMap(browser_context_)
-        ->RemoveObserver(this);
     subscriptions_.Clear();
+    for (const auto& type_to_count : subscription_type_counts_) {
+      if (type_to_count.second > 0) {
+        PermissionContextBase* context =
+            GetPermissionContext(type_to_count.first);
+        context->RemoveObserver(this);
+      }
+    }
+    subscription_type_counts_.clear();
   }
 }
 
@@ -545,12 +550,16 @@
   if (is_shutting_down_)
     return SubscriptionId();
 
-  if (subscriptions_.IsEmpty())
-    PermissionsClient::Get()
-        ->GetSettingsMap(browser_context_)
-        ->AddObserver(this);
-
   ContentSettingsType content_type = PermissionTypeToContentSetting(permission);
+  auto type_count = subscription_type_counts_.find(content_type);
+  if (type_count != subscription_type_counts_.end()) {
+    type_count->second++;
+  } else {
+    PermissionContextBase* context = GetPermissionContext(content_type);
+    context->AddObserver(this);
+    subscription_type_counts_.emplace(content_type, 1);
+  }
+
   auto subscription = std::make_unique<Subscription>();
 
   // The RFH may be null if the request is for a worker.
@@ -591,14 +600,19 @@
   if (is_shutting_down_)
     return;
 
-  if (subscriptions_.Lookup(subscription_id)) {
-    subscriptions_.Remove(subscription_id);
-  }
+  Subscription* subscription = subscriptions_.Lookup(subscription_id);
+  if (!subscription)
+    return;
 
-  if (subscriptions_.IsEmpty()) {
-    PermissionsClient::Get()
-        ->GetSettingsMap(browser_context_)
-        ->RemoveObserver(this);
+  ContentSettingsType type = subscription->permission;
+  subscriptions_.Remove(subscription_id);
+  auto type_count = subscription_type_counts_.find(type);
+  CHECK(type_count != subscription_type_counts_.end());
+  CHECK_GT(type_count->second, size_t(0));
+  type_count->second--;
+  if (type_count->second == 0) {
+    PermissionContextBase* context = GetPermissionContext(type);
+    context->RemoveObserver(this);
   }
 }
 
@@ -608,7 +622,7 @@
   return GetPermissionContext(permission)->IsPermissionKillSwitchOn();
 }
 
-void PermissionManager::OnContentSettingChanged(
+void PermissionManager::OnPermissionChanged(
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
     ContentSettingsType content_type) {
diff --git a/components/permissions/permission_manager.h b/components/permissions/permission_manager.h
index 6b107641..398cfe91 100644
--- a/components/permissions/permission_manager.h
+++ b/components/permissions/permission_manager.h
@@ -8,11 +8,13 @@
 #include <unordered_map>
 
 #include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/id_map.h"
 #include "base/macros.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/permissions/permission_context_base.h"
 #include "components/permissions/permission_request_id.h"
 #include "components/permissions/permission_util.h"
 #include "content/public/browser/permission_controller_delegate.h"
@@ -29,7 +31,7 @@
 
 class PermissionManager : public KeyedService,
                           public content::PermissionControllerDelegate,
-                          public content_settings::Observer {
+                          public permissions::Observer {
  public:
   using PermissionContextMap =
       std::unordered_map<ContentSettingsType,
@@ -164,6 +166,7 @@
   struct Subscription;
   using SubscriptionsMap =
       base::IDMap<std::unique_ptr<Subscription>, SubscriptionId>;
+  using SubscriptionTypeCounts = base::flat_map<ContentSettingsType, size_t>;
 
   PermissionContextBase* GetPermissionContext(ContentSettingsType type);
 
@@ -178,10 +181,10 @@
       int permission_id,
       ContentSetting status);
 
-  // content_settings::Observer implementation.
-  void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
-                               const ContentSettingsPattern& secondary_pattern,
-                               ContentSettingsType content_type) override;
+  // permissions::Observer:
+  void OnPermissionChanged(const ContentSettingsPattern& primary_pattern,
+                           const ContentSettingsPattern& secondary_pattern,
+                           ContentSettingsType content_type) override;
 
   PermissionResult GetPermissionStatusHelper(
       ContentSettingsType permission,
@@ -201,6 +204,13 @@
   SubscriptionsMap subscriptions_;
   SubscriptionId::Generator subscription_id_generator_;
 
+  // Tracks the number of Subscriptions in |subscriptions_| which have a
+  // certain ContentSettingsType. An entry for a given ContentSettingsType key
+  // is added on first use and never removed. This is done to utilize the
+  // flat_map's efficiency in accessing/editing items and minimize the use of
+  // the unefficient addition/removal of items.
+  SubscriptionTypeCounts subscription_type_counts_;
+
   PermissionContextMap permission_contexts_;
   using ContentSettingsTypeOverrides =
       base::flat_map<ContentSettingsType, ContentSetting>;
diff --git a/components/permissions/permission_manager_unittest.cc b/components/permissions/permission_manager_unittest.cc
index efa4abef..1ea13a2 100644
--- a/components/permissions/permission_manager_unittest.cc
+++ b/components/permissions/permission_manager_unittest.cc
@@ -127,15 +127,13 @@
     if (!quit_closure_.is_null())
       std::move(quit_closure_).Run();
     callback_called_ = true;
+    callback_count_++;
     callback_result_ = permission;
   }
 
  protected:
   PermissionManagerTest()
-      : url_("https://example.com"),
-        other_url_("https://foo.com"),
-        callback_called_(false),
-        callback_result_(PermissionStatus::ASK) {}
+      : url_("https://example.com"), other_url_("https://foo.com") {}
 
   PermissionManager* GetPermissionControllerDelegate() {
     return static_cast<PermissionManager*>(
@@ -184,10 +182,13 @@
 
   bool callback_called() const { return callback_called_; }
 
+  int callback_count() const { return callback_count_; }
+
   PermissionStatus callback_result() const { return callback_result_; }
 
   void Reset() {
     callback_called_ = false;
+    callback_count_ = 0;
     callback_result_ = PermissionStatus::ASK;
   }
 
@@ -259,8 +260,9 @@
 
   const GURL url_;
   const GURL other_url_;
-  bool callback_called_;
-  PermissionStatus callback_result_;
+  bool callback_called_ = false;
+  int callback_count_ = 0;
+  PermissionStatus callback_result_ = PermissionStatus::ASK;
   base::OnceClosure quit_closure_;
   std::unique_ptr<content::TestBrowserContext> browser_context_;
   TestPermissionsClient client_;
@@ -416,6 +418,28 @@
   EXPECT_FALSE(callback_called());
 }
 
+TEST_F(PermissionManagerTest,
+       ChangeAfterUnsubscribeOnlyNotifiesActiveSubscribers) {
+  content::PermissionControllerDelegate::SubscriptionId subscription_id =
+      GetPermissionControllerDelegate()->SubscribePermissionStatusChange(
+          PermissionType::GEOLOCATION, main_rfh(), url(),
+          base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
+                              base::Unretained(this)));
+
+  GetPermissionControllerDelegate()->SubscribePermissionStatusChange(
+      PermissionType::GEOLOCATION, main_rfh(), url(),
+      base::BindRepeating(&PermissionManagerTest::OnPermissionChange,
+                          base::Unretained(this)));
+
+  GetPermissionControllerDelegate()->UnsubscribePermissionStatusChange(
+      subscription_id);
+
+  GetHostContentSettingsMap()->SetContentSettingDefaultScope(
+      url(), url(), ContentSettingsType::GEOLOCATION, CONTENT_SETTING_ALLOW);
+
+  EXPECT_EQ(callback_count(), 1);
+}
+
 TEST_F(PermissionManagerTest, DifferentPrimaryUrlDoesNotNotify) {
   content::PermissionControllerDelegate::SubscriptionId subscription_id =
       GetPermissionControllerDelegate()->SubscribePermissionStatusChange(
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 9198afc..13894415 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -17306,11 +17306,11 @@
       'items': [
         {
           'value': True,
-          'caption': 'Enables PCIe tunneling for Thunderbolt/USB4 peripheral devices, limiting the device capabilities',
+          'caption': 'Enables PCIe tunneling for Thunderbolt/USB4 peripheral devices, peripheral devices will function at their full capabilities',
         },
         {
           'value': False,
-          'caption': 'Disables PCIe tunneling. Supported Thunderbolt/USB4 peripheral devices will function at their full capabilities',
+          'caption': 'Disables PCIe tunneling for Thunderbolt/USB4 peripheral devices, limiting the device capabilities',
         },
       ],
       'example_value': False,
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 154b624..e2c0ebd 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -470,23 +470,24 @@
 
 def _WritePolicyConstantHeader(policies, policy_atomic_groups, target_platform,
                                f, risk_tags):
-  f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n'
-          '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n'
-          '\n'
-          '#include <cstdint>\n'
-          '#include <string>\n'
-          '\n'
-          '#include "base/values.h"\n'
-          '#include "build/chromeos_buildflags.h"\n'
-          '#include "components/policy/core/common/policy_details.h"\n'
-          '#include "components/policy/core/common/policy_map.h"\n'
-          '#include "components/policy/proto/cloud_policy.pb.h"\n'
-          '\n'
-          'namespace policy {\n'
-          '\n'
-          'namespace internal {\n'
-          'struct SchemaData;\n'
-          '}\n\n')
+  f.write('''#ifndef COMPONENTS_POLICY_POLICY_CONSTANTS_H_
+#define COMPONENTS_POLICY_POLICY_CONSTANTS_H_
+
+#include <cstdint>
+#include <string>
+
+#include "build/chromeos_buildflags.h"
+#include "components/policy/core/common/policy_details.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/proto/cloud_policy.pb.h"
+
+namespace policy {
+
+namespace internal {
+struct SchemaData;
+}
+
+''')
 
   if target_platform == 'win':
     f.write('// The windows registry path where Chrome policy '
@@ -544,7 +545,7 @@
           % _ComputeTotalDevicePolicyExternalDataMaxSize(policies))
 
   f.write('\n}  // namespace policy\n\n'
-          '#endif  // CHROME_COMMON_POLICY_CONSTANTS_H_\n')
+          '#endif  // COMPONENTS_POLICY_POLICY_CONSTANTS_H_\n')
 
 
 def _WriteChromePolicyAccessHeader(f, protobuf_type):
@@ -1039,6 +1040,7 @@
 
 #include "base/check_op.h"
 #include "base/stl_util.h"  // base::size()
+#include "base/values.h"
 #include "build/branding_buildflags.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/core/common/schema_internal.h"
diff --git a/components/safe_browsing/core/db/v4_protocol_manager_util.h b/components/safe_browsing/core/db/v4_protocol_manager_util.h
index e2963304..72e0b7b 100644
--- a/components/safe_browsing/core/db/v4_protocol_manager_util.h
+++ b/components/safe_browsing/core/db/v4_protocol_manager_util.h
@@ -10,8 +10,8 @@
 
 #include <functional>
 #include <initializer_list>
+#include <iosfwd>
 #include <memory>
-#include <ostream>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
diff --git a/components/safe_browsing/core/db/v4_store.cc b/components/safe_browsing/core/db/v4_store.cc
index 6e65e0dd..398895f2 100644
--- a/components/safe_browsing/core/db/v4_store.cc
+++ b/components/safe_browsing/core/db/v4_store.cc
@@ -40,7 +40,8 @@
 const char kApplyUpdate[] = ".ApplyUpdate";
 const char kDecodeAdditions[] = ".DecodeAdditions";
 const char kDecodeRemovals[] = ".DecodeRemovals";
-const char kAdditionsHashesCount[] = ".AdditionsHashesCount";
+const char kAdditionsHashesCountPartialUpdate[] = ".AdditionsHashesCount";
+const char kAdditionsHashesCountFullUpdate[] = ".AdditionsHashesCount2";
 const char kRemovalsHashesCount[] = ".RemovalsHashesCount";
 const char kApplyUpdateDuration[] = ".ApplyUpdateDuration";
 const char kVerifyChecksumDuration[] = ".VerifyChecksumDuration";
@@ -61,7 +62,9 @@
 constexpr size_t kMaxStoreSizeBytes = 50 * 1000 * 1000;
 
 // The maximum size of additions hashes in a single update response.
-const int32_t ADDITIONS_HASHES_COUNT_MAX = 10000;
+const int32_t ADDITIONS_HASHES_COUNT_PARTIAL_UPDATE_MAX = 10000;
+const int32_t ADDITIONS_HASHES_COUNT_FULL_UPDATE_MAX = 5000000;
+
 // The maximum size of removals hashes in a single update response.
 const int32_t REMOVALS_HASHES_COUNT_MAX = 10000;
 
@@ -125,8 +128,15 @@
 void RecordAdditionsHashesCount(const std::string& base_metric,
                                 int32_t count,
                                 const base::FilePath& file_path) {
-  RecordCountWithAndWithoutSuffix(base_metric + kAdditionsHashesCount, count,
-                                  ADDITIONS_HASHES_COUNT_MAX, file_path);
+  if (base_metric == kProcessFullUpdate) {
+    RecordCountWithAndWithoutSuffix(
+        base_metric + kAdditionsHashesCountFullUpdate, count,
+        ADDITIONS_HASHES_COUNT_FULL_UPDATE_MAX, file_path);
+  } else {
+    RecordCountWithAndWithoutSuffix(
+        base_metric + kAdditionsHashesCountPartialUpdate, count,
+        ADDITIONS_HASHES_COUNT_PARTIAL_UPDATE_MAX, file_path);
+  }
 }
 
 void RecordRemovalsHashesCount(const std::string& base_metric,
diff --git a/components/services/storage/service_worker/service_worker_storage.cc b/components/services/storage/service_worker/service_worker_storage.cc
index a859dc56..1800b0af 100644
--- a/components/services/storage/service_worker/service_worker_storage.cc
+++ b/components/services/storage/service_worker/service_worker_storage.cc
@@ -22,7 +22,6 @@
 #include "base/task_runner_util.h"
 #include "base/trace_event/trace_event.h"
 #include "components/services/storage/public/cpp/constants.h"
-#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/service_worker/service_worker_disk_cache.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "net/base/completion_once_callback.h"
@@ -67,10 +66,10 @@
 
 ServiceWorkerStorage::DidDeleteRegistrationParams::DidDeleteRegistrationParams(
     int64_t registration_id,
-    GURL origin,
+    const StorageKey& key,
     DeleteRegistrationCallback callback)
     : registration_id(registration_id),
-      origin(origin),
+      key(key),
       callback(std::move(callback)) {}
 
 ServiceWorkerStorage::DidDeleteRegistrationParams::
@@ -108,13 +107,14 @@
   }
 
   std::vector<url::Origin> origins;
-  for (const auto& origin : registered_origins_)
-    origins.push_back(origin);
+  for (const auto& key : registered_keys_)
+    origins.push_back(key.origin());
   std::move(callback).Run(std::move(origins));
 }
 
 void ServiceWorkerStorage::FindRegistrationForClientUrl(
     const GURL& client_url,
+    const StorageKey& key,
     FindRegistrationDataCallback callback) {
   DCHECK(!client_url.has_ref());
   switch (state_) {
@@ -128,7 +128,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(base::BindOnce(
           &ServiceWorkerStorage::FindRegistrationForClientUrl,
-          weak_factory_.GetWeakPtr(), client_url, std::move(callback)));
+          weak_factory_.GetWeakPtr(), client_url, key, std::move(callback)));
       TRACE_EVENT_INSTANT1(
           "ServiceWorker",
           "ServiceWorkerStorage::FindRegistrationForClientUrl:LazyInitialize",
@@ -139,7 +139,7 @@
   }
 
   // Bypass database lookup when there is no stored registration.
-  if (!base::Contains(registered_origins_, url::Origin::Create(client_url))) {
+  if (!base::Contains(registered_keys_, key)) {
     std::move(callback).Run(
         /*data=*/nullptr, /*resources=*/nullptr,
         ServiceWorkerDatabase::Status::kErrorNotFound);
@@ -149,11 +149,12 @@
   database_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&FindForClientUrlInDB, database_.get(),
                                 base::ThreadTaskRunnerHandle::Get(), client_url,
-                                std::move(callback)));
+                                key, std::move(callback)));
 }
 
 void ServiceWorkerStorage::FindRegistrationForScope(
     const GURL& scope,
+    const StorageKey& key,
     FindRegistrationDataCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -167,14 +168,14 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(base::BindOnce(
           &ServiceWorkerStorage::FindRegistrationForScope,
-          weak_factory_.GetWeakPtr(), scope, std::move(callback)));
+          weak_factory_.GetWeakPtr(), scope, key, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
       break;
   }
 
   // Bypass database lookup when there is no stored registration.
-  if (!base::Contains(registered_origins_, url::Origin::Create(scope))) {
+  if (!base::Contains(registered_keys_, key)) {
     RunSoon(FROM_HERE,
             base::BindOnce(std::move(callback),
                            /*data=*/nullptr, /*resources=*/nullptr,
@@ -184,13 +185,13 @@
 
   database_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&FindForScopeInDB, database_.get(),
-                                base::ThreadTaskRunnerHandle::Get(), scope,
+                                base::ThreadTaskRunnerHandle::Get(), scope, key,
                                 std::move(callback)));
 }
 
 void ServiceWorkerStorage::FindRegistrationForId(
     int64_t registration_id,
-    const url::Origin& origin,
+    const StorageKey& key,
     FindRegistrationDataCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -203,7 +204,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(
           base::BindOnce(&ServiceWorkerStorage::FindRegistrationForId,
-                         weak_factory_.GetWeakPtr(), registration_id, origin,
+                         weak_factory_.GetWeakPtr(), registration_id, key,
                          std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
@@ -211,7 +212,7 @@
   }
 
   // Bypass database lookup when there is no stored registration.
-  if (!base::Contains(registered_origins_, origin)) {
+  if (!base::Contains(registered_keys_, key)) {
     std::move(callback).Run(
         /*data=*/nullptr, /*resources=*/nullptr,
         ServiceWorkerDatabase::Status::kErrorNotFound);
@@ -221,7 +222,7 @@
   database_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&FindForIdInDB, database_.get(),
                                 base::ThreadTaskRunnerHandle::Get(),
-                                registration_id, origin, std::move(callback)));
+                                registration_id, key, std::move(callback)));
 }
 
 void ServiceWorkerStorage::FindRegistrationForIdOnly(
@@ -250,8 +251,8 @@
                                 registration_id, std::move(callback)));
 }
 
-void ServiceWorkerStorage::GetRegistrationsForOrigin(
-    const url::Origin& origin,
+void ServiceWorkerStorage::GetRegistrationsForStorageKey(
+    const StorageKey& key,
     GetRegistrationsDataCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -264,9 +265,9 @@
     case STORAGE_STATE_INITIALIZING:
       // Fall-through.
     case STORAGE_STATE_UNINITIALIZED:
-      LazyInitialize(base::BindOnce(
-          &ServiceWorkerStorage::GetRegistrationsForOrigin,
-          weak_factory_.GetWeakPtr(), origin, std::move(callback)));
+      LazyInitialize(
+          base::BindOnce(&ServiceWorkerStorage::GetRegistrationsForStorageKey,
+                         weak_factory_.GetWeakPtr(), key, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
       break;
@@ -280,15 +281,15 @@
   base::PostTaskAndReplyWithResult(
       database_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ServiceWorkerDatabase::GetRegistrationsForStorageKey,
-                     base::Unretained(database_.get()), StorageKey(origin),
-                     registrations_ptr, resource_lists_ptr),
-      base::BindOnce(&ServiceWorkerStorage::DidGetRegistrationsForOrigin,
+                     base::Unretained(database_.get()), key, registrations_ptr,
+                     resource_lists_ptr),
+      base::BindOnce(&ServiceWorkerStorage::DidGetRegistrationsForStorageKey,
                      weak_factory_.GetWeakPtr(), std::move(callback),
                      std::move(registrations), std::move(resource_lists)));
 }
 
-void ServiceWorkerStorage::GetUsageForOrigin(
-    const url::Origin& origin,
+void ServiceWorkerStorage::GetUsageForStorageKey(
+    const StorageKey& key,
     GetUsageForOriginCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -300,9 +301,9 @@
     case STORAGE_STATE_INITIALIZING:
       // Fall-through.
     case STORAGE_STATE_UNINITIALIZED:
-      LazyInitialize(base::BindOnce(&ServiceWorkerStorage::GetUsageForOrigin,
-                                    weak_factory_.GetWeakPtr(), origin,
-                                    std::move(callback)));
+      LazyInitialize(
+          base::BindOnce(&ServiceWorkerStorage::GetUsageForStorageKey,
+                         weak_factory_.GetWeakPtr(), key, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
       break;
@@ -310,9 +311,9 @@
 
   database_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&ServiceWorkerStorage::GetUsageForOriginInDB,
-                     database_.get(), base::ThreadTaskRunnerHandle::Get(),
-                     origin, std::move(callback)));
+      base::BindOnce(&ServiceWorkerStorage::GetUsageForStorageKeyInDB,
+                     database_.get(), base::ThreadTaskRunnerHandle::Get(), key,
+                     std::move(callback)));
 }
 
 void ServiceWorkerStorage::GetAllRegistrations(
@@ -389,7 +390,7 @@
 
 void ServiceWorkerStorage::UpdateToActiveState(
     int64_t registration_id,
-    const GURL& origin,
+    const StorageKey& key,
     DatabaseStatusCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -400,7 +401,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(base::BindOnce(&ServiceWorkerStorage::UpdateToActiveState,
                                     weak_factory_.GetWeakPtr(), registration_id,
-                                    origin, std::move(callback)));
+                                    key, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
       break;
@@ -409,14 +410,13 @@
   base::PostTaskAndReplyWithResult(
       database_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ServiceWorkerDatabase::UpdateVersionToActive,
-                     base::Unretained(database_.get()), registration_id,
-                     StorageKey(url::Origin::Create(origin))),
+                     base::Unretained(database_.get()), registration_id, key),
       std::move(callback));
 }
 
 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
     int64_t registration_id,
-    const GURL& origin,
+    const StorageKey& key,
     base::Time last_update_check_time,
     DatabaseStatusCallback callback) {
   switch (state_) {
@@ -428,7 +428,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(
           base::BindOnce(&ServiceWorkerStorage::UpdateLastUpdateCheckTime,
-                         weak_factory_.GetWeakPtr(), registration_id, origin,
+                         weak_factory_.GetWeakPtr(), registration_id, key,
                          last_update_check_time, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
@@ -438,15 +438,14 @@
   base::PostTaskAndReplyWithResult(
       database_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ServiceWorkerDatabase::UpdateLastCheckTime,
-                     base::Unretained(database_.get()), registration_id,
-                     StorageKey(url::Origin::Create(origin)),
+                     base::Unretained(database_.get()), registration_id, key,
                      last_update_check_time),
       std::move(callback));
 }
 
 void ServiceWorkerStorage::UpdateNavigationPreloadEnabled(
     int64_t registration_id,
-    const GURL& origin,
+    const StorageKey& key,
     bool enable,
     DatabaseStatusCallback callback) {
   switch (state_) {
@@ -458,7 +457,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(
           base::BindOnce(&ServiceWorkerStorage::UpdateNavigationPreloadEnabled,
-                         weak_factory_.GetWeakPtr(), registration_id, origin,
+                         weak_factory_.GetWeakPtr(), registration_id, key,
                          enable, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
@@ -468,14 +467,14 @@
   base::PostTaskAndReplyWithResult(
       database_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ServiceWorkerDatabase::UpdateNavigationPreloadEnabled,
-                     base::Unretained(database_.get()), registration_id,
-                     StorageKey(url::Origin::Create(origin)), enable),
+                     base::Unretained(database_.get()), registration_id, key,
+                     enable),
       std::move(callback));
 }
 
 void ServiceWorkerStorage::UpdateNavigationPreloadHeader(
     int64_t registration_id,
-    const GURL& origin,
+    const StorageKey& key,
     const std::string& value,
     DatabaseStatusCallback callback) {
   switch (state_) {
@@ -487,7 +486,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(
           base::BindOnce(&ServiceWorkerStorage::UpdateNavigationPreloadHeader,
-                         weak_factory_.GetWeakPtr(), registration_id, origin,
+                         weak_factory_.GetWeakPtr(), registration_id, key,
                          value, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
@@ -497,14 +496,14 @@
   base::PostTaskAndReplyWithResult(
       database_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ServiceWorkerDatabase::UpdateNavigationPreloadHeader,
-                     base::Unretained(database_.get()), registration_id,
-                     StorageKey(url::Origin::Create(origin)), value),
+                     base::Unretained(database_.get()), registration_id, key,
+                     value),
       std::move(callback));
 }
 
 void ServiceWorkerStorage::DeleteRegistration(
     int64_t registration_id,
-    const GURL& origin,
+    const StorageKey& key,
     DeleteRegistrationCallback callback) {
   switch (state_) {
     case STORAGE_STATE_DISABLED:
@@ -520,7 +519,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(base::BindOnce(&ServiceWorkerStorage::DeleteRegistration,
                                     weak_factory_.GetWeakPtr(), registration_id,
-                                    origin, std::move(callback)));
+                                    key, std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
       break;
@@ -530,13 +529,13 @@
     DeleteStaleResources();
 
   auto params = std::make_unique<DidDeleteRegistrationParams>(
-      registration_id, origin, std::move(callback));
+      registration_id, key, std::move(callback));
 
   database_task_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(
           &DeleteRegistrationFromDB, database_.get(),
-          base::ThreadTaskRunnerHandle::Get(), registration_id, origin,
+          base::ThreadTaskRunnerHandle::Get(), registration_id, key,
           base::BindOnce(&ServiceWorkerStorage::DidDeleteRegistration,
                          weak_factory_.GetWeakPtr(), std::move(params))));
 }
@@ -704,7 +703,7 @@
 
 void ServiceWorkerStorage::StoreUserData(
     int64_t registration_id,
-    const url::Origin& origin,
+    const StorageKey& key,
     std::vector<mojom::ServiceWorkerUserDataPtr> user_data,
     DatabaseStatusCallback callback) {
   switch (state_) {
@@ -718,7 +717,7 @@
     case STORAGE_STATE_UNINITIALIZED:
       LazyInitialize(base::BindOnce(
           &ServiceWorkerStorage::StoreUserData, weak_factory_.GetWeakPtr(),
-          registration_id, origin, std::move(user_data), std::move(callback)));
+          registration_id, key, std::move(user_data), std::move(callback)));
       return;
     case STORAGE_STATE_INITIALIZED:
       break;
@@ -744,8 +743,8 @@
   base::PostTaskAndReplyWithResult(
       database_task_runner_.get(), FROM_HERE,
       base::BindOnce(&ServiceWorkerDatabase::WriteUserData,
-                     base::Unretained(database_.get()), registration_id,
-                     StorageKey(origin), std::move(user_data)),
+                     base::Unretained(database_.get()), registration_id, key,
+                     std::move(user_data)),
       std::move(callback));
 }
 
@@ -1199,11 +1198,11 @@
   }
 
   for (const auto& update : policy_updates) {
-    GURL url = update->origin.GetURL();
+    StorageKey key(update->origin);
     if (!update->purge_on_shutdown)
-      origins_to_purge_on_shutdown_.erase(url);
+      keys_to_purge_on_shutdown_.erase(key);
     else
-      origins_to_purge_on_shutdown_.insert(std::move(url));
+      keys_to_purge_on_shutdown_.insert(std::move(key));
   }
 
   std::move(callback).Run(ServiceWorkerDatabase::Status::kOk);
@@ -1305,10 +1304,10 @@
     next_registration_id_ = data->next_registration_id;
     next_version_id_ = data->next_version_id;
     next_resource_id_ = data->next_resource_id;
-    registered_origins_.swap(data->origins);
+    registered_keys_.swap(data->keys);
     state_ = STORAGE_STATE_INITIALIZED;
     base::UmaHistogramCounts1M("ServiceWorker.RegisteredOriginCount",
-                               registered_origins_.size());
+                               registered_keys_.size());
   } else {
     DVLOG(2) << "Failed to initialize: "
              << ServiceWorkerDatabase::StatusToString(status);
@@ -1320,7 +1319,7 @@
   pending_tasks_.clear();
 }
 
-void ServiceWorkerStorage::DidGetRegistrationsForOrigin(
+void ServiceWorkerStorage::DidGetRegistrationsForStorageKey(
     GetRegistrationsDataCallback callback,
     std::unique_ptr<RegistrationList> registration_data_list,
     std::unique_ptr<std::vector<ResourceList>> resource_lists,
@@ -1339,7 +1338,7 @@
 void ServiceWorkerStorage::DidStoreRegistrationData(
     StoreRegistrationDataCallback callback,
     uint64_t new_resources_total_size_bytes,
-    const GURL& origin,
+    const StorageKey& key,
     const ServiceWorkerDatabase::DeletedVersion& deleted_version,
     ServiceWorkerDatabase::Status status) {
   if (status != ServiceWorkerDatabase::Status::kOk) {
@@ -1348,7 +1347,7 @@
                             deleted_version.newly_purgeable_resources);
     return;
   }
-  registered_origins_.insert(url::Origin::Create(origin));
+  registered_keys_.insert(key);
 
   std::move(callback).Run(ServiceWorkerDatabase::Status::kOk,
                           deleted_version.version_id,
@@ -1370,7 +1369,7 @@
   }
 
   if (origin_state == OriginState::kDelete)
-    registered_origins_.erase(url::Origin::Create(params->origin));
+    registered_keys_.erase(params->key);
 
   std::move(params->callback)
       .Run(ServiceWorkerDatabase::Status::kOk, origin_state,
@@ -1523,7 +1522,7 @@
 void ServiceWorkerStorage::ClearSessionOnlyOrigins() {
   database_task_runner_->PostTask(
       FROM_HERE, base::BindOnce(&DeleteAllDataForOriginsFromDB, database_.get(),
-                                origins_to_purge_on_shutdown_));
+                                keys_to_purge_on_shutdown_));
 }
 
 void ServiceWorkerStorage::OnResourceReaderDisconnected(
@@ -1596,13 +1595,7 @@
     return;
   }
 
-  std::set<StorageKey> keys;
-  status = database->GetStorageKeysWithRegistrations(&keys);
-  // TODO(crbug.com/1199077) Remove adaptor once upstream code uses StorageKey.
-  std::transform(keys.begin(), keys.end(),
-                 std::inserter(data->origins, data->origins.begin()),
-                 [](const StorageKey& key) { return key.origin(); });
-
+  status = database->GetStorageKeysWithRegistrations(&data->keys);
   if (status != ServiceWorkerDatabase::Status::kOk) {
     original_task_runner->PostTask(
         FROM_HERE,
@@ -1618,12 +1611,10 @@
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     int64_t registration_id,
-    const GURL& origin,
+    const StorageKey& key,
     DeleteRegistrationInDBCallback callback) {
   DCHECK(database);
 
-  const StorageKey key(url::Origin::Create(origin));
-
   ServiceWorkerDatabase::DeletedVersion deleted_version;
   ServiceWorkerDatabase::Status status =
       database->DeleteRegistration(registration_id, key, &deleted_version);
@@ -1665,7 +1656,8 @@
       database->WriteRegistration(*registration, resources, &deleted_version);
   original_task_runner->PostTask(
       FROM_HERE,
-      base::BindOnce(std::move(callback), registration->script.GetOrigin(),
+      base::BindOnce(std::move(callback),
+                     StorageKey(url::Origin::Create(registration->script)),
                      deleted_version, status));
 }
 
@@ -1674,8 +1666,8 @@
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     const GURL& client_url,
+    const StorageKey& key,
     FindInDBCallback callback) {
-  StorageKey key(url::Origin::Create(client_url.GetOrigin()));
   RegistrationList registration_data_list;
   ServiceWorkerDatabase::Status status =
       database->GetRegistrationsForStorageKey(key, &registration_data_list,
@@ -1711,8 +1703,8 @@
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     const GURL& scope,
+    const StorageKey& key,
     FindInDBCallback callback) {
-  StorageKey key(url::Origin::Create(scope.GetOrigin()));
   RegistrationList registration_data_list;
   ServiceWorkerDatabase::Status status =
       database->GetRegistrationsForStorageKey(key, &registration_data_list,
@@ -1747,12 +1739,12 @@
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
     int64_t registration_id,
-    const url::Origin& origin,
+    const StorageKey& key,
     FindInDBCallback callback) {
   mojom::ServiceWorkerRegistrationDataPtr data;
   auto resources = std::make_unique<ResourceList>();
-  ServiceWorkerDatabase::Status status = database->ReadRegistration(
-      registration_id, StorageKey(origin), &data, resources.get());
+  ServiceWorkerDatabase::Status status =
+      database->ReadRegistration(registration_id, key, &data, resources.get());
   original_task_runner->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), std::move(data),
                                 std::move(resources), status));
@@ -1774,19 +1766,19 @@
                                   /*resources=*/nullptr, status));
     return;
   }
-  FindForIdInDB(database, original_task_runner, registration_id, key.origin(),
+  FindForIdInDB(database, original_task_runner, registration_id, key,
                 std::move(callback));
 }
 
 // static
-void ServiceWorkerStorage::GetUsageForOriginInDB(
+void ServiceWorkerStorage::GetUsageForStorageKeyInDB(
     ServiceWorkerDatabase* database,
     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
-    url::Origin origin,
+    const StorageKey& key,
     GetUsageForOriginCallback callback) {
   int64_t usage = 0;
   ServiceWorkerDatabase::Status status =
-      database->GetUsageForStorageKey(StorageKey(origin), usage);
+      database->GetUsageForStorageKey(key, usage);
   original_task_runner->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), status, usage));
 }
@@ -1860,16 +1852,10 @@
 
 void ServiceWorkerStorage::DeleteAllDataForOriginsFromDB(
     ServiceWorkerDatabase* database,
-    const std::set<GURL>& origins) {
+    const std::set<StorageKey>& keys) {
   DCHECK(database);
 
   std::vector<int64_t> newly_purgeable_resources;
-  // TODO(crbug.com/1199077) Remove adaptor once upstream code uses StorageKey.
-  std::set<StorageKey> keys;
-  std::transform(origins.begin(), origins.end(),
-                 std::inserter(keys, keys.begin()), [](const GURL& origin) {
-                   return StorageKey(url::Origin::Create(origin));
-                 });
   database->DeleteAllDataForStorageKeys(keys, &newly_purgeable_resources);
 }
 
diff --git a/components/services/storage/service_worker/service_worker_storage.h b/components/services/storage/service_worker/service_worker_storage.h
index 8cc0b70..7b389ab 100644
--- a/components/services/storage/service_worker/service_worker_storage.h
+++ b/components/services/storage/service_worker/service_worker_storage.h
@@ -21,6 +21,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/public/mojom/local_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/service_worker_storage_control.mojom.h"
 #include "components/services/storage/public/mojom/storage_policy_update.mojom.h"
@@ -54,9 +55,13 @@
 // restarted.
 class ServiceWorkerStorage {
  public:
+  // TODO(http://crbug.com/1199077) Update this once
+  // service_worker_storage_control.mojom is updated.
   using OriginState = mojom::ServiceWorkerStorageOriginState;
   using RegistrationList = std::vector<mojom::ServiceWorkerRegistrationDataPtr>;
   using ResourceList = std::vector<mojom::ServiceWorkerResourceRecordPtr>;
+  // TODO(http://crbug.com/1199077) Update this once
+  // service_worker_storage_control.mojom is updated.
   using GetRegisteredOriginsCallback =
       base::OnceCallback<void(const std::vector<url::Origin>& origins)>;
   using FindRegistrationDataCallback =
@@ -67,6 +72,8 @@
       ServiceWorkerDatabase::Status status,
       std::unique_ptr<RegistrationList> registrations,
       std::unique_ptr<std::vector<ResourceList>> resource_lists)>;
+  // TODO(http://crbug.com/1199077) Update this once
+  // service_worker_storage_control.mojom is updated.
   using GetUsageForOriginCallback =
       base::OnceCallback<void(ServiceWorkerDatabase::Status status,
                               int64_t usage)>;
@@ -104,6 +111,9 @@
       const base::FilePath& user_data_directory,
       scoped_refptr<base::SequencedTaskRunner> database_task_runner);
 
+  // TODO(http://crbug.com/1199077) Update return/callback value and rename when
+  // service_worker_storage_control.mojom is updated.
+  //
   // Returns all origins which have service worker registrations.
   void GetRegisteredOrigins(GetRegisteredOriginsCallback callback);
 
@@ -113,22 +123,24 @@
   // returns ServiceWorkerDatabase::Status::kErrorNotFound if no matching
   // registration is found.
   void FindRegistrationForClientUrl(const GURL& client_url,
+                                    const StorageKey& key,
                                     FindRegistrationDataCallback callback);
   void FindRegistrationForScope(const GURL& scope,
+                                const StorageKey& key,
                                 FindRegistrationDataCallback callback);
   void FindRegistrationForId(int64_t registration_id,
-                             const url::Origin& origin,
+                             const StorageKey& key,
                              FindRegistrationDataCallback callback);
   void FindRegistrationForIdOnly(int64_t registration_id,
                                  FindRegistrationDataCallback callback);
 
   // Returns all stored registrations for a given origin.
-  void GetRegistrationsForOrigin(const url::Origin& origin,
-                                 GetRegistrationsDataCallback callback);
+  void GetRegistrationsForStorageKey(const StorageKey& key,
+                                     GetRegistrationsDataCallback callback);
 
   // Reads the total resource size stored in the storage for a given origin.
-  void GetUsageForOrigin(const url::Origin& origin,
-                         GetUsageForOriginCallback callback);
+  void GetUsageForStorageKey(const StorageKey& key,
+                             GetUsageForOriginCallback callback);
 
   // Returns all stored registrations.
   void GetAllRegistrations(GetAllRegistrationsCallback callback);
@@ -141,31 +153,31 @@
 
   // Updates the state of the registration's stored version to active.
   void UpdateToActiveState(int64_t registration_id,
-                           const GURL& origin,
+                           const StorageKey& key,
                            DatabaseStatusCallback callback);
 
   // Updates the stored time to match the value of
   // registration->last_update_check().
   void UpdateLastUpdateCheckTime(int64_t registration_id,
-                                 const GURL& origin,
+                                 const StorageKey& key,
                                  base::Time last_update_check_time,
                                  DatabaseStatusCallback callback);
 
   // Updates the specified registration's navigation preload state in storage.
   // The caller is responsible for mutating the live registration's state.
   void UpdateNavigationPreloadEnabled(int64_t registration_id,
-                                      const GURL& origin,
+                                      const StorageKey& key,
                                       bool enable,
                                       DatabaseStatusCallback callback);
   void UpdateNavigationPreloadHeader(int64_t registration_id,
-                                     const GURL& origin,
+                                     const StorageKey& key,
                                      const std::string& value,
                                      DatabaseStatusCallback callback);
 
   // Deletes the registration specified by |registration_id|. This should be
   // called only from ServiceWorkerRegistry.
   void DeleteRegistration(int64_t registration_id,
-                          const GURL& origin,
+                          const StorageKey& key,
                           DeleteRegistrationCallback callback);
 
   // Removes traces of deleted data on disk.
@@ -215,7 +227,7 @@
 
   // Stored data is deleted when the associated registraton is deleted.
   void StoreUserData(int64_t registration_id,
-                     const url::Origin& origin,
+                     const StorageKey& key,
                      std::vector<mojom::ServiceWorkerUserDataPtr> user_data,
                      DatabaseStatusCallback callback);
   // Responds OK if all are successfully deleted or not found in the database.
@@ -304,7 +316,7 @@
     int64_t next_registration_id;
     int64_t next_version_id;
     int64_t next_resource_id;
-    std::set<url::Origin> origins;
+    std::set<StorageKey> keys;
 
     InitialData();
     ~InitialData();
@@ -313,11 +325,11 @@
   // Because there are too many params for base::Bind to wrap a closure around.
   struct DidDeleteRegistrationParams {
     int64_t registration_id;
-    GURL origin;
+    StorageKey key;
     DeleteRegistrationCallback callback;
 
     DidDeleteRegistrationParams(int64_t registration_id,
-                                GURL origin,
+                                const StorageKey& key,
                                 DeleteRegistrationCallback callback);
     ~DidDeleteRegistrationParams();
   };
@@ -326,7 +338,7 @@
       base::OnceCallback<void(std::unique_ptr<InitialData> data,
                               ServiceWorkerDatabase::Status status)>;
   using WriteRegistrationCallback = base::OnceCallback<void(
-      const GURL& origin,
+      const StorageKey& key,
       const ServiceWorkerDatabase::DeletedVersion& deleted_version_data,
       ServiceWorkerDatabase::Status status)>;
   using DeleteRegistrationInDBCallback = base::OnceCallback<void(
@@ -351,7 +363,7 @@
   void LazyInitialize(base::OnceClosure callback);
   void DidReadInitialData(std::unique_ptr<InitialData> data,
                           ServiceWorkerDatabase::Status status);
-  void DidGetRegistrationsForOrigin(
+  void DidGetRegistrationsForStorageKey(
       GetRegistrationsDataCallback callback,
       std::unique_ptr<RegistrationList> registrations,
       std::unique_ptr<std::vector<ResourceList>> resource_lists,
@@ -363,7 +375,7 @@
   void DidStoreRegistrationData(
       StoreRegistrationDataCallback callback,
       uint64_t new_resources_total_size_bytes,
-      const GURL& origin,
+      const StorageKey& key,
       const ServiceWorkerDatabase::DeletedVersion& deleted_version,
       ServiceWorkerDatabase::Status status);
   void DidDeleteRegistration(
@@ -417,7 +429,7 @@
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
       int64_t registration_id,
-      const GURL& origin,
+      const StorageKey& key,
       DeleteRegistrationInDBCallback callback);
   static void WriteRegistrationInDB(
       ServiceWorkerDatabase* database,
@@ -429,27 +441,29 @@
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
       const GURL& client_url,
+      const StorageKey& key,
       FindInDBCallback callback);
   static void FindForScopeInDB(
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
       const GURL& scope,
+      const StorageKey& key,
       FindInDBCallback callback);
   static void FindForIdInDB(
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
       int64_t registration_id,
-      const url::Origin& origin,
+      const StorageKey& key,
       FindInDBCallback callback);
   static void FindForIdOnlyInDB(
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
       int64_t registration_id,
       FindInDBCallback callback);
-  static void GetUsageForOriginInDB(
+  static void GetUsageForStorageKeyInDB(
       ServiceWorkerDatabase* database,
       scoped_refptr<base::SequencedTaskRunner> original_task_runner,
-      url::Origin origin,
+      const StorageKey& key,
       GetUsageForOriginCallback callback);
   static void GetUserDataInDB(
       ServiceWorkerDatabase* database,
@@ -480,7 +494,7 @@
       const std::string& key_prefix,
       GetUserDataForAllRegistrationsInDBCallback callback);
   static void DeleteAllDataForOriginsFromDB(ServiceWorkerDatabase* database,
-                                            const std::set<GURL>& origins);
+                                            const std::set<StorageKey>& keys);
   static void PerformStorageCleanupInDB(ServiceWorkerDatabase* database);
   static void GetPurgeableResourceIdsFromDB(
       ServiceWorkerDatabase* database,
@@ -500,9 +514,9 @@
   void DidDeleteDiskCache(DatabaseStatusCallback callback, bool result);
 
   // Origins having registations.
-  std::set<url::Origin> registered_origins_;
+  std::set<StorageKey> registered_keys_;
   // The set of origins whose storage should be cleaned on shutdown.
-  std::set<GURL> origins_to_purge_on_shutdown_;
+  std::set<StorageKey> keys_to_purge_on_shutdown_;
 
   // Pending database tasks waiting for initialization.
   std::vector<base::OnceClosure> pending_tasks_;
diff --git a/components/services/storage/service_worker/service_worker_storage_control_impl.cc b/components/services/storage/service_worker/service_worker_storage_control_impl.cc
index f76a7a7..ae636af 100644
--- a/components/services/storage/service_worker/service_worker_storage_control_impl.cc
+++ b/components/services/storage/service_worker/service_worker_storage_control_impl.cc
@@ -4,6 +4,7 @@
 
 #include "components/services/storage/service_worker/service_worker_storage_control_impl.h"
 
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/service_worker/service_worker_resource_ops.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -131,7 +132,7 @@
     const GURL& client_url,
     FindRegistrationForClientUrlCallback callback) {
   storage_->FindRegistrationForClientUrl(
-      client_url,
+      client_url, StorageKey(url::Origin::Create(client_url)),
       base::BindOnce(&ServiceWorkerStorageControlImpl::DidFindRegistration,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -140,7 +141,7 @@
     const GURL& scope,
     FindRegistrationForClientUrlCallback callback) {
   storage_->FindRegistrationForScope(
-      scope,
+      scope, StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ServiceWorkerStorageControlImpl::DidFindRegistration,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -151,7 +152,7 @@
     FindRegistrationForClientUrlCallback callback) {
   if (origin.has_value()) {
     storage_->FindRegistrationForId(
-        registration_id, *origin,
+        registration_id, StorageKey(*origin),
         base::BindOnce(&ServiceWorkerStorageControlImpl::DidFindRegistration,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   } else {
@@ -165,8 +166,8 @@
 void ServiceWorkerStorageControlImpl::GetRegistrationsForOrigin(
     const url::Origin& origin,
     GetRegistrationsForOriginCallback callback) {
-  storage_->GetRegistrationsForOrigin(
-      origin,
+  storage_->GetRegistrationsForStorageKey(
+      StorageKey(origin),
       base::BindOnce(
           &ServiceWorkerStorageControlImpl::DidGetRegistrationsForOrigin,
           weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -175,7 +176,7 @@
 void ServiceWorkerStorageControlImpl::GetUsageForOrigin(
     const url::Origin& origin,
     GetUsageForOriginCallback callback) {
-  storage_->GetUsageForOrigin(origin, std::move(callback));
+  storage_->GetUsageForStorageKey(StorageKey(origin), std::move(callback));
 }
 
 void ServiceWorkerStorageControlImpl::GetAllRegistrationsDeprecated(
@@ -199,7 +200,7 @@
     const GURL& origin,
     DeleteRegistrationCallback callback) {
   storage_->DeleteRegistration(
-      registration_id, origin,
+      registration_id, StorageKey(url::Origin::Create(origin)),
       base::BindOnce(&ServiceWorkerStorageControlImpl::DidDeleteRegistration,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
@@ -208,7 +209,9 @@
     int64_t registration_id,
     const GURL& origin,
     UpdateToActiveStateCallback callback) {
-  storage_->UpdateToActiveState(registration_id, origin, std::move(callback));
+  storage_->UpdateToActiveState(registration_id,
+                                StorageKey(url::Origin::Create(origin)),
+                                std::move(callback));
 }
 
 void ServiceWorkerStorageControlImpl::UpdateLastUpdateCheckTime(
@@ -217,7 +220,8 @@
     base::Time last_update_check_time,
     UpdateLastUpdateCheckTimeCallback callback) {
   storage_->UpdateLastUpdateCheckTime(
-      registration_id, origin, last_update_check_time, std::move(callback));
+      registration_id, StorageKey(url::Origin::Create(origin)),
+      last_update_check_time, std::move(callback));
 }
 
 void ServiceWorkerStorageControlImpl::UpdateNavigationPreloadEnabled(
@@ -225,8 +229,9 @@
     const GURL& origin,
     bool enable,
     UpdateNavigationPreloadEnabledCallback callback) {
-  storage_->UpdateNavigationPreloadEnabled(registration_id, origin, enable,
-                                           std::move(callback));
+  storage_->UpdateNavigationPreloadEnabled(
+      registration_id, StorageKey(url::Origin::Create(origin)), enable,
+      std::move(callback));
 }
 
 void ServiceWorkerStorageControlImpl::UpdateNavigationPreloadHeader(
@@ -234,8 +239,9 @@
     const GURL& origin,
     const std::string& value,
     UpdateNavigationPreloadHeaderCallback callback) {
-  storage_->UpdateNavigationPreloadHeader(registration_id, origin, value,
-                                          std::move(callback));
+  storage_->UpdateNavigationPreloadHeader(
+      registration_id, StorageKey(url::Origin::Create(origin)), value,
+      std::move(callback));
 }
 
 void ServiceWorkerStorageControlImpl::GetNewRegistrationId(
@@ -297,8 +303,8 @@
     const url::Origin& origin,
     std::vector<mojom::ServiceWorkerUserDataPtr> user_data,
     StoreUserDataCallback callback) {
-  storage_->StoreUserData(registration_id, origin, std::move(user_data),
-                          std::move(callback));
+  storage_->StoreUserData(registration_id, StorageKey(origin),
+                          std::move(user_data), std::move(callback));
 }
 
 void ServiceWorkerStorageControlImpl::ClearUserData(
diff --git a/components/services/storage/service_worker/service_worker_storage_unittest.cc b/components/services/storage/service_worker/service_worker_storage_unittest.cc
index 2d60a499..1059b46 100644
--- a/components/services/storage/service_worker/service_worker_storage_unittest.cc
+++ b/components/services/storage/service_worker/service_worker_storage_unittest.cc
@@ -105,11 +105,11 @@
   void LazyInitialize() { storage()->LazyInitializeForTest(); }
 
   ServiceWorkerDatabase::Status DeleteRegistration(int64_t registration_id,
-                                                   const GURL& origin) {
+                                                   const StorageKey& key) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
     storage()->DeleteRegistration(
-        registration_id, origin,
+        registration_id, key,
         base::BindLambdaForTesting(
             [&](ServiceWorkerDatabase::Status status,
                 ServiceWorkerStorage::OriginState, int64_t /*deleted_version*/,
@@ -138,33 +138,33 @@
     return result;
   }
 
-  ServiceWorkerDatabase::Status GetUsageForOrigin(const url::Origin& origin,
-                                                  int64_t& out_usage) {
+  ServiceWorkerDatabase::Status GetUsageForStorageKey(const StorageKey& key,
+                                                      int64_t& out_usage) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
-    storage()->GetUsageForOrigin(
-        origin, base::BindLambdaForTesting(
-                    [&](ServiceWorkerDatabase::Status status, int64_t usage) {
-                      result = status;
-                      out_usage = usage;
-                      loop.Quit();
-                    }));
+    storage()->GetUsageForStorageKey(
+        key, base::BindLambdaForTesting(
+                 [&](ServiceWorkerDatabase::Status status, int64_t usage) {
+                   result = status;
+                   out_usage = usage;
+                   loop.Quit();
+                 }));
     loop.Run();
     return result;
   }
 
-  ServiceWorkerDatabase::Status GetRegistrationsForOrigin(
-      const url::Origin& origin) {
+  ServiceWorkerDatabase::Status GetRegistrationsForStorageKey(
+      const StorageKey& key) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
-    storage()->GetRegistrationsForOrigin(
-        origin, base::BindLambdaForTesting(
-                    [&](ServiceWorkerDatabase::Status status,
-                        std::unique_ptr<ServiceWorkerStorage::RegistrationList>,
-                        std::unique_ptr<std::vector<ResourceList>>) {
-                      result = status;
-                      loop.Quit();
-                    }));
+    storage()->GetRegistrationsForStorageKey(
+        key, base::BindLambdaForTesting(
+                 [&](ServiceWorkerDatabase::Status status,
+                     std::unique_ptr<ServiceWorkerStorage::RegistrationList>,
+                     std::unique_ptr<std::vector<ResourceList>>) {
+                   result = status;
+                   loop.Quit();
+                 }));
     loop.Run();
     return result;
   }
@@ -207,7 +207,7 @@
 
   ServiceWorkerDatabase::Status StoreUserData(
       int64_t registration_id,
-      const url::Origin& origin,
+      const StorageKey& key,
       const std::vector<std::pair<std::string, std::string>>& key_value_pairs) {
     std::vector<mojom::ServiceWorkerUserDataPtr> user_data;
     for (const auto& kv : key_value_pairs) {
@@ -218,7 +218,7 @@
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
     storage()->StoreUserData(
-        registration_id, origin, std::move(user_data),
+        registration_id, key, std::move(user_data),
         base::BindLambdaForTesting([&](ServiceWorkerDatabase::Status status) {
           result = status;
           loop.Quit();
@@ -291,11 +291,11 @@
   }
 
   ServiceWorkerDatabase::Status UpdateToActiveState(int64_t registration_id,
-                                                    const url::Origin& origin) {
+                                                    const StorageKey& key) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
     storage()->UpdateToActiveState(
-        registration_id, origin.GetURL(),
+        registration_id, key,
         base::BindLambdaForTesting([&](ServiceWorkerDatabase::Status status) {
           result = status;
           loop.Quit();
@@ -305,11 +305,12 @@
   }
 
   ServiceWorkerDatabase::Status FindRegistrationForClientUrl(
-      const GURL& document_url) {
+      const GURL& document_url,
+      const StorageKey& key) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
     storage()->FindRegistrationForClientUrl(
-        document_url,
+        document_url, key,
         base::BindLambdaForTesting([&](mojom::ServiceWorkerRegistrationDataPtr,
                                        std::unique_ptr<ResourceList>,
                                        ServiceWorkerDatabase::Status status) {
@@ -320,11 +321,13 @@
     return result;
   }
 
-  ServiceWorkerDatabase::Status FindRegistrationForScope(const GURL& scope) {
+  ServiceWorkerDatabase::Status FindRegistrationForScope(
+      const GURL& scope,
+      const StorageKey& key) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
     storage()->FindRegistrationForScope(
-        scope,
+        scope, key,
         base::BindLambdaForTesting([&](mojom::ServiceWorkerRegistrationDataPtr,
                                        std::unique_ptr<ResourceList>,
                                        ServiceWorkerDatabase::Status status) {
@@ -335,13 +338,12 @@
     return result;
   }
 
-  ServiceWorkerDatabase::Status FindRegistrationForId(
-      int64_t registration_id,
-      const url::Origin& origin) {
+  ServiceWorkerDatabase::Status FindRegistrationForId(int64_t registration_id,
+                                                      const StorageKey& key) {
     ServiceWorkerDatabase::Status result;
     base::RunLoop loop;
     storage()->FindRegistrationForId(
-        registration_id, origin,
+        registration_id, key,
         base::BindLambdaForTesting([&](mojom::ServiceWorkerRegistrationDataPtr,
                                        std::unique_ptr<ResourceList>,
                                        ServiceWorkerDatabase::Status status) {
@@ -524,6 +526,7 @@
 TEST_F(ServiceWorkerStorageTest, DisabledStorage) {
   const GURL kScope("http://www.example.com/scope/");
   const url::Origin kOrigin = url::Origin::Create(kScope);
+  const StorageKey kKey(kOrigin);
   const GURL kScript("http://www.example.com/script.js");
   const GURL kDocumentUrl("http://www.example.com/scope/document.html");
   const int64_t kRegistrationId = 0;
@@ -533,16 +536,16 @@
   LazyInitialize();
   storage()->Disable();
 
-  EXPECT_EQ(FindRegistrationForClientUrl(kDocumentUrl),
+  EXPECT_EQ(FindRegistrationForClientUrl(kDocumentUrl, kKey),
             ServiceWorkerDatabase::Status::kErrorDisabled);
-  EXPECT_EQ(FindRegistrationForScope(kScope),
+  EXPECT_EQ(FindRegistrationForScope(kScope, kKey),
             ServiceWorkerDatabase::Status::kErrorDisabled);
-  EXPECT_EQ(FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope)),
+  EXPECT_EQ(FindRegistrationForId(kRegistrationId, kKey),
             ServiceWorkerDatabase::Status::kErrorDisabled);
   EXPECT_EQ(FindRegistrationForIdOnly(kRegistrationId),
             ServiceWorkerDatabase::Status::kErrorDisabled);
 
-  EXPECT_EQ(GetRegistrationsForOrigin(url::Origin::Create(kScope)),
+  EXPECT_EQ(GetRegistrationsForStorageKey(kKey),
             ServiceWorkerDatabase::Status::kErrorDisabled);
 
   EXPECT_EQ(GetAllRegistrations(),
@@ -557,10 +560,10 @@
       StoreRegistrationData(std::move(registration_data), std::move(resources)),
       ServiceWorkerDatabase::Status::kErrorDisabled);
 
-  EXPECT_EQ(UpdateToActiveState(kRegistrationId, kOrigin),
+  EXPECT_EQ(UpdateToActiveState(kRegistrationId, kKey),
             ServiceWorkerDatabase::Status::kErrorDisabled);
 
-  EXPECT_EQ(DeleteRegistration(kRegistrationId, kScope.GetOrigin()),
+  EXPECT_EQ(DeleteRegistration(kRegistrationId, kKey),
             ServiceWorkerDatabase::Status::kErrorDisabled);
 
   // Response reader and writer created by the disabled storage should fail to
@@ -576,7 +579,7 @@
             ServiceWorkerDatabase::Status::kErrorDisabled);
   EXPECT_EQ(GetUserDataByKeyPrefix(kRegistrationId, "prefix", user_data_out),
             ServiceWorkerDatabase::Status::kErrorDisabled);
-  EXPECT_EQ(StoreUserData(kRegistrationId, kOrigin, {{kUserDataKey, "foo"}}),
+  EXPECT_EQ(StoreUserData(kRegistrationId, kKey, {{kUserDataKey, "foo"}}),
             ServiceWorkerDatabase::Status::kErrorDisabled);
   EXPECT_EQ(ClearUserData(kRegistrationId, {kUserDataKey}),
             ServiceWorkerDatabase::Status::kErrorDisabled);
@@ -599,6 +602,7 @@
   const int64_t kRegistrationId = 1;
   const GURL kScope("http://www.test.not/scope/");
   const url::Origin kOrigin = url::Origin::Create(kScope);
+  const StorageKey kKey(kOrigin);
   const GURL kScript("http://www.test.not/script.js");
   LazyInitialize();
 
@@ -614,7 +618,7 @@
 
   // Store user data associated with the registration.
   std::vector<std::string> data_out;
-  EXPECT_EQ(StoreUserData(kRegistrationId, kOrigin, {{"key", "data"}}),
+  EXPECT_EQ(StoreUserData(kRegistrationId, kKey, {{"key", "data"}}),
             ServiceWorkerDatabase::Status::kOk);
   EXPECT_EQ(GetUserData(kRegistrationId, {"key"}, data_out),
             ServiceWorkerDatabase::Status::kOk);
@@ -639,7 +643,7 @@
 
   // Write/overwrite multiple user data keys.
   EXPECT_EQ(StoreUserData(
-                kRegistrationId, kOrigin,
+                kRegistrationId, kKey,
                 {{"key", "overwrite"}, {"key3", "data3"}, {"key4", "data4"}}),
             ServiceWorkerDatabase::Status::kOk);
   EXPECT_EQ(GetUserData(kRegistrationId, {"key2"}, data_out),
@@ -671,7 +675,7 @@
   EXPECT_EQ("data4", data_out[0]);
 
   // Get/delete multiple user data keys by prefixes.
-  EXPECT_EQ(StoreUserData(kRegistrationId, kOrigin,
+  EXPECT_EQ(StoreUserData(kRegistrationId, kKey,
                           {{"prefixA", "data1"},
                            {"prefixA2", "data2"},
                            {"prefixB", "data3"},
@@ -698,14 +702,14 @@
   EXPECT_TRUE(data_out.empty());
 
   // User data should be deleted when the associated registration is deleted.
-  ASSERT_EQ(StoreUserData(kRegistrationId, kOrigin, {{"key", "data"}}),
+  ASSERT_EQ(StoreUserData(kRegistrationId, kKey, {{"key", "data"}}),
             ServiceWorkerDatabase::Status::kOk);
   ASSERT_EQ(GetUserData(kRegistrationId, {"key"}, data_out),
             ServiceWorkerDatabase::Status::kOk);
   ASSERT_EQ(1u, data_out.size());
   ASSERT_EQ("data", data_out[0]);
 
-  EXPECT_EQ(DeleteRegistration(kRegistrationId, kScope.GetOrigin()),
+  EXPECT_EQ(DeleteRegistration(kRegistrationId, kKey),
             ServiceWorkerDatabase::Status::kOk);
   EXPECT_EQ(GetUserData(kRegistrationId, {"key"}, data_out),
             ServiceWorkerDatabase::Status::kErrorNotFound);
@@ -716,7 +720,7 @@
 
   // Data access with an invalid registration id should be failed.
   EXPECT_EQ(StoreUserData(blink::mojom::kInvalidServiceWorkerRegistrationId,
-                          kOrigin, {{"key", "data"}}),
+                          kKey, {{"key", "data"}}),
             ServiceWorkerDatabase::Status::kErrorFailed);
   EXPECT_EQ(GetUserData(blink::mojom::kInvalidServiceWorkerRegistrationId,
                         {"key"}, data_out),
@@ -733,12 +737,12 @@
             ServiceWorkerDatabase::Status::kErrorFailed);
 
   // Data access with an empty key should be failed.
-  EXPECT_EQ(StoreUserData(kRegistrationId, kOrigin,
+  EXPECT_EQ(StoreUserData(kRegistrationId, kKey,
                           std::vector<std::pair<std::string, std::string>>()),
             ServiceWorkerDatabase::Status::kErrorFailed);
-  EXPECT_EQ(StoreUserData(kRegistrationId, kOrigin, {{std::string(), "data"}}),
+  EXPECT_EQ(StoreUserData(kRegistrationId, kKey, {{std::string(), "data"}}),
             ServiceWorkerDatabase::Status::kErrorFailed);
-  EXPECT_EQ(StoreUserData(kRegistrationId, kOrigin,
+  EXPECT_EQ(StoreUserData(kRegistrationId, kKey,
                           {{std::string(), "data"}, {"key", "data"}}),
             ServiceWorkerDatabase::Status::kErrorFailed);
   EXPECT_EQ(GetUserData(kRegistrationId, std::vector<std::string>(), data_out),
@@ -770,9 +774,10 @@
 // called.
 TEST_F(ServiceWorkerStorageTest, StoreUserData_BeforeInitialize) {
   const int kRegistrationId = 0;
-  EXPECT_EQ(StoreUserData(kRegistrationId,
-                          url::Origin::Create(GURL("https://example.com")),
-                          {{"key", "data"}}),
+  EXPECT_EQ(StoreUserData(
+                kRegistrationId,
+                StorageKey(url::Origin::Create(GURL("https://example.com"))),
+                {{"key", "data"}}),
             ServiceWorkerDatabase::Status::kErrorNotFound);
 }
 
@@ -948,23 +953,24 @@
 
   // Storage usage should report total resource size from two registrations.
   const url::Origin origin = url::Origin::Create(kScope1.GetOrigin());
+  const StorageKey key(origin);
   int64_t usage;
-  EXPECT_EQ(GetUsageForOrigin(origin, usage),
+  EXPECT_EQ(GetUsageForStorageKey(key, usage),
             ServiceWorkerDatabase::Status::kOk);
   EXPECT_EQ(usage, resources_total_size_bytes1 + resources_total_size_bytes2);
 
   // Delete the first registration. Storage usage should report only the second
   // registration.
-  EXPECT_EQ(DeleteRegistration(kRegistrationId1, origin.GetURL()),
+  EXPECT_EQ(DeleteRegistration(kRegistrationId1, key),
             ServiceWorkerDatabase::Status::kOk);
-  EXPECT_EQ(GetUsageForOrigin(origin, usage),
+  EXPECT_EQ(GetUsageForStorageKey(key, usage),
             ServiceWorkerDatabase::Status::kOk);
   EXPECT_EQ(usage, resources_total_size_bytes2);
 
   // Delete the second registration. No storage usage should be reported.
-  EXPECT_EQ(DeleteRegistration(kRegistrationId2, origin.GetURL()),
+  EXPECT_EQ(DeleteRegistration(kRegistrationId2, key),
             ServiceWorkerDatabase::Status::kOk);
-  EXPECT_EQ(GetUsageForOrigin(origin, usage),
+  EXPECT_EQ(GetUsageForStorageKey(key, usage),
             ServiceWorkerDatabase::Status::kOk);
   EXPECT_EQ(usage, 0);
 }
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
index f3a4291..59f8e1ba 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
@@ -150,14 +150,6 @@
     ProfileDataSource getProfileDataSource();
 
     /**
-     * Executes the callback after all pending account list updates finish. If there are no
-     * pending account list updates, executes the callback right away.
-     * @param callback the callback to be executed
-     */
-    @MainThread
-    void waitForPendingUpdates(Runnable callback);
-
-    /**
      * Returns the Gaia id for the account associated with the given email address.
      * If an account with the given email address is not installed on the device
      * then null is returned.
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
index 59a364e..f4605b2a 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
@@ -252,21 +252,6 @@
     }
 
     /**
-     * Executes the callback after all pending account list updates finish. If there are no pending
-     * account list updates, executes the callback right away.
-     * @param callback the callback to be executed
-     */
-    @Override
-    public void waitForPendingUpdates(Runnable callback) {
-        ThreadUtils.assertOnUiThread();
-        if (!isUpdatePending().get()) {
-            callback.run();
-            return;
-        }
-        mCallbacksWaitingForAccountsFetch.add(callback);
-    }
-
-    /**
      * Checks whether there are pending updates for account list cache.
      * @return true if there are no pending updates, false otherwise
      */
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
index 5e6667c2..bd4d082 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
@@ -54,9 +54,6 @@
         return mFakeProfileDataSource;
     }
 
-    @Override
-    public void waitForPendingUpdates(Runnable callback) {}
-
     @MainThread
     @Override
     public void addObserver(AccountsChangeObserver observer) {
diff --git a/components/signin/public/identity_manager/identity_manager_builder.h b/components/signin/public/identity_manager/identity_manager_builder.h
index 29264ded..26818a57 100644
--- a/components/signin/public/identity_manager/identity_manager_builder.h
+++ b/components/signin/public/identity_manager/identity_manager_builder.h
@@ -64,14 +64,15 @@
   IdentityManagerBuildParams();
   ~IdentityManagerBuildParams();
 
-  AccountConsistencyMethod account_consistency;
+  AccountConsistencyMethod account_consistency =
+      AccountConsistencyMethod::kDisabled;
   std::unique_ptr<AccountTrackerService> account_tracker_service;
   std::unique_ptr<image_fetcher::ImageDecoder> image_decoder;
-  PrefService* local_state;
+  PrefService* local_state = nullptr;
   network::NetworkConnectionTracker* network_connection_tracker;
-  PrefService* pref_service;
+  PrefService* pref_service = nullptr;
   base::FilePath profile_path;
-  SigninClient* signin_client;
+  SigninClient* signin_client = nullptr;
   std::unique_ptr<ProfileOAuth2TokenService> token_service;
 
 #if !defined(OS_ANDROID)
@@ -80,14 +81,14 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-  ash::AccountManager* account_manager;
-  account_manager::AccountManagerFacade* account_manager_facade;
-  bool is_regular_profile;
+  ash::AccountManager* account_manager = nullptr;
+  account_manager::AccountManagerFacade* account_manager_facade = nullptr;
+  bool is_regular_profile = false;
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  account_manager::AccountManagerFacade* account_manager_facade;
-  bool is_regular_profile;
+  account_manager::AccountManagerFacade* account_manager_facade = nullptr;
+  bool is_regular_profile = false;
 #endif
 
 #if defined(OS_IOS)
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index db4cfa3..0b3e92c 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -116,7 +116,7 @@
 // Enables platform supported delegated ink trails instead of Skia backed
 // delegated ink trails.
 const base::Feature kUsePlatformDelegatedInk{"UsePlatformDelegatedInk",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Used to debug Android WebView Vulkan composite. Composite to an intermediate
 // buffer and draw the intermediate buffer to the secondary command buffer.
diff --git a/components/viz/host/host_gpu_memory_buffer_manager.cc b/components/viz/host/host_gpu_memory_buffer_manager.cc
index daebdf8..609a65c 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager.cc
@@ -72,6 +72,7 @@
       client_id_(client_id),
       gpu_memory_buffer_support_(std::move(gpu_memory_buffer_support)),
       pool_(base::MakeRefCounted<base::UnsafeSharedMemoryPool>()),
+      runs_on_ui_thread_(task_runner->BelongsToCurrentThread()),
       task_runner_(std::move(task_runner)) {
   if (!WillGetGmbConfigFromGpu()) {
     native_configurations_ = gpu::GetNativeGpuMemoryBufferConfigurations(
@@ -205,20 +206,33 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    gpu::SurfaceHandle surface_handle) {
+    gpu::SurfaceHandle surface_handle,
+    base::WaitableEvent* shutdown_event) {
   gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
   gfx::GpuMemoryBufferHandle handle;
   base::WaitableEvent wait_event(
       base::WaitableEvent::ResetPolicy::MANUAL,
       base::WaitableEvent::InitialState::NOT_SIGNALED);
   DCHECK(!task_runner_->BelongsToCurrentThread());
+
+  // A refcounted wrapper around a bool so that if the thread waiting on a
+  // PostTask to the main thread is quit due to shutdown and the task runs
+  // later on the message loop, it can detect this and not use the
+  // now deleted |handle| and |wait_event|. A boolean is fine since if it
+  // is set on the worker thread that means the main thread is blocked, and
+  // would only run once the worker thread set the boolean.
+  auto cancelled = base::MakeRefCounted<base::RefCountedData<bool>>(false);
+
   auto reply_callback = base::BindOnce(
-      [](gfx::GpuMemoryBufferHandle* handle, base::WaitableEvent* wait_event,
+      [](scoped_refptr<base::RefCountedData<bool>> cancelled,
+         gfx::GpuMemoryBufferHandle* handle, base::WaitableEvent* wait_event,
          gfx::GpuMemoryBufferHandle allocated_buffer_handle) {
+        if (cancelled->data)
+          return;
         *handle = std::move(allocated_buffer_handle);
         wait_event->Signal();
       },
-      &handle, &wait_event);
+      cancelled, &handle, &wait_event);
   // We block with a WaitableEvent until the callback is run. So using
   // base::Unretained() is safe here.
   auto allocate_callback =
@@ -228,7 +242,21 @@
   task_runner_->PostTask(FROM_HERE, std::move(allocate_callback));
   base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope
       allow_base_sync_primitives;
-  wait_event.Wait();
+  if (runs_on_ui_thread_ && shutdown_event) {
+    // If this class is running on the UI thread then
+    // TileManager::FinishTasksAndCleanUp could block on the worker thread where
+    // this task is running. That could in turn block on a task posted to the UI
+    // thread. We avoid this deadlock by having an event that TileManager can
+    // set to cancel this wait.
+    base::WaitableEvent* waitables[] = {&wait_event, shutdown_event};
+    size_t index =
+        base::WaitableEvent::WaitMany(waitables, base::size(waitables));
+    if (index == 1)
+      cancelled->data = true;
+  } else {
+    wait_event.Wait();
+  }
+
   if (handle.is_null())
     return nullptr;
   // The destruction callback can be called on any thread. So use an
diff --git a/components/viz/host/host_gpu_memory_buffer_manager.h b/components/viz/host/host_gpu_memory_buffer_manager.h
index 718425c..9a5f0d41 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager.h
+++ b/components/viz/host/host_gpu_memory_buffer_manager.h
@@ -79,7 +79,8 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) override;
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) override;
   void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
                                const gpu::SyncToken& sync_token) override;
   void CopyGpuMemoryBufferAsync(
@@ -154,6 +155,8 @@
   gpu::GpuMemoryBufferConfigurationSet native_configurations_;
   base::AtomicFlag native_configurations_initialized_;
 
+  const bool runs_on_ui_thread_;
+
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   base::WeakPtr<HostGpuMemoryBufferManager> weak_ptr_;
   base::WeakPtrFactory<HostGpuMemoryBufferManager> weak_factory_{this};
diff --git a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
index 527dc9c5..410e9e9 100644
--- a/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
+++ b/components/viz/host/host_gpu_memory_buffer_manager_unittest.cc
@@ -295,18 +295,17 @@
     std::unique_ptr<gfx::GpuMemoryBuffer> buffer;
     base::RunLoop run_loop;
     diff_thread.task_runner()->PostTask(
-        FROM_HERE, base::BindOnce(
-                       [](HostGpuMemoryBufferManager* manager,
-                          std::unique_ptr<gfx::GpuMemoryBuffer>* out_buffer,
-                          base::OnceClosure callback) {
-                         *out_buffer = manager->CreateGpuMemoryBuffer(
-                             gfx::Size(64, 64), gfx::BufferFormat::YVU_420,
-                             gfx::BufferUsage::GPU_READ,
-                             gpu::kNullSurfaceHandle);
-                         std::move(callback).Run();
-                       },
-                       gpu_memory_buffer_manager_.get(), &buffer,
-                       run_loop.QuitClosure()));
+        FROM_HERE,
+        base::BindOnce(
+            [](HostGpuMemoryBufferManager* manager,
+               std::unique_ptr<gfx::GpuMemoryBuffer>* out_buffer,
+               base::OnceClosure callback) {
+              *out_buffer = manager->CreateGpuMemoryBuffer(
+                  gfx::Size(64, 64), gfx::BufferFormat::YVU_420,
+                  gfx::BufferUsage::GPU_READ, gpu::kNullSurfaceHandle, nullptr);
+              std::move(callback).Run();
+            },
+            gpu_memory_buffer_manager_.get(), &buffer, run_loop.QuitClosure()));
     run_loop.Run();
     return buffer;
   }
diff --git a/components/viz/service/display/direct_renderer.cc b/components/viz/service/display/direct_renderer.cc
index c9e27f3..652e61d 100644
--- a/components/viz/service/display/direct_renderer.cc
+++ b/components/viz/service/display/direct_renderer.cc
@@ -798,12 +798,12 @@
   const AggregatedRenderPass* root_render_pass =
       current_frame()->root_render_pass;
   gfx::Rect root_damage_rect = current_frame()->root_damage_rect;
+  gfx::Rect frame_buffer_damage =
+      output_surface_->GetCurrentFramebufferDamage();
 
   if (render_pass == root_render_pass) {
     base::CheckedNumeric<int64_t> display_area =
         current_frame()->device_viewport_size.GetCheckedArea();
-    gfx::Rect frame_buffer_damage =
-        output_surface_->GetCurrentFramebufferDamage();
     base::CheckedNumeric<int64_t> root_damage_area =
         root_damage_rect.size().GetCheckedArea();
     if (display_area.IsValid() && root_damage_area.IsValid()) {
@@ -878,7 +878,17 @@
 
   DCHECK(render_pass->copy_requests.empty() ||
          (render_pass->damage_rect == render_pass->output_rect));
-  return render_pass->damage_rect;
+
+  gfx::Rect damage_rect = render_pass->damage_rect;
+  gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization);
+  if (render_pass->transform_to_root_target.GetInverse(&inverse_transform)) {
+    gfx::Rect frame_buffer_damage_in_render_space =
+        cc::MathUtil::MapEnclosingClippedRect(inverse_transform,
+                                              frame_buffer_damage);
+    damage_rect.Union(frame_buffer_damage_in_render_space);
+  }
+
+  return damage_rect;
 }
 
 gfx::Size DirectRenderer::CalculateTextureSizeForRenderPass(
diff --git a/components/viz/service/display/draw_polygon.h b/components/viz/service/display/draw_polygon.h
index 9bafa16..410279a 100644
--- a/components/viz/service/display/draw_polygon.h
+++ b/components/viz/service/display/draw_polygon.h
@@ -32,13 +32,6 @@
               const gfx::Transform& transform,
               int draw_order_index = 0);
 
-  // Split takes this DrawPolygon and splits it into two pieces that are on
-  // either side of |splitter|. Any edges of this polygon that cross the plane
-  // of |splitter| will have an intersection point that is shared by both
-  // polygons on either side.
-  // Split will only return true if it determines that we got back 2
-  // intersection points. Only when it returns true will front and back both be
-  // valid new polygons that are on opposite sides of the splitting plane.
   void SplitPolygon(std::unique_ptr<DrawPolygon> polygon,
                     std::unique_ptr<DrawPolygon>* front,
                     std::unique_ptr<DrawPolygon>* back,
diff --git a/components/viz/service/display_embedder/buffer_queue_unittest.cc b/components/viz/service/display_embedder/buffer_queue_unittest.cc
index 223324c..0284c68 100644
--- a/components/viz/service/display_embedder/buffer_queue_unittest.cc
+++ b/components/viz/service/display_embedder/buffer_queue_unittest.cc
@@ -92,10 +92,11 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) override {
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) override {
     if (surface_handle == gpu::kNullSurfaceHandle) {
       return TestGpuMemoryBufferManager::CreateGpuMemoryBuffer(
-          size, format, usage, surface_handle);
+          size, format, usage, surface_handle, shutdown_event);
     }
     if (allocate_succeeds_)
       return base::WrapUnique<gfx::GpuMemoryBuffer>(
diff --git a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
index 2e34752..44a4c4f4 100644
--- a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
+++ b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.cc
@@ -57,7 +57,8 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    gpu::SurfaceHandle surface_handle) {
+    gpu::SurfaceHandle surface_handle,
+    base::WaitableEvent* shutdown_event) {
   gfx::GpuMemoryBufferId id(next_gpu_memory_id_++);
   gfx::GpuMemoryBufferHandle buffer_handle =
       gpu_memory_buffer_factory_->CreateGpuMemoryBuffer(
diff --git a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
index ec0a55e..70b0d2ad 100644
--- a/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
+++ b/components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h
@@ -42,7 +42,8 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) override;
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) override;
   void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
                                const gpu::SyncToken& sync_token) override;
   void CopyGpuMemoryBufferAsync(
diff --git a/components/viz/test/test_gpu_memory_buffer_manager.cc b/components/viz/test/test_gpu_memory_buffer_manager.cc
index 45a9e28..94f9a6dd 100644
--- a/components/viz/test/test_gpu_memory_buffer_manager.cc
+++ b/components/viz/test/test_gpu_memory_buffer_manager.cc
@@ -177,7 +177,8 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    gpu::SurfaceHandle surface_handle) {
+    gpu::SurfaceHandle surface_handle,
+    base::WaitableEvent* shutdown_event) {
   base::AutoLock hold(lock_);
 
   if (fail_on_create_)
diff --git a/components/viz/test/test_gpu_memory_buffer_manager.h b/components/viz/test/test_gpu_memory_buffer_manager.h
index 10c9e57a..e7332ae 100644
--- a/components/viz/test/test_gpu_memory_buffer_manager.h
+++ b/components/viz/test/test_gpu_memory_buffer_manager.h
@@ -34,7 +34,8 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) override;
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) override;
   void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
                                const gpu::SyncToken& sync_token) override;
   void CopyGpuMemoryBufferAsync(
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index a6fbaaa..0c0b1024 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2852,10 +2852,7 @@
       "renderer_host/delegated_frame_host.cc",
       "renderer_host/delegated_frame_host.h",
     ]
-    deps += [
-      "//components/viz/service/main",
-      "//ui/compositor",
-    ]
+    deps += [ "//ui/compositor" ]
     if (is_mac) {
       sources += [
         "renderer_host/browser_compositor_view_mac.h",
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
index 5538780..7be8433 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.cc
@@ -8,8 +8,6 @@
 #include <numeric>
 #include <utility>
 
-#include <psapi.h>
-
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -18,7 +16,6 @@
 #include "base/win/scoped_safearray.h"
 #include "base/win/scoped_variant.h"
 #include "base/win/windows_version.h"
-#include "build/build_config.h"
 #include "content/browser/accessibility/browser_accessibility_com_win.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
@@ -33,25 +30,6 @@
 
 namespace {
 
-#if defined(COMPILER_MSVC)
-#define RETURN_ADDRESS() _ReturnAddress()
-#elif defined(COMPILER_GCC) && !defined(OS_NACL)
-#define RETURN_ADDRESS() \
-  __builtin_extract_return_addr(__builtin_return_address(0))
-#else
-#define RETURN_ADDRESS() nullptr
-#endif
-
-static std::pair<uintptr_t, uintptr_t> GetModuleAddressRange(
-    const wchar_t* module_name) {
-  MODULEINFO info;
-  CHECK(GetModuleInformation(GetCurrentProcess(), GetModuleHandle(module_name),
-                             &info, sizeof(info)));
-
-  const uintptr_t start = reinterpret_cast<uintptr_t>(info.lpBaseOfDll);
-  return std::make_pair(start, start + info.SizeOfImage);
-}
-
 std::string UiaIdentifierToStringPretty(int32_t id) {
   auto str = base::WideToUTF8(UiaIdentifierToString(id));
   // Remove UIA_ prefix, and EventId/PropertyId suffixes
@@ -197,10 +175,66 @@
   // Wait for shutdown signal
   shutdown_signal_.Wait();
 
+  // Due to a bug in Windows (fixed in Windows 10 19H1), events are raised
+  // exactly twice for any in-proc off-thread event listeners. We filter out the
+  // duplicate events here, and forward the remaining events to our owner.
   {
     base::AutoLock lock{on_event_lock_};
-    for (const std::string& event : event_logs_)
-      owner_->OnEvent(event);
+    if (event_logs_.size() == 1) {
+      // Only received events on a single thread... perhaps the bug was fixed?
+      // Forward all events.
+      for (auto&& event : event_logs_.begin()->second)
+        owner_->OnEvent(event);
+    } else if (event_logs_.size() == 2) {
+      // Events were raised on two threads, as expected.  Sort the lists and
+      // forward events, eliminating duplicates that occur in both threads.
+      auto&& events_thread1 = event_logs_.begin()->second;
+      auto&& events_thread2 = (++event_logs_.begin())->second;
+
+      std::sort(events_thread1.begin(), events_thread1.end());
+      std::sort(events_thread2.begin(), events_thread2.end());
+
+      auto it1 = events_thread1.begin();
+      auto it2 = events_thread2.begin();
+      while (it1 < events_thread1.end() && it2 < events_thread2.end()) {
+        if (*it1 == *it2) {
+          owner_->OnEvent(*it1);
+          it1++;
+          it2++;
+        } else if (*it1 < *it2) {
+          owner_->OnEvent(*it1);
+          it1++;
+        } else {
+          owner_->OnEvent(*it2);
+          it2++;
+        }
+      }
+      while (it1 < events_thread1.end())
+        owner_->OnEvent(*it1++);
+      while (it2 < events_thread2.end())
+        owner_->OnEvent(*it2++);
+    } else {
+      // Typically we'll get events on exactly two threads (one directly from
+      // UIA, the second from RPC), but sometimes RPC will split its events
+      // across different threads.
+      //
+      // Unfortunately, there is no robust method of eliminating duplicates in
+      // this case.  Tests with intentional duplicates could run afoul of this
+      // logic in rare scenarios; it is recommended that intentionally
+      // duplicated events be avoided in tests, when possible.
+      std::vector<std::string> combined;
+      for (auto&& log : event_logs_)
+        combined.insert(combined.end(), log.second.begin(), log.second.end());
+      std::sort(combined.begin(), combined.end());
+
+      std::string last_event;
+      for (auto&& event : combined) {
+        if (last_event != event)
+          owner_->OnEvent(last_event = event);
+        else
+          last_event = {};
+      }
+    }
   }
 
   // Cleanup
@@ -217,27 +251,24 @@
 }
 
 void AccessibilityEventRecorderUia::Thread::SendShutdownSignal() {
-  shutdown_signal_.Signal();
+  // We expect to see the shutdown sentinel exactly twice (due to the Windows
+  // bug detailed in |ThreadMain| and fixed in 19H1), so don't actually shut
+  // down the thread until the second call.
+  if (shutdown_sentinel_received_ ||
+      base::win::GetVersion() >= base::win::Version::WIN10_19H1)
+    shutdown_signal_.Signal();
+  else
+    shutdown_sentinel_received_ = true;
 }
 
 void AccessibilityEventRecorderUia::Thread::OnEvent(const std::string& event) {
   // We need to synchronize event logging, since UIA event callbacks can be
   // coming from multiple threads.
   base::AutoLock lock{on_event_lock_};
-  event_logs_.push_back(event);
+  event_logs_[base::PlatformThread::CurrentId()].push_back(event);
 }
 
-AccessibilityEventRecorderUia::Thread::EventHandler::EventHandler() {
-  // Some events are duplicated between UIAutomationCore.dll and RPCRT4.dll.
-  // On Win10, events are mainly sent from UIAutomationCore.dll with some
-  // duplicates sent from RPCRT4.dll.
-  // On Win7, events are mainly sent from RPCRT4.dll, with a few duplicates sent
-  // from UIAutomationCore.dll.
-  allowed_module_address_range_ = GetModuleAddressRange(
-      (base::win::GetVersion() == base::win::Version::WIN7)
-          ? L"RPCRT4.dll"
-          : L"UIAutomationCore.dll");
-}
+AccessibilityEventRecorderUia::Thread::EventHandler::EventHandler() {}
 
 AccessibilityEventRecorderUia::Thread::EventHandler::~EventHandler() {}
 
@@ -256,7 +287,7 @@
 STDMETHODIMP
 AccessibilityEventRecorderUia::Thread::EventHandler::HandleFocusChangedEvent(
     IUIAutomationElement* sender) {
-  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
+  if (!owner_)
     return S_OK;
 
   base::win::ScopedSafearray id;
@@ -288,18 +319,17 @@
     IUIAutomationElement* sender,
     PROPERTYID property_id,
     VARIANT new_value) {
-  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
-    return S_OK;
+  if (owner_) {
+    std::string prop_str = UiaIdentifierToStringPretty(property_id);
+    if (prop_str.empty()) {
+      VLOG(1) << "Ignoring UIA property-changed event " << property_id;
+      return S_OK;
+    }
 
-  std::string prop_str = UiaIdentifierToStringPretty(property_id);
-  if (prop_str.empty()) {
-    VLOG(1) << "Ignoring UIA property-changed event " << property_id;
-    return S_OK;
+    std::string log = base::StringPrintf("%s changed %s", prop_str.c_str(),
+                                         GetSenderInfo(sender).c_str());
+    owner_->OnEvent(log);
   }
-
-  std::string log = base::StringPrintf("%s changed %s", prop_str.c_str(),
-                                       GetSenderInfo(sender).c_str());
-  owner_->OnEvent(log);
   return S_OK;
 }
 
@@ -308,35 +338,34 @@
     HandleStructureChangedEvent(IUIAutomationElement* sender,
                                 StructureChangeType change_type,
                                 SAFEARRAY* runtime_id) {
-  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
-    return S_OK;
+  if (owner_) {
+    std::string type_str;
+    switch (change_type) {
+      case StructureChangeType_ChildAdded:
+        type_str = "ChildAdded";
+        break;
+      case StructureChangeType_ChildRemoved:
+        type_str = "ChildRemoved";
+        break;
+      case StructureChangeType_ChildrenInvalidated:
+        type_str = "ChildrenInvalidated";
+        break;
+      case StructureChangeType_ChildrenBulkAdded:
+        type_str = "ChildrenBulkAdded";
+        break;
+      case StructureChangeType_ChildrenBulkRemoved:
+        type_str = "ChildrenBulkRemoved";
+        break;
+      case StructureChangeType_ChildrenReordered:
+        type_str = "ChildrenReordered";
+        break;
+    }
 
-  std::string type_str;
-  switch (change_type) {
-    case StructureChangeType_ChildAdded:
-      type_str = "ChildAdded";
-      break;
-    case StructureChangeType_ChildRemoved:
-      type_str = "ChildRemoved";
-      break;
-    case StructureChangeType_ChildrenInvalidated:
-      type_str = "ChildrenInvalidated";
-      break;
-    case StructureChangeType_ChildrenBulkAdded:
-      type_str = "ChildrenBulkAdded";
-      break;
-    case StructureChangeType_ChildrenBulkRemoved:
-      type_str = "ChildrenBulkRemoved";
-      break;
-    case StructureChangeType_ChildrenReordered:
-      type_str = "ChildrenReordered";
-      break;
+    std::string log =
+        base::StringPrintf("StructureChanged/%s %s", type_str.c_str(),
+                           GetSenderInfo(sender).c_str());
+    owner_->OnEvent(log);
   }
-
-  std::string log =
-      base::StringPrintf("StructureChanged/%s %s", type_str.c_str(),
-                         GetSenderInfo(sender).c_str());
-  owner_->OnEvent(log);
   return S_OK;
 }
 
@@ -344,45 +373,33 @@
 AccessibilityEventRecorderUia::Thread::EventHandler::HandleAutomationEvent(
     IUIAutomationElement* sender,
     EVENTID event_id) {
-  if (!owner_ || !IsCallerFromAllowedModule(RETURN_ADDRESS()))
-    return S_OK;
+  if (owner_) {
+    if (event_id == owner_->shutdown_sentinel_) {
+      // This is a sentinel value that tells us the tests are finished.
+      owner_->SendShutdownSignal();
+    } else {
+      std::string event_str = UiaIdentifierToStringPretty(event_id);
+      if (event_str.empty()) {
+        VLOG(1) << "Ignoring UIA automation event " << event_id;
+        return S_OK;
+      }
 
-  if (event_id == owner_->shutdown_sentinel_) {
-    // This is a sentinel value that tells us the tests are finished.
-    owner_->SendShutdownSignal();
-  } else {
-    std::string event_str = UiaIdentifierToStringPretty(event_id);
-    if (event_str.empty()) {
-      VLOG(1) << "Ignoring UIA automation event " << event_id;
-      return S_OK;
+      // Remove duplicate menuclosed events with no event data.
+      // The "duplicates" are benign. UIA currently duplicates *all* events for
+      // in-process listeners, and the event-recorder tries to eliminate the
+      // duplicates... but since the recorder sometimes isn't able to retrieve
+      // the role, the duplicate-elimination logic doesn't see them as
+      // duplicates in this case.
+      std::string sender_info =
+          event_id == UIA_MenuClosedEventId ? "" : GetSenderInfo(sender);
+      std::string log =
+          base::StringPrintf("%s %s", event_str.c_str(), sender_info.c_str());
+      owner_->OnEvent(log);
     }
-
-    // Remove duplicate menuclosed events with no event data.
-    // The "duplicates" are benign. UIA currently duplicates *all* events for
-    // in-process listeners, and the event-recorder tries to eliminate the
-    // duplicates... but since the recorder sometimes isn't able to retrieve
-    // the role, the duplicate-elimination logic doesn't see them as
-    // duplicates in this case.
-    std::string sender_info =
-        event_id == UIA_MenuClosedEventId ? "" : GetSenderInfo(sender);
-    std::string log =
-        base::StringPrintf("%s %s", event_str.c_str(), sender_info.c_str());
-    owner_->OnEvent(log);
   }
   return S_OK;
 }
 
-// Due to a bug in Windows (fixed in Windows 10 19H1, but found broken in 20H2),
-// events are raised exactly twice for any in-proc off-thread event listeners.
-// To avoid this, in UIA API methods we can pass the RETURN_ADDRESS() to this
-// method to determine whether the caller belongs to a specific platform module.
-bool AccessibilityEventRecorderUia::Thread::EventHandler::
-    IsCallerFromAllowedModule(void* return_address) {
-  const auto address = reinterpret_cast<uintptr_t>(return_address);
-  return address >= allowed_module_address_range_.first &&
-         address < allowed_module_address_range_.second;
-}
-
 std::string AccessibilityEventRecorderUia::Thread::EventHandler::GetSenderInfo(
     IUIAutomationElement* sender) {
   std::string sender_info;
diff --git a/content/browser/accessibility/accessibility_event_recorder_uia_win.h b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
index 601fa625..8809a4a 100644
--- a/content/browser/accessibility/accessibility_event_recorder_uia_win.h
+++ b/content/browser/accessibility/accessibility_event_recorder_uia_win.h
@@ -9,8 +9,8 @@
 #include <stdint.h>
 #include <uiautomation.h>
 #include <wrl/client.h>
+#include <map>
 #include <string>
-#include <utility>
 #include <vector>
 
 #include "base/atomicops.h"
@@ -67,11 +67,12 @@
     base::OnceClosure initialization_complete_;
     base::OnceClosure shutdown_complete_;
     base::WaitableEvent shutdown_signal_;
+    bool shutdown_sentinel_received_ = false;
 
     // Thread-specific wrapper for OnEvent to handle necessary locking
     void OnEvent(const std::string& event);
     base::Lock on_event_lock_;
-    std::vector<std::string> event_logs_;
+    std::map<base::PlatformThreadId, std::vector<std::string>> event_logs_;
 
     // An implementation of various UIA interfaces that forward event
     // notifications to the owning event recorder.
@@ -118,9 +119,6 @@
       AccessibilityEventRecorderUia::Thread* owner_ = nullptr;
 
      private:
-      std::pair<uintptr_t, uintptr_t> allowed_module_address_range_;
-      bool IsCallerFromAllowedModule(void* return_address);
-
       std::string GetSenderInfo(IUIAutomationElement* sender);
 
       Microsoft::WRL::ComPtr<IUIAutomationElement> root_;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index efa25190..4336e1d0 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -42,6 +42,7 @@
 using ui::AXPropertyNode;
 using ui::AXFormatValue;
 using ui::AXMakeConst;
+using ui::AXMakeOrderedKey;
 using ui::AXMakeSetKey;
 
 namespace content {
@@ -329,18 +330,23 @@
 base::Value AccessibilityTreeFormatterMac::PopulateSize(
     NSSize size_value) const {
   base::Value size(base::Value::Type::DICTIONARY);
-  size.SetIntPath("w", static_cast<int>(size_value.width));
-  size.SetIntPath("h", static_cast<int>(size_value.height));
+  size.SetIntPath(AXMakeOrderedKey("w", 0), static_cast<int>(size_value.width));
+  size.SetIntPath(AXMakeOrderedKey("h", 1),
+                  static_cast<int>(size_value.height));
   return size;
 }
 
 base::Value AccessibilityTreeFormatterMac::PopulateRect(
     NSRect rect_value) const {
   base::Value rect(base::Value::Type::DICTIONARY);
-  rect.SetIntPath("x", static_cast<int>(rect_value.origin.x));
-  rect.SetIntPath("y", static_cast<int>(rect_value.origin.y));
-  rect.SetIntPath("w", static_cast<int>(rect_value.size.width));
-  rect.SetIntPath("h", static_cast<int>(rect_value.size.height));
+  rect.SetIntPath(AXMakeOrderedKey("x", 0),
+                  static_cast<int>(rect_value.origin.x));
+  rect.SetIntPath(AXMakeOrderedKey("y", 1),
+                  static_cast<int>(rect_value.origin.y));
+  rect.SetIntPath(AXMakeOrderedKey("w", 2),
+                  static_cast<int>(rect_value.size.width));
+  rect.SetIntPath(AXMakeOrderedKey("h", 3),
+                  static_cast<int>(rect_value.size.height));
   return rect;
 }
 
@@ -378,10 +384,12 @@
   }
 
   base::Value set(base::Value::Type::DICTIONARY);
-  set.SetStringPath(AXMakeSetKey("index1_anchor"),
+  set.SetStringPath(AXMakeSetKey(AXMakeOrderedKey("anchor", 0)),
                     NodeToLineIndex(cocoa_anchor, line_indexer));
-  set.SetIntPath(AXMakeSetKey("index2_offset"), position->text_offset());
-  set.SetStringPath(AXMakeSetKey("index3_affinity"), AXMakeConst(affinity));
+  set.SetIntPath(AXMakeSetKey(AXMakeOrderedKey("offset", 1)),
+                 position->text_offset());
+  set.SetStringPath(AXMakeSetKey(AXMakeOrderedKey("affinity", 2)),
+                    AXMakeConst(affinity));
   return set;
 }
 
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
index c99a058..9e4534f0 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
@@ -188,24 +188,22 @@
 }
 
 BluetoothDeviceChooserController::~BluetoothDeviceChooserController() {
-  if (chooser_) {
-    DCHECK(error_callback_);
-    std::move(error_callback_).Run(WebBluetoothResult::CHOOSER_CANCELLED);
+  if (callback_) {
+    std::move(callback_).Run(WebBluetoothResult::CHOOSER_CANCELLED,
+                             /*options=*/nullptr,
+                             /*device_id=*/std::string());
   }
 }
 
 void BluetoothDeviceChooserController::GetDevice(
     blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
-    SuccessCallback success_callback,
-    ErrorCallback error_callback) {
+    Callback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   // GetDevice should only be called once.
-  DCHECK(success_callback_.is_null());
-  DCHECK(error_callback_.is_null());
+  DCHECK(callback_.is_null());
 
-  success_callback_ = std::move(success_callback);
-  error_callback_ = std::move(error_callback);
+  callback_ = std::move(callback);
   options_ = std::move(options);
   LogRequestDeviceOptions(options_);
 
@@ -422,8 +420,6 @@
     BluetoothChooserEvent event,
     const std::string& device_address) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  // Shouldn't recieve an event from a closed chooser.
-  DCHECK(chooser_);
 
   switch (event) {
     case BluetoothChooserEvent::RESCAN:
@@ -462,23 +458,24 @@
 
 void BluetoothDeviceChooserController::PostSuccessCallback(
     const std::string& device_address) {
-  // TODO(reillyg): Note that this class still maintains ownership of the
-  // |error_callback_|, keeping any bound arguments alive even after it will no
-  // longer be used.
+  DCHECK(callback_);
+
   if (!base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(success_callback_),
-                                    std::move(options_), device_address))) {
+          FROM_HERE,
+          base::BindOnce(std::move(callback_), WebBluetoothResult::SUCCESS,
+                         std::move(options_), device_address))) {
     DLOG(WARNING) << "No TaskRunner.";
   }
 }
 
 void BluetoothDeviceChooserController::PostErrorCallback(
     WebBluetoothResult error) {
-  // TODO(reillyg): Note that this class still maintains ownership of the
-  // |success_callback_|, keeping any bound arguments alive even after it will
-  // no longer be used.
+  DCHECK(callback_);
+
   if (!base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::BindOnce(std::move(error_callback_), error))) {
+          FROM_HERE,
+          base::BindOnce(std::move(callback_), error, /*options=*/nullptr,
+                         /*device_id=*/std::string()))) {
     DLOG(WARNING) << "No TaskRunner.";
   }
 }
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.h b/content/browser/bluetooth/bluetooth_device_chooser_controller.h
index 30ed43e..6931155 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.h
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.h
@@ -33,11 +33,10 @@
 // GetDevice() twice for the same instance will DCHECK.
 class CONTENT_EXPORT BluetoothDeviceChooserController final {
  public:
-  using SuccessCallback =
-      base::OnceCallback<void(blink::mojom::WebBluetoothRequestDeviceOptionsPtr,
+  using Callback =
+      base::OnceCallback<void(blink::mojom::WebBluetoothResult result,
+                              blink::mojom::WebBluetoothRequestDeviceOptionsPtr,
                               const std::string& device_address)>;
-  using ErrorCallback =
-      base::OnceCallback<void(blink::mojom::WebBluetoothResult result)>;
 
   enum class TestScanDurationSetting { IMMEDIATE_TIMEOUT, NEVER_TIMEOUT };
 
@@ -61,17 +60,17 @@
   //   - Checks if the adapter is present.
   //   - Checks if the Web Bluetooth API has been disabled.
   //   - Checks if we are allowed to ask for scanning permission.
-  // If any of the previous checks failed then this function runs
-  // |error_callback| with the corresponding error. Otherwise this function
-  // populates the embedder provided BluetoothChooser with existing devices and
-  // starts a new discovery session.
+  // If any of the previous checks failed then this function runs |callback|
+  // with the corresponding error. Otherwise this function populates the
+  // embedder provided BluetoothChooser with existing devices and starts a new
+  // discovery session.
+  //
   // This function should only be called once per
   // BluetoothDeviceChooserController instance. Calling this function more than
   // once will DCHECK.
   void GetDevice(
       blink::mojom::WebBluetoothRequestDeviceOptionsPtr request_device_options,
-      SuccessCallback success_callback,
-      ErrorCallback error_callback);
+      Callback callback);
 
   // Adds a device to the chooser. Should only be called after GetDevice and
   // before either of the callbacks are run.
@@ -142,9 +141,8 @@
   // Contains the filters and optional services used when scanning.
   blink::mojom::WebBluetoothRequestDeviceOptionsPtr options_;
 
-  // Callbacks to be called with the result of the chooser.
-  SuccessCallback success_callback_;
-  ErrorCallback error_callback_;
+  // Callback to be called with the result of the chooser.
+  Callback callback_;
 
   // The currently opened BluetoothChooser.
   std::unique_ptr<BluetoothChooser> chooser_;
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index 68d91fb..953aefe8 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -1592,16 +1592,10 @@
   device_chooser_controller_ =
       std::make_unique<BluetoothDeviceChooserController>(
           this, render_frame_host_, std::move(adapter));
-
-  // TODO(crbug.com/730593): Remove AdaptCallbackForRepeating() by updating
-  // the callee interface.
-  auto copyable_callback = base::AdaptCallbackForRepeating(std::move(callback));
   device_chooser_controller_->GetDevice(
       std::move(options),
-      base::BindOnce(&WebBluetoothServiceImpl::OnGetDeviceSuccess,
-                     weak_ptr_factory_.GetWeakPtr(), copyable_callback),
-      base::BindOnce(&WebBluetoothServiceImpl::OnGetDeviceFailed,
-                     weak_ptr_factory_.GetWeakPtr(), copyable_callback));
+      base::BindOnce(&WebBluetoothServiceImpl::OnGetDevice,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void WebBluetoothServiceImpl::GetDevicesImpl(
@@ -1816,12 +1810,19 @@
       base::nullopt /* services */);
 }
 
-void WebBluetoothServiceImpl::OnGetDeviceSuccess(
+void WebBluetoothServiceImpl::OnGetDevice(
     RequestDeviceCallback callback,
+    blink::mojom::WebBluetoothResult result,
     blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
     const std::string& device_address) {
   device_chooser_controller_.reset();
 
+  if (result != blink::mojom::WebBluetoothResult::SUCCESS) {
+    // Errors are recorded by |device_chooser_controller_|.
+    std::move(callback).Run(result, /*device=*/nullptr);
+    return;
+  }
+
   const BluetoothDevice* const device = GetAdapter()->GetDevice(device_address);
   if (device == nullptr) {
     DVLOG(1) << "Device " << device_address << " no longer in adapter";
@@ -1856,14 +1857,6 @@
                           std::move(web_bluetooth_device));
 }
 
-void WebBluetoothServiceImpl::OnGetDeviceFailed(
-    RequestDeviceCallback callback,
-    blink::mojom::WebBluetoothResult result) {
-  // Errors are recorded by the *device_chooser_controller_.
-  std::move(callback).Run(result, nullptr /* device */);
-  device_chooser_controller_.reset();
-}
-
 void WebBluetoothServiceImpl::OnCreateGATTConnectionSuccess(
     const blink::WebBluetoothDeviceId& device_id,
     base::TimeTicks start_time,
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.h b/content/browser/bluetooth/web_bluetooth_service_impl.h
index 1791099..436183d 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.h
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -275,13 +275,11 @@
       RemoteServerGetPrimaryServicesCallback callback,
       device::BluetoothDevice* device);
 
-  // Callbacks for BluetoothDeviceChooserController::GetDevice.
-  void OnGetDeviceSuccess(
-      RequestDeviceCallback callback,
-      blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
-      const std::string& device_id);
-  void OnGetDeviceFailed(RequestDeviceCallback callback,
-                         blink::mojom::WebBluetoothResult result);
+  // Callback for BluetoothDeviceChooserController::GetDevice.
+  void OnGetDevice(RequestDeviceCallback callback,
+                   blink::mojom::WebBluetoothResult result,
+                   blink::mojom::WebBluetoothRequestDeviceOptionsPtr options,
+                   const std::string& device_id);
 
   // Callbacks for BluetoothDevice::CreateGattConnection.
   void OnCreateGATTConnectionSuccess(
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 941b008..dc861356 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1333,8 +1333,7 @@
 #endif
   ui::Clipboard::SetAllowedThreads(allowed_clipboard_threads);
 
-  if (GpuDataManagerImpl::GetInstance()->GpuProcessStartAllowed() &&
-      !established_gpu_channel && always_uses_gpu) {
+  if (!established_gpu_channel && always_uses_gpu) {
     TRACE_EVENT_INSTANT0("gpu", "Post task to launch GPU process",
                          TRACE_EVENT_SCOPE_THREAD);
     process_task_runner->PostTask(
diff --git a/content/browser/compositor/DEPS b/content/browser/compositor/DEPS
index c58c6d7..12cf700 100644
--- a/content/browser/compositor/DEPS
+++ b/content/browser/compositor/DEPS
@@ -2,10 +2,4 @@
  "+components/viz/common",
  "+components/viz/host",
  "+ui/platform_window",
-]
-
-specific_include_rules = {
-  "viz_process_transport_factory\.h" : [
-    "+components/viz/service/main/viz_compositor_thread_runner_impl.h",
-  ]
-}
+]
\ No newline at end of file
diff --git a/content/browser/compositor/viz_process_transport_factory.cc b/content/browser/compositor/viz_process_transport_factory.cc
index b1d4cf3..83a23404 100644
--- a/content/browser/compositor/viz_process_transport_factory.cc
+++ b/content/browser/compositor/viz_process_transport_factory.cc
@@ -167,53 +167,30 @@
       std::move(frame_sink_manager_client_receiver), resize_task_runner_,
       std::move(frame_sink_manager));
 
-  if (GpuDataManagerImpl::GetInstance()->GpuProcessStartAllowed()) {
-    // Hop to the IO thread, then send the other side of interface to viz
-    // process.
-    auto connect_on_io_thread =
-        [](mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
-           mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client,
-           const viz::DebugRendererSettings& debug_renderer_settings) {
-          // There should always be a GpuProcessHost instance, and GPU process,
-          // for running the compositor thread. The exception is during shutdown
-          // the GPU process won't be restarted and GpuProcessHost::Get() can
-          // return null.
-          auto* gpu_process_host = GpuProcessHost::Get();
-          if (gpu_process_host) {
-            gpu_process_host->gpu_host()->ConnectFrameSinkManager(
-                std::move(receiver), std::move(client),
-                debug_renderer_settings);
-          }
-        };
-    auto task_runner = base::FeatureList::IsEnabled(features::kProcessHostOnUI)
-                           ? GetUIThreadTaskRunner({})
-                           : GetIOThreadTaskRunner({});
-    task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(connect_on_io_thread,
-                       std::move(frame_sink_manager_receiver),
-                       std::move(frame_sink_manager_client),
-                       GetHostFrameSinkManager()->debug_renderer_settings()));
-  } else {
-    DCHECK(!viz_compositor_thread_);
-
-    // GPU process access is disabled. Start a new thread to run the display
-    // compositor in-process and connect HostFrameSinkManager to it.
-    viz_compositor_thread_ =
-        std::make_unique<viz::VizCompositorThreadRunnerImpl>();
-
-    viz::mojom::FrameSinkManagerParamsPtr params =
-        viz::mojom::FrameSinkManagerParams::New();
-    params->restart_id = viz::BeginFrameSource::kNotRestartableId;
-    base::Optional<uint32_t> activation_deadline_in_frames =
-        switches::GetDeadlineToSynchronizeSurfaces();
-    params->use_activation_deadline = activation_deadline_in_frames.has_value();
-    params->activation_deadline_in_frames =
-        activation_deadline_in_frames.value_or(0u);
-    params->frame_sink_manager = std::move(frame_sink_manager_receiver);
-    params->frame_sink_manager_client = std::move(frame_sink_manager_client);
-    viz_compositor_thread_->CreateFrameSinkManager(std::move(params));
-  }
+  // Hop to the IO thread, then send the other side of interface to viz process.
+  auto connect_on_io_thread =
+      [](mojo::PendingReceiver<viz::mojom::FrameSinkManager> receiver,
+         mojo::PendingRemote<viz::mojom::FrameSinkManagerClient> client,
+         const viz::DebugRendererSettings& debug_renderer_settings) {
+        // There should always be a GpuProcessHost instance, and GPU process,
+        // for running the compositor thread. The exception is during shutdown
+        // the GPU process won't be restarted and GpuProcessHost::Get() can
+        // return null.
+        auto* gpu_process_host = GpuProcessHost::Get();
+        if (gpu_process_host) {
+          gpu_process_host->gpu_host()->ConnectFrameSinkManager(
+              std::move(receiver), std::move(client), debug_renderer_settings);
+        }
+      };
+  auto task_runner = base::FeatureList::IsEnabled(features::kProcessHostOnUI)
+                         ? GetUIThreadTaskRunner({})
+                         : GetIOThreadTaskRunner({});
+  task_runner->PostTask(
+      FROM_HERE,
+      base::BindOnce(connect_on_io_thread,
+                     std::move(frame_sink_manager_receiver),
+                     std::move(frame_sink_manager_client),
+                     GetHostFrameSinkManager()->debug_renderer_settings()));
 }
 
 void VizProcessTransportFactory::CreateLayerTreeFrameSink(
diff --git a/content/browser/compositor/viz_process_transport_factory.h b/content/browser/compositor/viz_process_transport_factory.h
index 01b409b..6d2dc84 100644
--- a/content/browser/compositor/viz_process_transport_factory.h
+++ b/content/browser/compositor/viz_process_transport_factory.h
@@ -11,7 +11,6 @@
 #include "build/build_config.h"
 #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
 #include "components/viz/common/surfaces/subtree_capture_id_allocator.h"
-#include "components/viz/service/main/viz_compositor_thread_runner_impl.h"
 #include "content/browser/compositor/image_transport_factory.h"
 #include "gpu/command_buffer/common/context_result.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
@@ -135,10 +134,6 @@
 
   std::unique_ptr<cc::SingleThreadTaskGraphRunner> task_graph_runner_;
 
-  // Will start and run the VizCompositorThread for using an in-process display
-  // compositor.
-  std::unique_ptr<viz::VizCompositorThreadRunnerImpl> viz_compositor_thread_;
-
   base::flat_map<ui::Compositor*, CompositorData> compositor_data_map_;
 
   viz::FrameSinkIdAllocator frame_sink_id_allocator_;
diff --git a/content/browser/gpu/fallback.md b/content/browser/gpu/fallback.md
index adbd4c1..828c6245 100644
--- a/content/browser/gpu/fallback.md
+++ b/content/browser/gpu/fallback.md
@@ -81,13 +81,6 @@
 enabled.
 
 
-#### `DISABLED`
-
-The GPU process is disabled and will not be launched. The display compositor
-will be run in-process. This is only used on Windows, and on Chromecast audio
-devices.
-
-
 ### Special Cases
 
 There are a few platforms that expect hardware acceleration, with some
@@ -98,7 +91,7 @@
 
 Android requires hardware acceleration, except in the case of Chromecast
 audio-only builds. These run with the flag `--disable-gpu`, and the GPU process
-is not launched.
+is launched in `DISPLAY_COMPOSITOR` mode.
 
 
 #### Fuchsia
diff --git a/content/browser/gpu/gpu_data_manager_impl.cc b/content/browser/gpu/gpu_data_manager_impl.cc
index 2b70aa2..37cf078 100644
--- a/content/browser/gpu/gpu_data_manager_impl.cc
+++ b/content/browser/gpu/gpu_data_manager_impl.cc
@@ -155,11 +155,6 @@
   private_->StartUmaTimer();
 }
 
-bool GpuDataManagerImpl::GpuProcessStartAllowed() const {
-  base::AutoLock auto_lock(lock_);
-  return private_->GpuProcessStartAllowed();
-}
-
 void GpuDataManagerImpl::UpdateGpuInfo(
     const gpu::GPUInfo& gpu_info,
     const base::Optional<gpu::GPUInfo>& gpu_info_for_hardware_gpu) {
diff --git a/content/browser/gpu/gpu_data_manager_impl.h b/content/browser/gpu/gpu_data_manager_impl.h
index 15ee11b..e04aace 100644
--- a/content/browser/gpu/gpu_data_manager_impl.h
+++ b/content/browser/gpu/gpu_data_manager_impl.h
@@ -80,8 +80,6 @@
   // to measure what tasks are in the queue or to move mock time forward.
   void StartUmaTimer();
 
-  bool GpuProcessStartAllowed() const;
-
   bool IsDx12VulkanVersionAvailable() const;
   bool IsGpuFeatureInfoAvailable() const;
 
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index a2ba51561..1e20caac 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -528,13 +528,7 @@
   // process initialization fails or GPU process is too unstable then crash the
   // browser process to reset everything.
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-  // On Windows, with GPU access disabled, the display compositor is run in the
-  // browser process.
-#if defined(OS_WIN)
-  fallback_modes_.push_back(gpu::GpuMode::DISABLED);
-#else
   fallback_modes_.push_back(gpu::GpuMode::DISPLAY_COMPOSITOR);
-#endif  // OS_WIN
   if (SwiftShaderAllowed())
     fallback_modes_.push_back(gpu::GpuMode::SWIFTSHADER);
 #endif  // !OS_ANDROID && !OS_CHROMEOS
@@ -542,11 +536,11 @@
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kDisableGpu)) {
     // Chomecast audio-only builds run with the flag --disable-gpu. The GPU
-    // process should not be started in this case.
+    // process should not access hardware GPU in this case.
 #if BUILDFLAG(IS_CHROMECAST)
 #if BUILDFLAG(IS_CAST_AUDIO_ONLY)
     fallback_modes_.clear();
-    fallback_modes_.push_back(gpu::GpuMode::DISABLED);
+    fallback_modes_.push_back(gpu::GpuMode::DISPLAY_COMPOSITOR);
 #endif
 #elif defined(OS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH)
     CHECK(false) << "GPU acceleration is required on certain platforms!";
@@ -629,10 +623,6 @@
   return gpu_access_allowed_for_hardware_gpu_;
 }
 
-bool GpuDataManagerImplPrivate::GpuProcessStartAllowed() const {
-  return gpu_mode_ != gpu::GpuMode::DISABLED;
-}
-
 void GpuDataManagerImplPrivate::RequestDxdiagDx12VulkanGpuInfoIfNeeded(
     GpuInfoRequest request,
     bool delayed) {
@@ -1564,10 +1554,8 @@
   gpu_mode_ = fallback_modes_.back();
   fallback_modes_.pop_back();
   DCHECK_NE(gpu_mode_, gpu::GpuMode::UNKNOWN);
-  if (gpu_mode_ == gpu::GpuMode::DISPLAY_COMPOSITOR ||
-      gpu_mode_ == gpu::GpuMode::DISABLED) {
+  if (gpu_mode_ == gpu::GpuMode::DISPLAY_COMPOSITOR)
     OnGpuBlocked();
-  }
 }
 
 void GpuDataManagerImplPrivate::RecordCompositingMode() {
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.h b/content/browser/gpu/gpu_data_manager_impl_private.h
index 40b7369..b024f38c 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.h
+++ b/content/browser/gpu/gpu_data_manager_impl_private.h
@@ -44,7 +44,6 @@
   gpu::GPUInfo GetGPUInfoForHardwareGpu() const;
   bool GpuAccessAllowed(std::string* reason) const;
   bool GpuAccessAllowedForHardwareGpu(std::string* reason) const;
-  bool GpuProcessStartAllowed() const;
   void RequestDxdiagDx12VulkanGpuInfoIfNeeded(GpuInfoRequest request,
                                               bool delayed);
   bool IsEssentialGpuInfoAvailable() const;
diff --git a/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc b/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc
index a5a0d01..ee706a41 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc
@@ -291,11 +291,7 @@
   EXPECT_EQ(gpu::GpuMode::HARDWARE_GL, manager->GetGpuMode());
 
   manager->FallBackToNextGpuMode();
-#if defined(OS_WIN)
-  gpu::GpuMode expected_mode = gpu::GpuMode::DISABLED;
-#else
   gpu::GpuMode expected_mode = gpu::GpuMode::DISPLAY_COMPOSITOR;
-#endif  // !OS_WIN
   EXPECT_EQ(expected_mode, manager->GetGpuMode());
 }
 #endif  // !OS_FUCHSIA
@@ -314,7 +310,7 @@
 TEST_F(GpuDataManagerImplPrivateTest, ChromecastStartsWithGpuDisabled) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kDisableGpu);
   ScopedGpuDataManagerImplPrivate manager;
-  EXPECT_EQ(gpu::GpuMode::DISABLED, manager->GetGpuMode());
+  EXPECT_EQ(gpu::GpuMode::DISPLAY_COMPOSITOR, manager->GetGpuMode());
 }
 #endif  // IS_CHROMECAST
 
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index bcae47a..cbb30f89 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -139,6 +139,9 @@
 const char* GetProcessLifetimeUmaName(gpu::GpuMode gpu_mode) {
   switch (gpu_mode) {
     // TODO(rivr): Add separate histograms for the different hardware modes.
+    case gpu::GpuMode::UNKNOWN:
+      NOTREACHED();
+      return nullptr;
     case gpu::GpuMode::HARDWARE_GL:
     case gpu::GpuMode::HARDWARE_METAL:
     case gpu::GpuMode::HARDWARE_VULKAN:
@@ -147,9 +150,6 @@
       return kProcessLifetimeEventsSwiftShader;
     case gpu::GpuMode::DISPLAY_COMPOSITOR:
       return kProcessLifetimeEventsDisplayCompositor;
-    default:
-      NOTREACHED();
-      return nullptr;
   }
 }
 
@@ -548,14 +548,6 @@
                           ? BrowserThread::UI
                           : BrowserThread::IO);
 
-  // Don't grant further access to GPU if it is not allowed.
-  GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance();
-  DCHECK(gpu_data_manager);
-  if (!gpu_data_manager->GpuProcessStartAllowed()) {
-    DLOG(ERROR) << "!GpuDataManagerImpl::GpuProcessStartAllowed()";
-    return nullptr;
-  }
-
   // Do not launch the unsandboxed GPU info collection process if GPU is
   // disabled
   if (kind == GPU_PROCESS_KIND_INFO_COLLECTION) {
@@ -875,7 +867,6 @@
   process_->GetHost()->CreateChannelMojo();
 
   mode_ = GpuDataManagerImpl::GetInstance()->GetGpuMode();
-  DCHECK_NE(mode_, gpu::GpuMode::DISABLED);
 
   if (in_process_) {
     DCHECK_CURRENTLY_ON(base::FeatureList::IsEnabled(features::kProcessHostOnUI)
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
index 0b1c971..880c1e8f 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.cc
@@ -107,9 +107,10 @@
   GetWebContentsImpl()->Activate();
 }
 
-void PictureInPictureWindowControllerImpl::OnWindowDestroyed() {
+void PictureInPictureWindowControllerImpl::OnWindowDestroyed(
+    bool should_pause_video) {
   window_ = nullptr;
-  CloseInternal(true /* should_pause_video */);
+  CloseInternal(should_pause_video);
 }
 
 void PictureInPictureWindowControllerImpl::EmbedSurface(
diff --git a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
index 9cbf444..c9e5e7c5 100644
--- a/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
+++ b/content/browser/picture_in_picture/picture_in_picture_window_controller_impl.h
@@ -60,7 +60,7 @@
   void Show() override;
   void Close(bool should_pause_video) override;
   void CloseAndFocusInitiator() override;
-  void OnWindowDestroyed() override;
+  void OnWindowDestroyed(bool should_pause_video) override;
   OverlayWindow* GetWindowForTesting() override;
   void UpdateLayerBounds() override;
   bool IsPlayerActive() override;
diff --git a/content/browser/renderer_host/clipboard_host_impl.cc b/content/browser/renderer_host/clipboard_host_impl.cc
index 3eabbf27..a1927ae9 100644
--- a/content/browser/renderer_host/clipboard_host_impl.cc
+++ b/content/browser/renderer_host/clipboard_host_impl.cc
@@ -12,6 +12,7 @@
 #include "base/location.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
+#include "base/notreached.h"
 #include "base/pickle.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
@@ -311,6 +312,39 @@
                            std::move(result), std::move(callback)));
 }
 
+void ClipboardHostImpl::ReadPng(ui::ClipboardBuffer clipboard_buffer,
+                                ReadPngCallback callback) {
+  if (!IsRendererPasteAllowed(render_frame_routing_id_)) {
+    std::move(callback).Run(mojo_base::BigBuffer());
+    return;
+  }
+  auto data_dst = CreateDataEndpoint();
+  clipboard_->ReadPng(clipboard_buffer, data_dst.get(),
+                      base::BindOnce(&ClipboardHostImpl::OnReadPng,
+                                     weak_ptr_factory_.GetWeakPtr(),
+                                     clipboard_buffer, std::move(callback)));
+}
+
+void ClipboardHostImpl::OnReadPng(ui::ClipboardBuffer clipboard_buffer,
+                                  ReadPngCallback callback,
+                                  const std::vector<uint8_t>& data) {
+  std::string string_data(
+      reinterpret_cast<const char*>(data.data(), data.data() + data.size()));
+  // TODO(crbug.com/1201018): Create GetPngType() and use it here.
+  PasteIfPolicyAllowed(
+      clipboard_buffer, ui::ClipboardFormatType::GetBitmapType(),
+      std::move(string_data),
+      base::BindOnce(
+          [](std::vector<uint8_t> data, ReadPngCallback callback,
+             ClipboardPasteContentAllowed allowed) {
+            if (!allowed) {
+              std::move(callback).Run(mojo_base::BigBuffer());
+              return;
+            }
+            std::move(callback).Run(mojo_base::BigBuffer(data));
+          },
+          std::move(data), std::move(callback)));
+}
 void ClipboardHostImpl::ReadImage(ui::ClipboardBuffer clipboard_buffer,
                                   ReadImageCallback callback) {
   if (!IsRendererPasteAllowed(render_frame_routing_id_)) {
diff --git a/content/browser/renderer_host/clipboard_host_impl.h b/content/browser/renderer_host/clipboard_host_impl.h
index fd36e65..77a4ae0 100644
--- a/content/browser/renderer_host/clipboard_host_impl.h
+++ b/content/browser/renderer_host/clipboard_host_impl.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -17,6 +18,7 @@
 #include "build/build_config.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/common/content_export.h"
+#include "mojo/public/cpp/base/big_buffer.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "third_party/blink/public/mojom/clipboard/clipboard.mojom.h"
 #include "ui/base/clipboard/clipboard.h"
@@ -143,6 +145,9 @@
   FRIEND_TEST_ALL_PREFIXES(ClipboardHostImplScanTest,
                            PerformPasteIfContentAllowed);
 
+  // TODO(crbug.com/1201018): Integrate this with mojo.
+  using ReadPngCallback = base::OnceCallback<void(mojo_base::BigBuffer)>;
+
   // mojom::ClipboardHost
   void GetSequenceNumber(ui::ClipboardBuffer clipboard_buffer,
                          GetSequenceNumberCallback callback) override;
@@ -159,6 +164,7 @@
                ReadSvgCallback callback) override;
   void ReadRtf(ui::ClipboardBuffer clipboard_buffer,
                ReadRtfCallback callback) override;
+  void ReadPng(ui::ClipboardBuffer clipboard_buffer, ReadPngCallback callback);
   void ReadImage(ui::ClipboardBuffer clipboard_buffer,
                  ReadImageCallback callback) override;
   void ReadFiles(ui::ClipboardBuffer clipboard_buffer,
@@ -197,6 +203,9 @@
       IsClipboardPasteContentAllowedCallback callback,
       bool is_allowed);
 
+  void OnReadPng(ui::ClipboardBuffer clipboard_buffer,
+                 ReadPngCallback callback,
+                 const std::vector<uint8_t>& data);
   void OnReadImage(ui::ClipboardBuffer clipboard_buffer,
                    ReadImageCallback callback,
                    const SkBitmap& bitmap);
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 95932d3..b6409a6 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -25,7 +25,6 @@
 #include "base/process/process.h"
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
-#include "base/values.h"
 #include "build/build_config.h"
 #include "components/download/public/common/download_url_parameters.h"
 #include "components/power_scheduler/power_mode_voter.h"
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index e0f9c5f..a622025 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -69,6 +69,7 @@
 #include "device/fido/pin.h"
 #include "device/fido/public_key.h"
 #include "device/fido/test_callback_receiver.h"
+#include "device/fido/virtual_ctap2_device.h"
 #include "device/fido/virtual_fido_device.h"
 #include "device/fido/virtual_fido_device_factory.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -76,6 +77,7 @@
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom-shared.h"
 #include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "third_party/boringssl/src/include/openssl/ec_key.h"
 #include "third_party/boringssl/src/include/openssl/evp.h"
@@ -3951,7 +3953,10 @@
       : supports_pin_(supports_pin),
         expected_(pins),
         failure_reason_(failure_reason) {}
-  ~PINTestAuthenticatorRequestDelegate() override { DCHECK(expected_.empty()); }
+  ~PINTestAuthenticatorRequestDelegate() override {
+    DCHECK(expected_.empty())
+        << expected_.size() << " unsatisifed PIN expectations";
+  }
 
   bool SupportsPIN() const override { return supports_pin_; }
 
@@ -3959,7 +3964,7 @@
       CollectPINOptions options,
       base::OnceCallback<void(std::u16string)> provide_pin_cb) override {
     DCHECK(supports_pin_);
-    DCHECK(!expected_.empty());
+    DCHECK(!expected_.empty()) << "unexpected PIN request";
     if (expected_.front().reason == PINReason::kChallenge) {
       DCHECK(options.attempts == expected_.front().attempts)
           << "got: " << options.attempts
@@ -3993,6 +3998,11 @@
 
 class PINTestAuthenticatorContentBrowserClient : public ContentBrowserClient {
  public:
+  // ContentBrowserClient:
+  WebAuthenticationDelegate* GetWebAuthenticationDelegate() override {
+    return &web_authentication_delegate;
+  }
+
   std::unique_ptr<AuthenticatorRequestClientDelegate>
   GetWebAuthenticationRequestDelegate(
       RenderFrameHost* render_frame_host) override {
@@ -4000,6 +4010,8 @@
         supports_pin, expected, &failure_reason);
   }
 
+  TestWebAuthenticationDelegate web_authentication_delegate;
+
   bool supports_pin = true;
   std::list<PINExpectation> expected;
   base::Optional<InterestingFailureReason> failure_reason;
@@ -4410,6 +4422,86 @@
   EXPECT_EQ("567890", virtual_device_factory_->mutable_state()->pin);
 }
 
+TEST_F(PINAuthenticatorImplTest, MakeCredUvNotRqd) {
+  // Test that on an authenticator with the makeCredUvNotRqd option enabled,
+  // non-discoverable credentials can be created without requiring a PIN.
+  for (bool discoverable : {false, true}) {
+    for (bool request_uv : {false, true}) {
+      SCOPED_TRACE(testing::Message() << "discoverable=" << discoverable
+                                      << " request_uv=" << request_uv);
+
+      test_client_.web_authentication_delegate.supports_resident_keys = true;
+      ResetVirtualDevice();
+      device::VirtualCtap2Device::Config config;
+      config.u2f_support = true;
+      config.pin_support = true;
+      config.resident_key_support = true;
+      config.pin_uv_auth_token_support = true;
+      config.allow_non_resident_credential_creation_without_uv = true;
+      config.ctap2_versions = {device::Ctap2Version::kCtap2_1};
+      virtual_device_factory_->SetCtap2Config(config);
+      virtual_device_factory_->mutable_state()->pin = kTestPIN;
+      // PIN is still required for discoverable credentials, or if the caller
+      // requests it.
+      if (discoverable || request_uv) {
+        test_client_.expected = {{PINReason::kChallenge, kTestPIN,
+                                  device::kMaxPinRetries,
+                                  device::kMinPinLength}};
+      } else {
+        test_client_.expected = {};
+      }
+
+      PublicKeyCredentialCreationOptionsPtr request = make_credential_options();
+      request->authenticator_selection
+          ->SetUserVerificationRequirementForTesting(
+              request_uv ? device::UserVerificationRequirement::kPreferred
+                         : device::UserVerificationRequirement::kDiscouraged);
+      request->authenticator_selection->SetResidentKeyForTesting(
+          discoverable ? device::ResidentKeyRequirement::kPreferred
+                       : device::ResidentKeyRequirement::kDiscouraged);
+
+      MakeCredentialResult result =
+          AuthenticatorMakeCredential(std::move(request));
+      EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
+      // Requests shouldn't fall back to creating U2F credentials.
+      EXPECT_FALSE(virtual_device_factory_->mutable_state()
+                       ->registrations.begin()
+                       ->second.is_u2f);
+    }
+  }
+}
+
+TEST_F(PINAuthenticatorImplTest, MakeCredUvNotRqdAndAlwaysUv) {
+  // makeCredUvNotRqd and alwaysUv can be combined even though they contradict
+  // each other. In that case, makeCredUvNotRqd should be ignored and PIN/UV
+  // should be collected before creating non-discoverable credentials. If PIN/UV
+  // isn't configured, that should be taken care of first.
+  for (bool pin_set : {false, true}) {
+    SCOPED_TRACE(testing::Message() << "pin_set=" << pin_set);
+
+    ResetVirtualDevice();
+    device::VirtualCtap2Device::Config config;
+    config.pin_support = true;
+    config.pin_uv_auth_token_support = true;
+    config.always_uv = true;
+    config.allow_non_resident_credential_creation_without_uv = true;
+    config.ctap2_versions = {device::Ctap2Version::kCtap2_1};
+    virtual_device_factory_->SetCtap2Config(config);
+    if (pin_set) {
+      virtual_device_factory_->mutable_state()->pin = kTestPIN;
+      test_client_.expected = {{PINReason::kChallenge, kTestPIN,
+                                device::kMaxPinRetries, device::kMinPinLength}};
+    } else {
+      test_client_.expected = {{PINReason::kSet, kTestPIN,
+                                device::kMaxPinRetries, device::kMinPinLength}};
+    }
+
+    MakeCredentialResult result =
+        AuthenticatorMakeCredential(make_credential_options());
+    EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
+  }
+}
+
 TEST_F(PINAuthenticatorImplTest, GetAssertion) {
   typedef int Expectations[3][3];
   // kExpectedWithUISupport enumerates the expected behaviour when the embedder
diff --git a/content/common/partition_alloc_support.cc b/content/common/partition_alloc_support.cc
index 9ffd60b..35f0a640 100644
--- a/content/common/partition_alloc_support.cc
+++ b/content/common/partition_alloc_support.cc
@@ -51,23 +51,27 @@
   }
 }
 
-void EnablePCScanForMallocPartitionsIfNeeded() {
+bool EnablePCScanForMallocPartitionsIfNeeded() {
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_ALLOW_PCSCAN
   DCHECK(base::FeatureList::GetInstance());
   if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan)) {
     base::allocator::EnablePCScan();
+    return true;
   }
 #endif
+  return false;
 }
 
-void EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() {
+bool EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() {
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_ALLOW_PCSCAN
   DCHECK(base::FeatureList::GetInstance());
   if (base::FeatureList::IsEnabled(
           base::features::kPartitionAllocPCScanBrowserOnly)) {
     base::allocator::EnablePCScan();
+    return true;
   }
 #endif
+  return false;
 }
 
 // This function should be executed as early as possible once we can get the
@@ -201,16 +205,24 @@
 
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
-  EnablePCScanForMallocPartitionsIfNeeded();
+  bool scan_enabled = EnablePCScanForMallocPartitionsIfNeeded();
   // No specified process type means this is the Browser process.
   if (process_type.empty()) {
-    EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded();
+    scan_enabled = scan_enabled ||
+                   EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded();
   }
+  if (scan_enabled) {
+    if (base::FeatureList::IsEnabled(
+            base::features::kPartitionAllocPCScanStackScanning)) {
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-  // Notify PCScan about the main thread.
-  base::internal::PCScan::NotifyThreadCreated(base::internal::GetStackTop());
-#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
-  SetProcessNameForPCScan(process_type);
+      base::internal::PCScan::EnableStackScanning();
+      // Notify PCScan about the main thread.
+      base::internal::PCScan::NotifyThreadCreated(
+          base::internal::GetStackTop());
+#endif
+    }
+    SetProcessNameForPCScan(process_type);
+  }
 }
 
 void PartitionAllocSupport::ReconfigureAfterTaskRunnerInit(
diff --git a/content/public/browser/picture_in_picture_window_controller.h b/content/public/browser/picture_in_picture_window_controller.h
index 7df8e56..835cf65 100644
--- a/content/public/browser/picture_in_picture_window_controller.h
+++ b/content/public/browser/picture_in_picture_window_controller.h
@@ -38,7 +38,7 @@
 
   // Called by the window implementation to notify the controller that the
   // window was requested to be closed and destroyed by the system.
-  virtual void OnWindowDestroyed() = 0;
+  virtual void OnWindowDestroyed(bool should_pause_video) = 0;
 
   virtual OverlayWindow* GetWindowForTesting() = 0;
   virtual void UpdateLayerBounds() = 0;
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h
index 389f3da..a342e41 100644
--- a/content/public/browser/render_widget_host.h
+++ b/content/public/browser/render_widget_host.h
@@ -57,72 +57,45 @@
 class RenderWidgetHostObserver;
 class RenderWidgetHostView;
 
-// A RenderWidgetHost manages the browser side of a browser<->renderer
-// HWND connection.  The HWND lives in the browser process, and
-// windows events are sent over IPC to the corresponding object in the
-// renderer.  The renderer paints into shared memory, which we
-// transfer to a backing store and blit to the screen when Windows
-// sends us a WM_PAINT message.
+// A RenderWidgetHost acts as the abstraction for compositing and input
+// functionality. It can exist in 3 different scenarios:
 //
-// How Shutdown Works
+// 1. Popups, which are spawned in situations like <select> menus or
+//    HTML calendar widgets. These are browser-implemented widgets that
+//    are created and owned by WebContents in response to a renderer
+//    request. Since they are divorced from the web page (they are not
+//    clipped to the bounds of the page), they are an independent
+//    compositing and input target. As they are owned by WebContents,
+//    they are also destroyed by WebContents.
 //
-// There are two situations in which this object, a RenderWidgetHost, can be
-// instantiated:
+// 2. Main frames, which are a root frame of a WebContents. These frames
+//    are separated from the browser UI for compositing and input, as the
+//    renderer lives in its own coordinate space. These are attached to
+//    the lifetime of the main frame (currently, owned by the
+//    RenderViewHost, though that should change one day as per
+//    https://crbug.com/419087).
 //
-// 1. By a WebContents as the communication conduit for a rendered web page.
-//    The WebContents instantiates a derived class: RenderViewHost.
-// 2. By a WebContents as the communication conduit for a select widget. The
-//    WebContents instantiates the RenderWidgetHost directly.
+// 3. Child local root frames, which are iframes isolated from their
+//    parent frame for security or performance purposes. This allows
+//    them to be placed in an arbitrary process relative to their
+//    parent frame. Since they are isolated from the parent, they live
+//    in their own coordinate space and are an independent unit of
+//    compositing and input. These are attached to the lifetime of
+//    the local root frame, and are explicitly owned by the
+//    RenderFrameHost.
 //
-// For every WebContents there are several objects in play that need to be
-// properly destroyed or cleaned up when certain events occur.
+// A RenderWidgetHost is platform-agnostic. It defers platform-specific
+// behaviour to its RenderWidgetHostView, which ties the compositing
+// output into the native browser UI. Child local root frames also have
+// a separate "platform" RenderWidgetHostView type at this time, though
+// it stretches the abstraction uncomfortably.
 //
-// - WebContents - the WebContents itself, and its associated HWND.
-// - RenderViewHost - representing the communication conduit with the child
-//   process.
-// - RenderWidgetHostView - the view of the web page content, message handler,
-//   and plugin root.
-//
-// Normally, the WebContents contains a child RenderWidgetHostView that renders
-// the contents of the loaded page. It has a WS_CLIPCHILDREN style so that it
-// does no painting of its own.
-//
-// The lifetime of the RenderWidgetHostView is tied to the render process. If
-// the render process dies, the RenderWidgetHostView goes away and all
-// references to it must become nullptr.
-//
-// RenderViewHost (an owner delegate for RenderWidgetHost) is the conduit used
-// to communicate with the RenderView and is owned by the WebContents. If the
-// render process crashes, the RenderViewHost remains and restarts the render
-// process if needed to continue navigation.
-//
-// Some examples of how shutdown works:
-//
-// For a WebContents, its Destroy method tells the RenderViewHost to
-// shut down the render process and die.
-//
-// When the render process is destroyed it destroys the View: the
-// RenderWidgetHostView, which destroys its HWND and deletes that object.
-//
-// For select popups, the situation is a little different. The RenderWidgetHost
-// associated with the select popup owns the view and itself (is responsible
-// for destroying itself when the view is closed). The WebContents's only
-// responsibility with select popups is to create them when it is told to. When
-// the View is destroyed via an IPC message (triggered when WebCore destroys
-// the popup, e.g. if the user selects one of the options), or because
-// WM_CANCELMODE is received by the view, the View schedules the destruction of
-// the render process. However in this case since there's no WebContents
-// container, when the render process is destroyed, the RenderWidgetHost just
-// deletes itself, which is safe because no one else should have any references
-// to it (the WebContents does not).
-//
-// It should be noted that the RenderViewHost, not the RenderWidgetHost,
-// handles IPC messages relating to the render process going away, since the
-// way a RenderViewHost (WebContents) handles the process dying is different to
-// the way a select popup does. As such the RenderWidgetHostView handles these
-// messages for select popups. This placement is more out of convenience than
-// anything else. When the view is live, these messages are forwarded to it by
-// the RenderWidgetHost's IPC message map.
+// The RenderWidgetHostView has a complex and somewhat broken lifetime as
+// of this writing (see, e.g. https://crbug.com/1161585). It is eagerly
+// created along with the RenderWidgetHost on the first creation, before
+// the renderer process may exist. It is destroyed if the renderer process
+// exits, and not recreated at that time. Then it is recreated lazily when
+// the associated renderer frame/widget is recreated. 
 class CONTENT_EXPORT RenderWidgetHost {
  public:
   // Returns the RenderWidgetHost given its ID and the ID of its render process.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index f6c94b5b..a799656 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -907,6 +907,11 @@
 const base::Feature kWebGLImageChromium{"WebGLImageChromium",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enable WebGPU on gpu serivce side only. This is used with origin trial
+// before gpu service is enabled by default.
+const base::Feature kWebGPUService{"WebGPUService",
+                                   base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable browser mediation API for federated identity interactions.
 const base::Feature kWebID{"WebID", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 9299582..b0567cb 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -222,6 +222,7 @@
 CONTENT_EXPORT extern const base::Feature kWebBundles;
 CONTENT_EXPORT extern const base::Feature kWebBundlesFromNetwork;
 CONTENT_EXPORT extern const base::Feature kWebGLImageChromium;
+CONTENT_EXPORT extern const base::Feature kWebGPUService;
 CONTENT_EXPORT extern const base::Feature kWebID;
 CONTENT_EXPORT extern const base::Feature kWebOtpBackendAuto;
 CONTENT_EXPORT extern const base::Feature kWebPayments;
diff --git a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
index 8bbc057..009ba04 100644
--- a/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
+++ b/content/renderer/media/gpu/gpu_video_accelerator_factories_impl.cc
@@ -394,7 +394,7 @@
     gfx::BufferFormat format,
     gfx::BufferUsage usage) {
   return gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
-      size, format, usage, gpu::kNullSurfaceHandle);
+      size, format, usage, gpu::kNullSurfaceHandle, nullptr);
 }
 bool GpuVideoAcceleratorFactoriesImpl::ShouldUseGpuMemoryBuffersForVideoFrames(
     bool for_media_stream) const {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 0621c1da..e1b0b1ef 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -49,6 +49,7 @@
 #include "base/trace_event/base_tracing.h"
 #include "base/trace_event/trace_event.h"
 #include "base/unguessable_token.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "cc/base/switches.h"
@@ -2279,22 +2280,6 @@
   delete this;
 }
 
-base::Value RenderFrameImpl::GetJavaScriptExecutionResult(
-    v8::Local<v8::Value> result) {
-  if (!result.IsEmpty()) {
-    v8::Local<v8::Context> context = frame_->MainWorldScriptContext();
-    v8::Context::Scope context_scope(context);
-    V8ValueConverterImpl converter;
-    converter.SetDateAllowed(true);
-    converter.SetRegExpAllowed(true);
-    std::unique_ptr<base::Value> new_value =
-        converter.FromV8Value(result, context);
-    if (new_value)
-      return std::move(*new_value);
-  }
-  return base::Value();
-}
-
 void RenderFrameImpl::SnapshotAccessibilityTree(
     mojom::SnapshotAccessibilityTreeParamsPtr params,
     SnapshotAccessibilityTreeCallback callback) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index b9fabc0a..b824a4f 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -29,7 +29,6 @@
 #include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "base/unguessable_token.h"
-#include "base/values.h"
 #include "build/build_config.h"
 #include "cc/input/browser_controls_state.h"
 #include "content/common/buildflags.h"
@@ -886,8 +885,6 @@
   // finally get right encoding of page.
   void UpdateEncoding(blink::WebFrame* frame, const std::string& encoding_name);
 
-  base::Value GetJavaScriptExecutionResult(v8::Local<v8::Value> result);
-
   void InitializeMediaStreamDeviceObserver();
 
   // Sends a FrameHostMsg_BeginNavigation to the browser
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index eff724c6..90ea0f858 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -434,7 +434,7 @@
   std::unique_ptr<gfx::GpuMemoryBuffer> buffer =
       memory_buffer_manager()->CreateGpuMemoryBuffer(
           buffer_size, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
-          gpu::kNullSurfaceHandle);
+          gpu::kNullSurfaceHandle, nullptr);
   ASSERT_TRUE(buffer);
   EXPECT_EQ(format, buffer->GetFormat());
 
diff --git a/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt b/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt
index 74bd6ac..e100345 100644
--- a/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt
@@ -1,6 +1,6 @@
 AXWebArea
-++AXList AXSize={h: 400, w: 400}
-++++AXGroup AXSize={h: 200, w: 400}
+++AXList AXSize={w: 400, h: 400}
+++++AXGroup AXSize={w: 400, h: 200}
 ++++++AXStaticText AXValue='One'
-++++AXGroup AXSize={h: 200, w: 400}
+++++AXGroup AXSize={w: 400, h: 200}
 ++++++AXStaticText AXValue='Two'
diff --git a/content/test/data/accessibility/aria/aria-owns-list.html b/content/test/data/accessibility/aria/aria-owns-list.html
index 3c4ad3c..7bf79d6 100644
--- a/content/test/data/accessibility/aria/aria-owns-list.html
+++ b/content/test/data/accessibility/aria/aria-owns-list.html
@@ -1,5 +1,5 @@
 <!--
-@MAC-ALLOW:AXSize={h: *, w: 400}
+@MAC-ALLOW:AXSize={w: 400, h: *}
 @BLINK-ALLOW:pageSize=(400*
 -->
 <html>
diff --git a/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt b/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt
new file mode 100644
index 0000000..2cbca25
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-combo-box-expand-expected-uia-win7.txt
@@ -0,0 +1,8 @@
+AriaProperties changed on role=combobox
+AriaProperties changed on role=option, name=Apple
+AutomationFocusChanged on role=combobox
+AutomationFocusChanged on role=option, name=Apple
+AutomationFocusChanged on role=option, name=Apple
+ExpandCollapseExpandCollapseState changed on role=combobox
+SelectionItemIsSelected changed on role=option, name=Apple
+SelectionItem_ElementSelected on role=option, name=Apple
diff --git a/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt b/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt
new file mode 100644
index 0000000..5813e2a
--- /dev/null
+++ b/content/test/data/accessibility/event/aria-combo-box-next-expected-uia-win7.txt
@@ -0,0 +1,17 @@
+AriaProperties changed on role=option, name=Apple
+AriaProperties changed on role=option, name=Orange
+AutomationFocusChanged on role=option, name=Apple
+AutomationFocusChanged on role=option, name=Orange
+AutomationFocusChanged on role=option, name=Orange
+SelectionItemIsSelected changed on role=option, name=Apple
+SelectionItemIsSelected changed on role=option, name=Orange
+SelectionItem_ElementSelected on role=option, name=Orange
+=== Start Continuation ===
+AriaProperties changed on role=option, name=Banana
+AriaProperties changed on role=option, name=Orange
+AutomationFocusChanged on role=option, name=Banana
+AutomationFocusChanged on role=option, name=Banana
+AutomationFocusChanged on role=option, name=Orange
+SelectionItemIsSelected changed on role=option, name=Banana
+SelectionItemIsSelected changed on role=option, name=Orange
+SelectionItem_ElementSelected on role=option, name=Banana
diff --git a/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt b/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt
new file mode 100644
index 0000000..d5397e6
--- /dev/null
+++ b/content/test/data/accessibility/event/listbox-next-expected-uia-win7.txt
@@ -0,0 +1,8 @@
+AriaProperties changed on role=option, name=Apple
+AriaProperties changed on role=option, name=Orange
+AutomationFocusChanged on role=option, name=Apple
+AutomationFocusChanged on role=option, name=Orange
+AutomationFocusChanged on role=option, name=Orange
+SelectionItemIsSelected changed on role=option, name=Apple
+SelectionItemIsSelected changed on role=option, name=Orange
+SelectionItem_ElementSelected on role=option, name=Orange
diff --git a/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt b/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt
index 3a362e1c..7f8fe08 100644
--- a/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt
+++ b/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt
@@ -1,15 +1,15 @@
-AXWebArea AXSize={h: 600, w: 800} LocalPosition={x: 0, y: 0}
-++AXGroup AXSize={h: 150, w: 300} LocalPosition={x: 0, y: 0}
-++++AXButton AXSize={h: 50, w: 250} LocalPosition={x: 25, y: 25}
-++AXGroup AXSize={h: 150, w: 300} LocalPosition={x: 0, y: 150}
-++++AXButton AXSize={h: 50, w: 250} LocalPosition={x: 25, y: 175}
-++AXGroup AXSize={h: 150, w: 300} LocalPosition={x: 0, y: 300}
-++++AXGroup AXSize={h: 100, w: 300} LocalPosition={x: 0, y: 300}
-++++++AXWebArea AXSize={h: 100, w: 300} LocalPosition={x: 0, y: 300}
-++++++++AXGroup AXSize={h: 100, w: 300} LocalPosition={x: 0, y: 300}
-++++++++++AXButton AXSize={h: 50, w: 250} LocalPosition={x: 25, y: 325}
-++AXGroup AXSize={h: 150, w: 300} LocalPosition={x: 0, y: 450}
-++++AXGroup AXSize={h: 50, w: 150} LocalPosition={x: 0, y: 450}
-++++++AXWebArea AXSize={h: 50, w: 150} LocalPosition={x: 0, y: 450}
-++++++++AXGroup AXSize={h: 50, w: 150} LocalPosition={x: 0, y: 450}
-++++++++++AXButton AXSize={h: 25, w: 125} LocalPosition={x: 0, y: 450}
+AXWebArea AXSize={w: 800, h: 600} LocalPosition={x: 0, y: 0}
+++AXGroup AXSize={w: 300, h: 150} LocalPosition={x: 0, y: 0}
+++++AXButton AXSize={w: 250, h: 50} LocalPosition={x: 25, y: 25}
+++AXGroup AXSize={w: 300, h: 150} LocalPosition={x: 0, y: 150}
+++++AXButton AXSize={w: 250, h: 50} LocalPosition={x: 25, y: 175}
+++AXGroup AXSize={w: 300, h: 150} LocalPosition={x: 0, y: 300}
+++++AXGroup AXSize={w: 300, h: 100} LocalPosition={x: 0, y: 300}
+++++++AXWebArea AXSize={w: 300, h: 100} LocalPosition={x: 0, y: 300}
+++++++++AXGroup AXSize={w: 300, h: 100} LocalPosition={x: 0, y: 300}
+++++++++++AXButton AXSize={w: 250, h: 50} LocalPosition={x: 25, y: 325}
+++AXGroup AXSize={w: 300, h: 150} LocalPosition={x: 0, y: 450}
+++++AXGroup AXSize={w: 150, h: 50} LocalPosition={x: 0, y: 450}
+++++++AXWebArea AXSize={w: 150, h: 50} LocalPosition={x: 0, y: 450}
+++++++++AXGroup AXSize={w: 150, h: 50} LocalPosition={x: 0, y: 450}
+++++++++++AXButton AXSize={w: 125, h: 25} LocalPosition={x: 0, y: 450}
diff --git a/content/test/gpu/gpu_tests/gpu_process_integration_test.py b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
index 65ef9e7..8f475706 100644
--- a/content/test/gpu/gpu_tests/gpu_process_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_process_integration_test.py
@@ -381,28 +381,22 @@
       self.fail('GPU process not detected')
 
   def _GpuProcess_disable_gpu_and_swiftshader(self, test_path):
-    # Disable SwiftShader, so GPU process should not launch anywhere.
+    # Disable SwiftShader, GPU process should launch for display compositing.
     self.RestartBrowserIfNecessaryWithArgs(
         [cba.DISABLE_GPU, cba.DISABLE_SOFTWARE_RASTERIZER])
     self._NavigateAndWait(test_path)
-
-    # Windows will run the display compositor in the browser process if
-    # accelerated GL and Swiftshader are both disabled.
-    should_have_gpu_process = sys.platform != 'win32'
     has_gpu_process = self.tab.EvaluateJavaScript(
         'chrome.gpuBenchmarking.hasGpuProcess()')
-
-    if should_have_gpu_process and not has_gpu_process:
+    if not has_gpu_process:
       self.fail('GPU process not detected')
-    elif not should_have_gpu_process and has_gpu_process:
-      self.fail('GPU process detected')
 
   def _GpuProcess_disable_swiftshader(self, test_path):
     # Disable SwiftShader, GPU process should be able to launch.
     self.RestartBrowserIfNecessaryWithArgs([cba.DISABLE_SOFTWARE_RASTERIZER])
     self._NavigateAndWait(test_path)
-    if not self.tab.EvaluateJavaScript(
-        'chrome.gpuBenchmarking.hasGpuProcess()'):
+    has_gpu_process = self.tab.EvaluateJavaScript(
+        'chrome.gpuBenchmarking.hasGpuProcess()')
+    if not has_gpu_process:
       self.fail('GPU process not detected')
 
   def _GpuProcess_disabling_workarounds_works(self, test_path):
diff --git a/device/fido/authenticator_supported_options.cc b/device/fido/authenticator_supported_options.cc
index bf1d9c4..c1b4495 100644
--- a/device/fido/authenticator_supported_options.cc
+++ b/device/fido/authenticator_supported_options.cc
@@ -104,6 +104,10 @@
     option_map.emplace(kAlwaysUvKey, true);
   }
 
+  if (options.make_cred_uv_not_required) {
+    option_map.emplace(kMakeCredUvNotRqdKey, true);
+  }
+
   return cbor::Value(std::move(option_map));
 }
 
diff --git a/device/fido/authenticator_supported_options.h b/device/fido/authenticator_supported_options.h
index 37af6a4..1753f6ac 100644
--- a/device/fido/authenticator_supported_options.h
+++ b/device/fido/authenticator_supported_options.h
@@ -102,6 +102,9 @@
   // |always_uv| value of true will make uv=0 get assertion requests return
   // invalid signatures, which is okay for pre-flighting.
   bool always_uv = false;
+  // If true, indicates that the authenticator permits creation of non-resident
+  // credentials without UV.
+  bool make_cred_uv_not_required = false;
 };
 
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/device/fido/device_response_converter.cc b/device/fido/device_response_converter.cc
index ab80b78..173eb3b8 100644
--- a/device/fido/device_response_converter.cc
+++ b/device/fido/device_response_converter.cc
@@ -440,6 +440,14 @@
       options.always_uv = option_map_it->second.GetBool();
     }
 
+    option_map_it = option_map.find(CBOR(kMakeCredUvNotRqdKey));
+    if (option_map_it != option_map.end()) {
+      if (!option_map_it->second.is_bool()) {
+        return base::nullopt;
+      }
+      options.make_cred_uv_not_required = option_map_it->second.GetBool();
+    }
+
     response.options = std::move(options);
   }
 
diff --git a/device/fido/fido_constants.cc b/device/fido/fido_constants.cc
index cbb202f..db46dbd 100644
--- a/device/fido/fido_constants.cc
+++ b/device/fido/fido_constants.cc
@@ -38,6 +38,7 @@
 const char kEnterpriseAttestationKey[] = "ep";
 const char kLargeBlobsKey[] = "largeBlobs";
 const char kAlwaysUvKey[] = "alwaysUv";
+const char kMakeCredUvNotRqdKey[] = "makeCredUvNotRqd";
 
 const base::TimeDelta kDeviceTimeout = base::TimeDelta::FromSeconds(20);
 const base::TimeDelta kU2fRetryDelay = base::TimeDelta::FromMilliseconds(200);
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index b77a4ce..4b1829e8 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -351,6 +351,7 @@
 extern const char kEnterpriseAttestationKey[];
 extern const char kLargeBlobsKey[];
 extern const char kAlwaysUvKey[];
+extern const char kMakeCredUvNotRqdKey[];
 
 // HID transport specific constants.
 constexpr uint32_t kHidBroadcastChannel = 0xffffffff;
diff --git a/device/fido/fido_device_authenticator.cc b/device/fido/fido_device_authenticator.cc
index ef21fb8..c5bbca5 100644
--- a/device/fido/fido_device_authenticator.cc
+++ b/device/fido/fido_device_authenticator.cc
@@ -22,6 +22,7 @@
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_device.h"
 #include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_types.h"
 #include "device/fido/get_assertion_task.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/make_credential_task.h"
@@ -353,12 +354,14 @@
 FidoDeviceAuthenticator::PINUVDispositionForMakeCredential(
     const CtapMakeCredentialRequest& request,
     const FidoRequestHandlerBase::Observer* observer) {
+  DCHECK(device_->SupportedProtocolIsInitialized());
+  DCHECK(options_);
+
   const bool can_collect_pin = observer && observer->SupportsPIN();
   const bool pin_supported = Options()->client_pin_availability !=
                              ClientPinAvailability::kNotSupported;
   const bool pin_configured = Options()->client_pin_availability ==
                               ClientPinAvailability::kSupportedAndPinSet;
-
   const bool uv_configured =
       Options()->user_verification_availability ==
       UserVerificationAvailability::kSupportedAndConfigured;
@@ -371,8 +374,15 @@
       IsConvertibleToU2fRegisterCommand(request) &&
       !ShouldPreferCTAP2EvenIfItNeedsAPIN(request);
 
+  // CTAP 2.1 authenticators on the other hand can indicate that they allow
+  // credential creation with PIN or UV.
+  const bool can_make_ctap2_credential_without_uv =
+      request.user_verification == UserVerificationRequirement::kDiscouraged &&
+      options_->make_cred_uv_not_required;
+
   const UserVerificationRequirement uv_requirement =
-      (pin_configured && !u2f_fallback_possible)
+      (pin_configured && !u2f_fallback_possible &&
+       !can_make_ctap2_credential_without_uv)
           ? UserVerificationRequirement::kRequired
           : request.user_verification;
 
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index 49c3f18..88a8ea7 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -34,6 +34,12 @@
   }
 
   DCHECK_EQ(device->supported_protocol(), ProtocolVersion::kCtap2);
+
+  // No need to fall back to U2F if CTAP2 registrations don't require UV.
+  if (device->device_info()->options.make_cred_uv_not_required) {
+    return false;
+  }
+
   // Don't use U2F for requests that require UV or PIN which U2F doesn't
   // support. Note that |pin_auth| may also be set by GetTouchRequest(), but we
   // don't want those requests to use U2F either if CTAP is supported.
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index 94a85f5b2..895e507 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -33,6 +33,7 @@
 #include "device/fido/device_response_converter.h"
 #include "device/fido/fido_constants.h"
 #include "device/fido/fido_parsing_utils.h"
+#include "device/fido/fido_types.h"
 #include "device/fido/large_blob.h"
 #include "device/fido/opaque_attestation_statement.h"
 #include "device/fido/p256_public_key.h"
@@ -465,7 +466,6 @@
     : VirtualFidoDevice(std::move(state)), config_(config) {
   RegenerateKeyAgreementKey();
 
-  Init({ProtocolVersion::kCtap2});
   std::vector<ProtocolVersion> versions = {ProtocolVersion::kCtap2};
   if (config.u2f_support) {
     versions.emplace_back(ProtocolVersion::kU2f);
@@ -571,6 +571,11 @@
     options.always_uv = true;
   }
 
+  if (config.allow_non_resident_credential_creation_without_uv) {
+    options.make_cred_uv_not_required = true;
+    options_updated = true;
+  }
+
   if (options_updated) {
     device_info_->options = std::move(options);
   }
@@ -785,7 +790,7 @@
 
 base::Optional<CtapDeviceResponseCode>
 VirtualCtap2Device::CheckUserVerification(
-    bool is_make_credential,
+    CheckUserVerificationMode mode,
     const AuthenticatorGetInfoResponse& authenticator_info,
     const std::string& rp_id,
     const base::Optional<std::vector<uint8_t>>& pin_auth,
@@ -899,7 +904,7 @@
           return base::nullopt;
 
         if (!config_.user_verification_succeeds) {
-          if (is_make_credential)
+          if (mode != CheckUserVerificationMode::kGetAssertion)
             return CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid;
           return CtapDeviceResponseCode::kCtap2ErrOperationDenied;
         }
@@ -919,8 +924,9 @@
 
       // "Verify that the pinUvAuthToken has the {mc,ga} permission, if not,
       // return CTAP2_ERR_PIN_AUTH_INVALID."
-      auto permission = is_make_credential ? pin::Permissions::kMakeCredential
-                                           : pin::Permissions::kGetAssertion;
+      auto permission = mode == CheckUserVerificationMode::kGetAssertion
+                            ? pin::Permissions::kGetAssertion
+                            : pin::Permissions::kMakeCredential;
       if (!(mutable_state()->pin_uv_token_permissions &
             static_cast<uint8_t>(permission))) {
         return CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid;
@@ -949,7 +955,7 @@
       uv = true;
     }
 
-    if (is_make_credential && !uv) {
+    if (mode == CheckUserVerificationMode::kMakeCredential && !uv) {
       return CtapDeviceResponseCode::kCtap2ErrPinRequired;
     }
   }
@@ -980,9 +986,15 @@
   CtapMakeCredentialRequest request = std::move(*opt_request);
 
   bool user_verified = false;
+  const CheckUserVerificationMode check_uv_mode =
+      (!request.resident_key_required && !request.pin_auth &&
+       request.user_verification == UserVerificationRequirement::kDiscouraged &&
+       device_info_->options.make_cred_uv_not_required)
+          ? CheckUserVerificationMode::kMakeCredentialUvNotRequired
+          : CheckUserVerificationMode::kMakeCredential;
   const base::Optional<CtapDeviceResponseCode> uv_error = CheckUserVerification(
-      /*is_make_credential=*/true, *device_info_, request.rp.id,
-      request.pin_auth, request.pin_protocol, mutable_state()->pin_token,
+      check_uv_mode, *device_info_, request.rp.id, request.pin_auth,
+      request.pin_protocol, mutable_state()->pin_token,
       request.client_data_hash, request.user_verification,
       /*user_presence_required=*/true, &user_verified);
   if (uv_error != CtapDeviceResponseCode::kSuccess) {
@@ -1260,7 +1272,7 @@
 
   bool user_verified;
   const base::Optional<CtapDeviceResponseCode> uv_error = CheckUserVerification(
-      /*is_make_credential=*/false, *device_info_, request.rp_id,
+      CheckUserVerificationMode::kGetAssertion, *device_info_, request.rp_id,
       request.pin_auth, request.pin_protocol, mutable_state()->pin_token,
       request.client_data_hash, request.user_verification,
       request.user_presence_required, &user_verified);
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index 0bded46..83280377 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -185,6 +185,10 @@
     // with a given code. The actual command won't be executed.
     base::flat_map<CtapRequestCommand, CtapDeviceResponseCode>
         override_response_map;
+
+    // allow_non_resident_credential_creation_without_uv corresponds to the
+    // make_cred_uv_not_required field in AuthenticatorSupportedOptions.
+    bool allow_non_resident_credential_creation_without_uv = false;
   };
 
   VirtualCtap2Device();
@@ -255,8 +259,13 @@
 
   // CheckUserVerification implements the first, common steps of
   // makeCredential and getAssertion from the CTAP2 spec.
+  enum class CheckUserVerificationMode {
+    kGetAssertion,
+    kMakeCredential,
+    kMakeCredentialUvNotRequired,
+  };
   base::Optional<CtapDeviceResponseCode> CheckUserVerification(
-      bool is_make_credential,
+      CheckUserVerificationMode mode,
       const AuthenticatorGetInfoResponse& authenticator_info,
       const std::string& rp_id,
       const base::Optional<std::vector<uint8_t>>& pin_auth,
diff --git a/device/gamepad/gamepad_id_list.cc b/device/gamepad/gamepad_id_list.cc
index fffa1df..9aad703 100644
--- a/device/gamepad/gamepad_id_list.cc
+++ b/device/gamepad/gamepad_id_list.cc
@@ -468,6 +468,7 @@
     {0x18d1, 0x9400, kXInputTypeNone},
     // Lab126, Inc.
     {0x1949, 0x0402, kXInputTypeNone},
+    {0x1949, 0x041a, kXInputTypeXbox360},
     // Gampaq Co.Ltd
     {0x19fa, 0x0607, kXInputTypeNone},
     // ACRUX
@@ -671,6 +672,26 @@
   return GamepadId::kUnknownGamepad;
 }
 
+std::pair<uint16_t, uint16_t> GamepadIdList::GetDeviceIdsFromGamepadId(
+    GamepadId gamepad_id) const {
+  // For most devices, the vendor/product ID pair is unique to a single gamepad
+  // model. The GamepadId for these devices contains the 16-bit vendor and
+  // product IDs packed into a 32-bit value. Some devices use duplicate or
+  // invalid vendor and product IDs and are assigned "fake" GamepadIds that are
+  // not derived from the vendor and product IDs.
+
+  // Handle devices that have been assigned fake GamepadId values.
+  if (gamepad_id == GamepadId::kPowerALicPro)
+    return {0, 0};
+
+  // Handle devices that use packed vendor/product GamepadId values.
+  auto vendor_and_product = static_cast<uint32_t>(gamepad_id);
+  const uint16_t vendor_id = vendor_and_product >> 16;
+  const uint16_t product_id = vendor_and_product & 0xffff;
+  DCHECK(GetGamepadInfo(vendor_id, product_id));
+  return {vendor_id, product_id};
+}
+
 std::vector<std::tuple<uint16_t, uint16_t, XInputType>>
 GamepadIdList::GetGamepadListForTesting() const {
   std::vector<std::tuple<uint16_t, uint16_t, XInputType>> gamepads;
diff --git a/device/gamepad/gamepad_id_list.h b/device/gamepad/gamepad_id_list.h
index 9ba2cf17..751b7618 100644
--- a/device/gamepad/gamepad_id_list.h
+++ b/device/gamepad/gamepad_id_list.h
@@ -36,6 +36,7 @@
   // Fake IDs for devices which report as 0x0000 0x0000
   kPowerALicPro = 0x0000ff00,
   // ID values for supported devices.
+  kAmazonProduct041a = 0x1949041a,
   kAsusTekProduct4500 = 0x0b054500,
   kBroadcomProduct8502 = 0x0a5c8502,
   kDragonRiseProduct0006 = 0x00790006,
@@ -109,6 +110,9 @@
   // Returns a singleton instance of the GamepadId list.
   static GamepadIdList& Get();
 
+  GamepadIdList(const GamepadIdList& entry) = delete;
+  GamepadIdList& operator=(const GamepadIdList& entry) = delete;
+
   // Returns a GamepadId value suitable for identifying a specific model of
   // gamepad. If the gamepad is not contained in the list of known gamepads,
   // returns kUnknownGamepad.
@@ -116,6 +120,9 @@
                          uint16_t vendor_id,
                          uint16_t product_id) const;
 
+  std::pair<uint16_t, uint16_t> GetDeviceIdsFromGamepadId(
+      GamepadId gamepad_id) const;
+
   // Return the XInput flavor (Xbox, Xbox 360, or Xbox One) for the device with
   // the specified |vendor_id| and |product_id|, or kXInputTypeNone if the
   // device is not XInput or the XInput flavor is unknown.
@@ -128,8 +135,6 @@
  private:
   friend base::LazyInstanceTraitsBase<GamepadIdList>;
   GamepadIdList();
-
-  DISALLOW_COPY_AND_ASSIGN(GamepadIdList);
 };
 
 }  // namespace device
diff --git a/device/gamepad/xbox_controller_mac.h b/device/gamepad/xbox_controller_mac.h
index c9dc7ff..7fb9c641 100644
--- a/device/gamepad/xbox_controller_mac.h
+++ b/device/gamepad/xbox_controller_mac.h
@@ -14,10 +14,10 @@
 
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_ioplugininterface.h"
-#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequenced_task_runner.h"
 #include "device/gamepad/abstract_haptic_gamepad.h"
+#include "device/gamepad/gamepad_id_list.h"
 #include "device/gamepad/public/mojom/gamepad.mojom-forward.h"
 
 struct IOUSBDeviceStruct320;
@@ -27,28 +27,6 @@
 
 class XboxControllerMac final : public AbstractHapticGamepad {
  public:
-  static const uint16_t kVendorMicrosoft = 0x045e;
-  static const uint16_t kProductXbox360Controller = 0x028e;
-  static const uint16_t kProductXboxOneController2013 = 0x02d1;
-  static const uint16_t kProductXboxOneController2015 = 0x02dd;
-  static const uint16_t kProductXboxOneEliteController = 0x02e3;
-  static const uint16_t kProductXboxOneSController = 0x02ea;
-  static const uint16_t kProductXboxOneEliteController2 = 0x0b00;
-  static const uint16_t kProductXboxAdaptiveController = 0x0b0a;
-  static const uint16_t kProductXboxSeriesXController = 0x0b12;
-
-  enum ControllerType {
-    UNKNOWN_CONTROLLER,
-    XBOX_360_CONTROLLER,
-    XBOX_ONE_CONTROLLER_2013,
-    XBOX_ONE_CONTROLLER_2015,
-    XBOX_ONE_ELITE_CONTROLLER,
-    XBOX_ONE_ELITE_CONTROLLER_2,
-    XBOX_ONE_S_CONTROLLER,
-    XBOX_ADAPTIVE_CONTROLLER,
-    XBOX_SERIES_X_CONTROLLER,
-  };
-
   enum LEDPattern {
     LED_OFF = 0,
 
@@ -107,6 +85,8 @@
   };
 
   explicit XboxControllerMac(Delegate* delegate);
+  XboxControllerMac(const XboxControllerMac& entry) = delete;
+  XboxControllerMac& operator=(const XboxControllerMac& entry) = delete;
   ~XboxControllerMac() override;
 
   // Open the Xbox controller represented by |service| and perform any necessary
@@ -126,12 +106,12 @@
   void SetVibration(double strong_magnitude, double weak_magnitude) override;
   base::WeakPtr<AbstractHapticGamepad> GetWeakPtr() override;
 
-  UInt32 location_id() { return location_id_; }
-  uint16_t GetVendorId() const;
-  uint16_t GetProductId() const;
-  ControllerType GetControllerType() const;
-  std::string GetControllerTypeString() const;
-  std::string GetIdString() const;
+  uint32_t location_id() const { return location_id_; }
+  GamepadId gamepad_id() const { return gamepad_id_; }
+  XInputType xinput_type() const { return xinput_type_; }
+  uint16_t vendor_id() const { return vendor_id_; }
+  uint16_t product_id() const { return product_id_; }
+  std::string product_name() const { return product_name_; }
   bool SupportsVibration() const;
 
  private:
@@ -209,19 +189,22 @@
   // LED_NUM_PATTERNS if unknown.
   LEDPattern led_pattern_ = LED_NUM_PATTERNS;
 
-  UInt32 location_id_ = 0;
+  uint32_t location_id_ = 0;
 
   Delegate* delegate_ = nullptr;
 
-  ControllerType controller_type_ = UNKNOWN_CONTROLLER;
+  XInputType xinput_type_ = kXInputTypeNone;
+  GamepadId gamepad_id_ = GamepadId::kUnknownGamepad;
+  uint16_t vendor_id_ = 0;
+  uint16_t product_id_ = 0;
+  std::string product_name_;
+
   int read_endpoint_ = 0;
   int control_endpoint_ = 0;
 
   uint8_t counter_ = 0;
 
   base::WeakPtrFactory<XboxControllerMac> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(XboxControllerMac);
 };
 
 }  // namespace device
diff --git a/device/gamepad/xbox_controller_mac.mm b/device/gamepad/xbox_controller_mac.mm
index 1582b31..823e2f7 100644
--- a/device/gamepad/xbox_controller_mac.mm
+++ b/device/gamepad/xbox_controller_mac.mm
@@ -22,9 +22,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_ioobject.h"
 #include "base/sequenced_task_runner.h"
-#include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "device/gamepad/gamepad_id_list.h"
 #include "device/gamepad/gamepad_uma.h"
 
 namespace device {
@@ -299,46 +297,13 @@
                 &normalized_data->axes[2], &normalized_data->axes[3]);
 }
 
-XboxControllerMac::ControllerType ControllerTypeFromIds(uint16_t vendor_id,
-                                                        uint16_t product_id) {
-  if (vendor_id == XboxControllerMac::kVendorMicrosoft) {
-    switch (product_id) {
-      case XboxControllerMac::kProductXbox360Controller:
-        return XboxControllerMac::XBOX_360_CONTROLLER;
-      case XboxControllerMac::kProductXboxOneController2013:
-        return XboxControllerMac::XBOX_ONE_CONTROLLER_2013;
-      case XboxControllerMac::kProductXboxOneController2015:
-        return XboxControllerMac::XBOX_ONE_CONTROLLER_2015;
-      case XboxControllerMac::kProductXboxOneEliteController:
-        return XboxControllerMac::XBOX_ONE_ELITE_CONTROLLER;
-      case XboxControllerMac::kProductXboxOneEliteController2:
-        return XboxControllerMac::XBOX_ONE_ELITE_CONTROLLER_2;
-      case XboxControllerMac::kProductXboxOneSController:
-        return XboxControllerMac::XBOX_ONE_S_CONTROLLER;
-      case XboxControllerMac::kProductXboxSeriesXController:
-        return XboxControllerMac::XBOX_SERIES_X_CONTROLLER;
-      case XboxControllerMac::kProductXboxAdaptiveController:
-        return XboxControllerMac::XBOX_ADAPTIVE_CONTROLLER;
-      default:
-        break;
-    }
-  }
-  return XboxControllerMac::UNKNOWN_CONTROLLER;
-}
+std::string GetDeviceName(io_service_t service) {
+  io_name_t device_name;
+  kern_return_t kr = IORegistryEntryGetName(service, device_name);
+  if (kr != KERN_SUCCESS)
+    return "Unknown Gamepad";
 
-bool ControllerNeedsXboxOneInit(XboxControllerMac::ControllerType type) {
-  switch (type) {
-    case XboxControllerMac::XBOX_ONE_CONTROLLER_2013:
-    case XboxControllerMac::XBOX_ONE_CONTROLLER_2015:
-    case XboxControllerMac::XBOX_ONE_ELITE_CONTROLLER:
-    case XboxControllerMac::XBOX_ONE_ELITE_CONTROLLER_2:
-    case XboxControllerMac::XBOX_ONE_S_CONTROLLER:
-    case XboxControllerMac::XBOX_SERIES_X_CONTROLLER:
-    case XboxControllerMac::XBOX_ADAPTIVE_CONTROLLER:
-      return true;
-    default:
-      return false;
-  }
+  return std::string(device_name);
 }
 
 }  // namespace
@@ -368,20 +333,18 @@
 
 void XboxControllerMac::SetVibration(double strong_magnitude,
                                      double weak_magnitude) {
+  if (!SupportsVibration())
+    return;
+
   // Clamp magnitudes to [0,1]
   strong_magnitude =
       std::max<double>(0.0, std::min<double>(strong_magnitude, 1.0));
   weak_magnitude = std::max<double>(0.0, std::min<double>(weak_magnitude, 1.0));
 
-  if (controller_type_ == XBOX_360_CONTROLLER) {
+  if (xinput_type_ == kXInputTypeXbox360) {
     WriteXbox360Rumble(static_cast<uint8_t>(strong_magnitude * 255.0),
                        static_cast<uint8_t>(weak_magnitude * 255.0));
-  } else if (controller_type_ == XBOX_ONE_CONTROLLER_2013 ||
-             controller_type_ == XBOX_ONE_CONTROLLER_2015 ||
-             controller_type_ == XBOX_ONE_ELITE_CONTROLLER ||
-             controller_type_ == XBOX_ONE_ELITE_CONTROLLER_2 ||
-             controller_type_ == XBOX_ONE_S_CONTROLLER ||
-             controller_type_ == XBOX_SERIES_X_CONTROLLER) {
+  } else if (xinput_type_ == kXInputTypeXboxOne) {
     WriteXboxOneRumble(static_cast<uint8_t>(strong_magnitude * 255.0),
                        static_cast<uint8_t>(weak_magnitude * 255.0));
   }
@@ -404,33 +367,48 @@
   if (!SUCCEEDED(res) || !device_)
     return OPEN_FAILED;
 
-  uint16_t vendor_id;
-  kr = (*device_)->GetDeviceVendor(device_, &vendor_id);
+  kr = (*device_)->GetDeviceVendor(device_, &vendor_id_);
   if (kr != KERN_SUCCESS)
     return OPEN_FAILED;
 
-  uint16_t product_id;
-  kr = (*device_)->GetDeviceProduct(device_, &product_id);
+  kr = (*device_)->GetDeviceProduct(device_, &product_id_);
   if (kr != KERN_SUCCESS)
     return OPEN_FAILED;
 
   // Record a connected XInput gamepad. Non-XInput devices are recorded
   // elsewhere.
-  DCHECK_NE(kXInputTypeNone,
-            GamepadIdList::Get().GetXInputType(vendor_id, product_id));
-  GamepadId gamepad_id = GamepadIdList::Get().GetGamepadId(
-      base::StringPiece(), vendor_id, product_id);
-  RecordConnectedGamepad(gamepad_id);
+  xinput_type_ = GamepadIdList::Get().GetXInputType(vendor_id_, product_id_);
+  DCHECK_NE(xinput_type_, kXInputTypeNone);
+  gamepad_id_ = GamepadIdList::Get().GetGamepadId(base::StringPiece(),
+                                                  vendor_id_, product_id_);
+  RecordConnectedGamepad(gamepad_id_);
 
-  // Only genuine Microsoft Xbox, Xbox 360, and Xbox One devices are supported.
-  if (vendor_id != kVendorMicrosoft)
-    return OPEN_FAILED;
-
-  controller_type_ = ControllerTypeFromIds(vendor_id, product_id);
+  // Get the product name. Use hard-coded strings for older devices to avoid
+  // breaking applications that expect these strings. New devices should use
+  // the product name reported by the device.
+  switch (gamepad_id_) {
+    case GamepadId::kMicrosoftProduct028e:  // Xbox 360
+      product_name_ = "Xbox 360 Controller";
+      break;
+    case GamepadId::kMicrosoftProduct02d1:  // Xbox One
+    case GamepadId::kMicrosoftProduct02dd:  // Xbox One, 2015 firmware
+    case GamepadId::kMicrosoftProduct02e3:  // Xbox One Elite
+    case GamepadId::kMicrosoftProduct0b00:  // Xbox One Elite v2
+    case GamepadId::kMicrosoftProduct02ea:  // Xbox One S
+    case GamepadId::kMicrosoftProduct0b0a:  // Xbox Adaptive
+      product_name_ = "Xbox One Controller";
+      break;
+    case GamepadId::kMicrosoftProduct0b12:  // Xbox Series X
+      product_name_ = "Xbox Series X Controller";
+      break;
+    default:
+      product_name_ = GetDeviceName(service);
+      break;
+  }
 
   IOUSBFindInterfaceRequest request;
-  switch (controller_type_) {
-    case XBOX_360_CONTROLLER:
+  switch (xinput_type_) {
+    case kXInputTypeXbox360:
       read_endpoint_ = kXbox360ReadEndpoint;
       control_endpoint_ = kXbox360ControlEndpoint;
       request.bInterfaceClass = 255;
@@ -438,13 +416,7 @@
       request.bInterfaceProtocol = 1;
       request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
       break;
-    case XBOX_ONE_CONTROLLER_2013:
-    case XBOX_ONE_CONTROLLER_2015:
-    case XBOX_ONE_ELITE_CONTROLLER:
-    case XBOX_ONE_ELITE_CONTROLLER_2:
-    case XBOX_ONE_S_CONTROLLER:
-    case XBOX_SERIES_X_CONTROLLER:
-    case XBOX_ADAPTIVE_CONTROLLER:
+    case kXInputTypeXboxOne:
       read_endpoint_ = kXboxOneReadEndpoint;
       control_endpoint_ = kXboxOneControlEndpoint;
       request.bInterfaceClass = 255;
@@ -577,7 +549,8 @@
       if (direction != kUSBOut)
         return OPEN_FAILED;
 
-      if (ControllerNeedsXboxOneInit(controller_type_) && !WriteXboxOneInit())
+      // Xbox One controllers require an initialization packet.
+      if (xinput_type_ == kXInputTypeXboxOne && !WriteXboxOneInit())
         return OPEN_FAILED;
     }
   }
@@ -612,76 +585,9 @@
   }
 }
 
-uint16_t XboxControllerMac::GetVendorId() const {
-  switch (controller_type_) {
-    case XBOX_360_CONTROLLER:
-    case XBOX_ONE_CONTROLLER_2013:
-    case XBOX_ONE_CONTROLLER_2015:
-    case XBOX_ONE_ELITE_CONTROLLER:
-    case XBOX_ONE_ELITE_CONTROLLER_2:
-    case XBOX_ONE_S_CONTROLLER:
-    case XBOX_SERIES_X_CONTROLLER:
-    case XBOX_ADAPTIVE_CONTROLLER:
-      return kVendorMicrosoft;
-    default:
-      return 0;
-  }
-}
-
-uint16_t XboxControllerMac::GetProductId() const {
-  switch (controller_type_) {
-    case XBOX_360_CONTROLLER:
-      return kProductXbox360Controller;
-    case XBOX_ONE_CONTROLLER_2013:
-      return kProductXboxOneController2013;
-    case XBOX_ONE_CONTROLLER_2015:
-      return kProductXboxOneController2015;
-    case XBOX_ONE_ELITE_CONTROLLER:
-      return kProductXboxOneEliteController;
-    case XBOX_ONE_ELITE_CONTROLLER_2:
-      return kProductXboxOneEliteController2;
-    case XBOX_ONE_S_CONTROLLER:
-      return kProductXboxOneSController;
-    case XBOX_SERIES_X_CONTROLLER:
-      return kProductXboxSeriesXController;
-    case XBOX_ADAPTIVE_CONTROLLER:
-      return kProductXboxAdaptiveController;
-    default:
-      return 0;
-  }
-}
-
-XboxControllerMac::ControllerType XboxControllerMac::GetControllerType() const {
-  return controller_type_;
-}
-
-std::string XboxControllerMac::GetControllerTypeString() const {
-  switch (controller_type_) {
-    case XBOX_360_CONTROLLER:
-      return "Xbox 360 Controller";
-    case XBOX_ONE_CONTROLLER_2013:
-    case XBOX_ONE_CONTROLLER_2015:
-    case XBOX_ONE_ELITE_CONTROLLER:
-    case XBOX_ONE_ELITE_CONTROLLER_2:
-    case XBOX_ONE_S_CONTROLLER:
-    case XBOX_ADAPTIVE_CONTROLLER:
-      return "Xbox One Controller";
-    case XBOX_SERIES_X_CONTROLLER:
-      return "Xbox Series X Controller";
-    default:
-      return "Unrecognized Controller";
-  }
-}
-
-std::string XboxControllerMac::GetIdString() const {
-  return base::StringPrintf("%s (STANDARD GAMEPAD Vendor: %04x Product: %04x)",
-                            GetControllerTypeString().c_str(), GetVendorId(),
-                            GetProductId());
-}
-
 bool XboxControllerMac::SupportsVibration() const {
   // The Xbox Adaptive Controller has no vibration actuators.
-  return controller_type_ != XBOX_ADAPTIVE_CONTROLLER;
+  return gamepad_id_ != GamepadId::kMicrosoftProduct0b0a;
 }
 
 // static
@@ -711,9 +617,10 @@
     return;
   }
 
-  if (controller->GetControllerType() == XBOX_360_CONTROLLER)
+  auto xinput_type = controller->xinput_type();
+  if (xinput_type == kXInputTypeXbox360)
     controller->ProcessXbox360Packet(bytes_read);
-  else
+  else if (xinput_type == kXInputTypeXboxOne)
     controller->ProcessXboxOnePacket(bytes_read);
 
   // Queue up another read.
@@ -789,7 +696,9 @@
         return;
       }
       Data normalized_data;
-      if (controller_type_ == XboxControllerMac::XBOX_SERIES_X_CONTROLLER) {
+      if (gamepad_id_ == GamepadId::kMicrosoftProduct0b12) {
+        // Xbox Series X received a firmware update that modified the input
+        // report. Distinguish the old and new reports by size.
         if (length == kXboxSeriesXOldFirmwareButtonDataBytes) {
           XboxSeriesXOldFirmwareButtonData* data =
               reinterpret_cast<XboxSeriesXOldFirmwareButtonData*>(buffer);
diff --git a/device/gamepad/xbox_data_fetcher_mac.cc b/device/gamepad/xbox_data_fetcher_mac.cc
index 752e50b..f242852d 100644
--- a/device/gamepad/xbox_data_fetcher_mac.cc
+++ b/device/gamepad/xbox_data_fetcher_mac.cc
@@ -19,9 +19,27 @@
 #include "base/mac/foundation_util.h"
 #include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "device/gamepad/gamepad_id_list.h"
 
 namespace device {
 
+namespace {
+
+// XboxDataFetcher recognizes the following devices connected over USB.
+constexpr GamepadId kSupportedDeviceIds[]{
+    GamepadId::kAmazonProduct041a,     // Amazon Luna Controller
+    GamepadId::kMicrosoftProduct028e,  // Xbox 360
+    GamepadId::kMicrosoftProduct02d1,  // Xbox One
+    GamepadId::kMicrosoftProduct02dd,  // Xbox One, 2015 firmware
+    GamepadId::kMicrosoftProduct02e3,  // Xbox Elite
+    GamepadId::kMicrosoftProduct02ea,  // Xbox One S
+    GamepadId::kMicrosoftProduct0b00,  // Xbox Elite 2
+    GamepadId::kMicrosoftProduct0b0a,  // Xbox Adaptive
+    GamepadId::kMicrosoftProduct0b12,  // Xbox Series X
+};
+
+}  // namespace
+
 XboxDataFetcher::PendingController::PendingController(
     XboxDataFetcher* fetcher,
     std::unique_ptr<XboxControllerMac> controller)
@@ -176,68 +194,17 @@
 
   listening_ = true;
 
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxOneEliteController,
-          &xbox_one_elite_device_added_iter_,
-          &xbox_one_elite_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxOneEliteController2,
-          &xbox_one_elite_2_device_added_iter_,
-          &xbox_one_elite_2_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxOneController2013,
-          &xbox_one_2013_device_added_iter_,
-          &xbox_one_2013_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxOneController2015,
-          &xbox_one_2015_device_added_iter_,
-          &xbox_one_2015_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxSeriesXController,
-          &xbox_series_x_device_added_iter_,
-          &xbox_series_x_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxOneSController,
-          &xbox_one_s_device_added_iter_, &xbox_one_s_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXbox360Controller,
-          &xbox_360_device_added_iter_, &xbox_360_device_removed_iter_))
-    return false;
-
-  if (!RegisterForDeviceNotifications(
-          XboxControllerMac::kVendorMicrosoft,
-          XboxControllerMac::kProductXboxAdaptiveController,
-          &xbox_adaptive_device_added_iter_,
-          &xbox_adaptive_device_removed_iter_))
-    return false;
+  for (const auto& entry : kSupportedDeviceIds) {
+    auto ids = GamepadIdList::Get().GetDeviceIdsFromGamepadId(entry);
+    if (!RegisterForDeviceNotifications(ids.first, ids.second))
+      return false;
+  }
 
   return true;
 }
 
-bool XboxDataFetcher::RegisterForDeviceNotifications(
-    int vendor_id,
-    int product_id,
-    base::mac::ScopedIOObject<io_iterator_t>* added_iter,
-    base::mac::ScopedIOObject<io_iterator_t>* removed_iter) {
+bool XboxDataFetcher::RegisterForDeviceNotifications(int vendor_id,
+                                                     int product_id) {
   base::ScopedCFTypeRef<CFNumberRef> vendor_cf(
       CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor_id));
   base::ScopedCFTypeRef<CFNumberRef> product_cf(
@@ -254,24 +221,28 @@
   // things balanced.
   CFRetain(matching_dict);
   IOReturn ret;
+  base::mac::ScopedIOObject<io_iterator_t> added_iterator;
   ret = IOServiceAddMatchingNotification(port_.get(), kIOFirstMatchNotification,
                                          matching_dict, DeviceAdded, this,
-                                         added_iter->InitializeInto());
+                                         added_iterator.InitializeInto());
   if (ret != kIOReturnSuccess) {
     LOG(ERROR) << "Error listening for Xbox controller add events: " << ret;
     return false;
   }
-  DeviceAdded(this, added_iter->get());
+  DeviceAdded(this, added_iterator.get());
+  device_event_iterators_.push_back(std::move(added_iterator));
 
   CFRetain(matching_dict);
+  base::mac::ScopedIOObject<io_iterator_t> removed_iterator;
   ret = IOServiceAddMatchingNotification(port_.get(), kIOTerminatedNotification,
                                          matching_dict, DeviceRemoved, this,
-                                         removed_iter->InitializeInto());
+                                         removed_iterator.InitializeInto());
   if (ret != kIOReturnSuccess) {
     LOG(ERROR) << "Error listening for Xbox controller remove events: " << ret;
     return false;
   }
-  DeviceRemoved(this, removed_iter->get());
+  DeviceRemoved(this, removed_iterator.get());
+  device_event_iterators_.push_back(std::move(removed_iterator));
   return true;
 }
 
@@ -324,8 +295,11 @@
   controller->SetLEDPattern((XboxControllerMac::LEDPattern)(
       XboxControllerMac::LED_FLASH_TOP_LEFT + controller->location_id()));
 
-  state->data.SetID(base::UTF8ToUTF16(controller->GetIdString()));
-  state->data.mapping = GamepadMapping::kStandard;
+  GamepadDataFetcher::UpdateGamepadStrings(
+      controller->product_name(), controller->vendor_id(),
+      controller->product_id(),
+      /*has_standard_mapping=*/true, state->data);
+
   state->data.connected = true;
   state->data.axes_length = 4;
   state->data.buttons_length = 17;
@@ -381,13 +355,13 @@
     pad.buttons[i].pressed = data.buttons[i - 2];
     pad.buttons[i].value = data.buttons[i - 2] ? 1.0f : 0.0f;
   }
-  if (controller->GetControllerType() ==
-      XboxControllerMac::XBOX_360_CONTROLLER) {
+  if (controller->xinput_type() == kXInputTypeXbox360) {
+    // Map the Xbox button on Xbox 360 to buttons[16].
     pad.buttons[16].pressed = data.buttons[14];
     pad.buttons[16].value = data.buttons[14] ? 1.0f : 0.0f;
   }
-  if (controller->GetControllerType() ==
-      XboxControllerMac::XBOX_SERIES_X_CONTROLLER) {
+  if (controller->gamepad_id() == GamepadId::kMicrosoftProduct0b12) {
+    // Map the Share button on Xbox Series X to buttons[17].
     pad.buttons[17].pressed = data.buttons[14];
     pad.buttons[17].value = data.buttons[14] ? 1.0f : 0.0f;
     pad.buttons_length = 18;
diff --git a/device/gamepad/xbox_data_fetcher_mac.h b/device/gamepad/xbox_data_fetcher_mac.h
index 824672d..8b657759 100644
--- a/device/gamepad/xbox_data_fetcher_mac.h
+++ b/device/gamepad/xbox_data_fetcher_mac.h
@@ -15,7 +15,6 @@
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/mac/scoped_ionotificationportref.h"
 #include "base/mac/scoped_ioobject.h"
-#include "base/macros.h"
 #include "device/gamepad/gamepad_data_fetcher.h"
 #include "device/gamepad/xbox_controller_mac.h"
 
@@ -29,6 +28,8 @@
       Factory;
 
   XboxDataFetcher();
+  XboxDataFetcher(const XboxDataFetcher& entry) = delete;
+  XboxDataFetcher& operator=(const XboxDataFetcher& entry) = delete;
   ~XboxDataFetcher() override;
 
   GamepadSource source() override;
@@ -51,6 +52,8 @@
   struct PendingController {
    public:
     PendingController(XboxDataFetcher*, std::unique_ptr<XboxControllerMac>);
+    PendingController(const PendingController& entry);
+    PendingController& operator=(const PendingController& entry);
     ~PendingController();
 
     XboxDataFetcher* fetcher;
@@ -67,11 +70,7 @@
 
   bool TryOpenDevice(io_service_t iterator);
   bool RegisterForNotifications();
-  bool RegisterForDeviceNotifications(
-      int vendor_id,
-      int product_id,
-      base::mac::ScopedIOObject<io_iterator_t>* added_iter,
-      base::mac::ScopedIOObject<io_iterator_t>* removed_iter);
+  bool RegisterForDeviceNotifications(int vendor_id, int product_id);
   bool RegisterForInterestNotifications(io_service_t service,
                                         PendingController* pending);
   void PendingControllerBecameAvailable(io_service_t service,
@@ -105,25 +104,12 @@
   // do need to maintain a reference to it so we can invalidate it.
   CFRunLoopSourceRef source_ = nullptr;
   base::mac::ScopedIONotificationPortRef port_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_360_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_360_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_2013_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_2013_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_2015_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_2015_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_elite_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_elite_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_elite_2_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t>
-      xbox_one_elite_2_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_s_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_one_s_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_series_x_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_series_x_device_removed_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_adaptive_device_added_iter_;
-  base::mac::ScopedIOObject<io_iterator_t> xbox_adaptive_device_removed_iter_;
 
-  DISALLOW_COPY_AND_ASSIGN(XboxDataFetcher);
+  // Iterators returned by calls to IOServiceAddMatchingNotification for
+  // kIOFirstMatchNotification (connection) and kIOTerminatedNotification
+  // (disconnection) events. These iterators are not referenced directly but
+  // must be kept alive in order to continue to receive notifications.
+  std::vector<base::mac::ScopedIOObject<io_iterator_t>> device_event_iterators_;
 };
 
 }  // namespace device
diff --git a/device/vr/openxr/openxr_input_helper.cc b/device/vr/openxr/openxr_input_helper.cc
index f86e7551..bc4fa0f 100644
--- a/device/vr/openxr/openxr_input_helper.cc
+++ b/device/vr/openxr/openxr_input_helper.cc
@@ -138,6 +138,7 @@
         static_cast<OpenXrHandednessType>(i), instance, session_,
         path_helper_.get(), extension_helper, &bindings));
     controller_states_[i].primary_button_pressed = false;
+    controller_states_[i].squeeze_button_pressed = false;
   }
 
   for (auto it = bindings.begin(); it != bindings.end(); it++) {
@@ -172,6 +173,7 @@
   if (XR_FAILED(SyncActions(predicted_display_time))) {
     for (OpenXrControllerState& state : controller_states_) {
       state.primary_button_pressed = false;
+      state.squeeze_button_pressed = false;
     }
     return input_states;
   }
@@ -181,6 +183,8 @@
 
     base::Optional<GamepadButton> primary_button =
         controller->GetButton(OpenXrButtonType::kTrigger);
+    base::Optional<GamepadButton> squeeze_button =
+        controller->GetButton(OpenXrButtonType::kSqueeze);
 
     // Having a trigger button is the minimum for an webxr input.
     // No trigger button indicates input is not connected.
@@ -207,6 +211,15 @@
         controller_states_[i].primary_button_pressed &&
         !state->primary_input_pressed;
     controller_states_[i].primary_button_pressed = state->primary_input_pressed;
+    if (squeeze_button) {
+      state->primary_squeeze_pressed = squeeze_button.value().pressed;
+      state->primary_squeeze_clicked =
+          controller_states_[i].squeeze_button_pressed &&
+          !state->primary_squeeze_pressed;
+      controller_states_[i].squeeze_button_pressed =
+          state->primary_squeeze_pressed;
+    }
+
     state->gamepad = GetWebXRGamepad(*controller);
 
     // Return hand state if controller is a hand and the hand tracking feature
diff --git a/device/vr/openxr/openxr_input_helper.h b/device/vr/openxr/openxr_input_helper.h
index b8be503..b2c0bc46 100644
--- a/device/vr/openxr/openxr_input_helper.h
+++ b/device/vr/openxr/openxr_input_helper.h
@@ -56,6 +56,7 @@
   struct OpenXrControllerState {
     OpenXrController controller;
     bool primary_button_pressed;
+    bool squeeze_button_pressed;
   };
   std::array<OpenXrControllerState,
              static_cast<size_t>(OpenXrHandednessType::kCount)>
diff --git a/docs/android_build_instructions.md b/docs/android_build_instructions.md
index 191fab88..4222400d 100644
--- a/docs/android_build_instructions.md
+++ b/docs/android_build_instructions.md
@@ -181,35 +181,36 @@
 
 The Google Play Store allows apps to send customized `.apk` or `.aab` files
 depending on the version of Android running on a device. Chrome uses this
-feature to target 4 different versions using 4 different ninja targets:
+feature to package optimized versions for different OS versions.
 
-1. `chrome_public_apk` (ChromePublic.apk)
-   * Used for local development and tests (simpler than using bundle targets).
-   * Same configuration as chrome_modern_public_bundle.
-2. `chrome_modern_public_bundle` (MonochromePublic.aab)
+1. `chrome_modern_public_bundle` (ChromeModernPublic.aab)
    * `minSdkVersion=21` (Lollipop).
    * Uses [Crazy Linker](https://cs.chromium.org/chromium/src/base/android/linker/BUILD.gn?rcl=6bb29391a86f2be58c626170156cbfaa2cbc5c91&l=9).
    * Stores native library with "crazy." prefix to prevent extraction.
-3. `monochrome_public_bundle` (MonochromePublic.aab)
+   * WebView packaged independently (`system_webview_bundle`).
+2. `monochrome_public_bundle` (MonochromePublic.aab)
    * `minSdkVersion=24` (Nougat).
-   * Contains both WebView and Chrome within the same APK.
-     * This bundle is larger than ChromeModern, but much smaller than SUM(SystemWebView, ChromeModern)
+   * Contains both Chrome and WebView (to save disk space).
    * Does not use Crazy Linker (WebView requires system linker).
-     * But system linker supports crazy linker features now anyways.
-4. `trichrome_chrome_bundle` and `trichrome_library_apk` (TrichromeChrome.aab and TrichromeLibrary.apk)
-   * `minSdkVersion=Q` (Q).
-   * TrichromeChrome contains only the Chrome code that is not shared with WebView.
-   * TrichromeLibrary contains the shared code and is a "static shared library APK".
-   * Stores libmonochrome.so uncompressed within TrichromeLibrary.apk.
-   * Uses `android_dlopen_ext` to load native libraries with shared RELRO's
+3. `trichrome_chrome_bundle` (TrichromeChrome.aab)
+   * `minSdkVersion=29` (Android 10).
+   * Native code shared with WebView through a "Static Shared Library APK": `trichrome_library_apk` 
+   * Corresponding WebView target: `trichrome_webview_bundle`
+4. `chrome_public_apk` (ChromePublic.apk)
+   * Used for only local development and tests (simpler than using bundle
+     targets).
+   * Same configuration as chrome_modern_public_bundle, except without
+     separating things into modules.
 
-**Note**: These instructions use `chrome_public_apk`, but any of the other
-targets can be substituted.
-
-**Note**: These targets are actually the open-source equivalents to the
-closed-source targets that get shipped to the Play Store.
-
-**Note**: For more in-depth differences, see [android_native_libraries.md](android_native_libraries.md).
+*** note
+**Notes:**
+* These instructions use `chrome_public_apk`, but any of the other targets can
+  be substituted.
+* For more about bundles, see [android_dynamic feature modules.md](android_dynamic_feature_modules.md).
+* For more about native library packaging & loading, see [android_native_libraries.md](android_native_libraries.md).
+* There are closed-source equivalents to these targets (for Googlers), which
+  are identical but link in some extra code.
+***
 
 ## Updating your checkout
 
diff --git a/docs/android_dynamic_feature_modules.md b/docs/android_dynamic_feature_modules.md
index 8bd10eb..3d41f855 100644
--- a/docs/android_dynamic_feature_modules.md
+++ b/docs/android_dynamic_feature_modules.md
@@ -1,12 +1,44 @@
-# Dynamic Feature Modules (DFMs)
-
-[Android App bundles and Dynamic Feature Modules (DFMs)](https://developer.android.com/guide/app-bundle)
-is a Play Store feature that allows delivering pieces of an app when they are
-needed rather than at install time. We use DFMs to modularize Chrome and make
-Chrome's install size smaller.
+# App Bundles and Dynamic Feature Modules (DFMs)
 
 [TOC]
 
+## About Bundles
+[Android App bundles] is a Play Store feature that allows packaging an app as
+multiple `.apk` files, known as "splits". Bundles are zip files with an `.aab`
+extension. See [android_build_instructions.md#multiple-chrome-targets] for a
+list of buildable bundle targets.
+
+Bundles provide three main advantages over monolithic `.apk` files:
+1. Language resources are split into language-specific `.apk` files, known as
+   "resource splits". Delivering only the active languages reduces the overhead
+   of UI strings.
+2. Features can be packaged into lazily loaded `.apk` files, known as
+   "feature splits". Feature splits have no performance overhead until used.
+   * Except on versions prior to Android O, where support for
+     [android:isolatedSplits] was added. On prior versions, all installed splits
+     are loaded on application launch.
+   * E.g.: The `chrome` feature split makes renderers more efficient by having
+     them not load Java code that they don't need.
+   * E.g.: The `image_editor` feature split defers loading of Share-related code
+     until a Share action is performed.
+   * See also: [go/isolated-splits-dev-guide] (Googlers only).
+3. Feature splits can be downloaded on-demand, saving disk space for users that
+   do not need the functionality they provide. These are known as
+   "Dynamic feature modules", or "DFMs".
+   * E.g. Chrome's VR support is packaged in this way, via the `vr` module.
+
+You can inspect which `.apk` files are produced by a bundle target via:
+```
+out/Default/bin/${target_name} build-bundle-apks --output-apks foo.apks
+unzip -l foo.apks
+```
+
+The remainder of this doc focuses on DFMs.
+
+[android_build_instructions.md#multiple-chrome-targets]: android_build_instructions.md#multiple-chrome-targets
+[Android App Bundles]: https://developer.android.com/guide/app-bundle
+[android:isolatedSplits]: https://developer.android.com/reference/android/R.attr#isolatedSplits
+[go/isolated-splits-dev-guide]: http://go/isolated-splits-dev-guide
 
 ## Limitations
 
@@ -26,13 +58,6 @@
 `YOUR_FEATURE_NAME`.
 ***
 
-*** note
-**Note:** Chrome's bundles use the [android:isolatedSplits](https://developer.android.com/reference/android/R.attr#isolatedSplits)
-attribute. For more details and advice on when to create a DFM, see
-[go/isolated-splits-dev-guide](http://go/isolated-splits-dev-guide)
-**(Googlers only)**.
-***
-
 ### Reference DFM
 
 In addition to this guide, the
@@ -725,7 +750,6 @@
 DFM for users to get it. There are three install options: _on-demand_,
 _deferred_ and _conditional_.
 
-
 #### On-demand install
 
 On-demand requesting a module will try to download and install the
@@ -861,6 +885,13 @@
 </manifest>
 ```
 
+You can also specify no conditions to have your module always installed.
+You might want to do this in order to delay the performance implications
+of loading your module until its first use (true only on Android O+ where
+[android:isolatedSplits](https://developer.android.com/reference/android/R.attr#isolatedSplits)
+is supported. See [go/isolated-splits-dev-guide](http://go/isolated-splits-dev-guide)
+(googlers only).
+
 ### Metrics
 
 After adding your module to `AndroidFeatureModuleName` (see
@@ -878,12 +909,11 @@
   install your module successfully after on-demand requesting it.
 
 
-### Integration test APK and Android K support
+### chrome_public_apk and Integration Tests
 
-On Android K we still ship an APK. To make the Foo feature available on Android
-K add its code to the APK build. For this, add the `java` target to
-the `chrome_public_common_apk_or_module_tmpl` in
-`//chrome/android/chrome_public_apk_tmpl.gni` like so:
+To make the Foo feature available in the non-bundle `chrome_public_apk`
+target, add the `java` target to the `chrome_public_common_apk_or_module_tmpl`
+in `//chrome/android/chrome_public_apk_tmpl.gni` like so:
 
 ```gn
 template("chrome_public_common_apk_or_module_tmpl") {
@@ -899,6 +929,5 @@
 }
 ```
 
-This will also add Foo's Java to the integration test APK. You may also have to
-add `java` as a dependency of `chrome_test_java` if you want to call into Foo
-from test code.
+You may also have to add `java` as a dependency of `chrome_test_java` if you want
+to call into Foo from test code.
diff --git a/extensions/browser/api/storage/session_storage_manager.cc b/extensions/browser/api/storage/session_storage_manager.cc
index 022b6c24..a100f4e4 100644
--- a/extensions/browser/api/storage/session_storage_manager.cc
+++ b/extensions/browser/api/storage/session_storage_manager.cc
@@ -69,7 +69,7 @@
 
 std::map<std::string, const base::Value*>
 SessionStorageManager::ExtensionStorage::Get(
-    const std::vector<std::string>& keys) {
+    const std::vector<std::string>& keys) const {
   std::map<std::string, const base::Value*> values;
   for (auto& key : keys) {
     auto value_it = values_.find(key);
@@ -80,7 +80,7 @@
 }
 
 std::map<std::string, const base::Value*>
-SessionStorageManager::ExtensionStorage::GetAll() {
+SessionStorageManager::ExtensionStorage::GetAll() const {
   std::map<std::string, const base::Value*> values;
   for (auto& value : values_) {
     values.emplace(value.first, &value.second->value);
@@ -145,9 +145,26 @@
         base::Optional<base::Value>(std::move(value.second->value)), nullptr);
     changes.push_back(std::move(change));
   }
+
+  used_total_ = 0;
   values_.clear();
 }
 
+size_t SessionStorageManager::ExtensionStorage::GetBytesInUse(
+    const std::vector<std::string>& keys) const {
+  size_t total = 0;
+  for (const auto& key : keys) {
+    auto value_it = values_.find(key);
+    if (value_it != values_.end())
+      total += value_it->second->size;
+  }
+  return total;
+}
+
+size_t SessionStorageManager::ExtensionStorage::GetTotalBytesInUse() const {
+  return used_total_;
+}
+
 // Implementation of SessionStorageManager.
 SessionStorageManager::SessionStorageManager(size_t quota_bytes_per_extension)
     : quota_bytes_per_extension_(quota_bytes_per_extension) {}
@@ -220,4 +237,26 @@
     storage_it->second->Clear(changes);
 }
 
+size_t SessionStorageManager::GetBytesInUse(const ExtensionId& extension_id,
+                                            const std::string& key) const {
+  return GetBytesInUse(extension_id, std::vector<std::string>(1, key));
+}
+
+size_t SessionStorageManager::GetBytesInUse(
+    const ExtensionId& extension_id,
+    const std::vector<std::string>& keys) const {
+  auto storage_it = extensions_storage_.find(extension_id);
+  if (storage_it != extensions_storage_.end())
+    return storage_it->second->GetBytesInUse(keys);
+  return 0;
+}
+
+size_t SessionStorageManager::GetTotalBytesInUse(
+    const ExtensionId& extension_id) const {
+  auto storage_it = extensions_storage_.find(extension_id);
+  if (storage_it != extensions_storage_.end())
+    return storage_it->second->GetTotalBytesInUse();
+  return 0;
+}
+
 }  // namespace extensions
diff --git a/extensions/browser/api/storage/session_storage_manager.h b/extensions/browser/api/storage/session_storage_manager.h
index 6159455..0e0a1dd 100644
--- a/extensions/browser/api/storage/session_storage_manager.h
+++ b/extensions/browser/api/storage/session_storage_manager.h
@@ -81,6 +81,19 @@
   void Clear(const ExtensionId& extension_id,
              std::vector<ValueChange>& changes);
 
+  // Gets the total amount of bytes being used by multiple keys and values of
+  // the given `extension_id`.
+  size_t GetBytesInUse(const ExtensionId& extension_id,
+                       const std::vector<std::string>& keys) const;
+
+  // Gets the total amount of bytes being used by a key for the given
+  // `extension_id`.
+  size_t GetBytesInUse(const ExtensionId& extension_id,
+                       const std::string& key) const;
+
+  // Gets the total amount of bytes being used by the given `extension_id`.
+  size_t GetTotalBytesInUse(const ExtensionId& extension_id) const;
+
  private:
   struct SessionValue {
     SessionValue(base::Value value, size_t size);
@@ -98,10 +111,10 @@
 
     // Returns a map with keys and values found in storage.
     std::map<std::string, const base::Value*> Get(
-        const std::vector<std::string>& keys);
+        const std::vector<std::string>& keys) const;
 
     // Returns a map with all keys and values found in storage.
-    std::map<std::string, const base::Value*> GetAll();
+    std::map<std::string, const base::Value*> GetAll() const;
 
     // Stores the input values in the values map, and updates the changes list
     // if a change occurs.
@@ -115,6 +128,12 @@
     // Clears the storage.
     void Clear(std::vector<ValueChange>& changes);
 
+    // Gets the total amount of bytes being used by multiple keys and values.
+    size_t GetBytesInUse(const std::vector<std::string>& keys) const;
+
+    // Gets the total amount of bytes stored.
+    size_t GetTotalBytesInUse() const;
+
    private:
     // Returns the updated usage for the input values and adds them as session
     // values if there is available space, or returns the max quota bytes.
diff --git a/extensions/browser/api/storage/session_storage_manager_unittest.cc b/extensions/browser/api/storage/session_storage_manager_unittest.cc
index b25fe26..133f7c9 100644
--- a/extensions/browser/api/storage/session_storage_manager_unittest.cc
+++ b/extensions/browser/api/storage/session_storage_manager_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/values.h"
 #include "extensions/browser/api/storage/session_storage_manager.h"
+#include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -20,6 +21,9 @@
 
 using ValueChangeList =
     std::vector<extensions::SessionStorageManager::ValueChange>;
+using testing::AllOf;
+using testing::Ge;
+using testing::Le;
 
 }  // namespace
 
@@ -332,4 +336,102 @@
           .empty());
 }
 
+TEST_F(SessionStorageManagerUnittest, GetBytesInUse) {
+  // `value` is a string with 32 bytes in size. Due to reserved space and
+  // overhead of members (and also likely dependent on OS/compiler variations),
+  // the actual memory usage is >32 bytes. It should be less than 100 bytes,
+  // though, so we use this as a benchmark.
+  const base::Value value(std::string(32, 'a'));
+  // GetBytesInUse includes both the key and the value it stores. The key's size
+  // is estimated using short string optimization, which could return a size of
+  // 0. Since this test uses short keys, we will use the value size as the size
+  // bounds.
+  const size_t kOneEntryLowerBound = 32u;
+  const size_t kOneEntryUpperBound = 100u;
+
+  EXPECT_EQ(0u, manager_->GetBytesInUse(kTestExtensionId1, "key1"));
+  EXPECT_EQ(0u, manager_->GetBytesInUse(kTestExtensionId1, "key2"));
+  EXPECT_EQ(0u, manager_->GetBytesInUse(kTestExtensionId1, "key3"));
+  EXPECT_EQ(
+      0u, manager_->GetBytesInUse(kTestExtensionId1, {"key1", "key2", "key3"}));
+  EXPECT_EQ(0u, manager_->GetTotalBytesInUse(kTestExtensionId1));
+
+  {
+    ValueChangeList changes;
+    std::map<std::string, base::Value> values;
+    values.emplace("key1", value.Clone());
+    EXPECT_TRUE(manager_->Set(kTestExtensionId1, std::move(values), changes));
+  }
+
+  EXPECT_THAT(manager_->GetBytesInUse(kTestExtensionId1, "key1"),
+              AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key2"), 0u);
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key3"), 0u);
+  EXPECT_THAT(
+      manager_->GetBytesInUse(kTestExtensionId1, {"key1", "key2", "key3"}),
+      AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+  EXPECT_THAT(manager_->GetTotalBytesInUse(kTestExtensionId1),
+              AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+
+  {
+    ValueChangeList changes;
+    std::map<std::string, base::Value> values;
+    values.emplace("key2", value.Clone());
+    EXPECT_TRUE(manager_->Set(kTestExtensionId1, std::move(values), changes));
+  }
+
+  EXPECT_THAT(manager_->GetBytesInUse(kTestExtensionId1, "key1"),
+              AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+  EXPECT_THAT(manager_->GetBytesInUse(kTestExtensionId1, "key2"),
+              AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key3"), 0u);
+  EXPECT_THAT(
+      manager_->GetBytesInUse(kTestExtensionId1, {"key1", "key2", "key3"}),
+      AllOf(Ge(2u * kOneEntryLowerBound), Le(2u * kOneEntryUpperBound)));
+  EXPECT_THAT(
+      manager_->GetTotalBytesInUse(kTestExtensionId1),
+      AllOf(Ge(2u * kOneEntryLowerBound), Le(2u * kOneEntryUpperBound)));
+
+  {
+    ValueChangeList changes;
+    manager_->Remove(kTestExtensionId1, "key1", changes);
+  }
+
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key1"), 0u);
+  EXPECT_THAT(manager_->GetBytesInUse(kTestExtensionId1, "key2"),
+              AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key3"), 0u);
+  EXPECT_THAT(
+      manager_->GetBytesInUse(kTestExtensionId1, {"key1", "key2", "key3"}),
+      AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+  EXPECT_THAT(manager_->GetTotalBytesInUse(kTestExtensionId1),
+              AllOf(Ge(kOneEntryLowerBound), Le(kOneEntryUpperBound)));
+
+  {
+    ValueChangeList changes;
+    manager_->Clear(kTestExtensionId1, changes);
+  }
+
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key1"), 0u);
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key2"), 0u);
+  EXPECT_EQ(manager_->GetBytesInUse(kTestExtensionId1, "key3"), 0u);
+  EXPECT_EQ(
+      manager_->GetBytesInUse(kTestExtensionId1, {"key1", "key2", "key3"}), 0u);
+  EXPECT_EQ(manager_->GetTotalBytesInUse(kTestExtensionId1), 0u);
+
+  // The key should also count towards used storage. This ensures that
+  // extensions don't game our quota limit by using the key itself to store
+  // data.
+  const std::string massive_key(500, 'a');
+  {
+    ValueChangeList changes;
+    std::map<std::string, base::Value> values;
+    values.emplace(massive_key, base::Value());
+    EXPECT_TRUE(manager_->Set(kTestExtensionId1, std::move(values), changes));
+  }
+
+  EXPECT_THAT(manager_->GetBytesInUse(kTestExtensionId1, massive_key),
+              AllOf(Ge(500u), Le(600u)));
+}
+
 }  // namespace extensions
diff --git a/gpu/command_buffer/client/gpu_memory_buffer_manager.h b/gpu/command_buffer/client/gpu_memory_buffer_manager.h
index 8422a9fa..b4663b1 100644
--- a/gpu/command_buffer/client/gpu_memory_buffer_manager.h
+++ b/gpu/command_buffer/client/gpu_memory_buffer_manager.h
@@ -7,11 +7,16 @@
 
 #include <memory>
 
+#include "base/threading/platform_thread.h"
 #include "gpu/gpu_export.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
+namespace base {
+class WaitableEvent;
+}
+
 namespace gpu {
 
 struct SyncToken;
@@ -22,12 +27,15 @@
   virtual ~GpuMemoryBufferManager();
 
   // Creates a GpuMemoryBuffer that can be shared with another process. It can
-  // be called on any thread.
+  // be called on any thread. If |shutdown_event| is specified, then the browser
+  // implementation (HostGpuMemoryBufferManager) will cancel pending create
+  // calls if this is signalled.
   virtual std::unique_ptr<gfx::GpuMemoryBuffer> CreateGpuMemoryBuffer(
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) = 0;
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) = 0;
 
   // Associates destruction sync point with |buffer|. It can be called on any
   // thread.
diff --git a/gpu/command_buffer/client/raster_cmd_helper_autogen.h b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
index dd4a13dc..e304002 100644
--- a/gpu/command_buffer/client/raster_cmd_helper_autogen.h
+++ b/gpu/command_buffer/client/raster_cmd_helper_autogen.h
@@ -236,9 +236,8 @@
                                           GLuint dst_sk_alpha_type,
                                           GLint shm_id,
                                           GLuint shm_offset,
+                                          GLuint color_space_offset,
                                           GLuint pixels_offset,
-                                          GLint result_shm_id,
-                                          GLuint result_shm_offset,
                                           const GLbyte* mailbox) {
   const uint32_t size =
       raster::cmds::ReadbackImagePixelsINTERNALImmediate::ComputeSize();
@@ -247,8 +246,8 @@
           raster::cmds::ReadbackImagePixelsINTERNALImmediate>(size);
   if (c) {
     c->Init(src_x, src_y, dst_width, dst_height, row_bytes, dst_sk_color_type,
-            dst_sk_alpha_type, shm_id, shm_offset, pixels_offset, result_shm_id,
-            result_shm_offset, mailbox);
+            dst_sk_alpha_type, shm_id, shm_offset, color_space_offset,
+            pixels_offset, mailbox);
   }
 }
 
diff --git a/gpu/command_buffer/client/raster_implementation.cc b/gpu/command_buffer/client/raster_implementation.cc
index ba08253b..769a5f1 100644
--- a/gpu/command_buffer/client/raster_implementation.cc
+++ b/gpu/command_buffer/client/raster_implementation.cc
@@ -359,6 +359,37 @@
   CHECK_EQ(0, raster_implementation_->use_count_);
 }
 
+struct RasterImplementation::AsyncReadbackRequest {
+  AsyncReadbackRequest(void* dst_pixels,
+                       GLuint dst_size,
+                       GLuint pixels_offset,
+                       GLuint finished_query,
+                       std::unique_ptr<ScopedMappedMemoryPtr> shared_memory,
+                       base::OnceCallback<void(GrSurfaceOrigin, bool)> callback)
+      : dst_pixels(dst_pixels),
+        dst_size(dst_size),
+        pixels_offset(pixels_offset),
+        shared_memory(std::move(shared_memory)),
+        callback(std::move(callback)),
+        query(finished_query),
+        done(false),
+        readback_successful(false) {}
+  ~AsyncReadbackRequest() {
+    // RasterDecoder::ReadbackImagePixels always stores the result pixels with
+    // top left origin.
+    std::move(callback).Run(kTopLeft_GrSurfaceOrigin, readback_successful);
+  }
+
+  void* dst_pixels;
+  GLuint dst_size;
+  GLuint pixels_offset;
+  std::unique_ptr<ScopedMappedMemoryPtr> shared_memory;
+  base::OnceCallback<void(GrSurfaceOrigin, bool)> callback;
+  GLuint query;
+  bool done;
+  bool readback_successful;
+};
+
 RasterImplementation::RasterImplementation(
     RasterCmdHelper* helper,
     TransferBufferInterface* transfer_buffer,
@@ -406,6 +437,9 @@
   // shared will fail and abort (ie, it will stop running).
   WaitForCmd();
 
+  // Run callbacks for all pending AsyncReadbackRequests to inform them of the
+  // failure
+  CancelRequests();
   query_tracker_.reset();
 
   // Make sure the commands make it the service.
@@ -1123,11 +1157,12 @@
   GLuint total_size =
       pixels_offset + base::bits::Align(src_size, sizeof(uint64_t));
 
-  ScopedSharedMemoryPtr scoped_shared_memory(total_size, transfer_buffer_,
-                                             mapped_memory_.get(), helper());
-  GLint shm_id = scoped_shared_memory.shm_id();
-  GLuint shm_offset = scoped_shared_memory.offset();
-  void* address = scoped_shared_memory.address();
+  std::unique_ptr<ScopedSharedMemoryPtr> scoped_shared_memory =
+      std::make_unique<ScopedSharedMemoryPtr>(total_size, transfer_buffer_,
+                                              mapped_memory_.get(), helper());
+  GLint shm_id = scoped_shared_memory->shm_id();
+  GLuint shm_offset = scoped_shared_memory->offset();
+  void* address = scoped_shared_memory->address();
 
   if (src_info.colorSpace()) {
     size_t bytes_written = src_info.colorSpace()->writeToMemory(address);
@@ -1274,14 +1309,157 @@
   return decode_sync_token;
 }
 
+void RasterImplementation::ReadbackImagePixelsINTERNAL(
+    const gpu::Mailbox& source_mailbox,
+    const SkImageInfo& dst_info,
+    GLuint dst_row_bytes,
+    int src_x,
+    int src_y,
+    base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done,
+    void* dst_pixels) {
+  DCHECK_GE(dst_row_bytes, dst_info.minRowBytes());
+
+  // We can't use GetResultAs<>() to store our result because it uses
+  // TransferBuffer under the hood and this function is potentially
+  // asynchronous. Instead, store the result at the beginning of the shared
+  // memory we allocate to transfer pixels.
+  GLuint color_space_offset = base::bits::AlignUp(
+      sizeof(cmds::ReadbackImagePixelsINTERNALImmediate::Result),
+      sizeof(uint64_t));
+
+  // Add the size of the SkColorSpace while maintaining 8-byte alignment.
+  GLuint pixels_offset = color_space_offset;
+  if (dst_info.colorSpace()) {
+    pixels_offset = base::bits::AlignUp(
+        color_space_offset + dst_info.colorSpace()->writeToMemory(nullptr),
+        sizeof(uint64_t));
+  }
+
+  GLuint dst_size = dst_info.computeByteSize(dst_row_bytes);
+  GLuint total_size =
+      pixels_offset + base::bits::Align(dst_size, sizeof(uint64_t));
+
+  std::unique_ptr<ScopedMappedMemoryPtr> scoped_shared_memory =
+      std::make_unique<ScopedMappedMemoryPtr>(total_size, helper(),
+                                              mapped_memory_.get());
+  GLint shm_id = scoped_shared_memory->shm_id();
+  GLuint shm_offset = scoped_shared_memory->offset();
+  void* shm_address = scoped_shared_memory->address();
+
+  // Readback success/failure result is stored at the beginning of the shared
+  // memory region. Client is responsible for initialization so we do so here.
+  auto* readback_result =
+      static_cast<cmds::ReadbackImagePixelsINTERNALImmediate::Result*>(
+          shm_address);
+  *readback_result = 0;
+
+  if (dst_info.colorSpace()) {
+    size_t bytes_written = dst_info.colorSpace()->writeToMemory(
+        static_cast<uint8_t*>(shm_address) + color_space_offset);
+    DCHECK_LE(bytes_written + color_space_offset, pixels_offset);
+  }
+
+  bool is_async = !!readback_done;
+
+  GLuint query;
+  if (is_async) {
+    GenQueriesEXT(1, &query);
+
+    // This query is currently sufficient because the readback implementation in
+    // RasterDecoder is synchronous. If that call is changed to be asynchronous
+    // later we'll need to implement a more sophisticated query.
+    BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, query);
+  }
+
+  helper_->ReadbackImagePixelsINTERNALImmediate(
+      src_x, src_y, dst_info.width(), dst_info.height(), dst_row_bytes,
+      dst_info.colorType(), dst_info.alphaType(), shm_id, shm_offset,
+      color_space_offset, pixels_offset, source_mailbox.name);
+
+  if (is_async) {
+    EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
+
+    auto request = std::make_unique<AsyncReadbackRequest>(
+        dst_pixels, dst_size, pixels_offset, query,
+        std::move(scoped_shared_memory), std::move(readback_done));
+    auto* request_ptr = request.get();
+    request_queue_.push(std::move(request));
+    SignalQuery(query,
+                base::BindOnce(&RasterImplementation::OnAsyncReadbackDone,
+                               base::Unretained(this), request_ptr));
+  } else {
+    WaitForCmd();
+
+    if (!*readback_result)
+      return;
+
+    memcpy(dst_pixels, static_cast<uint8_t*>(shm_address) + pixels_offset,
+           dst_size);
+  }
+}
+
+void RasterImplementation::OnAsyncReadbackDone(
+    AsyncReadbackRequest* finished_request) {
+  finished_request->done = true;
+
+  // Only process requests in the order they were sent, regardless of when they
+  // finish.
+  while (!request_queue_.empty()) {
+    auto& request = request_queue_.front();
+    if (!request->done)
+      break;
+
+    // Readback success/failure is stored at the beginning of the shared memory
+    // region.
+    auto* result =
+        static_cast<cmds::ReadbackImagePixelsINTERNALImmediate::Result*>(
+            request->shared_memory->address());
+    if (*result) {
+      memcpy(request->dst_pixels,
+             static_cast<uint8_t*>(request->shared_memory->address()) +
+                 request->pixels_offset,
+             request->dst_size);
+      request->readback_successful = true;
+    }
+
+    if (request_queue_.front()->query)
+      DeleteQueriesEXT(1, &request->query);
+
+    request_queue_.pop();
+  }
+}
+
+void RasterImplementation::CancelRequests() {
+  while (!request_queue_.empty()) {
+    if (request_queue_.front()->query)
+      DeleteQueriesEXT(1, &request_queue_.front()->query);
+
+    request_queue_.pop();
+  }
+}
+
 void RasterImplementation::ReadbackARGBPixelsAsync(
     const gpu::Mailbox& source_mailbox,
     GLenum source_target,
-    const gfx::Size& dst_size,
+    GrSurfaceOrigin source_origin,
+    const SkImageInfo& dst_info,
+    GLuint dst_row_bytes,
     unsigned char* out,
-    GLenum format,
-    base::OnceCallback<void(bool)> readback_done) {
-  NOTREACHED();
+    base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done) {
+  DCHECK(!!readback_done);
+  // Note: It's possible the GL implementation supports other readback
+  // types. However, as of this writing, no caller of this method will
+  // request a different |color_type| (i.e., requiring using some other GL
+  // format).
+  if (dst_info.colorType() != kRGBA_8888_SkColorType &&
+      dst_info.colorType() != kBGRA_8888_SkColorType) {
+    std::move(readback_done)
+        .Run(kTopLeft_GrSurfaceOrigin, /*readback_sucess=*/false);
+    return;
+  }
+
+  ReadbackImagePixelsINTERNAL(source_mailbox, dst_info, dst_row_bytes, 0, 0,
+                              std::move(readback_done), out);
 }
 
 void RasterImplementation::ReadbackYUVPixelsAsync(
@@ -1309,43 +1487,9 @@
     int src_x,
     int src_y,
     void* dst_pixels) {
-  DCHECK_GE(dst_row_bytes, dst_info.minRowBytes());
-
-  // Get the size of the SkColorSpace while maintaining 8-byte alignment.
-  GLuint pixels_offset = 0;
-  if (dst_info.colorSpace()) {
-    pixels_offset = base::bits::Align(
-        dst_info.colorSpace()->writeToMemory(nullptr), sizeof(uint64_t));
-  }
-
-  GLuint dst_size = dst_info.computeByteSize(dst_row_bytes);
-  GLuint total_size =
-      pixels_offset + base::bits::Align(dst_size, sizeof(uint64_t));
-
-  ScopedSharedMemoryPtr scoped_shared_memory(total_size, transfer_buffer_,
-                                             mapped_memory_.get(), helper());
-  GLint shm_id = scoped_shared_memory.shm_id();
-  GLuint shm_offset = scoped_shared_memory.offset();
-  void* address = scoped_shared_memory.address();
-  auto result =
-      GetResultAs<cmds::ReadbackImagePixelsINTERNALImmediate::Result>();
-  *result = 0;
-
-  if (dst_info.colorSpace()) {
-    size_t bytes_written = dst_info.colorSpace()->writeToMemory(address);
-    DCHECK_LE(bytes_written, pixels_offset);
-  }
-
-  helper_->ReadbackImagePixelsINTERNALImmediate(
-      src_x, src_y, dst_info.width(), dst_info.height(), dst_row_bytes,
-      dst_info.colorType(), dst_info.alphaType(), shm_id, shm_offset,
-      pixels_offset, GetResultShmId(), result.offset(), source_mailbox.name);
-  WaitForCmd();
-
-  if (!*result)
-    return;
-
-  memcpy(dst_pixels, static_cast<uint8_t*>(address) + pixels_offset, dst_size);
+  ReadbackImagePixelsINTERNAL(
+      source_mailbox, dst_info, dst_row_bytes, src_x, src_y,
+      base::OnceCallback<void(GrSurfaceOrigin, bool)>(), dst_pixels);
 }
 
 void RasterImplementation::IssueImageDecodeCacheEntryCreation(
diff --git a/gpu/command_buffer/client/raster_implementation.h b/gpu/command_buffer/client/raster_implementation.h
index b8ca305..1ecbd3f 100644
--- a/gpu/command_buffer/client/raster_implementation.h
+++ b/gpu/command_buffer/client/raster_implementation.h
@@ -160,10 +160,11 @@
   void ReadbackARGBPixelsAsync(
       const gpu::Mailbox& source_mailbox,
       GLenum source_target,
-      const gfx::Size& dst_size,
+      GrSurfaceOrigin source_origin,
+      const SkImageInfo& dst_info,
+      GLuint dst_row_bytes,
       unsigned char* out,
-      GLenum format,
-      base::OnceCallback<void(bool)> readback_done) override;
+      base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done) override;
   void ReadbackYUVPixelsAsync(
       const gpu::Mailbox& source_mailbox,
       GLenum source_target,
@@ -348,6 +349,20 @@
       SyncToken* decode_sync_token,
       ClientDiscardableHandle handle);
 
+  void ReadbackImagePixelsINTERNAL(
+      const gpu::Mailbox& source_mailbox,
+      const SkImageInfo& dst_info,
+      GLuint dst_row_bytes,
+      int src_x,
+      int src_y,
+      base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done,
+      void* dst_pixels);
+
+  struct AsyncReadbackRequest;
+  void OnAsyncReadbackDone(AsyncReadbackRequest* request);
+  void CancelRequests();
+  base::queue<std::unique_ptr<AsyncReadbackRequest>> request_queue_;
+
 // Set to 1 to have the client fail when a GL error is generated.
 // This helps find bugs in the renderer since the debugger stops on the error.
 #if DCHECK_IS_ON()
diff --git a/gpu/command_buffer/client/raster_implementation_gles.cc b/gpu/command_buffer/client/raster_implementation_gles.cc
index b2bd85d..1db20329 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles.cc
@@ -251,32 +251,38 @@
 void RasterImplementationGLES::ReadbackARGBPixelsAsync(
     const gpu::Mailbox& source_mailbox,
     GLenum source_target,
-    const gfx::Size& dst_size,
+    GrSurfaceOrigin src_origin,
+    const SkImageInfo& dst_info,
+    GLuint dst_row_bytes,
     unsigned char* out,
-    GLenum format,
-    base::OnceCallback<void(bool)> readback_done) {
+    base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done) {
   DCHECK(!readback_done.is_null());
-
+  DCHECK(dst_info.colorType() == kRGBA_8888_SkColorType ||
+         dst_info.colorType() == kBGRA_8888_SkColorType);
+  GLenum format =
+      dst_info.colorType() == kRGBA_8888_SkColorType ? GL_RGBA : GL_BGRA_EXT;
+  gfx::Size dst_gfx_size(dst_info.width(), dst_info.height());
   GLuint texture_id = CreateAndConsumeForGpuRaster(source_mailbox);
   BeginSharedImageAccessDirectCHROMIUM(
       texture_id, GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
 
   GetGLHelper()->ReadbackTextureAsync(
-      texture_id, source_target, dst_size, out, format,
+      texture_id, source_target, dst_gfx_size, out, format,
       base::BindOnce(&RasterImplementationGLES::OnReadARGBPixelsAsync,
                      weak_ptr_factory_.GetWeakPtr(), texture_id,
-                     std::move(readback_done)));
+                     std::move(readback_done), src_origin));
 }
 
 void RasterImplementationGLES::OnReadARGBPixelsAsync(
     GLuint texture_id,
-    base::OnceCallback<void(bool)> readback_done,
+    base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done,
+    GrSurfaceOrigin source_origin,
     bool success) {
   DCHECK(texture_id);
   EndSharedImageAccessDirectCHROMIUM(texture_id);
   DeleteGpuRasterTexture(texture_id);
 
-  std::move(readback_done).Run(success);
+  std::move(readback_done).Run(source_origin, success);
 }
 
 void RasterImplementationGLES::ReadbackYUVPixelsAsync(
diff --git a/gpu/command_buffer/client/raster_implementation_gles.h b/gpu/command_buffer/client/raster_implementation_gles.h
index 1f9691ac4..edf7659 100644
--- a/gpu/command_buffer/client/raster_implementation_gles.h
+++ b/gpu/command_buffer/client/raster_implementation_gles.h
@@ -111,10 +111,11 @@
   void ReadbackARGBPixelsAsync(
       const gpu::Mailbox& source_mailbox,
       GLenum source_target,
-      const gfx::Size& dst_size,
+      GrSurfaceOrigin source_origin,
+      const SkImageInfo& dst_info,
+      GLuint dst_row_bytes,
       unsigned char* out,
-      GLenum format,
-      base::OnceCallback<void(bool)> callback) override;
+      base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done) override;
 
   void ReadbackYUVPixelsAsync(
       const gpu::Mailbox& source_mailbox,
@@ -167,9 +168,11 @@
 
  private:
   GLHelper* GetGLHelper();
-  void OnReadARGBPixelsAsync(GLuint texture_id,
-                             base::OnceCallback<void(bool)> callback,
-                             bool success);
+  void OnReadARGBPixelsAsync(
+      GLuint texture_id,
+      base::OnceCallback<void(GrSurfaceOrigin, bool)> callback,
+      GrSurfaceOrigin result_origin,
+      bool success);
   void OnReadYUVPixelsAsync(GLuint copy_texture_id,
                             base::OnceCallback<void()> on_release_mailbox,
                             base::OnceCallback<void(bool)> readback_done,
diff --git a/gpu/command_buffer/client/raster_interface.h b/gpu/command_buffer/client/raster_interface.h
index d3b5bd3..4b569dc 100644
--- a/gpu/command_buffer/client/raster_interface.h
+++ b/gpu/command_buffer/client/raster_interface.h
@@ -14,6 +14,7 @@
 #include "gpu/command_buffer/common/sync_token.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
 #include "third_party/skia/include/core/SkYUVAInfo.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
 
 namespace cc {
 class DisplayItemList;
@@ -105,18 +106,19 @@
       bool needs_mips) = 0;
 
   // Starts an asynchronous readback of |source_mailbox| into caller-owned
-  // memory |out|. Currently supports the GL_RGBA format and GL_BGRA_EXT format
-  // with the GL_EXT_read_format_bgra GL extension. |out| must remain valid
-  // until |readback_done| is called with a bool indicating if the readback was
-  // successful. On success |out| will contain the pixel data copied back from
-  // the GPU process.
+  // memory |out|. Currently supports the kRGBA_8888_SkColorType and
+  // kBGRA_8888_SkColorType color types. |out| must remain valid
+  // until |readback_done| is called with the origin of the pixels in |out| and
+  // a bool indicating if the readback was successful. On success |out| will
+  // contain the pixel data copied back from the GPU process.
   virtual void ReadbackARGBPixelsAsync(
       const gpu::Mailbox& source_mailbox,
       GLenum source_target,
-      const gfx::Size& dst_size,
+      GrSurfaceOrigin source_origin,
+      const SkImageInfo& dst_info,
+      GLuint dst_row_bytes,
       unsigned char* out,
-      GLenum format,
-      base::OnceCallback<void(bool)> readback_done) = 0;
+      base::OnceCallback<void(GrSurfaceOrigin, bool)> readback_done) = 0;
 
   // Starts an asynchronus readback and translation of RGBA |source_mailbox|
   // into caller-owned |[yuv]_plane_data|. All provided pointers must remain
diff --git a/gpu/command_buffer/common/raster_cmd_format_autogen.h b/gpu/command_buffer/common/raster_cmd_format_autogen.h
index b112cb2..421971b 100644
--- a/gpu/command_buffer/common/raster_cmd_format_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_autogen.h
@@ -1010,9 +1010,8 @@
             GLuint _dst_sk_alpha_type,
             GLint _shm_id,
             GLuint _shm_offset,
+            GLuint _color_space_offset,
             GLuint _pixels_offset,
-            GLint _result_shm_id,
-            GLuint _result_shm_offset,
             const GLbyte* _mailbox) {
     SetHeader();
     src_x = _src_x;
@@ -1024,9 +1023,8 @@
     dst_sk_alpha_type = _dst_sk_alpha_type;
     shm_id = _shm_id;
     shm_offset = _shm_offset;
+    color_space_offset = _color_space_offset;
     pixels_offset = _pixels_offset;
-    result_shm_id = _result_shm_id;
-    result_shm_offset = _result_shm_offset;
     memcpy(ImmediateDataAddress(this), _mailbox, ComputeDataSize());
   }
 
@@ -1040,14 +1038,13 @@
             GLuint _dst_sk_alpha_type,
             GLint _shm_id,
             GLuint _shm_offset,
+            GLuint _color_space_offset,
             GLuint _pixels_offset,
-            GLint _result_shm_id,
-            GLuint _result_shm_offset,
             const GLbyte* _mailbox) {
     static_cast<ValueType*>(cmd)->Init(
         _src_x, _src_y, _dst_width, _dst_height, _row_bytes, _dst_sk_color_type,
-        _dst_sk_alpha_type, _shm_id, _shm_offset, _pixels_offset,
-        _result_shm_id, _result_shm_offset, _mailbox);
+        _dst_sk_alpha_type, _shm_id, _shm_offset, _color_space_offset,
+        _pixels_offset, _mailbox);
     const uint32_t size = ComputeSize();
     return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
   }
@@ -1062,13 +1059,12 @@
   uint32_t dst_sk_alpha_type;
   int32_t shm_id;
   uint32_t shm_offset;
+  uint32_t color_space_offset;
   uint32_t pixels_offset;
-  int32_t result_shm_id;
-  uint32_t result_shm_offset;
 };
 
-static_assert(sizeof(ReadbackImagePixelsINTERNALImmediate) == 52,
-              "size of ReadbackImagePixelsINTERNALImmediate should be 52");
+static_assert(sizeof(ReadbackImagePixelsINTERNALImmediate) == 48,
+              "size of ReadbackImagePixelsINTERNALImmediate should be 48");
 static_assert(
     offsetof(ReadbackImagePixelsINTERNALImmediate, header) == 0,
     "offset of ReadbackImagePixelsINTERNALImmediate header should be 0");
@@ -1101,18 +1097,14 @@
 static_assert(
     offsetof(ReadbackImagePixelsINTERNALImmediate, shm_offset) == 36,
     "offset of ReadbackImagePixelsINTERNALImmediate shm_offset should be 36");
-static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate, pixels_offset) ==
-                  40,
-              "offset of ReadbackImagePixelsINTERNALImmediate pixels_offset "
-              "should be 40");
-static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate, result_shm_id) ==
-                  44,
-              "offset of ReadbackImagePixelsINTERNALImmediate result_shm_id "
-              "should be 44");
 static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate,
-                       result_shm_offset) == 48,
+                       color_space_offset) == 40,
               "offset of ReadbackImagePixelsINTERNALImmediate "
-              "result_shm_offset should be 48");
+              "color_space_offset should be 40");
+static_assert(offsetof(ReadbackImagePixelsINTERNALImmediate, pixels_offset) ==
+                  44,
+              "offset of ReadbackImagePixelsINTERNALImmediate pixels_offset "
+              "should be 44");
 
 struct ConvertYUVAMailboxesToRGBINTERNALImmediate {
   typedef ConvertYUVAMailboxesToRGBINTERNALImmediate ValueType;
diff --git a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
index 6fab8971..92b5f51 100644
--- a/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
+++ b/gpu/command_buffer/common/raster_cmd_format_test_autogen.h
@@ -426,12 +426,12 @@
   };
   cmds::ReadbackImagePixelsINTERNALImmediate& cmd =
       *GetBufferAs<cmds::ReadbackImagePixelsINTERNALImmediate>();
-  void* next_cmd = cmd.Set(
-      &cmd, static_cast<GLint>(11), static_cast<GLint>(12),
-      static_cast<GLuint>(13), static_cast<GLuint>(14), static_cast<GLuint>(15),
-      static_cast<GLuint>(16), static_cast<GLuint>(17), static_cast<GLint>(18),
-      static_cast<GLuint>(19), static_cast<GLuint>(20), static_cast<GLint>(21),
-      static_cast<GLuint>(22), data);
+  void* next_cmd = cmd.Set(&cmd, static_cast<GLint>(11), static_cast<GLint>(12),
+                           static_cast<GLuint>(13), static_cast<GLuint>(14),
+                           static_cast<GLuint>(15), static_cast<GLuint>(16),
+                           static_cast<GLuint>(17), static_cast<GLint>(18),
+                           static_cast<GLuint>(19), static_cast<GLuint>(20),
+                           static_cast<GLuint>(21), data);
   EXPECT_EQ(
       static_cast<uint32_t>(cmds::ReadbackImagePixelsINTERNALImmediate::kCmdId),
       cmd.header.command);
@@ -446,9 +446,8 @@
   EXPECT_EQ(static_cast<GLuint>(17), cmd.dst_sk_alpha_type);
   EXPECT_EQ(static_cast<GLint>(18), cmd.shm_id);
   EXPECT_EQ(static_cast<GLuint>(19), cmd.shm_offset);
-  EXPECT_EQ(static_cast<GLuint>(20), cmd.pixels_offset);
-  EXPECT_EQ(static_cast<GLint>(21), cmd.result_shm_id);
-  EXPECT_EQ(static_cast<GLuint>(22), cmd.result_shm_offset);
+  EXPECT_EQ(static_cast<GLuint>(20), cmd.color_space_offset);
+  EXPECT_EQ(static_cast<GLuint>(21), cmd.pixels_offset);
   CheckBytesWrittenMatchesExpectedSize(
       next_cmd, sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)));
 }
diff --git a/gpu/command_buffer/raster_cmd_buffer_functions.txt b/gpu/command_buffer/raster_cmd_buffer_functions.txt
index 1283b8c3..f8f59d76 100644
--- a/gpu/command_buffer/raster_cmd_buffer_functions.txt
+++ b/gpu/command_buffer/raster_cmd_buffer_functions.txt
@@ -37,7 +37,7 @@
 // |mailboxes| argument is the concatenation of the source mailbox and the destination mailbox (32 bytes total)
 GL_APICALL void         GL_APIENTRY glCopySubTextureINTERNAL (GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height, GLboolean unpack_flip_y, const GLbyte* mailboxes);
 GL_APICALL void         GL_APIENTRY glWritePixelsINTERNAL (GLint x_offset, GLint y_offset, GLuint src_width, GLuint src_height, GLuint row_bytes, GLuint src_sk_color_type, GLuint src_sk_alpha_type, GLint shm_id, GLuint shm_offset, GLuint pixels_offset, const GLbyte* mailbox);
-GL_APICALL void         GL_APIENTRY glReadbackImagePixelsINTERNAL (GLint src_x, GLint src_y, GLuint dst_width, GLuint dst_height, GLuint row_bytes, GLuint dst_sk_color_type, GLuint dst_sk_alpha_type, GLint shm_id, GLuint shm_offset, GLuint pixels_offset, GLint result_shm_id, GLuint result_shm_offset, const GLbyte* mailbox);
+GL_APICALL void         GL_APIENTRY glReadbackImagePixelsINTERNAL (GLint src_x, GLint src_y, GLuint dst_width, GLuint dst_height, GLuint row_bytes, GLuint dst_sk_color_type, GLuint dst_sk_alpha_type, GLint shm_id, GLuint shm_offset, GLuint color_space_offset, GLuint pixels_offset, const GLbyte* mailbox);
 GL_APICALL void         GL_APIENTRY glConvertYUVAMailboxesToRGBINTERNAL (GLenum planes_yuv_color_space, GLenum plane_config, GLenum subsampling, const GLbyte* mailboxes);
 GL_APICALL void         GL_APIENTRY glTraceBeginCHROMIUM (const char* category_name, const char* trace_name);
 GL_APICALL void         GL_APIENTRY glTraceEndCHROMIUM (void);
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 3f7dba8b..e7807371 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -611,9 +611,8 @@
                                      GLuint dst_sk_alpha_type,
                                      GLint shm_id,
                                      GLuint shm_offset,
+                                     GLuint color_space_offset,
                                      GLuint pixels_offset,
-                                     GLint result_shm_id,
-                                     GLuint result_shm_offset,
                                      const volatile GLbyte* mailbox);
   void DoConvertYUVAMailboxesToRGBINTERNAL(GLenum yuv_color_space,
                                            GLenum plane_config,
@@ -2630,9 +2629,8 @@
     GLuint dst_sk_alpha_type,
     GLint shm_id,
     GLuint shm_offset,
+    GLuint color_space_offset,
     GLuint pixels_offset,
-    GLint result_shm_id,
-    GLuint result_shm_offset,
     const volatile GLbyte* mailbox) {
   if (dst_sk_color_type > kLastEnum_SkColorType) {
     LOCAL_SET_GL_ERROR(GL_INVALID_ENUM, "ReadbackImagePixels",
@@ -2657,19 +2655,26 @@
     return;
   }
 
-  // If present, the color space is serialized into shared memory before the
-  // pixel data.
+  // If present, the color space is serialized into shared memory after the
+  // result and before the pixel data.
+  if (color_space_offset > pixels_offset) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glReadbackImagePixels",
+                       "|pixels_offset| must be >= |color_space_offset|");
+    return;
+  }
+  unsigned int color_space_size = pixels_offset - color_space_offset;
+
   sk_sp<SkColorSpace> dst_color_space;
-  if (pixels_offset > 0) {
-    void* color_space_bytes =
-        GetSharedMemoryAs<void*>(shm_id, shm_offset, pixels_offset);
+  if (color_space_size) {
+    void* color_space_bytes = GetSharedMemoryAs<void*>(
+        shm_id, shm_offset + color_space_offset, color_space_size);
     if (!color_space_bytes) {
       LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glReadbackImagePixels",
                          "Failed to retrieve serialized SkColorSpace.");
       return;
     }
     dst_color_space =
-        SkColorSpace::Deserialize(color_space_bytes, pixels_offset);
+        SkColorSpace::Deserialize(color_space_bytes, color_space_size);
     if (!dst_color_space) {
       LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glReadbackImagePixels",
                          "Failed to deserialize expected SkColorSpace");
@@ -2723,27 +2728,29 @@
     return;
   }
 
-  void* shm_address =
+  void* pixel_address =
       GetSharedMemoryAs<void*>(shm_id, shm_offset + pixels_offset, byte_size);
-  if (!shm_address) {
+  if (!pixel_address) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glReadbackImagePixels",
-                       "Failed to retrieve memory for readPixels");
+                       "Failed to retrieve memory for readPixels output");
     return;
   }
 
   typedef cmds::ReadbackImagePixelsINTERNALImmediate::Result Result;
-  Result* result = nullptr;
-  if (result_shm_id != 0) {
-    result = GetSharedMemoryAs<Result*>(result_shm_id, result_shm_offset,
-                                        sizeof(*result));
+  Result* result =
+      GetSharedMemoryAs<Result*>(shm_id, shm_offset, sizeof(Result));
+  if (!result) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glReadbackImagePixels",
+                       "Failed to retrieve memory for readPixels result");
+    return;
   }
 
   bool success =
-      sk_image->readPixels(dst_info, shm_address, row_bytes, src_x, src_y);
+      sk_image->readPixels(dst_info, pixel_address, row_bytes, src_x, src_y);
   if (!success) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glReadbackImagePixels",
                        "Failed to read pixels from SkImage");
-  } else if (result != nullptr) {
+  } else {
     *result = 1;
   }
 }
diff --git a/gpu/command_buffer/service/raster_decoder_autogen.h b/gpu/command_buffer/service/raster_decoder_autogen.h
index 7813f03..72a9308d 100644
--- a/gpu/command_buffer/service/raster_decoder_autogen.h
+++ b/gpu/command_buffer/service/raster_decoder_autogen.h
@@ -350,9 +350,8 @@
   GLuint dst_sk_alpha_type = static_cast<GLuint>(c.dst_sk_alpha_type);
   GLint shm_id = static_cast<GLint>(c.shm_id);
   GLuint shm_offset = static_cast<GLuint>(c.shm_offset);
+  GLuint color_space_offset = static_cast<GLuint>(c.color_space_offset);
   GLuint pixels_offset = static_cast<GLuint>(c.pixels_offset);
-  GLint result_shm_id = static_cast<GLint>(c.result_shm_id);
-  GLuint result_shm_offset = static_cast<GLuint>(c.result_shm_offset);
   uint32_t mailbox_size;
   if (!gles2::GLES2Util::ComputeDataSize<GLbyte, 16>(1, &mailbox_size)) {
     return error::kOutOfBounds;
@@ -368,8 +367,8 @@
   }
   DoReadbackImagePixelsINTERNAL(src_x, src_y, dst_width, dst_height, row_bytes,
                                 dst_sk_color_type, dst_sk_alpha_type, shm_id,
-                                shm_offset, pixels_offset, result_shm_id,
-                                result_shm_offset, mailbox);
+                                shm_offset, color_space_offset, pixels_offset,
+                                mailbox);
   return error::kNoError;
 }
 
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index 28dd778..c8ec4f5 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -158,7 +158,8 @@
   gpu_preferences.ignore_gpu_blocklist =
       command_line->HasSwitch(switches::kIgnoreGpuBlocklist);
   gpu_preferences.enable_webgpu =
-      command_line->HasSwitch(switches::kEnableUnsafeWebGPU);
+      command_line->HasSwitch(switches::kEnableUnsafeWebGPU) ||
+      command_line->HasSwitch(switches::kEnableUnsafeWebGPUService);
   gpu_preferences.enable_dawn_backend_validation =
       command_line->HasSwitch(switches::kEnableDawnBackendValidation);
   if (command_line->HasSwitch(switches::kEnableDawnFeatures)) {
diff --git a/gpu/config/gpu_mode.h b/gpu/config/gpu_mode.h
index a14a9cb..a32aa99 100644
--- a/gpu/config/gpu_mode.h
+++ b/gpu/config/gpu_mode.h
@@ -20,9 +20,6 @@
   SWIFTSHADER,
   // The GPU process is running for the display compositor.
   DISPLAY_COMPOSITOR,
-  // The GPU process is disabled and won't start. This is only used on Windows
-  // when GPU acceleration and SwiftShader are both disabled.
-  DISABLED,
 };
 
 }  // namespace gpu
diff --git a/gpu/config/gpu_switches.cc b/gpu/config/gpu_switches.cc
index e2225eab..9241bffd 100644
--- a/gpu/config/gpu_switches.cc
+++ b/gpu/config/gpu_switches.cc
@@ -42,6 +42,7 @@
     "disable-gpu-process-for-dx12-info-collection";
 
 const char kEnableUnsafeWebGPU[] = "enable-unsafe-webgpu";
+const char kEnableUnsafeWebGPUService[] = "enable-unsafe-webgpu-service";
 
 // Enable validation layers in Dawn backends.
 const char kEnableDawnBackendValidation[] = "enable-dawn-backend-validation";
diff --git a/gpu/config/gpu_switches.h b/gpu/config/gpu_switches.h
index 5c21aa8..061cbf7c 100644
--- a/gpu/config/gpu_switches.h
+++ b/gpu/config/gpu_switches.h
@@ -19,6 +19,7 @@
 GPU_EXPORT extern const char kShaderDiskCacheSizeKB[];
 GPU_EXPORT extern const char kDisableGpuProcessForDX12InfoCollection[];
 GPU_EXPORT extern const char kEnableUnsafeWebGPU[];
+GPU_EXPORT extern const char kEnableUnsafeWebGPUService[];
 GPU_EXPORT extern const char kEnableDawnBackendValidation[];
 GPU_EXPORT extern const char kEnableDawnFeatures[];
 GPU_EXPORT extern const char kDisableDawnFeatures[];
diff --git a/gpu/ipc/client/gpu_in_process_context_tests.cc b/gpu/ipc/client/gpu_in_process_context_tests.cc
index 7723fb4..12c45cf 100644
--- a/gpu/ipc/client/gpu_in_process_context_tests.cc
+++ b/gpu/ipc/client/gpu_in_process_context_tests.cc
@@ -87,7 +87,8 @@
   // Calling CreateImageCHROMIUM() should allocate an image id starting at 1.
   std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer1 =
       gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
-          kBufferSize, kBufferFormat, kBufferUsage, gpu::kNullSurfaceHandle);
+          kBufferSize, kBufferFormat, kBufferUsage, gpu::kNullSurfaceHandle,
+          nullptr);
   GLuint image_id1 = gl_->CreateImageCHROMIUM(
       gpu_memory_buffer1->AsClientBuffer(), kBufferSize.width(),
       kBufferSize.height(), GL_RGBA);
@@ -101,7 +102,8 @@
       CreateGLInProcessContext();
   std::unique_ptr<gfx::GpuMemoryBuffer> buffer2 =
       gpu_memory_buffer_manager_->CreateGpuMemoryBuffer(
-          kBufferSize, kBufferFormat, kBufferUsage, gpu::kNullSurfaceHandle);
+          kBufferSize, kBufferFormat, kBufferUsage, gpu::kNullSurfaceHandle,
+          nullptr);
   GLuint image_id2 = context2->GetImplementation()->CreateImageCHROMIUM(
       buffer2->AsClientBuffer(), kBufferSize.width(), kBufferSize.height(),
       GL_RGBA);
diff --git a/headless/lib/browser/headless_clipboard.cc b/headless/lib/browser/headless_clipboard.cc
index 6c1c088..92c6a13 100644
--- a/headless/lib/browser/headless_clipboard.cc
+++ b/headless/lib/browser/headless_clipboard.cc
@@ -151,6 +151,15 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void HeadlessClipboard::ReadPng(ui::ClipboardBuffer buffer,
+                                const ui::DataTransferEndpoint* data_dst,
+                                ReadPngCallback callback) const {
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void HeadlessClipboard::ReadImage(ui::ClipboardBuffer buffer,
                                   const ui::DataTransferEndpoint* data_dst,
                                   ReadImageCallback callback) const {
diff --git a/headless/lib/browser/headless_clipboard.h b/headless/lib/browser/headless_clipboard.h
index dabf6f1..198984953 100644
--- a/headless/lib/browser/headless_clipboard.h
+++ b/headless/lib/browser/headless_clipboard.h
@@ -57,6 +57,9 @@
   void ReadRTF(ui::ClipboardBuffer buffer,
                const ui::DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ui::ClipboardBuffer buffer,
+               const ui::DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ui::ClipboardBuffer buffer,
                  const ui::DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 7b86e12..518d3d14 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -491,6 +491,12 @@
         location_regexp_exclude: ".+/[+]/infra/config/.+"
       }
       builders {
+        name: "chromium/try/cast_shell_linux_dbg"
+        location_regexp: ".+/[+]/chromecast/.+"
+        location_regexp_exclude: ".+/[+]/docs/.+"
+        location_regexp_exclude: ".+/[+]/infra/config/.+"
+      }
+      builders {
         name: "chromium/try/chromeos-amd64-generic-cfi-thin-lto-rel"
         includable_only: true
       }
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 9da134f..c0870ec 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -171,6 +171,11 @@
   Path regular expressions:
   * [`//infra/config/.+`](https://cs.chromium.org/chromium/src/infra/config/)
 
+* [cast_shell_linux_dbg](https://ci.chromium.org/p/chromium/builders/try/cast_shell_linux_dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+cast_shell_linux_dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+cast_shell_linux_dbg))
+
+  Path regular expressions:
+  * [`//chromecast/.+`](https://cs.chromium.org/chromium/src/chromecast/)
+
 * [chromeos-amd64-generic-dbg](https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+chromeos-amd64-generic-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+chromeos-amd64-generic-dbg))
 
   Path regular expressions:
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index bfee4eac..36d858be 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -2754,6 +2754,68 @@
       }
     }
     builders {
+      name: "Cast Linux Debug"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"jobs\":500,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.linux\",\"recipe\":\"chromium\"}"
+      execution_timeout_secs: 10800
+      build_numbers: YES
+      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.gtests_local"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "ChromeOS FYI Release (amd64-generic)"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
@@ -37749,6 +37811,71 @@
       }
     }
     builders {
+      name: "cast_shell_linux_dbg"
+      swarming_host: "chromium-swarm.appspot.com"
+      swarming_tags: "vpython:native-python-wrapper"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-18.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:0"
+      exe {
+        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
+        cipd_version: "refs/heads/master"
+        cmd: "recipes"
+      }
+      properties: "{\"$build/goma\":{\"enable_ats\":true,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\",\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"tryserver.chromium.linux\",\"recipe\":\"chromium_trybot\"}"
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "win_toolchain"
+        path: "win_toolchain"
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink"
+        value: 100
+      }
+      experiments {
+        key: "chromium.resultdb.result_sink.junit_tests"
+        value: 100
+      }
+      experiments {
+        key: "luci.use_realms"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "luci-resultdb"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome/test:|content/test:fuchsia_)telemetry_gpu_integration_test/.+"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+    }
+    builders {
       name: "chromeos-amd64-generic-cfi-thin-lto-rel"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index be6c4a9a..ea9084c5 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -311,6 +311,11 @@
     short_name: "aud"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Cast Linux Debug"
+    category: "chromium.linux|cast"
+    short_name: "dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
     category: "chromium.linux|fuchsia|a64"
     short_name: "rel"
@@ -974,6 +979,11 @@
     short_name: "vid"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Cast Linux Debug"
+    category: "chromium.linux|cast"
+    short_name: "dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
     category: "chromium.linux|fuchsia|a64"
     short_name: "rel"
@@ -1970,6 +1980,9 @@
     name: "buildbucket/luci.chromium.try/cast_shell_linux"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/cast_shell_linux_dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-dbg"
   }
   builders {
@@ -8114,6 +8127,11 @@
     short_name: "aud"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Cast Linux Debug"
+    category: "cast"
+    short_name: "dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Fuchsia ARM64"
     category: "fuchsia|a64"
     short_name: "rel"
@@ -12803,6 +12821,9 @@
     name: "buildbucket/luci.chromium.try/cast_shell_linux"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/cast_shell_linux_dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-cfi-thin-lto-rel"
   }
   builders {
@@ -14035,6 +14056,9 @@
     name: "buildbucket/luci.chromium.try/cast_shell_linux"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/cast_shell_linux_dbg"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/chromium_presubmit"
   }
   builders {
diff --git a/infra/config/generated/luci-notify.cfg b/infra/config/generated/luci-notify.cfg
index 6f727714..d9eb661 100644
--- a/infra/config/generated/luci-notify.cfg
+++ b/infra/config/generated/luci-notify.cfg
@@ -278,6 +278,20 @@
 }
 notifiers {
   notifications {
+    on_occurrence: FAILURE
+    failed_step_regexp: "\\b(bot_update|compile|gclient runhooks|runhooks|update|\\w*nocompile_test)\\b"
+    email {
+      recipients: "thomasanderson@chromium.org"
+    }
+  }
+  builders {
+    bucket: "ci"
+    name: "Cast Linux Debug"
+    repository: "https://chromium.googlesource.com/chromium/src"
+  }
+}
+notifiers {
+  notifications {
     on_change: true
     email {
       recipients: "chromesec-lkgr-failures@google.com"
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index 6b0a42d7..59e29fea 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -504,6 +504,16 @@
   }
 }
 job {
+  id: "Cast Linux Debug"
+  realm: "ci"
+  acl_sets: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Cast Linux Debug"
+  }
+}
+job {
   id: "ChromeOS FYI Release (amd64-generic)"
   realm: "ci"
   acl_sets: "ci"
@@ -6744,6 +6754,7 @@
   triggers: "Cast Android (dbg)"
   triggers: "Cast Audio Linux"
   triggers: "Cast Linux"
+  triggers: "Cast Linux Debug"
   triggers: "ChromeOS FYI Release (amd64-generic)"
   triggers: "ChromeOS FYI Release (kevin)"
   triggers: "ChromiumOS ASAN Release"
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 2d3a3959..ed3019b 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -4779,6 +4779,20 @@
 )
 
 ci.linux_builder(
+    name = "Cast Linux Debug",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cast",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+    os = os.LINUX_BIONIC,
+    # TODO(crbug.com/1173333): Make it tree-closing.
+    tree_closing = False,
+)
+
+ci.linux_builder(
     name = "Deterministic Fuchsia (dbg)",
     console_view_entry = consoles.console_view_entry(
         category = "fuchsia|x64",
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 9b622e6..8fcb7cd 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -974,6 +974,18 @@
 )
 
 try_.chromium_linux_builder(
+    name = "cast_shell_linux_dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chromecast/.+",
+        ],
+    ),
+    os = os.LINUX_BIONIC,
+)
+
+try_.chromium_linux_builder(
     name = "cast-binary-size",
     builderless = True,
     executable = "recipe:binary_size_cast_trybot",
diff --git a/ios/chrome/browser/ui/first_run/BUILD.gn b/ios/chrome/browser/ui/first_run/BUILD.gn
index 7385a3ab..612b12c 100644
--- a/ios/chrome/browser/ui/first_run/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/BUILD.gn
@@ -50,6 +50,7 @@
     "resources:checkbox",
     "resources:checkbox_checked",
     "resources:first_run_location_permissions",
+    "resources:read_more_arrow",
     "//base",
     "//base:i18n",
     "//components/metrics",
@@ -143,6 +144,7 @@
   ]
   deps = [
     "//base",
+    "//base:i18n",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/util",
   ]
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 bd8ebab..a416112 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
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/first_run/first_run_screen_view_controller.h"
 
 #include "base/check.h"
+#include "base/i18n/rtl.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/button_util.h"
 #import "ios/chrome/common/ui/util/pointer_interaction_util.h"
@@ -33,6 +34,7 @@
 constexpr CGFloat kSubtitleBottomMarginViewHeight = 0.05;
 constexpr CGFloat kContentWidthMultiplier = 0.65;
 constexpr CGFloat kContentMaxWidth = 327;
+constexpr CGFloat kMoreArrowMargin = 4;
 constexpr CGFloat kPreviousContentVisibleOnScroll = 0.15;
 
 }  // namespace
@@ -228,15 +230,6 @@
   actionBottomConstraint.active = YES;
 }
 
-- (void)setPrimaryActionString:(NSString*)text {
-  _primaryActionString = text;
-  if (_primaryActionButton &&
-      (!self.scrollToEndMandatory || self.didReachBottom)) {
-    [_primaryActionButton setTitle:_primaryActionString
-                          forState:UIControlStateNormal];
-  }
-}
-
 - (void)viewWillAppear:(BOOL)animated {
   // Only add the scroll view delegate after all the view layouts are fully
   // done.
@@ -251,14 +244,66 @@
       if ([self isScrolledToBottom]) {
         self.didReachBottom = YES;
       } else {
+        NSDictionary* textAttributes = @{
+          NSForegroundColorAttributeName :
+              [UIColor colorNamed:kSolidButtonTextColor],
+          NSFontAttributeName :
+              [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
+        };
+
         // TODO(crbug.com/1186762): Use final string and add localization.
-        [self.primaryActionButton setTitle:@"More - testing"
-                                  forState:UIControlStateNormal];
+        NSMutableAttributedString* attributedString =
+            [[NSMutableAttributedString alloc] initWithString:@"More - testing"
+                                                   attributes:textAttributes];
+
+        // Use |ceilf()| when calculating the icon's bounds to ensure the
+        // button's content height does not shrink by fractional points, as the
+        // attributed string's actual height is slightly smaller than the
+        // assigned height.
+        NSTextAttachment* attachment = [[NSTextAttachment alloc] init];
+        attachment.image = [[UIImage imageNamed:@"read_more_arrow"]
+            imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+        CGFloat height = ceilf(attributedString.size.height);
+        CGFloat capHeight =
+            ceilf([UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
+                      .capHeight);
+        CGFloat horizontalOffset =
+            base::i18n::IsRTL() ? -1.f * kMoreArrowMargin : kMoreArrowMargin;
+        CGFloat verticalOffset = (capHeight - height) / 2.f;
+        attachment.bounds =
+            CGRectMake(horizontalOffset, verticalOffset, height, height);
+        [attributedString
+            appendAttributedString:
+                [NSAttributedString attributedStringWithAttachment:attachment]];
+
+        // Make the title change without animation, as the UIButton's default
+        // animation when using setTitle:forState: doesn't handle adding a
+        // UIImage well (the old title gets abruptly pushed to the side as it's
+        // fading out to make room for the new image, which looks awkward).
+        __weak FirstRunScreenViewController* weakSelf = self;
+        [UIView performWithoutAnimation:^{
+          [weakSelf.primaryActionButton
+              setAttributedTitle:attributedString
+                        forState:UIControlStateNormal];
+          [weakSelf.primaryActionButton layoutIfNeeded];
+        }];
       }
     }
   });
 }
 
+- (void)setPrimaryActionString:(NSString*)text {
+  _primaryActionString = text;
+  // Change the button's label, unless scrolling to the end is mandatory and the
+  // scroll view hasn't been scrolled to the end at least once yet.
+  if (_primaryActionButton &&
+      (!self.scrollToEndMandatory || self.didReachBottom)) {
+    [_primaryActionButton setAttributedTitle:nil forState:UIControlStateNormal];
+    [_primaryActionButton setTitle:_primaryActionString
+                          forState:UIControlStateNormal];
+  }
+}
+
 #pragma mark - Private
 
 - (UIScrollView*)scrollView {
@@ -336,6 +381,8 @@
     _primaryActionButton.titleLabel.adjustsFontForContentSizeCategory = YES;
     _primaryActionButton.accessibilityIdentifier =
         kFirstRunPrimaryActionAccessibilityIdentifier;
+    _primaryActionButton.titleEdgeInsets =
+        UIEdgeInsetsMake(0, kMoreArrowMargin, 0, kMoreArrowMargin);
     [_primaryActionButton addTarget:self
                              action:@selector(didTapPrimaryActionButton)
                    forControlEvents:UIControlEventTouchUpInside];
@@ -415,6 +462,8 @@
   if (self.scrollToEndMandatory && !self.didReachBottom &&
       [self isScrolledToBottom]) {
     self.didReachBottom = YES;
+    [self.primaryActionButton setAttributedTitle:nil
+                                        forState:UIControlStateNormal];
     [self.primaryActionButton setTitle:self.primaryActionString
                               forState:UIControlStateNormal];
   }
@@ -434,7 +483,7 @@
         CGPointMake(0, self.scrollView.contentSize.height -
                            self.scrollView.bounds.size.height +
                            self.scrollView.contentInset.bottom + 1);
-    // Scroll the the smaller of the two offsets.
+    // Scroll to the smaller of the two offsets.
     CGPoint newOffset =
         targetOffset.y < bottomOffset.y ? targetOffset : bottomOffset;
     [self.scrollView setContentOffset:newOffset animated:YES];
diff --git a/ios/chrome/browser/ui/first_run/resources/BUILD.gn b/ios/chrome/browser/ui/first_run/resources/BUILD.gn
index 8706aa5..ea952e0 100644
--- a/ios/chrome/browser/ui/first_run/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/resources/BUILD.gn
@@ -43,3 +43,11 @@
     "sync_screen_banner.imageset/sync_screen_banner_light@3x.png",
   ]
 }
+
+imageset("read_more_arrow") {
+  sources = [
+    "read_more_arrow.imageset/Contents.json",
+    "read_more_arrow.imageset/read_more_arrow@2x.png",
+    "read_more_arrow.imageset/read_more_arrow@3x.png",
+  ]
+}
diff --git a/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/Contents.json b/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/Contents.json
new file mode 100644
index 0000000..7b26d0f3
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/Contents.json
@@ -0,0 +1,18 @@
+{
+  "images": [
+    {
+      "idiom": "universal",
+      "scale": "2x",
+      "filename": "read_more_arrow@2x.png"
+    },
+    {
+      "idiom": "universal",
+      "scale": "3x",
+      "filename": "read_more_arrow@3x.png"
+    }
+  ],
+  "info": {
+    "version": 1,
+    "author": "xcode"
+  }
+}
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/read_more_arrow@2x.png b/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/read_more_arrow@2x.png
new file mode 100644
index 0000000..2c647e2
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/read_more_arrow@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/read_more_arrow@3x.png b/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/read_more_arrow@3x.png
new file mode 100644
index 0000000..ce015a3
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/resources/read_more_arrow.imageset/read_more_arrow@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h b/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h
index c76cd6f4..b372730 100644
--- a/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h
+++ b/ios/chrome/browser/ui/menu/tab_context_menu_delegate.h
@@ -14,7 +14,7 @@
 }
 
 // Methods used to create context menu actions for tabs.
-@protocol TabContextMenuDelegate
+@protocol TabContextMenuDelegate <NSObject>
 
 // Tells the delegate to trigger the URL sharing flow for the given |URL| and
 // |title|, with the origin |view| representing the UI component for that URL.
@@ -30,6 +30,10 @@
 - (synced_sessions::DistantSession const*)sessionForTableSectionWithIdentifier:
     (NSInteger)sectionIdentifier;
 
+@optional
+// Tells the delegate to add |URL| and |title| to the reading list.
+- (void)addToReadingListURL:(const GURL&)URL title:(NSString*)title;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_MENU_TAB_CONTEXT_MENU_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
index 53fcfbf6..c26d4b6 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/BUILD.gn
@@ -26,8 +26,11 @@
     "grid_drag_drop_handler.h",
     "grid_empty_view.h",
     "grid_image_data_source.h",
+    "grid_item.h",
+    "grid_item.mm",
     "grid_layout.h",
     "grid_layout.mm",
+    "grid_menu_actions_data_source.h",
     "grid_theme.h",
     "grid_view_controller.h",
     "grid_view_controller.mm",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm
index f5cd823..c92f5c1 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.mm
@@ -71,6 +71,13 @@
 - (instancetype)initWithFrame:(CGRect)frame {
   self = [super initWithFrame:frame];
   if (self) {
+    // The background color must be set to avoid the corners behind the rounded
+    // layer from showing when dragging and dropping. Unfortunately, using
+    // |UIColor.clearColor| here will not remain transparent, so a solid color
+    // must be chosen. Using the grid color prevents the corners from showing
+    // while it transitions to the presented context menu/dragging state.
+    self.backgroundColor = [UIColor colorNamed:kGridBackgroundColor];
+
     [self setupSelectedBackgroundView];
     UIView* contentView = self.contentView;
     contentView.layer.cornerRadius = kGridCellCornerRadius;
@@ -183,10 +190,6 @@
   if (_theme == theme)
     return;
 
-  // This background color must be set to avoid the corners behind the rounded
-  // layer from showing when dragging and dropping.
-  self.backgroundColor = [UIColor clearColor];
-
   self.iconView.backgroundColor = UIColor.clearColor;
   switch (theme) {
     // This is necessary for iOS 13 because on iOS 13, this will return
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.h
index ce3d29b..33a7df07 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.h
@@ -11,11 +11,13 @@
 
 class Browser;
 @protocol TabContextMenuDelegate;
+@protocol GridMenuActionsDataSource;
 
 //  GridContextMenuHelper controls the creation of context menus for the Grid
 //  view.
 @interface GridContextMenuHelper : NSObject <GridContextMenuProvider>
 - (instancetype)initWithBrowser:(Browser*)browser
+              actionsDataSource:(id<GridMenuActionsDataSource>)actionsDataSource
          tabContextMenuDelegate:
              (id<TabContextMenuDelegate>)tabContextMenuDelegate
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm
index 46fb7e59..be3cf7b9 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_helper.mm
@@ -8,7 +8,9 @@
 #import "ios/chrome/browser/ui/menu/action_factory.h"
 #import "ios/chrome/browser/ui/menu/menu_histograms.h"
 #import "ios/chrome/browser/ui/menu/tab_context_menu_delegate.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_cell.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_menu_actions_data_source.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -19,6 +21,7 @@
 @property(nonatomic, assign) Browser* browser;
 
 @property(nonatomic, weak) id<TabContextMenuDelegate> contextMenuDelegate;
+@property(nonatomic, weak) id<GridMenuActionsDataSource> actionsDataSource;
 
 @end
 
@@ -27,20 +30,20 @@
 #pragma mark - GridContextMenuProvider
 
 - (instancetype)initWithBrowser:(Browser*)browser
+              actionsDataSource:(id<GridMenuActionsDataSource>)actionsDataSource
          tabContextMenuDelegate:
              (id<TabContextMenuDelegate>)tabContextMenuDelegate {
   self = [super init];
   if (self) {
     _browser = browser;
     _contextMenuDelegate = tabContextMenuDelegate;
+    _actionsDataSource = actionsDataSource;
   }
   return self;
 }
 
-- (UIContextMenuConfiguration*)contextMenuConfigurationForItem:
-                                   (TabSwitcherItem*)item
-                                                      fromView:(UIView*)view
-    API_AVAILABLE(ios(13.0)) {
+- (UIContextMenuConfiguration*)contextMenuConfigurationForGridCell:
+    (GridCell*)gridCell API_AVAILABLE(ios(13.0)) {
   __weak __typeof(self) weakSelf = self;
 
   UIContextMenuActionProvider actionProvider =
@@ -59,15 +62,25 @@
             [[ActionFactory alloc] initWithBrowser:strongSelf.browser
                                           scenario:MenuScenario::kTabGridEntry];
 
+        GridItem* item = [weakSelf.actionsDataSource
+            gridItemForCellIdentifier:gridCell.itemIdentifier];
+
         NSMutableArray<UIMenuElement*>* menuElements =
             [[NSMutableArray alloc] init];
 
         [menuElements addObject:[actionFactory actionToShareWithBlock:^{
                         [weakSelf.contextMenuDelegate shareURL:item.URL
                                                          title:item.title
-                                                      fromView:view];
+                                                      fromView:gridCell];
                       }]];
-
+        if ([weakSelf.contextMenuDelegate
+                respondsToSelector:@selector(addToReadingListURL:title:)]) {
+          [menuElements
+              addObject:[actionFactory actionToAddToReadingListWithBlock:^{
+                [weakSelf.contextMenuDelegate addToReadingListURL:item.URL
+                                                            title:item.title];
+              }]];
+        }
         return [UIMenu menuWithTitle:@"" children:menuElements];
       };
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_provider.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_provider.h
index 35b271c..ff955d80 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_provider.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_context_menu_provider.h
@@ -7,17 +7,14 @@
 
 #import <UIKit/UIKit.h>
 
-@class TabSwitcherItem;
+@class GridCell;
 
 // Protocol for instances that will provide menus to the Grid view.
 @protocol GridContextMenuProvider
 
-// Returns a context menu configuration instance for the given |item|, which is
-// represented on the UI by |view|.
-- (UIContextMenuConfiguration*)contextMenuConfigurationForItem:
-                                   (TabSwitcherItem*)item
-                                                      fromView:(UIView*)view
-    API_AVAILABLE(ios(13.0));
+// Returns a context menu configuration instance for the given |gridCell|.
+- (UIContextMenuConfiguration*)contextMenuConfigurationForGridCell:
+    (GridCell*)gridCell API_AVAILABLE(ios(13.0));
 
 @end
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h
new file mode 100644
index 0000000..3ca9bc60
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h
@@ -0,0 +1,29 @@
+// 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 IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_ITEM_H_
+
+#import <Foundation/Foundation.h>
+
+class GURL;
+
+// Model object representing details about an item from the tab grid.
+@interface GridItem : NSObject
+
+// Create an item with |title|, and |url|.
+- (instancetype)initWithTitle:(NSString*)title
+                          url:(GURL)URL NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+// The title for the grid cell.
+@property(nonatomic, copy) NSString* title;
+
+// The URL of the tab represented by the Grid cell.
+@property(nonatomic, assign) GURL URL;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_ITEM_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.mm
new file mode 100644
index 0000000..533e3e8
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.mm
@@ -0,0 +1,23 @@
+// 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 "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h"
+
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@implementation GridItem
+
+- (instancetype)initWithTitle:(NSString*)title url:(GURL)URL {
+  if ((self = [super init])) {
+    _title = title;
+    _URL = URL;
+  }
+  return self;
+}
+
+@end
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_menu_actions_data_source.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_menu_actions_data_source.h
new file mode 100644
index 0000000..90e9e39
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_menu_actions_data_source.h
@@ -0,0 +1,22 @@
+// 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 IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_MENU_ACTIONS_DATA_SOURCE_H_
+#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_MENU_ACTIONS_DATA_SOURCE_H_
+
+#import <Foundation/Foundation.h>
+
+@class GridItem;
+
+// Protocol that is used to pull the data required to execute the grid menus
+// actions.
+@protocol GridMenuActionsDataSource
+
+// Asks the delegate for the GridItem object representing the the grid cell with
+// |identifier|.
+- (GridItem*)gridItemForCellIdentifier:(NSString*)identifier;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_MENU_ACTIONS_DATA_SOURCE_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
index e6dbc9d0..cc26088 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
@@ -407,9 +407,9 @@
   if (!IsTabGridContextMenuEnabled()) {
     return nil;
   }
-  UIView* cell = [self.collectionView cellForItemAtIndexPath:indexPath];
-  TabSwitcherItem* item = self.items[indexPath.item];
-  return [self.menuProvider contextMenuConfigurationForItem:item fromView:cell];
+  GridCell* cell = base::mac::ObjCCastStrict<GridCell>(
+      [self.collectionView cellForItemAtIndexPath:indexPath]);
+  return [self.menuProvider contextMenuConfigurationForGridCell:cell];
 }
 
 #pragma mark - UIPointerInteractionDelegate
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
index 8a5c9e7..6525796 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -25,6 +25,7 @@
 #import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
+#import "ios/chrome/browser/ui/commands/reading_list_add_command.h"
 #import "ios/chrome/browser/ui/commands/thumb_strip_commands.h"
 #import "ios/chrome/browser/ui/gestures/view_controller_trait_collection_observer.h"
 #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
@@ -550,12 +551,14 @@
   if (@available(iOS 13.0, *)) {
     self.regularTabsGridContextMenuHelper =
         [[GridContextMenuHelper alloc] initWithBrowser:self.regularBrowser
+                                     actionsDataSource:self.regularTabsMediator
                                 tabContextMenuDelegate:self];
     self.baseViewController.regularTabsContextMenuProvider =
         self.regularTabsGridContextMenuHelper;
-    self.incognitoTabsGridContextMenuHelper =
-        [[GridContextMenuHelper alloc] initWithBrowser:self.incognitoBrowser
-                                tabContextMenuDelegate:self];
+    self.incognitoTabsGridContextMenuHelper = [[GridContextMenuHelper alloc]
+               initWithBrowser:self.incognitoBrowser
+             actionsDataSource:self.incognitoTabsMediator
+        tabContextMenuDelegate:self];
     self.baseViewController.incognitoTabsContextMenuProvider =
         self.incognitoTabsGridContextMenuHelper;
   }
@@ -837,6 +840,16 @@
   [self.sharingCoordinator start];
 }
 
+- (void)addToReadingListURL:(const GURL&)URL title:(NSString*)title {
+  // TODO(crbug.com/1045047): Use HandlerForProtocol after commands
+  // protocol clean up.
+  id<BrowserCommands> readingListAdder = static_cast<id<BrowserCommands>>(
+      self.regularBrowser->GetCommandDispatcher());
+  ReadingListAddCommand* command =
+      [[ReadingListAddCommand alloc] initWithURL:URL title:title];
+  [readingListAdder addToReadingList:command];
+}
+
 - (void)removeSessionAtTableSectionWithIdentifier:(NSInteger)sectionIdentifier {
   [self.baseViewController.remoteTabsViewController
       removeSessionAtTableSectionWithIdentifier:sectionIdentifier];
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
index 865b90b..7354f80 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/ios/ios_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
+#import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/features.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_constants.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_constants.h"
@@ -23,11 +25,13 @@
 #error "This file requires ARC support."
 #endif
 
+using base::test::ios::kWaitForUIElementTimeout;
 using chrome_test_util::TabGridOtherDevicesPanelButton;
 using chrome_test_util::LongPressCellAndDragToEdge;
 using chrome_test_util::LongPressCellAndDragToOffsetOf;
 using chrome_test_util::TapAtOffsetOf;
 using chrome_test_util::WindowWithNumber;
+using chrome_test_util::AddToReadingListButton;
 
 namespace {
 char kURL1[] = "http://firstURL";
@@ -42,6 +46,9 @@
 char kResponse3[] = "Test Page 3 content";
 char kResponse4[] = "Test Page 4 content";
 
+const CFTimeInterval kSnackbarAppearanceTimeout = 5;
+const CFTimeInterval kSnackbarDisappearanceTimeout = 11;
+
 // Matcher for the 'Close All' confirmation button.
 id<GREYMatcher> CloseAllTabsConfirmationWithNumberOfTabs(
     NSInteger numberOfTabs) {
@@ -69,6 +76,20 @@
 
 @implementation TabGridTestCase
 
+- (AppLaunchConfiguration)appConfigurationForTestCase {
+  AppLaunchConfiguration config;
+
+  // Features are enabled or disabled based on the name of the test that is
+  // running. This is done because it is inefficient to use
+  // ensureAppLaunchedWithConfiguration for each test.
+  if ([self isRunningTest:@selector(testTabGridItemContextMenuShare)] ||
+      [self isRunningTest:@selector
+            (testTabGridItemContextMenuAddToReadingList)]) {
+    config.features_enabled.push_back(kTabGridContextMenu);
+  }
+  return config;
+}
+
 - (void)setUp {
   [super setUp];
 
@@ -199,6 +220,8 @@
   [ChromeEarlGreyUI assertHistoryHasNoEntries];
 }
 
+#pragma mark - Recent Tabs Context Menu
+
 // Tests the Copy Link action on a recent tab's context menu.
 - (void)testRecentTabsContextMenuCopyLink {
   if (![ChromeEarlGrey isNativeContextMenusEnabled]) {
@@ -207,7 +230,7 @@
   }
 
   [self prepareRecentTabWithURL:_URL1 response:kResponse1];
-  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+  [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
 
   [ChromeEarlGrey
       verifyCopyLinkActionWithText:[NSString stringWithUTF8String:_URL1.spec()
@@ -223,7 +246,7 @@
   }
 
   [self prepareRecentTabWithURL:_URL1 response:kResponse1];
-  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+  [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
 
   [ChromeEarlGrey verifyOpenInNewTabActionWithURL:_URL1.GetContent()];
 
@@ -244,7 +267,7 @@
   }
 
   [self prepareRecentTabWithURL:_URL1 response:kResponse1];
-  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+  [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
 
   [ChromeEarlGrey verifyOpenInNewWindowActionWithContent:kResponse1];
 }
@@ -257,12 +280,85 @@
   }
 
   [self prepareRecentTabWithURL:_URL1 response:kResponse1];
-  [self longPressRecentTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+  [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
 
   [ChromeEarlGrey
       verifyShareActionWithPageTitle:[NSString stringWithUTF8String:kTitle1]];
 }
 
+#pragma mark - Tab Grid Item Context Menu
+
+// Tests the Share action on a tab grid item's context menu.
+- (void)testTabGridItemContextMenuShare {
+  if (!base::ios::IsRunningOnIOS13OrLater()) {
+    EARL_GREY_TEST_SKIPPED(
+        @"Tab Grid context menu only supported on iOS 13 and later.");
+  }
+
+  [ChromeEarlGrey loadURL:_URL1];
+  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
+      performAction:grey_tap()];
+
+  [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+
+  [ChromeEarlGrey
+      verifyShareActionWithPageTitle:[NSString stringWithUTF8String:kTitle1]];
+}
+
+// Tests the Add to Reading list action on a tab grid item's context menu.
+- (void)testTabGridItemContextMenuAddToReadingList {
+  if (!base::ios::IsRunningOnIOS13OrLater()) {
+    EARL_GREY_TEST_SKIPPED(
+        @"Tab Grid context menu only supported on iOS 13 and later.");
+  }
+
+  [ChromeEarlGrey loadURL:_URL1];
+  [ChromeEarlGrey waitForWebStateContainingText:kResponse1];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::ShowTabsButton()]
+      performAction:grey_tap()];
+
+  NSString* snackBarLabel =
+      l10n_util::GetNSStringWithFixup(IDS_IOS_READING_LIST_SNACKBAR_MESSAGE);
+  // Start custom monitor, because there's a chance the snackbar is
+  // already gone by the time we wait for it (and it was like that sometimes).
+  [ChromeEarlGrey watchForButtonsWithLabels:@[ snackBarLabel ]
+                                    timeout:kSnackbarAppearanceTimeout];
+
+  [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
+
+  // Add the page to the reading list.
+  [[EarlGrey selectElementWithMatcher:AddToReadingListButton()]
+      performAction:grey_tap()];
+
+  // Wait for the snackbar to appear.
+  id<GREYMatcher> snackbar_matcher =
+      chrome_test_util::ButtonWithAccessibilityLabelId(
+          IDS_IOS_READING_LIST_SNACKBAR_MESSAGE);
+  ConditionBlock wait_for_appearance = ^{
+    return [ChromeEarlGrey watcherDetectedButtonWithLabel:snackBarLabel];
+  };
+  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
+                 kSnackbarAppearanceTimeout, wait_for_appearance),
+             @"Snackbar did not appear.");
+
+  // Wait for the snackbar to disappear.
+  ConditionBlock wait_for_disappearance = ^{
+    NSError* error = nil;
+    [[EarlGrey selectElementWithMatcher:snackbar_matcher]
+        assertWithMatcher:grey_nil()
+                    error:&error];
+    return error == nil;
+  };
+  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
+                 kSnackbarDisappearanceTimeout, wait_for_disappearance),
+             @"Snackbar did not disappear.");
+}
+
+#pragma mark -
+
 // Tests that tapping on "Close All" shows a confirmation dialog.
 // It also tests that tapping on "Close x Tab(s)" on the confirmation dialog
 // displays an empty grid and tapping on "Cancel" doesn't modify the grid.
@@ -797,7 +893,9 @@
       performAction:grey_tap()];
 }
 
-- (void)longPressRecentTabWithTitle:(NSString*)title {
+// Long press on the recent tab entry or the tab item in the tab grid with
+// |title|.
+- (void)longPressTabWithTitle:(NSString*)title {
   // The test page may be there multiple times.
   [[[EarlGrey
       selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(title),
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.h
index 8962ee7..31732a26 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.h
@@ -10,6 +10,7 @@
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_commands.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_drag_drop_handler.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_image_data_source.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_menu_actions_data_source.h"
 
 class Browser;
 @protocol GridConsumer;
@@ -35,8 +36,10 @@
 @end
 
 // Mediates between model layer and tab grid UI layer.
-@interface TabGridMediator
-    : NSObject <GridCommands, GridDragDropHandler, GridImageDataSource>
+@interface TabGridMediator : NSObject <GridCommands,
+                                       GridDragDropHandler,
+                                       GridImageDataSource,
+                                       GridMenuActionsDataSource>
 
 // The source browser.
 @property(nonatomic, assign) Browser* browser;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
index daecfc9..c14b326 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
@@ -29,6 +29,7 @@
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/tabs/tab_title_util.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_consumer.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_item.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -51,9 +52,8 @@
   TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
   TabSwitcherItem* item =
       [[TabSwitcherItem alloc] initWithIdentifier:tab_helper->tab_id()];
-  item.URL = web_state->GetVisibleURL();
   // chrome://newtab (NTP) tabs have no title.
-  if (IsURLNtp(item.URL)) {
+  if (IsURLNtp(web_state->GetVisibleURL())) {
     item.hidesTitle = YES;
   }
   item.title = tab_util::GetTabTitle(web_state);
@@ -582,6 +582,16 @@
   [self.appearanceCache removeAllObjects];
 }
 
+#pragma mark - GridMenuActionsDataSource
+
+- (GridItem*)gridItemForCellIdentifier:(NSString*)identifier {
+  web::WebState* webState = GetWebStateWithId(self.webStateList, identifier);
+  GridItem* item =
+      [[GridItem alloc] initWithTitle:tab_util::GetTabTitle(webState)
+                                  url:webState->GetVisibleURL()];
+  return item;
+}
+
 #pragma mark - Private
 
 // Calls |-populateItems:selectedItemID:| on the consumer.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm
index c8537a3..c783d6d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_strip/tab_strip_mediator.mm
@@ -31,9 +31,8 @@
   TabIdTabHelper* tab_helper = TabIdTabHelper::FromWebState(web_state);
   TabSwitcherItem* item =
       [[TabSwitcherItem alloc] initWithIdentifier:tab_helper->tab_id()];
-  item.URL = web_state->GetVisibleURL();
   // chrome://newtab (NTP) tabs have no title.
-  if (IsURLNtp(item.URL)) {
+  if (IsURLNtp(web_state->GetVisibleURL())) {
     item.hidesTitle = YES;
   }
   item.title = tab_util::GetTabTitle(web_state);
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h b/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h
index f25d150..edf3e56d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h
@@ -7,8 +7,6 @@
 
 #import <Foundation/Foundation.h>
 
-class GURL;
-
 // Model object representing an item in the tab switchers.
 @interface TabSwitcherItem : NSObject
 
@@ -20,9 +18,6 @@
 
 @property(nonatomic, readonly) NSString* identifier;
 @property(nonatomic, copy) NSString* title;
-// TODO(crbug.com/1201770): Move URL out of TabSwitcherItem and provide it from
-// the TabGridMediator & TabStripMediator.
-@property(nonatomic, assign) GURL URL;
 @property(nonatomic, assign) BOOL hidesTitle;
 @end
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.mm
index eee0c27..e5cce62 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_item.mm
@@ -4,8 +4,6 @@
 
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_item.h"
 
-#include "url/gurl.h"
-
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 70a4a42..5a5faa5c 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -133,6 +133,9 @@
 // Matcher for show tabs button.
 id<GREYMatcher> ShowTabsButton();
 
+// Matcher for Add to reading list button.
+id<GREYMatcher> AddToReadingListButton();
+
 // Matcher for SettingsSwitchCell.
 id<GREYMatcher> SettingsSwitchCell(NSString* accessibility_identifier,
                                    BOOL is_toggled_on);
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index c14e0ec..cb006aa 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -174,6 +174,10 @@
   return [ChromeMatchersAppInterface showTabsButton];
 }
 
+id<GREYMatcher> AddToReadingListButton() {
+  return [ChromeMatchersAppInterface addToReadingListButton];
+}
+
 id<GREYMatcher> SettingsSwitchCell(NSString* accessibility_identifier,
                                    BOOL is_toggled_on) {
   return [ChromeMatchersAppInterface settingsSwitchCell:accessibility_identifier
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index 3c0229d..b1e44b0 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -127,6 +127,9 @@
 // Matcher for show tabs button.
 + (id<GREYMatcher>)showTabsButton;
 
+// Matcher for Add to reading list button.
++ (id<GREYMatcher>)addToReadingListButton;
+
 // Matcher for SettingsSwitchCell.
 + (id<GREYMatcher>)settingsSwitchCell:(NSString*)accessibilityIdentifier
                           isToggledOn:(BOOL)isToggledOn;
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index e654edfc..443fc15 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -375,6 +375,13 @@
                     grey_sufficientlyVisible(), nil);
 }
 
++ (id<GREYMatcher>)addToReadingListButton {
+  return grey_allOf([ChromeMatchersAppInterface
+                        buttonWithAccessibilityLabelID:
+                            (IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST)],
+                    grey_sufficientlyVisible(), nil);
+}
+
 + (id<GREYMatcher>)settingsSwitchCell:(NSString*)accessibilityIdentifier
                           isToggledOn:(BOOL)isToggledOn {
   return [ChromeMatchersAppInterface settingsSwitchCell:accessibilityIdentifier
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 b75ad4e..a7b3906 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 @@
-048cd5631ad59a4fea59b557163f44d082b728d9
\ No newline at end of file
+af473741b0893b91f298182752f8afb43ab0bae1
\ 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 7004c05..6467fa85 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 @@
-0b0f49306283948e277879986128680f39aea73f
\ No newline at end of file
+dfc3e3e54a9340c4443095f084e03ac00ea6677c
\ 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 e90fab3..7133789 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 @@
-482521668a5e900d51da504fe121ea97101e914c
\ No newline at end of file
+c8107e6ad59cc352a411d23acd7e9d8d84c8e6d7
\ 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 980f9b8..7befa00a 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 @@
-5f371c63572fa7a135116c9706b67f52cdcad701
\ No newline at end of file
+fbeacd5feea338ab8a9de821a4f3f34111190518
\ 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 e705527..32296a3 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 @@
-b39b20b08f5a67536bbbbce42ce4f7aa98d3cc3d
\ No newline at end of file
+4befcaadf04f2721a04c8b0c310a76c669bb6d4a
\ 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 abd8480..4151c6e9 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 @@
-3193e5d149744941f26f882ba22b7cc263fabbb6
\ No newline at end of file
+4e5cafa05df79249c215445d14d6665e68ad1341
\ 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 2bbe9f8..c5828d68 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 @@
-63b3d72e60c2b281e12e1c18650d44865b214af4
\ No newline at end of file
+08cbfb2091556d38835c3f4357398c27a756ded5
\ 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 761be1d6..cd5b44a 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 @@
-d98727ce83ddcd8764dd3937ca247c578669d375
\ No newline at end of file
+52848e1b48f1409681b3fe975edadf2f4ba13a39
\ 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 f685578f..9e24e641 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 @@
-2005abb18663d5fd048f99edd889dd845527749f
\ No newline at end of file
+c6f1d6a98a6131c4f738af34217535d722edff42
\ 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 e751ec80..effe259 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 @@
-abd53525722f74cdb483fc5510c40a48bb7c63ae
\ No newline at end of file
+e0ce1206256c5e9f6fb05a8d48794318f8fc1110
\ No newline at end of file
diff --git a/media/capture/video/chromeos/camera_buffer_factory.cc b/media/capture/video/chromeos/camera_buffer_factory.cc
index 6099afb..a4e3b5eb 100644
--- a/media/capture/video/chromeos/camera_buffer_factory.cc
+++ b/media/capture/video/chromeos/camera_buffer_factory.cc
@@ -24,7 +24,7 @@
     return nullptr;
   }
   return buf_manager->CreateGpuMemoryBuffer(size, format, usage,
-                                            gpu::kNullSurfaceHandle);
+                                            gpu::kNullSurfaceHandle, nullptr);
 }
 
 // There's no good way to resolve the HAL pixel format to the platform-specific
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
index 98c6136..9d47ddc1 100644
--- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -396,7 +396,7 @@
                 CreateGpuMemoryBuffer(
                     _, gfx::BufferFormat::YUV_420_BIPLANAR,
                     gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE,
-                    gpu::kNullSurfaceHandle))
+                    gpu::kNullSurfaceHandle, nullptr))
         .Times(1)
         .WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
                              CreateFakeGpuMemoryBuffer));
@@ -404,7 +404,7 @@
         mock_gpu_memory_buffer_manager_,
         CreateGpuMemoryBuffer(_, gfx::BufferFormat::R_8,
                               gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE,
-                              gpu::kNullSurfaceHandle))
+                              gpu::kNullSurfaceHandle, nullptr))
         .Times(AtMost(1))
         .WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
                              CreateFakeGpuMemoryBuffer));
@@ -413,7 +413,7 @@
                     gfx::Size(kDefaultWidth, kDefaultHeight),
                     gfx::BufferFormat::YUV_420_BIPLANAR,
                     gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE,
-                    gpu::kNullSurfaceHandle))
+                    gpu::kNullSurfaceHandle, nullptr))
         .Times(1)
         .WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
                              CreateFakeGpuMemoryBuffer));
@@ -421,7 +421,7 @@
                 CreateGpuMemoryBuffer(
                     gfx::Size(kJpegMaxBufferSize, 1), gfx::BufferFormat::R_8,
                     gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE,
-                    gpu::kNullSurfaceHandle))
+                    gpu::kNullSurfaceHandle, nullptr))
         .Times(AtMost(1))
         .WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
                              CreateFakeGpuMemoryBuffer));
diff --git a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
index b1b59282..a3cd9a46 100644
--- a/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate_unittest.cc
@@ -232,7 +232,7 @@
               CreateGpuMemoryBuffer(
                   _, gfx::BufferFormat::YUV_420_BIPLANAR,
                   gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE,
-                  gpu::kNullSurfaceHandle))
+                  gpu::kNullSurfaceHandle, nullptr))
       .Times(1)
       .WillOnce(Invoke(&unittest_internal::MockGpuMemoryBufferManager::
                            CreateFakeGpuMemoryBuffer));
diff --git a/media/capture/video/chromeos/request_manager_unittest.cc b/media/capture/video/chromeos/request_manager_unittest.cc
index f65e184..eb62aafc 100644
--- a/media/capture/video/chromeos/request_manager_unittest.cc
+++ b/media/capture/video/chromeos/request_manager_unittest.cc
@@ -65,7 +65,8 @@
       gfx::BufferFormat format,
       gfx::BufferUsage usage) override {
     return unittest_internal::MockGpuMemoryBufferManager::
-        CreateFakeGpuMemoryBuffer(size, format, usage, gpu::kNullSurfaceHandle);
+        CreateFakeGpuMemoryBuffer(size, format, usage, gpu::kNullSurfaceHandle,
+                                  nullptr);
   }
 
   ChromiumPixelFormat ResolveStreamBufferFormat(
diff --git a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
index 4065c119..1148b4d 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -369,15 +369,14 @@
   str << "]";
   VLOG(1) << "Configuring scaled resolutions: " << str.str();
   _scaledFrameTransformers.clear();
-  for (size_t i = 0; i < resolutions.size(); ++i) {
-    DCHECK(i == 0 || resolutions[i - 1].height() > resolutions[i].height());
+  for (const auto& resolution : resolutions) {
     // Configure the transformer to and from NV12 pixel buffers - we only want
     // to pay scaling costs, not conversion costs.
     auto scaledFrameTransformer = media::SampleBufferTransformer::Create();
     scaledFrameTransformer->Reconfigure(
         media::SampleBufferTransformer::
             kBestTransformerForPixelBufferToNv12Output,
-        kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, resolutions[i],
+        kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, resolution,
         kPixelBufferPoolSize);
     _scaledFrameTransformers.push_back(std::move(scaledFrameTransformer));
   }
@@ -769,10 +768,8 @@
         scaledFrameSize == captureFormat.frame_size) {
       continue;
     }
-    CVPixelBufferRef bufferToScale =
-        !scaledPixelBuffers.empty() ? scaledPixelBuffers.back() : pixelBuffer;
     base::ScopedCFTypeRef<CVPixelBufferRef> scaledPixelBuffer =
-        scaledFrameTransformer->Transform(bufferToScale);
+        scaledFrameTransformer->Transform(pixelBuffer);
     if (!scaledPixelBuffer) {
       LOG(ERROR) << "Failed to downscale frame, skipping resolution "
                  << scaledFrameSize.ToString();
diff --git a/media/capture/video/mock_gpu_memory_buffer_manager.cc b/media/capture/video/mock_gpu_memory_buffer_manager.cc
index 0246f6c..8d1e5f9 100644
--- a/media/capture/video/mock_gpu_memory_buffer_manager.cc
+++ b/media/capture/video/mock_gpu_memory_buffer_manager.cc
@@ -24,7 +24,8 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    gpu::SurfaceHandle surface_handle) {
+    gpu::SurfaceHandle surface_handle,
+    base::WaitableEvent* shutdown_event) {
   auto gmb = std::make_unique<FakeGpuMemoryBuffer>(size, format);
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // For faking a valid JPEG blob buffer.
diff --git a/media/capture/video/mock_gpu_memory_buffer_manager.h b/media/capture/video/mock_gpu_memory_buffer_manager.h
index 614cf8b..d617825 100644
--- a/media/capture/video/mock_gpu_memory_buffer_manager.h
+++ b/media/capture/video/mock_gpu_memory_buffer_manager.h
@@ -19,12 +19,13 @@
 
   ~MockGpuMemoryBufferManager() override;
 
-  MOCK_METHOD4(
-      CreateGpuMemoryBuffer,
-      std::unique_ptr<gfx::GpuMemoryBuffer>(const gfx::Size& size,
-                                            gfx::BufferFormat format,
-                                            gfx::BufferUsage usage,
-                                            gpu::SurfaceHandle surface_handle));
+  MOCK_METHOD5(CreateGpuMemoryBuffer,
+               std::unique_ptr<gfx::GpuMemoryBuffer>(
+                   const gfx::Size& size,
+                   gfx::BufferFormat format,
+                   gfx::BufferUsage usage,
+                   gpu::SurfaceHandle surface_handle,
+                   base::WaitableEvent* shutdown_event));
 
   MOCK_METHOD2(SetDestructionSyncToken,
                void(gfx::GpuMemoryBuffer* buffer,
@@ -43,7 +44,8 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle);
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event);
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockGpuMemoryBufferManager);
diff --git a/media/capture/video/video_capture_device_client_unittest.cc b/media/capture/video/video_capture_device_client_unittest.cc
index 0d5f18b5..c4a218f 100644
--- a/media/capture/video/video_capture_device_client_unittest.cc
+++ b/media/capture/video/video_capture_device_client_unittest.cc
@@ -112,7 +112,7 @@
       gpu_memory_buffer_manager_->CreateFakeGpuMemoryBuffer(
           kBufferDimensions, gfx::BufferFormat::YUV_420_BIPLANAR,
           gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE,
-          gpu::kNullSurfaceHandle);
+          gpu::kNullSurfaceHandle, nullptr);
   {
     InSequence s;
     const int expected_buffer_id = 0;
@@ -152,7 +152,8 @@
   std::unique_ptr<gfx::GpuMemoryBuffer> buffer =
       gpu_memory_buffer_manager_->CreateFakeGpuMemoryBuffer(
           kBufferDimensions, gfx::BufferFormat::YUV_420_BIPLANAR,
-          gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gpu::kNullSurfaceHandle);
+          gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, gpu::kNullSurfaceHandle,
+          nullptr);
   EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _)).Times(0);
   device_client_->OnIncomingCapturedGfxBuffer(
       buffer.get(), kFrameFormat, 0 /*clockwise rotation*/, base::TimeTicks(),
@@ -311,7 +312,7 @@
             size_and_rotation.input_resolution,
             gfx::BufferFormat::YUV_420_BIPLANAR,
             gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
-            gpu::kNullSurfaceHandle);
+            gpu::kNullSurfaceHandle, nullptr);
 
     gfx::Size coded_size;
     EXPECT_CALL(*receiver_, MockOnFrameReadyInBuffer(_, _, _))
diff --git a/media/capture/video/video_capture_feedback.cc b/media/capture/video/video_capture_feedback.cc
index 013a5ea..692d4d02 100644
--- a/media/capture/video/video_capture_feedback.cc
+++ b/media/capture/video/video_capture_feedback.cc
@@ -4,7 +4,6 @@
 
 #include "media/capture/video/video_capture_feedback.h"
 
-#include <algorithm>
 #include <cmath>
 
 #include "base/logging.h"
@@ -16,11 +15,6 @@
 // Arbitrary limit above what is considered a reasonable request.
 constexpr size_t kCombinedMappedSizesCountLimit = 6;
 
-void SortSizesDescending(std::vector<gfx::Size>& sizes) {
-  std::sort(sizes.begin(), sizes.end(),
-            [](gfx::Size& a, gfx::Size& b) { return a.height() > b.height(); });
-}
-
 }  // namespace
 
 VideoCaptureFeedback::VideoCaptureFeedback() = default;
@@ -75,7 +69,6 @@
     }
     mapped_sizes.push_back(mapped_size);
   }
-  SortSizesDescending(mapped_sizes);
 }
 
 bool VideoCaptureFeedback::Empty() const {
@@ -109,7 +102,6 @@
 VideoCaptureFeedback& VideoCaptureFeedback::WithMappedSizes(
     std::vector<gfx::Size> mapped_sizes) {
   this->mapped_sizes = std::move(mapped_sizes);
-  SortSizesDescending(mapped_sizes);
   return *this;
 }
 
diff --git a/media/gpu/test/local_gpu_memory_buffer_manager.cc b/media/gpu/test/local_gpu_memory_buffer_manager.cc
index 270fe173..43e8b43 100644
--- a/media/gpu/test/local_gpu_memory_buffer_manager.cc
+++ b/media/gpu/test/local_gpu_memory_buffer_manager.cc
@@ -244,7 +244,8 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    gpu::SurfaceHandle surface_handle) {
+    gpu::SurfaceHandle surface_handle,
+    base::WaitableEvent* shutdown_event) {
   if (!gbm_device_) {
     LOG(ERROR) << "Invalid GBM device";
     return nullptr;
diff --git a/media/gpu/test/local_gpu_memory_buffer_manager.h b/media/gpu/test/local_gpu_memory_buffer_manager.h
index 57a6fd8..d2c8315c 100644
--- a/media/gpu/test/local_gpu_memory_buffer_manager.h
+++ b/media/gpu/test/local_gpu_memory_buffer_manager.h
@@ -36,7 +36,8 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) override;
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) override;
   void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
                                const gpu::SyncToken& sync_token) override;
   void CopyGpuMemoryBufferAsync(
diff --git a/net/BUILD.gn b/net/BUILD.gn
index d13af6a8..15c13db 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -936,7 +936,6 @@
       "quiche/common/platform/impl/quiche_flag_utils_impl.h",
       "quiche/common/platform/impl/quiche_flags_impl.cc",
       "quiche/common/platform/impl/quiche_flags_impl.h",
-      "quiche/common/platform/impl/quiche_text_utils_impl.h",
       "socket/client_socket_factory.cc",
       "socket/client_socket_factory.h",
       "socket/client_socket_pool.cc",
@@ -4094,7 +4093,6 @@
     "base/expiring_cache_unittest.cc",
     "base/file_stream_unittest.cc",
     "base/filename_util_unittest.cc",
-    "base/hex_utils_test.cc",
     "base/host_mapping_rules_unittest.cc",
     "base/host_port_pair_unittest.cc",
     "base/interval_test.cc",
diff --git a/net/base/hash_value.cc b/net/base/hash_value.cc
index a673514..ec440bb 100644
--- a/net/base/hash_value.cc
+++ b/net/base/hash_value.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "net/base/hash_value.h"
-#include "base/strings/string_util.h"
 
 #include <stdlib.h>
 #include <algorithm>
diff --git a/net/base/hex_utils.cc b/net/base/hex_utils.cc
index 2e241d3e5..3115b41 100644
--- a/net/base/hex_utils.cc
+++ b/net/base/hex_utils.cc
@@ -4,9 +4,9 @@
 
 #include "net/base/hex_utils.h"
 
-#include <algorithm>
-
-#include "base/strings/stringprintf.h"
+#include "base/strings/abseil_string_conversions.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/third_party/quiche/src/common/quiche_text_utils.h"
 
 namespace net {
 
@@ -18,37 +18,7 @@
 }
 
 std::string HexDump(base::StringPiece input) {
-  const int kBytesPerLine = 16;  // Maximum bytes dumped per line.
-  int offset = 0;
-  const char* buf = input.data();
-  int bytes_remaining = input.size();
-  std::string output;
-  const char* p = buf;
-  while (bytes_remaining > 0) {
-    const int line_bytes = std::min(bytes_remaining, kBytesPerLine);
-    base::StringAppendF(&output, "0x%04x:  ", offset);
-    for (int i = 0; i < kBytesPerLine; ++i) {
-      if (i < line_bytes) {
-        base::StringAppendF(&output, "%02x", static_cast<unsigned char>(p[i]));
-      } else {
-        output += "  ";
-      }
-      if (i % 2) {
-        output += ' ';
-      }
-    }
-    output += ' ';
-    for (int i = 0; i < line_bytes; ++i) {
-      // Replace non-printable characters and 0x20 (space) with '.'
-      output += (p[i] > 0x20 && p[i] < 0x7f) ? p[i] : '.';
-    }
-
-    bytes_remaining -= line_bytes;
-    offset += line_bytes;
-    p += line_bytes;
-    output += '\n';
-  }
-  return output;
+  return quiche::QuicheTextUtils::HexDump(base::StringPieceToStringView(input));
 }
 
 }  // namespace net
diff --git a/net/base/hex_utils.h b/net/base/hex_utils.h
index c33c24c..295091e 100644
--- a/net/base/hex_utils.h
+++ b/net/base/hex_utils.h
@@ -7,7 +7,6 @@
 
 #include <string>
 
-#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "net/base/net_export.h"
 
diff --git a/net/base/hex_utils_test.cc b/net/base/hex_utils_test.cc
deleted file mode 100644
index a812be1..0000000
--- a/net/base/hex_utils_test.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "net/base/hex_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-namespace test {
-
-TEST(HexUtilsTest, HexDump) {
-  EXPECT_EQ("", HexDump(""));
-  EXPECT_EQ("0x0000:  4865 6c6c 6f20 776f 726c 6421            Hello.world!\n",
-            HexDump("Hello world!"));
-  EXPECT_EQ(
-      "0x0000:  5052 4920 2a20 4854 5450 2f32 2e30 0d0a  PRI.*.HTTP/2.0..\n"
-      "0x0010:  0d0a 534d 0d0a 0d0a                      ..SM....\n",
-      HexDump("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"));
-  // Verify that 0x21 and 0x7e are printable, 0x20 and 0x7f are not.
-  EXPECT_EQ("0x0000:  2021 7e7f                                .!~.\n",
-            HexDump("\x20\x21\x7e\x7f"));
-  // Verify that values above numeric_limits<unsigned char>::max() are cast
-  // properly on platforms where char is unsigned.
-  EXPECT_EQ("0x0000:  90aa ff                                  ...\n",
-            HexDump("\x90\xaa\xff"));
-}
-
-}  // namespace test
-
-}  // namespace net
diff --git a/net/dns/dns_query.cc b/net/dns/dns_query.cc
index 79f7cefe..eb6d6e7 100644
--- a/net/dns/dns_query.cc
+++ b/net/dns/dns_query.cc
@@ -183,8 +183,9 @@
   if (header.flags & dns_protocol::kFlagResponse) {
     return false;
   }
-  if (header.qdcount > 1) {
-    VLOG(1) << "Not supporting parsing a DNS query with multiple questions.";
+  if (header.qdcount != 1) {
+    VLOG(1) << "Not supporting parsing a DNS query with multiple (or zero) "
+               "questions.";
     return false;
   }
   std::string qname;
diff --git a/net/dns/dns_query_unittest.cc b/net/dns/dns_query_unittest.cc
index 81a7c85..8862558 100644
--- a/net/dns/dns_query_unittest.cc
+++ b/net/dns/dns_query_unittest.cc
@@ -326,6 +326,9 @@
   EXPECT_TRUE(query.Parse(data.size()));
 }
 
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
 TEST(DnsQueryParseTest, FailsTooLongName) {
   const char kHeader[] =
       "\x5f\x15"   // ID
@@ -357,6 +360,9 @@
   EXPECT_FALSE(query.Parse(data.size()));
 }
 
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
 TEST(DnsQueryParseTest, FailsTooLongSingleLabelName) {
   const char kHeader[] =
       "\x5f\x15"   // ID
@@ -387,6 +393,167 @@
   EXPECT_FALSE(query.Parse(data.size()));
 }
 
+// Test that a query cannot be parsed with a name extending past the end of the
+// data.
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsQueryParseTest, FailsNonendedName) {
+  const char kData[] =
+      "\x5f\x15"                    // ID
+      "\x00\x00"                    // FLAGS
+      "\x00\x01"                    // 1 question
+      "\x00\x00"                    // 0 answers
+      "\x00\x00"                    // 0 authority records
+      "\x00\x00"                    // 0 additional records
+      "\003www\006google\006test";  // Nonended name.
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_FALSE(query.Parse(sizeof(kData) - 1));
+}
+
+// Test that a query cannot be parsed with a name without final null
+// termination. Parsing should assume the name has not ended and find the first
+// byte of the TYPE field instead, making the actual type unparsable.
+// Tests against incorrect name null termination, which is anti-pattern #4 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsQueryParseTest, FailsNameWithoutTerminator) {
+  const char kData[] =
+      "\x5f\x15"                   // ID
+      "\x00\x00"                   // FLAGS
+      "\x00\x01"                   // 1 question
+      "\x00\x00"                   // 0 answers
+      "\x00\x00"                   // 0 authority records
+      "\x00\x00"                   // 0 additional records
+      "\003www\006google\004test"  // Name without termination.
+      "\x00\x01"                   // TYPE=A
+      "\x00\x01";                  // CLASS=IN
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_FALSE(query.Parse(sizeof(kData) - 1));
+}
+
+TEST(DnsQueryParseTest, FailsQueryWithNoQuestions) {
+  const char kData[] =
+      "\x5f\x15"   // ID
+      "\x00\x00"   // FLAGS
+      "\x00\x00"   // 0 questions
+      "\x00\x00"   // 0 answers
+      "\x00\x00"   // 0 authority records
+      "\x00\x00";  // 0 additional records
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_FALSE(query.Parse(sizeof(kData) - 1));
+}
+
+TEST(DnsQueryParseTest, FailsQueryWithMultipleQuestions) {
+  const char kData[] =
+      "\x5f\x15"                       // ID
+      "\x00\x00"                       // FLAGS
+      "\x00\x02"                       // 2 questions
+      "\x00\x00"                       // 0 answers
+      "\x00\x00"                       // 0 authority records
+      "\x00\x00"                       // 0 additional records
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x01"                       // TYPE=A
+      "\x00\x01"                       // CLASS=IN
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x1c"                       // TYPE=AAAA
+      "\x00\x01";                      // CLASS=IN
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_FALSE(query.Parse(sizeof(kData) - 1));
+}
+
+// Test that if more questions are at the end of the buffer than the number of
+// questions claimed in the query header, the extra questions are safely
+// ignored.
+TEST(DnsQueryParseTest, IgnoresExtraQuestion) {
+  const char kData[] =
+      "\x5f\x15"                       // ID
+      "\x00\x00"                       // FLAGS
+      "\x00\x01"                       // 1 question
+      "\x00\x00"                       // 0 answers
+      "\x00\x00"                       // 0 authority records
+      "\x00\x00"                       // 0 additional records
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x01"                       // TYPE=A
+      "\x00\x01"                       // CLASS=IN
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x1c"                       // TYPE=AAAA
+      "\x00\x01";                      // CLASS=IN
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_TRUE(query.Parse(sizeof(kData) - 1));
+
+  std::string expected_qname("\003www\006google\004test\000", 17);
+  EXPECT_EQ(query.qname(), expected_qname);
+
+  EXPECT_EQ(query.qtype(), dns_protocol::kTypeA);
+}
+
+// Test that the query fails to parse if it does not contain the number of
+// questions claimed in the query header.
+// Tests against incorrect record count field validation, which is anti-pattern
+// #5 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsQueryParseTest, FailsQueryWithMissingQuestion) {
+  const char kData[] =
+      "\x5f\x15"   // ID
+      "\x00\x00"   // FLAGS
+      "\x00\x01"   // 1 question
+      "\x00\x00"   // 0 answers
+      "\x00\x00"   // 0 authority records
+      "\x00\x00";  // 0 additional records
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_FALSE(query.Parse(sizeof(kData) - 1));
+}
+
+// Test that DnsQuery parsing disallows name compression pointers (which should
+// never be useful when only single-question queries are parsed).
+// Indirectly tests against incorrect name compression pointer validation, which
+// is anti-pattern #6 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsQueryParseTest, FailsQueryWithNamePointer) {
+  const char kData[] =
+      "\x5f\x15"                   // ID
+      "\x00\x00"                   // FLAGS
+      "\x00\x01"                   // 1 question
+      "\x00\x00"                   // 0 answers
+      "\x00\x00"                   // 0 authority records
+      "\x00\x00"                   // 0 additional records
+      "\003www\006google\300\035"  // Name with pointer to byte 29
+      "\x00\x01"                   // TYPE=A
+      "\x00\x01"                   // CLASS=IN
+      "\004test\000";              // Byte 29 (name pointer destination): test.
+
+  auto packet = base::MakeRefCounted<IOBufferWithSize>(sizeof(kData) - 1);
+  memcpy(packet->data(), kData, sizeof(kData) - 1);
+  DnsQuery query(packet);
+
+  EXPECT_FALSE(query.Parse(sizeof(kData) - 1));
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/dns_record_fuzzer.cc b/net/dns/dns_record_fuzzer.cc
index cdb84d0..9c052332 100644
--- a/net/dns/dns_record_fuzzer.cc
+++ b/net/dns/dns_record_fuzzer.cc
@@ -6,6 +6,9 @@
 #include <stdint.h>
 
 #include <memory>
+#include <vector>
+
+#include <fuzzer/FuzzedDataProvider.h>
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
@@ -29,7 +32,12 @@
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   InitLogging();
 
-  net::DnsRecordParser parser(data, size, 0);
+  FuzzedDataProvider data_provider(data, size);
+  size_t num_records = data_provider.ConsumeIntegral<size_t>();
+  std::vector<uint8_t> packet = data_provider.ConsumeRemainingBytes<uint8_t>();
+
+  net::DnsRecordParser parser(packet.data(), packet.size(), /*offset=*/0,
+                              num_records);
   if (!parser.IsValid()) {
     return 0;
   }
diff --git a/net/dns/dns_response.cc b/net/dns/dns_response.cc
index da41e19..d249068d 100644
--- a/net/dns/dns_response.cc
+++ b/net/dns/dns_response.cc
@@ -111,14 +111,15 @@
          (owned_rdata.empty() ? rdata.size() : owned_rdata.size());
 }
 
-DnsRecordParser::DnsRecordParser()
-    : packet_(nullptr), length_(0), cur_(nullptr) {}
+DnsRecordParser::DnsRecordParser() = default;
 
 DnsRecordParser::DnsRecordParser(const void* packet,
                                  size_t length,
-                                 size_t offset)
+                                 size_t offset,
+                                 size_t num_records)
     : packet_(reinterpret_cast<const char*>(packet)),
       length_(length),
+      num_records_(num_records),
       cur_(packet_ + offset) {
   DCHECK_LE(offset, length);
 }
@@ -224,6 +225,11 @@
 
 bool DnsRecordParser::ReadRecord(DnsResourceRecord* out) {
   DCHECK(packet_);
+
+  // Disallow parsing any more than the claimed number of records.
+  if (num_records_parsed_ >= num_records_)
+    return false;
+
   size_t consumed = ReadName(cur_, &out->name);
   if (!consumed)
     return false;
@@ -236,6 +242,7 @@
       reader.ReadU16(&rdlen) &&
       reader.ReadPiece(&out->rdata, rdlen)) {
     cur_ = reader.ptr();
+    ++num_records_parsed_;
     return true;
   }
   return false;
@@ -356,7 +363,10 @@
 DnsResponse::DnsResponse(const void* data, size_t length, size_t answer_offset)
     : io_buffer_(base::MakeRefCounted<IOBufferWithSize>(length)),
       io_buffer_size_(length),
-      parser_(io_buffer_->data(), length, answer_offset) {
+      parser_(io_buffer_->data(),
+              length,
+              answer_offset,
+              std::numeric_limits<size_t>::max()) {
   DCHECK(data);
   memcpy(io_buffer_->data(), data, length);
 }
@@ -403,9 +413,15 @@
   dotted_qnames_.push_back(std::move(dotted_qname).value());
   qtypes_.push_back(query.qtype());
 
-  // Construct the parser.
+  size_t num_records = base::NetToHost16(header()->ancount) +
+                       base::NetToHost16(header()->nscount) +
+                       base::NetToHost16(header()->arcount);
+
+  // Construct the parser. Only allow parsing up to `num_records` records. If
+  // more records are present in the buffer, it's just garbage extra data after
+  // the formal end of the response and should be ignored.
   parser_ = DnsRecordParser(io_buffer_->data(), nbytes,
-                            kHeaderSize + question.size());
+                            kHeaderSize + question.size(), num_records);
   return true;
 }
 
@@ -419,7 +435,14 @@
   if ((base::NetToHost16(header()->flags) & dns_protocol::kFlagResponse) == 0)
     return false;
 
-  parser_ = DnsRecordParser(io_buffer_->data(), nbytes, kHeaderSize);
+  size_t num_records = base::NetToHost16(header()->ancount) +
+                       base::NetToHost16(header()->nscount) +
+                       base::NetToHost16(header()->arcount);
+  // Only allow parsing up to `num_records` records. If more records are present
+  // in the buffer, it's just garbage extra data after the formal end of the
+  // response and should be ignored.
+  parser_ =
+      DnsRecordParser(io_buffer_->data(), nbytes, kHeaderSize, num_records);
 
   unsigned qdcount = base::NetToHost16(header()->qdcount);
   for (unsigned i = 0; i < qdcount; ++i) {
diff --git a/net/dns/dns_response.h b/net/dns/dns_response.h
index 225bbcc..ebc3eec 100644
--- a/net/dns/dns_response.h
+++ b/net/dns/dns_response.h
@@ -70,9 +70,15 @@
   // Construct an uninitialized iterator.
   DnsRecordParser();
 
-  // Construct an iterator to process the |packet| of given |length|.
-  // |offset| points to the beginning of the answer section.
-  DnsRecordParser(const void* packet, size_t length, size_t offset);
+  // Construct an iterator to process the `packet` of given `length`.
+  // `offset` points to the beginning of the answer section. `ReadRecord()` will
+  // fail if called more than `num_records` times, no matter whether or not
+  // there is additional data at the end of the buffer that may appear to be a
+  // valid record.
+  DnsRecordParser(const void* packet,
+                  size_t length,
+                  size_t offset,
+                  size_t num_records);
 
   // Returns |true| if initialized.
   bool IsValid() const { return packet_ != nullptr; }
@@ -101,10 +107,12 @@
   bool ReadQuestion(std::string& out_dotted_qname, uint16_t& out_qtype);
 
  private:
-  const char* packet_;
-  size_t length_;
+  const char* packet_ = nullptr;
+  size_t length_ = 0;
+  size_t num_records_ = 0;
+  size_t num_records_parsed_ = 0;
   // Current offset within the packet.
-  const char* cur_;
+  const char* cur_ = nullptr;
 };
 
 // Buffer-holder for the DNS response allowing easy access to the header fields
diff --git a/net/dns/dns_response_unittest.cc b/net/dns/dns_response_unittest.cc
index 0564203..27bcd03 100644
--- a/net/dns/dns_response_unittest.cc
+++ b/net/dns/dns_response_unittest.cc
@@ -32,11 +32,11 @@
   const char data[] = { 0 };
 
   EXPECT_FALSE(DnsRecordParser().IsValid());
-  EXPECT_TRUE(DnsRecordParser(data, 1, 0).IsValid());
-  EXPECT_TRUE(DnsRecordParser(data, 1, 1).IsValid());
+  EXPECT_TRUE(DnsRecordParser(data, 1, 0, 0).IsValid());
+  EXPECT_TRUE(DnsRecordParser(data, 1, 1, 0).IsValid());
 
-  EXPECT_FALSE(DnsRecordParser(data, 1, 0).AtEnd());
-  EXPECT_TRUE(DnsRecordParser(data, 1, 1).AtEnd());
+  EXPECT_FALSE(DnsRecordParser(data, 1, 0, 0).AtEnd());
+  EXPECT_TRUE(DnsRecordParser(data, 1, 1, 0).AtEnd());
 }
 
 TEST(DnsRecordParserTest, ReadName) {
@@ -56,7 +56,7 @@
   };
 
   std::string out;
-  DnsRecordParser parser(data, sizeof(data), 0);
+  DnsRecordParser parser(data, sizeof(data), 0, /*num_records=*/0);
   ASSERT_TRUE(parser.IsValid());
 
   EXPECT_EQ(0x11u, parser.ReadName(data + 0x00, &out));
@@ -79,7 +79,7 @@
   EXPECT_EQ(0x2u, parser.ReadName(data + 0x17, nullptr));
 
   // Check that it works even if initial position is different.
-  parser = DnsRecordParser(data, sizeof(data), 0x12);
+  parser = DnsRecordParser(data, sizeof(data), 0x12, /*num_records=*/0);
   EXPECT_EQ(0x6u, parser.ReadName(data + 0x11, nullptr));
 }
 
@@ -97,7 +97,7 @@
       0x02, 'x', 'x',
   };
 
-  DnsRecordParser parser(data, sizeof(data), 0);
+  DnsRecordParser parser(data, sizeof(data), 0, /*num_records=*/0);
   ASSERT_TRUE(parser.IsValid());
 
   std::string out;
@@ -176,7 +176,7 @@
     ASSERT_EQ(data_vector.size(), name_len + 1);
     const uint8_t* data = data_vector.data();
 
-    DnsRecordParser parser(data, data_vector.size(), 0);
+    DnsRecordParser parser(data, data_vector.size(), 0, /*num_records=*/0);
     ASSERT_TRUE(parser.IsValid());
 
     std::string out;
@@ -185,6 +185,9 @@
   }
 }
 
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
 TEST(DnsRecordParserTest, ReadNameTooLongFail) {
   const size_t name_len_cases[] = {256, 257, 258, 300, 10000};
 
@@ -195,7 +198,7 @@
     ASSERT_EQ(data_vector.size(), name_len + 1);
     const uint8_t* data = data_vector.data();
 
-    DnsRecordParser parser(data, data_vector.size(), 0);
+    DnsRecordParser parser(data, data_vector.size(), 0, /*num_records=*/0);
     ASSERT_TRUE(parser.IsValid());
 
     std::string out;
@@ -203,6 +206,102 @@
   }
 }
 
+// Tests against incorrect name compression pointer validation, which is anti-
+// pattern #6 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsRecordParserTest, RejectsNamesWithLoops) {
+  const char kData[] =
+      "\003www\007example\300\031"  // www.example with pointer to byte 25
+      "aaaaaaaaaaa"                 // Garbage data to spread things out.
+      "\003foo\300\004";            // foo with pointer to byte 4.
+
+  DnsRecordParser parser(kData, /*length=*/sizeof(kData) - 1, /*offset=*/0,
+                         /*num_records=*/0);
+  ASSERT_TRUE(parser.IsValid());
+
+  std::string out;
+  EXPECT_EQ(0u, parser.ReadName(kData, &out));
+}
+
+// Tests against incorrect name compression pointer validation, which is anti-
+// pattern #6 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsRecordParserTest, RejectsNamesPointingOutsideData) {
+  const char kData[] =
+      "\003www\007example\300\031";  // www.example with pointer to byte 25
+
+  DnsRecordParser parser(kData, /*length=*/sizeof(kData) - 1, /*offset=*/0,
+                         /*num_records=*/0);
+  ASSERT_TRUE(parser.IsValid());
+
+  std::string out;
+  EXPECT_EQ(0u, parser.ReadName(kData, &out));
+}
+
+TEST(DnsRecordParserTest, ParsesValidPointer) {
+  const char kData[] =
+      "\003www\007example\300\022"  // www.example with pointer to byte 25.
+      "aaaa"                        // Garbage data to spread things out.
+      "\004test\000";               // .test
+
+  DnsRecordParser parser(kData, /*length=*/sizeof(kData) - 1, /*offset=*/0,
+                         /*num_records=*/0);
+  ASSERT_TRUE(parser.IsValid());
+
+  std::string out;
+  EXPECT_EQ(14u, parser.ReadName(kData, &out));
+  EXPECT_EQ(out, "www.example.test");
+}
+
+// Per RFC 1035, section 4.1.4, the first 2 bits of a DNS name label determine
+// if it is a length label (if the bytes are 00) or a pointer label (if the
+// bytes are 11). It is a common DNS parsing bug to treat 01 or 10 as pointer
+// labels, but these are reserved and invalid. Such labels should always result
+// in DnsRecordParser rejecting the name.
+//
+// Tests against incorrect name compression pointer validation, which is anti-
+// pattern #6 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsRecordParserTest, RejectsNamesWithInvalidLabelTypeAsPointer) {
+  const char kData[] =
+      "\003www\007example\200\022"  // www.example with invalid label as pointer
+      "aaaa"                        // Garbage data to spread things out.
+      "\004test\000";               // .test
+
+  DnsRecordParser parser(kData, /*length=*/sizeof(kData) - 1, /*offset=*/0,
+                         /*num_records=*/0);
+  ASSERT_TRUE(parser.IsValid());
+
+  std::string out;
+  EXPECT_EQ(0u, parser.ReadName(kData, &out));
+}
+
+// Per RFC 1035, section 4.1.4, the first 2 bits of a DNS name label determine
+// if it is a length label (if the bytes are 00) or a pointer label (if the
+// bytes are 11). Such labels should always result in DnsRecordParser rejecting
+// the name.
+//
+// Tests against incorrect name compression pointer validation, which is anti-
+// pattern #6 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsRecordParserTest, RejectsNamesWithInvalidLabelTypeAsLength) {
+  const char kData[] =
+      "\003www\007example\104"  // www.example with invalid label as length
+      "test\000";  // test. (in case \104 is interpreted as length=4)
+
+  // Append a bunch of zeroes to the buffer in case \104 is interpreted as a
+  // long length.
+  std::string data(kData, sizeof(kData) - 1);
+  data.append(256, '\000');
+
+  DnsRecordParser parser(data.data(), data.size(), /*offset=*/0,
+                         /*num_records=*/0);
+  ASSERT_TRUE(parser.IsValid());
+
+  std::string out;
+  EXPECT_EQ(0u, parser.ReadName(data.data(), &out));
+}
+
 TEST(DnsRecordParserTest, ReadRecord) {
   const uint8_t data[] = {
       // Type CNAME record.
@@ -223,7 +322,7 @@
   };
 
   std::string out;
-  DnsRecordParser parser(data, sizeof(data), 0);
+  DnsRecordParser parser(data, sizeof(data), 0, /*num_records=*/2);
 
   DnsResourceRecord record;
   EXPECT_TRUE(parser.ReadRecord(&record));
@@ -246,7 +345,7 @@
   EXPECT_TRUE(parser.AtEnd());
 
   // Test truncated record.
-  parser = DnsRecordParser(data, sizeof(data) - 2, 0);
+  parser = DnsRecordParser(data, sizeof(data) - 2, 0, /*num_records=*/2);
   EXPECT_TRUE(parser.ReadRecord(&record));
   EXPECT_FALSE(parser.AtEnd());
   EXPECT_FALSE(parser.ReadRecord(&record));
@@ -267,12 +366,15 @@
       "\xc0\xa8\x00\x01",  // 192.168.0.1
       14);
 
-  DnsRecordParser parser(data.data(), data.size(), 0);
+  DnsRecordParser parser(data.data(), data.size(), 0, /*num_records=*/1);
 
   DnsResourceRecord record;
   EXPECT_TRUE(parser.ReadRecord(&record));
 }
 
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
 TEST(DnsRecordParserTest, RejectRecordWithTooLongName) {
   std::string dotted_name;
   const std::vector<uint8_t> dns_name =
@@ -288,12 +390,105 @@
       "\xc0\xa8\x00\x01",  // 192.168.0.1
       14);
 
-  DnsRecordParser parser(data.data(), data.size(), 0);
+  DnsRecordParser parser(data.data(), data.size(), 0, /*num_records=*/1);
 
   DnsResourceRecord record;
   EXPECT_FALSE(parser.ReadRecord(&record));
 }
 
+// Test that a record cannot be parsed with a name extending past the end of the
+// data.
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsRecordParserTest, RejectRecordWithNonendedName) {
+  const char kNonendedName[] = "\003www\006google\006www";
+
+  DnsRecordParser parser(kNonendedName, sizeof(kNonendedName) - 1, 0,
+                         /*num_records=*/1);
+
+  DnsResourceRecord record;
+  EXPECT_FALSE(parser.ReadRecord(&record));
+}
+
+// Test that a record cannot be parsed with a name without final null
+// termination. Parsing should assume the name has not ended and find the first
+// byte of the TYPE field instead, making the remainder of the record
+// unparsable.
+// Tests against incorrect name null termination, which is anti-pattern #4 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsRecordParserTest, RejectRecordNameMissingNullTermination) {
+  const char kData[] =
+      "\003www\006google\004test"  // Name without termination.
+      "\x00\x01"                   // TYPE=A
+      "\x00\x01"                   // CLASS=IN
+      "\x00\x01\x51\x80"           // TTL=1 day
+      "\x00\x04"                   // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x01";          // 192.168.0.1
+
+  DnsRecordParser parser(kData, sizeof(kData) - 1, 0, /*num_records=*/1);
+
+  DnsResourceRecord record;
+  EXPECT_FALSE(parser.ReadRecord(&record));
+}
+
+// Test that no more records can be parsed once the claimed number of records
+// have been parsed.
+TEST(DnsRecordParserTest, RejectReadingTooManyRecords) {
+  const char kData[] =
+      "\003www\006google\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x01"  // 192.168.0.1
+      "\003www\010chromium\004test\000"
+      "\x00\x01"           // TYPE=A
+      "\x00\x01"           // CLASS=IN
+      "\x00\x01\x51\x80"   // TTL=1 day
+      "\x00\x04"           // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x02";  // 192.168.0.2
+
+  DnsRecordParser parser(
+      kData, /*length=*/sizeof(kData) - 1, /*offset=*/0,
+      /*num_records=*/1);  // Claim 1 record despite there being 2 in `kData`.
+
+  DnsResourceRecord record1;
+  EXPECT_TRUE(parser.ReadRecord(&record1));
+
+  // Expect second record cannot be parsed because only 1 was expected.
+  DnsResourceRecord record2;
+  EXPECT_FALSE(parser.ReadRecord(&record2));
+}
+
+// Test that no more records can be parsed once the end of the buffer is
+// reached, even if more records are claimed.
+TEST(DnsRecordParserTest, RejectReadingPastEnd) {
+  const char kData[] =
+      "\003www\006google\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x01"  // 192.168.0.1
+      "\003www\010chromium\004test\000"
+      "\x00\x01"           // TYPE=A
+      "\x00\x01"           // CLASS=IN
+      "\x00\x01\x51\x80"   // TTL=1 day
+      "\x00\x04"           // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x02";  // 192.168.0.2
+
+  DnsRecordParser parser(
+      kData, /*length=*/sizeof(kData) - 1, /*offset=*/0,
+      /*num_records=*/3);  // Claim 3 record despite there being 2 in `kData`.
+
+  DnsResourceRecord record;
+  EXPECT_TRUE(parser.ReadRecord(&record));
+  EXPECT_TRUE(parser.ReadRecord(&record));
+  EXPECT_FALSE(parser.ReadRecord(&record));
+}
+
 TEST(DnsResponseTest, InitParse) {
   // This includes \0 at the end.
   const char qname_data[] = "\x0A""codereview""\x08""chromium""\x03""org";
@@ -449,6 +644,62 @@
   EXPECT_THAT(resp.id(), testing::Optional(0xcafe));
 }
 
+TEST(DnsResponseTest, InitParseRejectsResponseWithoutQuestions) {
+  const char kResponse[] =
+      "\x02\x45"                       // ID=581
+      "\x81\x80"                       // Standard query response, RA, no error
+      "\x00\x00"                       // 0 questions
+      "\x00\x01"                       // 1 answers
+      "\x00\x00"                       // 0 authority records
+      "\x00\x00"                       // 0 additional records
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x01"                       // TYPE=A
+      "\x00\x01"                       // CLASS=IN
+      "\x00\x00\x2a\x30"               // TTL=3 hours
+      "\x00\x04"                       // RDLENGTH=4 bytes
+      "\xa0\xa0\xa0\xa0";              // 10.10.10.10
+
+  DnsResponse resp;
+  memcpy(resp.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  // Validate that the response is fine if not matching against a query.
+  ASSERT_TRUE(resp.InitParseWithoutQuery(sizeof(kResponse) - 1));
+
+  const char kQueryName[] = "\003www\006google\004test\000";
+  DnsQuery query(
+      /*id=*/581, base::StringPiece(kQueryName, sizeof(kQueryName) - 1),
+      dns_protocol::kTypeA);
+  EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
+}
+
+TEST(DnsResponseTest, InitParseRejectsResponseWithTooManyQuestions) {
+  const char kResponse[] =
+      "\x02\x46"                       // ID=582
+      "\x81\x80"                       // Standard query response, RA, no error
+      "\x00\x02"                       // 2 questions
+      "\x00\x00"                       // 0 answers
+      "\x00\x00"                       // 0 authority records
+      "\x00\x00"                       // 0 additional records
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x01"                       // TYPE=A
+      "\x00\x01"                       // CLASS=IN
+      "\003www\010chromium\004test\000"  // www.chromium.test
+      "\x00\x01"                         // TYPE=A
+      "\x00\x01";                        // CLASS=IN
+
+  DnsResponse resp;
+  memcpy(resp.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  // Validate that the response is fine if not matching against a query.
+  ASSERT_TRUE(resp.InitParseWithoutQuery(sizeof(kResponse) - 1));
+
+  const char kQueryName[] = "\003www\006google\004test\000";
+  DnsQuery query(
+      /*id=*/582, base::StringPiece(kQueryName, sizeof(kQueryName) - 1),
+      dns_protocol::kTypeA);
+  EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
+}
+
 TEST(DnsResponseTest, InitParseWithoutQuery) {
   DnsResponse resp;
   memcpy(resp.io_buffer()->data(), kT0ResponseDatagram,
@@ -720,9 +971,12 @@
   EXPECT_TRUE(resp2.InitParse(response_data.size(), query));
 }
 
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
 TEST(DnsResponseTest, InitParseRejectsQuestionWithTooLongName) {
   const char kResponseHeader[] =
-      "\x02\x45"   // ID
+      "\x02\x45"   // ID=581
       "\x81\x80"   // Standard query response, RA, no error
       "\x00\x01"   // 1 question
       "\x00\x00"   // 0 answers
@@ -744,11 +998,231 @@
   DnsResponse resp;
   memcpy(resp.io_buffer()->data(), response_data.data(), response_data.size());
 
-  // Need to use InitParseWithoutQuery because DnsQuery disallows being created
-  // with too long of a name, so `InitParse(query)` could only ever test that
-  // responses are rejected when the contained question doesn't match the
-  // expected query.
   EXPECT_FALSE(resp.InitParseWithoutQuery(response_data.size()));
+
+  // Note that `DnsQuery` disallows construction without a valid name, so
+  // `InitParse()` can never be tested with a `query` that matches against a
+  // too-long name in the response. Test with an arbitrary valid query name to
+  // ensure no issues if this code is exercised after receiving a response with
+  // a too-long name.
+  const char kQueryName[] = "\005query\004test\000";
+  DnsQuery query(
+      /*id=*/581, base::StringPiece(kQueryName, sizeof(kQueryName) - 1),
+      dns_protocol::kTypeA);
+  EXPECT_FALSE(resp.InitParse(response_data.size(), query));
+}
+
+// Test that `InitParse[...]()` rejects a response with a question name
+// extending past the end of the response.
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsResponseTest, InitParseRejectsQuestionWithNonendedName) {
+  const char kResponse[] =
+      "\x02\x45"                    // ID
+      "\x81\x80"                    // Standard query response, RA, no error
+      "\x00\x01"                    // 1 question
+      "\x00\x00"                    // 0 answers
+      "\x00\x00"                    // 0 authority records
+      "\x00\x00"                    // 0 additional records
+      "\003www\006google\006test";  // Name extending past the end.
+
+  DnsResponse resp;
+  memcpy(resp.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  EXPECT_FALSE(resp.InitParseWithoutQuery(sizeof(kResponse) - 1));
+
+  const char kQueryName[] = "\003www\006google\006testtt\000";
+  DnsQuery query(
+      /*id=*/581, base::StringPiece(kQueryName, sizeof(kQueryName) - 1),
+      dns_protocol::kTypeA);
+  EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
+}
+
+// Test that `InitParse[...]()` rejects responses that do not contain at least
+// the claimed number of questions.
+// Tests against incorrect record count field validation, which is anti-pattern
+// #5 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsResponseTest, InitParseRejectsResponseWithMissingQuestions) {
+  const char kResponse[] =
+      "\x02\x45"                       // ID
+      "\x81\x80"                       // Standard query response, RA, no error
+      "\x00\x03"                       // 3 questions
+      "\x00\x00"                       // 0 answers
+      "\x00\x00"                       // 0 authority records
+      "\x00\x00"                       // 0 additional records
+      "\003www\006google\004test\000"  // www.google.test
+      "\x00\x01"                       // TYPE=A
+      "\x00\x01"                       // CLASS=IN
+      "\003www\010chromium\004test\000"  // www.chromium.test
+      "\x00\x01"                         // TYPE=A
+      "\x00\x01";                        // CLASS=IN
+  // Missing third question.
+
+  DnsResponse resp;
+  memcpy(resp.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  EXPECT_FALSE(resp.InitParseWithoutQuery(sizeof(kResponse) - 1));
+
+  const char kQueryName[] = "\003www\006google\004test\000";
+  DnsQuery query(
+      /*id=*/581, base::StringPiece(kQueryName, sizeof(kQueryName) - 1),
+      dns_protocol::kTypeA);
+  EXPECT_FALSE(resp.InitParse(sizeof(kResponse) - 1, query));
+}
+
+// Test that a parsed DnsResponse only allows parsing the number of records
+// claimed in the response header.
+// Tests against incorrect record count field validation, which is anti-pattern
+// #5 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsResponseTest, ParserLimitedToNumClaimedRecords) {
+  const char kResponse[] =
+      "\x02\x45"  // ID
+      "\x81\x80"  // Standard query response, RA, no error
+      "\x00\x01"  // 1 question
+      "\x00\x01"  // 1 answers
+      "\x00\x02"  // 2 authority records
+      "\x00\x01"  // 1 additional records
+      "\003www\006google\004test\000"
+      "\x00\x01"  // TYPE=A
+      "\x00\x01"  // CLASS=IN
+      // 6 total records.
+      "\003www\006google\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x01"  // 192.168.0.1
+      "\003www\010chromium\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x02"  // 192.168.0.2
+      "\003www\007google1\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x03"  // 192.168.0.3
+      "\003www\011chromium1\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x04"  // 192.168.0.4
+      "\003www\007google2\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x05"  // 192.168.0.5
+      "\003www\011chromium2\004test\000"
+      "\x00\x01"           // TYPE=A
+      "\x00\x01"           // CLASS=IN
+      "\x00\x01\x51\x80"   // TTL=1 day
+      "\x00\x04"           // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x06";  // 192.168.0.6
+
+  DnsResponse resp1;
+  memcpy(resp1.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  ASSERT_TRUE(resp1.InitParseWithoutQuery(sizeof(kResponse) - 1));
+  DnsRecordParser parser1 = resp1.Parser();
+  ASSERT_TRUE(parser1.IsValid());
+
+  // Response header only claims 4 records, so expect parser to only allow
+  // parsing that many, ignoring extra records in the data.
+  DnsResourceRecord record;
+  EXPECT_TRUE(parser1.ReadRecord(&record));
+  EXPECT_TRUE(parser1.ReadRecord(&record));
+  EXPECT_TRUE(parser1.ReadRecord(&record));
+  EXPECT_TRUE(parser1.ReadRecord(&record));
+  EXPECT_FALSE(parser1.ReadRecord(&record));
+  EXPECT_FALSE(parser1.ReadRecord(&record));
+
+  // Repeat using InitParse()
+  DnsResponse resp2;
+  memcpy(resp2.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  const char kQueryName[] = "\003www\006google\004test\000";
+  DnsQuery query(
+      /*id=*/581, base::StringPiece(kQueryName, sizeof(kQueryName) - 1),
+      dns_protocol::kTypeA);
+
+  ASSERT_TRUE(resp2.InitParse(sizeof(kResponse) - 1, query));
+  DnsRecordParser parser2 = resp2.Parser();
+  ASSERT_TRUE(parser2.IsValid());
+
+  // Response header only claims 4 records, so expect parser to only allow
+  // parsing that many, ignoring extra records in the data.
+  EXPECT_TRUE(parser2.ReadRecord(&record));
+  EXPECT_TRUE(parser2.ReadRecord(&record));
+  EXPECT_TRUE(parser2.ReadRecord(&record));
+  EXPECT_TRUE(parser2.ReadRecord(&record));
+  EXPECT_FALSE(parser2.ReadRecord(&record));
+  EXPECT_FALSE(parser2.ReadRecord(&record));
+}
+
+// Test that a parsed DnsResponse does not allow parsing past the end of the
+// input, even if more records are claimed in the response header.
+// Tests against incorrect record count field validation, which is anti-pattern
+// #5 from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST(DnsResponseTest, ParserLimitedToBufferSize) {
+  const char kResponse[] =
+      "\x02\x45"  // ID
+      "\x81\x80"  // Standard query response, RA, no error
+      "\x00\x01"  // 1 question
+      "\x00\x01"  // 1 answers
+      "\x00\x02"  // 2 authority records
+      "\x00\x01"  // 1 additional records
+      "\003www\006google\004test\000"
+      "\x00\x01"  // TYPE=A
+      "\x00\x01"  // CLASS=IN
+      // 2 total records.
+      "\003www\006google\004test\000"
+      "\x00\x01"          // TYPE=A
+      "\x00\x01"          // CLASS=IN
+      "\x00\x01\x51\x80"  // TTL=1 day
+      "\x00\x04"          // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x01"  // 192.168.0.1
+      "\003www\010chromium\004test\000"
+      "\x00\x01"           // TYPE=A
+      "\x00\x01"           // CLASS=IN
+      "\x00\x01\x51\x80"   // TTL=1 day
+      "\x00\x04"           // RDLENGTH=4 bytes
+      "\xc0\xa8\x00\x02";  // 192.168.0.2
+
+  DnsResponse resp1;
+  memcpy(resp1.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  ASSERT_TRUE(resp1.InitParseWithoutQuery(sizeof(kResponse) - 1));
+  DnsRecordParser parser1 = resp1.Parser();
+  ASSERT_TRUE(parser1.IsValid());
+
+  // Response header claims 4 records, but only 2 present in input.
+  DnsResourceRecord record;
+  EXPECT_TRUE(parser1.ReadRecord(&record));
+  EXPECT_TRUE(parser1.ReadRecord(&record));
+  EXPECT_FALSE(parser1.ReadRecord(&record));
+  EXPECT_FALSE(parser1.ReadRecord(&record));
+
+  // Repeat using InitParse()
+  DnsResponse resp2;
+  memcpy(resp2.io_buffer()->data(), kResponse, sizeof(kResponse) - 1);
+
+  ASSERT_TRUE(resp2.InitParseWithoutQuery(sizeof(kResponse) - 1));
+  DnsRecordParser parser2 = resp2.Parser();
+  ASSERT_TRUE(parser2.IsValid());
+
+  // Response header claims 4 records, but only 2 present in input.
+  EXPECT_TRUE(parser2.ReadRecord(&record));
+  EXPECT_TRUE(parser2.ReadRecord(&record));
+  EXPECT_FALSE(parser2.ReadRecord(&record));
+  EXPECT_FALSE(parser2.ReadRecord(&record));
 }
 
 TEST(DnsResponseWriteTest, SingleARecordAnswer) {
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 887361d..3c77651 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -14,6 +14,7 @@
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/containers/circular_deque.h"
+#include "base/numerics/safe_math.h"
 #include "base/optional.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
@@ -70,10 +71,27 @@
 
 const char kMockHostname[] = "mock.http";
 
-std::string DomainFromDot(const base::StringPiece& dotted) {
-  std::string out;
-  EXPECT_TRUE(DNSDomainFromDot(dotted, &out));
-  return out;
+// Like `net::DNSDomainFromDot()` except allows converting more names than
+// accepted by that utility.
+std::string DomainFromDot(base::StringPiece dotted_name) {
+  std::string dns_name;
+
+  while (true) {
+    size_t next_dot = dotted_name.find('.');
+    if (next_dot == base::StringPiece::npos) {
+      dns_name.append(1, base::checked_cast<base::StringPiece::value_type>(
+                             dotted_name.size()));
+      dns_name.append(static_cast<std::string>(dotted_name));
+      dns_name.append(1, 0);
+      return dns_name;
+    } else {
+      dns_name.append(
+          1, base::checked_cast<base::StringPiece::value_type>(next_dot));
+      dns_name.append(
+          static_cast<std::string>(dotted_name.substr(0, next_dot)));
+      dotted_name = dotted_name.substr(next_dot + 1);
+    }
+  }
 }
 
 enum class Transport { UDP, TCP, HTTPS };
@@ -1118,6 +1136,10 @@
   helper0.RunUntilComplete();
 }
 
+// Test that responses are not accepted when only the response ID mismatches.
+// Tests against incorrect transaction ID validation, which is anti-pattern #1
+// from the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
 TEST_F(DnsTransactionTest, MismatchedResponseFail) {
   ConfigureFactory();
 
@@ -3875,6 +3897,24 @@
       0u /* doh_server_index */, session_.get()));
 }
 
+// Test that queries cannot be sent when they contain a too-long name.
+// Tests against incorrect name length validation, which is anti-pattern #3 from
+// the "NAME:WRECK" report:
+// https://www.forescout.com/company/resources/namewreck-breaking-and-fixing-dns-implementations/
+TEST_F(DnsTransactionTestWithMockTime, RejectsQueryingLongNames) {
+  std::string long_dotted_name;
+  while (long_dotted_name.size() <= dns_protocol::kMaxNameLength) {
+    long_dotted_name.append("naaaaaamelabel.");
+  }
+  long_dotted_name.append("test");
+
+  TransactionHelper helper0(ERR_INVALID_ARGUMENT);
+  helper0.StartTransaction(transaction_factory_.get(), long_dotted_name.c_str(),
+                           dns_protocol::kTypeA, false /* secure */,
+                           resolve_context_.get());
+  helper0.RunUntilComplete();
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/mdns_cache_unittest.cc b/net/dns/mdns_cache_unittest.cc
index 77e428e..92813e2 100644
--- a/net/dns/mdns_cache_unittest.cc
+++ b/net/dns/mdns_cache_unittest.cc
@@ -139,7 +139,7 @@
 // Test a single insert, corresponding lookup, and unsuccessful lookup.
 TEST_F(MDnsCacheTest, InsertLookupSingle) {
   DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
-                         sizeof(dns_protocol::Header));
+                         sizeof(dns_protocol::Header), kT1RecordCount);
   std::string dotted_qname;
   uint16_t qtype;
   parser.ReadQuestion(dotted_qname, qtype);
@@ -173,7 +173,7 @@
 // Test that records expire when their ttl has passed.
 TEST_F(MDnsCacheTest, Expiration) {
   DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
-                         sizeof(dns_protocol::Header));
+                         sizeof(dns_protocol::Header), kT1RecordCount);
   std::string dotted_qname;
   uint16_t qtype;
   parser.ReadQuestion(dotted_qname, qtype);
@@ -227,8 +227,8 @@
 // unique records) causes the cache to output a "record changed" event.
 TEST_F(MDnsCacheTest, RecordChange) {
   DnsRecordParser parser(kTestResponsesDifferentAnswers,
-                         sizeof(kTestResponsesDifferentAnswers),
-                         0);
+                         sizeof(kTestResponsesDifferentAnswers), 0,
+                         /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> record1;
   std::unique_ptr<const RecordParsed> record2;
@@ -246,8 +246,8 @@
 // cache causes the cache to output a "no change" event.
 TEST_F(MDnsCacheTest, RecordNoChange) {
   DnsRecordParser parser(kTestResponsesSameAnswers,
-                         sizeof(kTestResponsesSameAnswers),
-                         0);
+                         sizeof(kTestResponsesSameAnswers), 0,
+                         /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> record1;
   std::unique_ptr<const RecordParsed> record2;
@@ -265,8 +265,8 @@
 // insertion.
 TEST_F(MDnsCacheTest, RecordPreemptExpirationTime) {
   DnsRecordParser parser(kTestResponsesSameAnswers,
-                         sizeof(kTestResponsesSameAnswers),
-                         0);
+                         sizeof(kTestResponsesSameAnswers), 0,
+                         /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> record1;
   std::unique_ptr<const RecordParsed> record2;
@@ -289,8 +289,8 @@
 // records from the cache if they are.
 TEST_F(MDnsCacheTest, GoodbyePacket) {
   DnsRecordParser parser(kTestResponsesGoodbyePacket,
-                         sizeof(kTestResponsesGoodbyePacket),
-                         0);
+                         sizeof(kTestResponsesGoodbyePacket), 0,
+                         /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> record_goodbye;
   std::unique_ptr<const RecordParsed> record_hello;
@@ -300,8 +300,8 @@
   record_goodbye = RecordParsed::CreateFrom(&parser, default_time_);
   record_hello = RecordParsed::CreateFrom(&parser, default_time_);
   parser = DnsRecordParser(kTestResponsesGoodbyePacket,
-                           sizeof(kTestResponsesGoodbyePacket),
-                           0);
+                           sizeof(kTestResponsesGoodbyePacket), 0,
+                           /*num_records=*/2);
   record_goodbye2 = RecordParsed::CreateFrom(&parser, default_time_);
 
   base::TimeDelta ttl = base::TimeDelta::FromSeconds(record_hello->ttl());
@@ -321,8 +321,7 @@
 
 TEST_F(MDnsCacheTest, AnyRRType) {
   DnsRecordParser parser(kTestResponseTwoRecords,
-                         sizeof(kTestResponseTwoRecords),
-                         0);
+                         sizeof(kTestResponseTwoRecords), 0, /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> record1;
   std::unique_ptr<const RecordParsed> record2;
@@ -348,7 +347,7 @@
 
 TEST_F(MDnsCacheTest, RemoveRecord) {
   DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
-                         sizeof(dns_protocol::Header));
+                         sizeof(dns_protocol::Header), kT1RecordCount);
   std::string dotted_qname;
   uint16_t qtype;
   parser.ReadQuestion(dotted_qname, qtype);
@@ -377,7 +376,7 @@
 
 TEST_F(MDnsCacheTest, IsCacheOverfilled) {
   DnsRecordParser parser(kTestResponseTwoRecords,
-                         sizeof(kTestResponseTwoRecords), 0);
+                         sizeof(kTestResponseTwoRecords), 0, /*num_records=*/2);
   std::unique_ptr<const RecordParsed> record1 =
       RecordParsed::CreateFrom(&parser, default_time_);
   const RecordParsed* record1_ptr = record1.get();
@@ -397,7 +396,7 @@
 
 TEST_F(MDnsCacheTest, ClearOnOverfilledCleanup) {
   DnsRecordParser parser(kTestResponseTwoRecords,
-                         sizeof(kTestResponseTwoRecords), 0);
+                         sizeof(kTestResponseTwoRecords), 0, /*num_records=*/2);
   std::unique_ptr<const RecordParsed> record1 =
       RecordParsed::CreateFrom(&parser, default_time_);
   const RecordParsed* record1_ptr = record1.get();
@@ -430,7 +429,8 @@
 
 TEST_F(MDnsCacheTest, CaseInsensitive) {
   DnsRecordParser parser(kTestResponsesDifferentCapitalization,
-                         sizeof(kTestResponsesDifferentCapitalization), 0);
+                         sizeof(kTestResponsesDifferentCapitalization), 0,
+                         /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> record1;
   std::unique_ptr<const RecordParsed> record2;
diff --git a/net/dns/record_parsed_unittest.cc b/net/dns/record_parsed_unittest.cc
index 796ec34..e2b2e7f 100644
--- a/net/dns/record_parsed_unittest.cc
+++ b/net/dns/record_parsed_unittest.cc
@@ -28,7 +28,7 @@
 
 TEST(RecordParsedTest, ParseSingleRecord) {
   DnsRecordParser parser(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
-                         sizeof(dns_protocol::Header));
+                         sizeof(dns_protocol::Header), kT1RecordCount);
   std::unique_ptr<const RecordParsed> record;
   const CnameRecordRdata* rdata;
 
@@ -53,7 +53,7 @@
 
 TEST(RecordParsedTest, CacheFlushBitCompare) {
   DnsRecordParser parser1(kT1ResponseDatagram, sizeof(kT1ResponseDatagram),
-                         sizeof(dns_protocol::Header));
+                          sizeof(dns_protocol::Header), kT1RecordCount);
   std::string dotted_qname;
   uint16_t qtype;
   parser1.ReadQuestion(dotted_qname, qtype);
@@ -62,8 +62,8 @@
       RecordParsed::CreateFrom(&parser1, base::Time());
 
   DnsRecordParser parser2(kT1ResponseWithCacheFlushBit,
-                          sizeof(kT1ResponseWithCacheFlushBit),
-                          0);
+                          sizeof(kT1ResponseWithCacheFlushBit), 0,
+                          kT1RecordCount);
 
   std::unique_ptr<const RecordParsed> record2 =
       RecordParsed::CreateFrom(&parser2, base::Time());
@@ -89,7 +89,8 @@
       "\000\014"
       // RDATA="garbage data"
       "garbage data";
-  DnsRecordParser parser(kRecordData, sizeof(kRecordData) - 1, 0 /* offset */);
+  DnsRecordParser parser(kRecordData, sizeof(kRecordData) - 1, 0 /* offset */,
+                         /*num_records=*/1);
 
   std::unique_ptr<const RecordParsed> record =
       RecordParsed::CreateFrom(&parser, base::Time());
@@ -130,7 +131,8 @@
       "\000\004"
       // RDATA=8.8.8.8
       "\010\010\010\010";
-  DnsRecordParser parser(kData, sizeof(kData) - 1, 0 /* offset */);
+  DnsRecordParser parser(kData, sizeof(kData) - 1, 0 /* offset */,
+                         /*num_records=*/2);
 
   std::unique_ptr<const RecordParsed> unknown_record =
       RecordParsed::CreateFrom(&parser, base::Time());
@@ -170,7 +172,8 @@
       "\000\001"
       // RDATA=truncated name
       "\001";
-  DnsRecordParser parser(kRecordData, sizeof(kRecordData) - 1, 0 /* offset */);
+  DnsRecordParser parser(kRecordData, sizeof(kRecordData) - 1, 0 /* offset */,
+                         /*num_records=*/1);
 
   std::unique_ptr<const RecordParsed> record =
       RecordParsed::CreateFrom(&parser, base::Time());
diff --git a/net/dns/record_rdata_unittest.cc b/net/dns/record_rdata_unittest.cc
index 88f4c2bd..46e2da93 100644
--- a/net/dns/record_rdata_unittest.cc
+++ b/net/dns/record_rdata_unittest.cc
@@ -47,7 +47,7 @@
                                                                // "google.com"
           };
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   const unsigned first_record_len = 22;
   base::StringPiece record1_strpiece = MakeStringPiece(
       record, first_record_len);
@@ -84,7 +84,7 @@
       0x7F, 0x00, 0x00, 0x01  // 127.0.0.1
   };
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<ARecordRdata> record_obj =
@@ -105,7 +105,7 @@
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09  // 1234:5678::9A
   };
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<AAAARecordRdata> record_obj =
@@ -124,7 +124,7 @@
   const uint8_t record[] = {0x03, 'w', 'w', 'w',  0x06, 'g', 'o', 'o',
                             'g',  'l', 'e', 0x03, 'c',  'o', 'm', 0x00};
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<CnameRecordRdata> record_obj =
@@ -143,7 +143,7 @@
   const uint8_t record[] = {0x03, 'w', 'w', 'w',  0x06, 'g', 'o', 'o',
                             'g',  'l', 'e', 0x03, 'c',  'o', 'm', 0x00};
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<PtrRecordRdata> record_obj =
@@ -162,7 +162,7 @@
   const uint8_t record[] = {0x03, 'w', 'w', 'w',  0x06, 'g', 'o', 'o',
                             'g',  'l', 'e', 0x03, 'c',  'o', 'm'};
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<TxtRecordRdata> record_obj =
@@ -187,7 +187,7 @@
                             'o',  'g',  'l',  'e',  0x03, 'c', 'o',
                             'm',  0x00, 0x00, 0x02, 0x40, 0x01};
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<NsecRecordRdata> record_obj =
@@ -213,7 +213,7 @@
   const uint8_t record[] = {0x03, 'w', 'w',  'w', 0x06, 'g', 'o',  'o',  'g',
                             'l',  'e', 0x03, 'c', 'o',  'm', 0x00, 0x00, 0x00};
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<NsecRecordRdata> record_obj =
@@ -233,7 +233,7 @@
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-  DnsRecordParser parser(record, sizeof(record), 0);
+  DnsRecordParser parser(record, sizeof(record), 0, /*num_records=*/0);
   base::StringPiece record_strpiece = MakeStringPiece(record, sizeof(record));
 
   std::unique_ptr<NsecRecordRdata> record_obj =
@@ -255,7 +255,7 @@
       0xDE, 0xAD, 0xBE, 0xEF  // OPT data
   };
 
-  DnsRecordParser parser(rdata, sizeof(rdata), 0);
+  DnsRecordParser parser(rdata, sizeof(rdata), 0, /*num_records=*/0);
   base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
 
   std::unique_ptr<OptRecordRdata> rdata_obj =
@@ -278,7 +278,7 @@
       0xDE, 0xAD, 0xBE, 0xEF  // OPT data
   };
 
-  DnsRecordParser parser(rdata, sizeof(rdata), 0);
+  DnsRecordParser parser(rdata, sizeof(rdata), 0, /*num_records=*/0);
   base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
 
   std::unique_ptr<OptRecordRdata> rdata_obj =
@@ -295,7 +295,7 @@
       0xDE, 0xAD   // OPT data
   };
 
-  DnsRecordParser parser(rdata, sizeof(rdata), 0);
+  DnsRecordParser parser(rdata, sizeof(rdata), 0, /*num_records=*/0);
   base::StringPiece rdata_strpiece = MakeStringPiece(rdata, sizeof(rdata));
 
   std::unique_ptr<OptRecordRdata> rdata_obj =
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index 19cc54b..edbd0665 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -4,7 +4,6 @@
 
 // The rules for parsing content-types were borrowed from Firefox:
 // http://lxr.mozilla.org/mozilla/source/netwerk/base/src/nsURLHelper.cpp#834
-#include "base/strings/string_util.h"
 
 #include "net/http/http_util.h"
 
diff --git a/net/http2/platform/impl/http2_string_utils_impl.h b/net/http2/platform/impl/http2_string_utils_impl.h
index f20a085..e39261e 100644
--- a/net/http2/platform/impl/http2_string_utils_impl.h
+++ b/net/http2/platform/impl/http2_string_utils_impl.h
@@ -13,7 +13,7 @@
 #include "base/strings/string_util.h"
 #include "net/base/escape.h"
 #include "net/base/hex_utils.h"
-#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
+#include "net/third_party/quiche/src/common/quiche_text_utils.h"
 
 namespace http2 {
 
diff --git a/net/quic/bidirectional_stream_quic_impl_unittest.cc b/net/quic/bidirectional_stream_quic_impl_unittest.cc
index 9d7a488..aca69072 100644
--- a/net/quic/bidirectional_stream_quic_impl_unittest.cc
+++ b/net/quic/bidirectional_stream_quic_impl_unittest.cc
@@ -44,7 +44,7 @@
 #include "net/socket/socket_test_util.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_with_task_environment.h"
-#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
+#include "net/third_party/quiche/src/common/quiche_text_utils.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h"
 #include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
 #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h"
diff --git a/net/quic/platform/impl/quic_test_impl.h b/net/quic/platform/impl/quic_test_impl.h
index 8ce1924..0d9825f 100644
--- a/net/quic/platform/impl/quic_test_impl.h
+++ b/net/quic/platform/impl/quic_test_impl.h
@@ -66,15 +66,6 @@
 
 std::string QuicGetTestMemoryCachePathImpl();
 
-#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
-#define EXPECT_QUIC_DEBUG_DEATH_IMPL(condition, message) \
-  EXPECT_DEBUG_DEATH(condition, message)
-#else
-#define EXPECT_QUIC_DEBUG_DEATH_IMPL(condition, message) \
-  do {                                                   \
-  } while (0)
-#endif
-
 #define QUIC_SLOW_TEST_IMPL(name) DISABLED_##name
 
 #endif  // NET_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_
diff --git a/net/quic/quic_test_packet_printer.cc b/net/quic/quic_test_packet_printer.cc
index 6333461..cb9581e 100644
--- a/net/quic/quic_test_packet_printer.cc
+++ b/net/quic/quic_test_packet_printer.cc
@@ -6,7 +6,7 @@
 
 #include <ostream>
 
-#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
+#include "base/strings/string_number_conversions.h"
 #include "net/third_party/quiche/src/quic/core/crypto/null_decrypter.h"
 #include "net/third_party/quiche/src/quic/core/quic_framer.h"
 #include "net/third_party/quiche/src/quic/core/quic_utils.h"
@@ -72,17 +72,13 @@
   bool OnStreamFrame(const QuicStreamFrame& frame) override {
     *output_ << "OnStreamFrame: " << frame;
     *output_ << "         data: { "
-             << absl::BytesToHexString(
-                    absl::string_view(frame.data_buffer, frame.data_length))
-             << " }\n";
+             << base::HexEncode(frame.data_buffer, frame.data_length) << " }\n";
     return true;
   }
   bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
     *output_ << "OnCryptoFrame: " << frame;
     *output_ << "         data: { "
-             << absl::BytesToHexString(
-                    absl::string_view(frame.data_buffer, frame.data_length))
-             << " }\n";
+             << base::HexEncode(frame.data_buffer, frame.data_length) << " }\n";
     return true;
   }
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
diff --git a/net/quiche/common/platform/impl/quiche_test_impl.h b/net/quiche/common/platform/impl/quiche_test_impl.h
index a505bcc..a2db91e 100644
--- a/net/quiche/common/platform/impl/quiche_test_impl.h
+++ b/net/quiche/common/platform/impl/quiche_test_impl.h
@@ -17,4 +17,13 @@
 }  // namespace test
 }  // namespace quiche
 
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+#define EXPECT_QUICHE_DEBUG_DEATH_IMPL(condition, message) \
+  EXPECT_DEBUG_DEATH(condition, message)
+#else
+#define EXPECT_QUICHE_DEBUG_DEATH_IMPL(condition, message) \
+  do {                                                     \
+  } while (0)
+#endif
+
 #endif  // NET_QUICHE_COMMON_PLATFORM_IMPL_QUICHE_TEST_IMPL_H_
diff --git a/net/quiche/common/platform/impl/quiche_text_utils_impl.h b/net/quiche/common/platform/impl/quiche_text_utils_impl.h
deleted file mode 100644
index 6842bdf..0000000
--- a/net/quiche/common/platform/impl/quiche_text_utils_impl.h
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_QUICHE_COMMON_PLATFORM_IMPL_QUICHE_TEXT_UTILS_IMPL_H_
-#define NET_QUICHE_COMMON_PLATFORM_IMPL_QUICHE_TEXT_UTILS_IMPL_H_
-
-#include <algorithm>
-#include <cstdint>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include "base/strings/abseil_string_conversions.h"
-#include "net/base/hex_utils.h"
-#include "third_party/abseil-cpp/absl/strings/ascii.h"
-#include "third_party/abseil-cpp/absl/strings/escaping.h"
-#include "third_party/abseil-cpp/absl/strings/match.h"
-#include "third_party/abseil-cpp/absl/strings/str_cat.h"
-#include "third_party/abseil-cpp/absl/strings/str_split.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace quiche {
-
-// Chromium implementation of quiche::QuicheTextUtils.
-class QuicheTextUtilsImpl {
- public:
-  // Returns true of |data| starts with |prefix|, case sensitively.
-  static bool StartsWith(absl::string_view data, absl::string_view prefix) {
-    return absl::StartsWith(data, prefix);
-  }
-
-  // Returns true if |data| end with |suffix|, case sensitively.
-  static bool EndsWith(absl::string_view data, absl::string_view suffix) {
-    return absl::EndsWith(data, suffix);
-  }
-
-  // Returns true of |data| ends with |suffix|, case insensitively.
-  static bool EndsWithIgnoreCase(absl::string_view data,
-                                 absl::string_view suffix) {
-    return absl::EndsWithIgnoreCase(data, suffix);
-  }
-
-  // Returns a new std::string in which |data| has been converted to lower case.
-  static std::string ToLower(absl::string_view data) {
-    return absl::AsciiStrToLower(data);
-  }
-
-  // Remove leading and trailing whitespace from |data|.
-  static void RemoveLeadingAndTrailingWhitespace(absl::string_view* data) {
-    *data = absl::StripAsciiWhitespace(*data);
-  }
-
-  // Returns true if |in| represents a valid uint64, and stores that value in
-  // |out|.
-  static bool StringToUint64(absl::string_view in, uint64_t* out) {
-    return absl::SimpleAtoi(in, out);
-  }
-
-  // Returns true if |in| represents a valid int, and stores that value in
-  // |out|.
-  static bool StringToInt(absl::string_view in, int* out) {
-    return absl::SimpleAtoi(in, out);
-  }
-
-  // Returns true if |in| represents a valid uint32, and stores that value in
-  // |out|.
-  static bool StringToUint32(absl::string_view in, uint32_t* out) {
-    return absl::SimpleAtoi(in, out);
-  }
-
-  // Returns true if |in| represents a valid size_t, and stores that value in
-  // |out|.
-  static bool StringToSizeT(absl::string_view in, size_t* out) {
-    return absl::SimpleAtoi(in, out);
-  }
-
-  // Returns a new std::string representing |in|.
-  static std::string Uint64ToString(uint64_t in) { return absl::StrCat(in); }
-
-  // This converts |length| bytes of binary to a 2*|length|-character
-  // hexadecimal representation.
-  // Return value: 2*|length| characters of ASCII std::string.
-  static std::string HexEncode(absl::string_view data) {
-    return absl::BytesToHexString(data);
-  }
-
-  static std::string Hex(uint32_t v) { return absl::StrCat(absl::Hex(v)); }
-
-  // Converts |data| from a hexadecimal ASCII string to a binary string
-  // that is |data.length()/2| bytes long. On failure returns empty string.
-  static std::string HexDecode(absl::string_view data) {
-    return absl::HexStringToBytes(data);
-  }
-
-  // Base64 encodes with no padding |data_len| bytes of |data| into |output|.
-  static void Base64Encode(const uint8_t* data,
-                           size_t data_len,
-                           std::string* output) {
-    absl::Base64Escape(
-        std::string(reinterpret_cast<const char*>(data), data_len), output);
-    // Remove padding.
-    size_t len = output->size();
-    if (len >= 2) {
-      if ((*output)[len - 1] == '=') {
-        len--;
-        if ((*output)[len - 1] == '=') {
-          len--;
-        }
-        output->resize(len);
-      }
-    }
-  }
-
-  // Decodes a base64-encoded |input|.  Returns nullopt when the input is
-  // invalid.
-  static absl::optional<std::string> Base64Decode(absl::string_view input) {
-    std::string output;
-    if (!absl::Base64Unescape(input, &output)) {
-      return absl::optional<std::string>();
-    }
-    return output;
-  }
-
-  // Returns a std::string containing hex and ASCII representations of |binary|,
-  // side-by-side in the style of hexdump. Non-printable characters will be
-  // printed as '.' in the ASCII output.
-  // For example, given the input "Hello, QUIC!\01\02\03\04", returns:
-  // "0x0000:  4865 6c6c 6f2c 2051 5549 4321 0102 0304  Hello,.QUIC!...."
-  static std::string HexDump(absl::string_view binary_input) {
-    return net::HexDump(base::StringViewToStringPiece(binary_input));
-  }
-
-  // Returns true if |data| contains any uppercase characters.
-  static bool ContainsUpperCase(absl::string_view data) {
-    return std::any_of(data.begin(), data.end(), absl::ascii_isupper);
-  }
-
-  // Returns true if |data| contains only decimal digits.
-  static bool IsAllDigits(absl::string_view data) {
-    return std::all_of(data.begin(), data.end(), absl::ascii_isdigit);
-  }
-
-  // Splits |data| into a vector of pieces delimited by |delim|.
-  static std::vector<absl::string_view> Split(absl::string_view data,
-                                              char delim) {
-    return absl::StrSplit(data, delim);
-  }
-};
-
-}  // namespace quiche
-
-#endif  // NET_QUICHE_COMMON_PLATFORM_IMPL_QUICHE_TEXT_UTILS_IMPL_H_
diff --git a/net/spdy/platform/impl/spdy_string_utils_impl.h b/net/spdy/platform/impl/spdy_string_utils_impl.h
index cf54bcef..18d2bfcb 100644
--- a/net/spdy/platform/impl/spdy_string_utils_impl.h
+++ b/net/spdy/platform/impl/spdy_string_utils_impl.h
@@ -11,7 +11,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "net/base/hex_utils.h"
-#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
+#include "net/third_party/quiche/src/common/quiche_text_utils.h"
 #include "third_party/abseil-cpp/absl/strings/string_view.h"
 
 namespace spdy {
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index e532ddf..635b8f4e2 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -45,13 +45,15 @@
       "src/common/platform/api/quiche_flag_utils.h",
       "src/common/platform/api/quiche_flags.h",
       "src/common/platform/api/quiche_logging.h",
-      "src/common/platform/api/quiche_text_utils.h",
       "src/common/platform/api/quiche_time_utils.h",
+      "src/common/quiche_circular_deque.h",
       "src/common/quiche_data_reader.cc",
       "src/common/quiche_data_reader.h",
       "src/common/quiche_data_writer.cc",
       "src/common/quiche_data_writer.h",
       "src/common/quiche_endian.h",
+      "src/common/quiche_text_utils.cc",
+      "src/common/quiche_text_utils.h",
       "src/common/simple_linked_hash_map.h",
       "src/http2/core/http2_priority_write_scheduler.h",
       "src/http2/core/priority_write_scheduler.h",
@@ -425,7 +427,6 @@
       "src/quic/core/quic_blocked_writer_interface.h",
       "src/quic/core/quic_buffer_allocator.cc",
       "src/quic/core/quic_buffer_allocator.h",
-      "src/quic/core/quic_circular_deque.h",
       "src/quic/core/quic_clock.cc",
       "src/quic/core/quic_clock.h",
       "src/quic/core/quic_coalesced_packet.cc",
@@ -1177,10 +1178,11 @@
 source_set("quiche_tests") {
   testonly = true
   sources = [
-    "src/common/platform/api/quiche_text_utils_test.cc",
     "src/common/platform/api/quiche_time_utils_test.cc",
+    "src/common/quiche_circular_deque_test.cc",
     "src/common/quiche_data_writer_test.cc",
     "src/common/quiche_endian_test.cc",
+    "src/common/quiche_text_utils_test.cc",
     "src/common/simple_linked_hash_map_test.cc",
     "src/http2/core/http2_priority_write_scheduler_test.cc",
     "src/http2/core/priority_write_scheduler_test.cc",
@@ -1339,7 +1341,6 @@
     "src/quic/core/quic_arena_scoped_ptr_test.cc",
     "src/quic/core/quic_bandwidth_test.cc",
     "src/quic/core/quic_buffered_packet_store_test.cc",
-    "src/quic/core/quic_circular_deque_test.cc",
     "src/quic/core/quic_coalesced_packet_test.cc",
     "src/quic/core/quic_config_test.cc",
     "src/quic/core/quic_connection_id_manager_test.cc",
diff --git a/net/tools/quic/crypto_message_printer_bin.cc b/net/tools/quic/crypto_message_printer_bin.cc
index 905a7e9..a3feb054 100644
--- a/net/tools/quic/crypto_message_printer_bin.cc
+++ b/net/tools/quic/crypto_message_printer_bin.cc
@@ -10,7 +10,7 @@
 #include <iostream>
 
 #include "base/command_line.h"
-#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
+#include "base/strings/string_number_conversions.h"
 #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h"
 
 using quic::Perspective;
@@ -50,8 +50,9 @@
   quic::CryptoFramer framer;
   framer.set_visitor(&printer);
   framer.set_process_truncated_messages(true);
-  std::string input = absl::HexStringToBytes(argv[1]);
-  if (!framer.ProcessInput(input)) {
+  std::string input;
+  if (!base::HexStringToString(argv[1], &input) ||
+      !framer.ProcessInput(input)) {
     return 1;
   }
   if (framer.InputBytesRemaining() != 0) {
diff --git a/net/tools/quic/quic_simple_client_bin.cc b/net/tools/quic/quic_simple_client_bin.cc
index 66d6faa..ed7fd700 100644
--- a/net/tools/quic/quic_simple_client_bin.cc
+++ b/net/tools/quic/quic_simple_client_bin.cc
@@ -36,7 +36,6 @@
 #include "base/logging.h"
 #include "net/base/net_errors.h"
 #include "net/quic/address_utils.h"
-#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h"
 #include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_server_id.h"
diff --git a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index 1c3530d..d850948 100644
--- a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -131,7 +131,8 @@
     const gfx::Size& size,
     gfx::BufferFormat format,
     gfx::BufferUsage usage,
-    gpu::SurfaceHandle surface_handle) {
+    gpu::SurfaceHandle surface_handle,
+    base::WaitableEvent* shutdown_event) {
   // Note: this can be called from multiple threads at the same time. Some of
   // those threads may not have a TaskRunner set.
   DCHECK_EQ(gpu::kNullSurfaceHandle, surface_handle);
diff --git a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
index 4208426..04c1ddc7 100644
--- a/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
+++ b/services/viz/public/cpp/gpu/client_gpu_memory_buffer_manager.h
@@ -60,7 +60,8 @@
       const gfx::Size& size,
       gfx::BufferFormat format,
       gfx::BufferUsage usage,
-      gpu::SurfaceHandle surface_handle) override;
+      gpu::SurfaceHandle surface_handle,
+      base::WaitableEvent* shutdown_event) override;
   void SetDestructionSyncToken(gfx::GpuMemoryBuffer* buffer,
                                const gpu::SyncToken& sync_token) override;
   void CopyGpuMemoryBufferAsync(
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 899338c..5523d2a 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -17311,58 +17311,6 @@
       },
       {
         "args": [
-          "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "experiment_percentage": 100,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_test_vr_apk-vega"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "chrome_public_test_vr_apk-vega",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "OVR1.180808.003",
-              "device_os_type": "userdebug",
-              "device_type": "vega",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_public_test_vr_apk",
-        "test_id_prefix": "ninja://chrome/android:chrome_public_test_vr_apk/"
-      },
-      {
-        "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 78baaa5c..b02d4d5 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -38499,33 +38499,6 @@
         },
         "test": "ozone_unittests",
         "test_id_prefix": "ninja://ui/ozone:ozone_unittests/"
-      },
-      {
-        "args": [
-          "--board=amd64-generic",
-          "--use-vm"
-        ],
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "vaapi_unittest_amd64-generic",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86",
-              "kvm": "1",
-              "os": "Ubuntu-16.04"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "vaapi_unittest",
-        "test_id_prefix": "ninja://media/gpu/vaapi:vaapi_unittest/"
       }
     ]
   },
@@ -58828,9 +58801,9 @@
               "os": "Mac-10.14.6"
             }
           ],
-          "hard_timeout": 900,
+          "hard_timeout": 1800,
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 20
+          "shards": 12
         },
         "test_id_prefix": "ninja://:blink_web_tests/"
       }
diff --git a/testing/buildbot/client.v8.chromium.json b/testing/buildbot/client.v8.chromium.json
index 214ba83..912ec5b5 100644
--- a/testing/buildbot/client.v8.chromium.json
+++ b/testing/buildbot/client.v8.chromium.json
@@ -15,7 +15,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -32,7 +32,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -50,7 +50,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -67,7 +67,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -84,7 +84,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -101,7 +101,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -118,7 +118,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -135,7 +135,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -152,7 +152,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -169,7 +169,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -186,7 +186,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -203,7 +203,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -220,7 +220,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -237,7 +237,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -254,7 +254,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -271,7 +271,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -288,7 +288,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -305,7 +305,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -322,7 +322,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -339,7 +339,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -356,7 +356,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -373,7 +373,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -390,7 +390,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -407,7 +407,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -424,7 +424,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -441,7 +441,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -458,7 +458,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -475,7 +475,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -492,7 +492,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -517,7 +517,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -538,7 +538,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
@@ -564,7 +564,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
@@ -591,7 +591,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
@@ -616,7 +616,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -633,7 +633,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -651,7 +651,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -668,7 +668,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -685,7 +685,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -702,7 +702,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -719,7 +719,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -736,7 +736,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -753,7 +753,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -770,7 +770,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -787,7 +787,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -804,7 +804,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -821,7 +821,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -838,7 +838,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -855,7 +855,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -872,7 +872,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -889,7 +889,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -906,7 +906,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -923,7 +923,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -941,7 +941,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -958,7 +958,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -975,7 +975,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -992,7 +992,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1009,7 +1009,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1026,7 +1026,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1043,7 +1043,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1060,7 +1060,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1077,7 +1077,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1094,7 +1094,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1119,7 +1119,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -1140,7 +1140,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
@@ -1166,7 +1166,7 @@
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
             {
-              "os": "Ubuntu-16.04"
+              "os": "Ubuntu-18.04"
             }
           ],
           "idempotent": false,
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 833d802..83352887 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -485,7 +485,10 @@
       },
       'mac10.14-blink-rel-dummy': {
         'swarming': {
-          'shards': 20,
+          # Increase timeout and reduce shards.
+          # See https://crbug.com/1203565.
+          'shards': 12,
+          'hard_timeout': 1800,
         },
       },
       'mac10.15-blink-rel-dummy': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index f008513..862991e 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -272,16 +272,6 @@
       'trichrome_chrome_bundle_smoke_test': {},
     },
 
-    'android_vega_vr_gtests': {
-      'chrome_public_test_vr_apk-vega': {
-        'args': [
-          '--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json',
-        ],
-        'test': 'chrome_public_test_vr_apk',
-        'experiment_percentage': 100,
-      },
-    },
-
     'android_webapk_launch_tests': {
       'chrome_java_test_webapk_launch_tests': {},
     },
@@ -4141,12 +4131,12 @@
       },
     },
 
+    # TODO(crbug.com/1204231): Re-enable vaapi_unittest.
     'lacros_device_or_vm_gtests': {
       'aura_unittests': {},
       'cc_unittests': {},
       'interactive_ui_tests': {},
       'ozone_unittests': {},
-      'vaapi_unittest': {},
     },
 
     'lacros_skylab_poc': {
@@ -5634,7 +5624,6 @@
       'android_monochrome_smoke_tests',
       'android_oreo_standard_gtests',
       'android_smoke_tests',
-      'android_vega_vr_gtests',
     ],
 
     'android_pie_gtests': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 2f60106..b3dcbe6 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -6206,7 +6206,7 @@
     'machines': {
       'Linux - Future': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'additional_compile_targets': ['all'],
         'test_suites': {
@@ -6216,7 +6216,7 @@
       },
       'Linux - Future (dbg)': {
         'mixins': [
-          'linux-xenial',
+          'linux-bionic',
         ],
         'additional_compile_targets': ['all'],
         'test_suites': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5026d3b..e09290f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1885,6 +1885,21 @@
             ]
         }
     ],
+    "ClipboardHistoryScreenshotNudgeStudy": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ClipboardHistoryScreenshotNudge"
+                    ]
+                }
+            ]
+        }
+    ],
     "CnameAliasDetectionInSubresourceFilter": [
         {
             "platforms": [
@@ -2064,7 +2079,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Translations_20210322",
+                    "name": "TranslationsCaption_20210422",
                     "params": {
                         "availability": "any",
                         "event_trigger": "name:translations_bubble_shown;comparator:<3;window:30;storage:180",
@@ -2072,6 +2087,7 @@
                         "session_rate": "<1"
                     },
                     "enable_features": [
+                        "ContextualSearchForceCaption",
                         "ContextualSearchLiteralSearchTap",
                         "ContextualSearchTranslations",
                         "IPH_ContextualSearchTranslationEnable"
@@ -5010,7 +5026,7 @@
             ]
         }
     ],
-    "OptimizationHintsDesktop": [
+    "OptimizationHintsDesktopM91": [
         {
             "platforms": [
                 "chromeos",
@@ -5021,12 +5037,9 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_20210323",
+                    "name": "Enabled_20210426",
                     "enable_features": [
-                        "OptimizationHints"
-                    ],
-                    "disable_features": [
-                        "OptimizationGuideModelDownloading",
+                        "OptimizationHints",
                         "OptimizationHintsFetching"
                     ]
                 }
@@ -5660,6 +5673,12 @@
             "experiments": [
                 {
                     "name": "Enabled",
+                    "params": {
+                        "availability": "any",
+                        "event_trigger": "name:profile_switch_trigger;comparator:==0;window:360;storage:360",
+                        "event_used": "name:profile_menu_shown;comparator:==0;window:360;storage:360",
+                        "session_rate": "any"
+                    },
                     "enable_features": [
                         "IPH_ProfileSwitch"
                     ]
diff --git a/third_party/android_deps/README.md b/third_party/android_deps/README.md
index 9c1fe87a..b8dda00d 100644
--- a/third_party/android_deps/README.md
+++ b/third_party/android_deps/README.md
@@ -30,14 +30,24 @@
 2. Run `fetch_all.py` to update your current workspace with the changes. This
    will update, among other things, your top-level DEPS file.
 
-3. `git add` all the 3pp related changes and create a CL for review. Revert all
-   of the non-3pp files as they will be committed in a followup CL.
+3. `git add` all the 3pp related changes and create a CL for review. Keep the
+   3pp/ and .gradle changes in the CL and revert the other files. The other
+   files will be committed in a follow up CL.
 
 4. Land the first CL in step 3 and wait for the corresponding 3pp packager to
-   create the new CIPD packages. See [`//docs/cipd_and_3pp.md`][cipd_and_3pp_doc]
-   for how it works.
+   create the new CIPD packages. The 3pp packager runs every 6 hours. You can
+   see the latest runs
+   [here](https://ci.chromium.org/p/chromium/builders/ci/3pp-linux-amd64-packager).
+   See [`//docs/cipd_and_3pp.md`][cipd_and_3pp_doc] for how it works.
 
-5. Run `fetch_all.py` again. There should not be any 3pp related changes. Create
+5. If your follow up cl takes more than a day please revert the original cl.
+  The bot runs 4 times a day and once it uploads to cipd there is no need to
+  keep the modified 3pp files. When you are ready to land the follow up cl, you
+  can land everything together since the cipd packages have already been
+  uploaded.
+
+
+6. Run `fetch_all.py` again. There should not be any 3pp related changes. Create
    a commit.
 
    If the CL is doing more than upgrading existing packages or adding packages
diff --git a/third_party/blink/public/mojom/frame/BUILD.gn b/third_party/blink/public/mojom/frame/BUILD.gn
index 391ccea..1c899a4 100644
--- a/third_party/blink/public/mojom/frame/BUILD.gn
+++ b/third_party/blink/public/mojom/frame/BUILD.gn
@@ -27,6 +27,7 @@
     "policy_container.mojom",
     "reporting_observer.mojom",
     "sudden_termination_disabler_type.mojom",
+    "text_autosizer_page_info.mojom",
     "tree_scope_type.mojom",
     "triggering_event_info.mojom",
     "user_activation_notification_type.mojom",
diff --git a/third_party/blink/public/mojom/frame/frame.mojom b/third_party/blink/public/mojom/frame/frame.mojom
index 2a253738..514393d1 100644
--- a/third_party/blink/public/mojom/frame/frame.mojom
+++ b/third_party/blink/public/mojom/frame/frame.mojom
@@ -45,6 +45,7 @@
 import "third_party/blink/public/mojom/frame/policy_container.mojom";
 import "third_party/blink/public/mojom/frame/reporting_observer.mojom";
 import "third_party/blink/public/mojom/frame/sudden_termination_disabler_type.mojom";
+import "third_party/blink/public/mojom/frame/text_autosizer_page_info.mojom";
 import "third_party/blink/public/mojom/frame/triggering_event_info.mojom";
 import "third_party/blink/public/mojom/frame/user_activation_notification_type.mojom";
 import "third_party/blink/public/mojom/frame/user_activation_update_types.mojom";
@@ -138,16 +139,6 @@
 // to accept in the browser process.
 const uint16 kMaxTitleChars = 4096; // 4 * 1024;
 
-struct TextAutosizerPageInfo {
-  // Frame width in density-independent pixels.
-  int32 main_frame_width;
-
-  // Layout width in CSS pixels.
-  int32 main_frame_layout_width;
-
-  float device_scale_adjustment;
-};
-
 // This struct holds parameters included in the OpenURL method sent by the
 // renderer to the browser, |is_history_navigation_in_new_child_frame| is true
 // in the case that the browser process should look for an existing history item
diff --git a/third_party/blink/public/mojom/frame/text_autosizer_page_info.mojom b/third_party/blink/public/mojom/frame/text_autosizer_page_info.mojom
new file mode 100644
index 0000000..16035f7
--- /dev/null
+++ b/third_party/blink/public/mojom/frame/text_autosizer_page_info.mojom
@@ -0,0 +1,15 @@
+// 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.
+
+module blink.mojom;
+
+struct TextAutosizerPageInfo {
+  // Frame width in density-independent pixels.
+  int32 main_frame_width;
+
+  // Layout width in CSS pixels.
+  int32 main_frame_layout_width;
+
+  float device_scale_adjustment;
+};
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 132c3e3..4e3d490a 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3201,6 +3201,8 @@
   kNewCanvas2DAPI = 3886,
   kServiceWorkerSubresourceFilter = 3887,
 
+  kWebGPU = 3888,
+
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
   // Also, run update_use_counter_feature_enum.py in
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py
index 5fa288d..faf860e 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/codegen_expr.py
@@ -313,7 +313,7 @@
     if exposed_selector_terms:
         selector_terms.append(expr_or(exposed_selector_terms))
     if feature_selector_names:
-        selector_terms.append(ref_selected(feature_selector_names))
+        selector_terms.append(ref_selected(set(feature_selector_names)))
 
     terms = []
     terms.append(expr_and(all_enabled_terms))
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
index 509839e..bfe74bd 100644
--- a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
@@ -27,8 +27,8 @@
   ConversionCheckers null_checkers;
 
   auto convert_inner = [this, &null_underlying, &null_checkers](size_t) {
-    return this->inner_interpolation_type_->MaybeConvertNeutral(null_underlying,
-                                                                null_checkers);
+    return inner_interpolation_type_->MaybeConvertNeutral(null_underlying,
+                                                          null_checkers);
   };
 
   return ListInterpolationFunctions::CreateList(underlying_length,
@@ -47,7 +47,7 @@
 
   return ListInterpolationFunctions::CreateList(
       list->length(), [this, list, state, &null_checkers](size_t index) {
-        return this->inner_interpolation_type_->MaybeConvertValue(
+        return inner_interpolation_type_->MaybeConvertValue(
             list->Item(index), state, null_checkers);
       });
 }
diff --git a/third_party/blink/renderer/core/css/css_computed_style_declaration.cc b/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
index 71d59fa..366573c5 100644
--- a/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
+++ b/third_party/blink/renderer/core/css/css_computed_style_declaration.cc
@@ -166,7 +166,7 @@
   return style->GetFontDescription().IsMonospace();
 }
 const ComputedStyle* CSSComputedStyleDeclaration::ComputeComputedStyle() const {
-  Node* styled_node = this->StyledNode();
+  Node* styled_node = StyledNode();
   DCHECK(styled_node);
   const ComputedStyle* style = styled_node->EnsureComputedStyle(
       styled_node->IsPseudoElement() ? kPseudoIdNone
diff --git a/third_party/blink/renderer/core/css/css_property_value_set.cc b/third_party/blink/renderer/core/css/css_property_value_set.cc
index 7141f4e..9aba0de 100644
--- a/third_party/blink/renderer/core/css/css_property_value_set.cc
+++ b/third_party/blink/renderer/core/css/css_property_value_set.cc
@@ -97,9 +97,9 @@
     CSSParserMode css_parser_mode)
     : CSSPropertyValueSet(css_parser_mode, length) {
   CSSPropertyValueMetadata* metadata_array =
-      const_cast<CSSPropertyValueMetadata*>(this->MetadataArray());
+      const_cast<CSSPropertyValueMetadata*>(MetadataArray());
   Member<const CSSValue>* value_array =
-      const_cast<Member<const CSSValue>*>(this->ValueArray());
+      const_cast<Member<const CSSValue>*>(ValueArray());
   for (unsigned i = 0; i < array_size_; ++i) {
     metadata_array[i] = properties[i].Metadata();
     value_array[i] = properties[i].Value();
diff --git a/third_party/blink/renderer/core/css/css_selector_list.cc b/third_party/blink/renderer/core/css/css_selector_list.cc
index df2c8e74..28640e8 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.cc
+++ b/third_party/blink/renderer/core/css/css_selector_list.cc
@@ -47,7 +47,7 @@
 CSSSelectorList CSSSelectorList::Copy() const {
   CSSSelectorList list;
 
-  unsigned length = this->ComputeLength();
+  unsigned length = ComputeLength();
   list.selector_array_ =
       reinterpret_cast<CSSSelector*>(WTF::Partitions::FastMalloc(
           WTF::Partitions::ComputeAllocationSize(length, sizeof(CSSSelector)),
@@ -101,14 +101,14 @@
 }
 
 const CSSSelector* CSSSelectorList::FirstForCSSOM() const {
-  const CSSSelector* s = this->First();
+  const CSSSelector* s = First();
   if (!s)
     return nullptr;
-  while (this->Next(*s))
-    s = this->Next(*s);
-  if (this->NextInFullList(*s))
-    return this->NextInFullList(*s);
-  return this->First();
+  while (Next(*s))
+    s = Next(*s);
+  if (NextInFullList(*s))
+    return NextInFullList(*s);
+  return First();
 }
 
 unsigned CSSSelectorList::ComputeLength() const {
diff --git a/third_party/blink/renderer/core/css/css_selector_list.h b/third_party/blink/renderer/core/css/css_selector_list.h
index 26d334788..bbdf6369 100644
--- a/third_party/blink/renderer/core/css/css_selector_list.h
+++ b/third_party/blink/renderer/core/css/css_selector_list.h
@@ -105,7 +105,7 @@
 
   wtf_size_t IndexOfNextSelectorAfter(wtf_size_t index) const {
     const CSSSelector& current = SelectorAt(index);
-    const CSSSelector* next = this->Next(current);
+    const CSSSelector* next = Next(current);
     if (!next)
       return kNotFound;
     return SelectorIndex(*next);
diff --git a/third_party/blink/renderer/core/css/font_face_set_document.cc b/third_party/blink/renderer/core/css/font_face_set_document.cc
index 6fa363f..2aba9e3c 100644
--- a/third_party/blink/renderer/core/css/font_face_set_document.cc
+++ b/third_party/blink/renderer/core/css/font_face_set_document.cc
@@ -135,7 +135,7 @@
 
 const HeapLinkedHashSet<Member<FontFace>>&
 FontFaceSetDocument::CSSConnectedFontFaceList() const {
-  Document* document = this->GetDocument();
+  Document* document = GetDocument();
   document->GetStyleEngine().UpdateActiveStyle();
   return GetFontSelector()->GetFontFaceCache()->CssConnectedFontFaces();
 }
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index f614523..c7b69be 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -91,6 +91,7 @@
 #include "third_party/blink/renderer/core/mathml/mathml_space_element.h"
 #include "third_party/blink/renderer/core/mathml_names.h"
 #include "third_party/blink/renderer/core/media_type_names.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/scrolling/snap_coordinator.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/style/style_initial_data.h"
diff --git a/third_party/blink/renderer/core/css/style_sheet_candidate.cc b/third_party/blink/renderer/core/css/style_sheet_candidate.cc
index 1bd5edd1..f9e6b0a78 100644
--- a/third_party/blink/renderer/core/css/style_sheet_candidate.cc
+++ b/third_party/blink/renderer/core/css/style_sheet_candidate.cc
@@ -65,7 +65,7 @@
 
 bool StyleSheetCandidate::CanBeActivated(
     const String& current_preferrable_name) const {
-  StyleSheet* sheet = this->Sheet();
+  StyleSheet* sheet = Sheet();
   auto* css_style_sheet = DynamicTo<CSSStyleSheet>(sheet);
   if (!css_style_sheet || sheet->disabled())
     return false;
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
index b751822..584a1db 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.cc
@@ -402,7 +402,8 @@
 }
 
 bool DisplayLockUtilities::IsInLockedSubtreeCrossingFrames(
-    const Node& source_node) {
+    const Node& source_node,
+    IncludeSelfOrNot self) {
   if (!RuntimeEnabledFeatures::CSSContentVisibilityEnabled())
     return false;
   if (LocalFrameView* frame_view = source_node.GetDocument().View()) {
@@ -411,9 +412,10 @@
   }
   const Node* node = &source_node;
 
-  // Since we handled the self-check above, we need to do inclusive checks
-  // starting from the parent.
-  node = FlatTreeTraversal::Parent(*node);
+  // If we don't need to check self, skip to the parent immediately.
+  if (self == kExcludeSelf)
+    node = FlatTreeTraversal::Parent(*node);
+
   // If we don't have a flat-tree parent, get the |source_node|'s owner node
   // instead.
   if (!node)
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_utilities.h b/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
index 6e6839e..a2ceca8 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_utilities.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_context.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
 namespace blink {
@@ -161,7 +162,9 @@
 
   // Returns true if the element is in a locked subtree (or is self-locked with
   // no self-updates). This crosses frames while navigating the ancestor chain.
-  static bool IsInLockedSubtreeCrossingFrames(const Node& node);
+  static bool IsInLockedSubtreeCrossingFrames(
+      const Node& node,
+      IncludeSelfOrNot self = kExcludeSelf);
 
   // Called when the focused element changes. These functions update locks to
   // ensure that focused element ancestors remain unlocked for 'auto' state.
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index d1565b9..b45e302 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -238,7 +238,7 @@
 }
 
 bool PseudoElement::LayoutObjectIsNeeded(const ComputedStyle& style) const {
-  return PseudoElementLayoutObjectIsNeeded(&style, this->parentElement());
+  return PseudoElementLayoutObjectIsNeeded(&style, parentElement());
 }
 
 bool PseudoElement::CanGeneratePseudoElement(PseudoId pseudo_id) const {
diff --git a/third_party/blink/renderer/core/dom/slot_assignment.cc b/third_party/blink/renderer/core/dom/slot_assignment.cc
index 8585fb62..5ac4a53d 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/dom/slot_assignment.h"
 
+#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal_forbidden_scope.h"
 #include "third_party/blink/renderer/core/dom/node.h"
@@ -18,6 +19,7 @@
 #include "third_party/blink/renderer/core/html/html_slot_element.h"
 #include "third_party/blink/renderer/core/html/parser/nesting_level_incrementer.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
 
 namespace blink {
 
@@ -256,6 +258,16 @@
     SlotAssignmentRecalcForbiddenScope forbid_slot_recalc(
         owner_->GetDocument());
 
+    // Before forbidding flat tree traversal, figure out which slots' subtrees
+    // are in display locked state. Note that it could be the slot itself is
+    // locked or it could be that some flat tree ancestor of the slot is locked.
+    HeapHashMap<Member<HTMLSlotElement>, bool> display_lock_map;
+    for (Member<HTMLSlotElement> slot : Slots()) {
+      display_lock_map.Set(
+          slot, DisplayLockUtilities::IsInLockedSubtreeCrossingFrames(
+                    *slot, kIncludeSelf));
+    }
+
     FlatTreeTraversalForbiddenScope forbid_flat_tree_traversal(
         owner_->GetDocument());
 
@@ -338,7 +350,7 @@
     }
 
     for (auto& slot : Slots())
-      slot->DidRecalcAssignedNodes();
+      slot->DidRecalcAssignedNodes(display_lock_map.at(slot));
   }
 
   // Update an dir=auto flag from a host of slots to its all descendants.
diff --git a/third_party/blink/renderer/core/editing/editor_key_bindings.cc b/third_party/blink/renderer/core/editing/editor_key_bindings.cc
index 0017af6..8cef9ca 100644
--- a/third_party/blink/renderer/core/editing/editor_key_bindings.cc
+++ b/third_party/blink/renderer/core/editing/editor_key_bindings.cc
@@ -45,7 +45,7 @@
     return false;
 
   String command_name = Behavior().InterpretKeyEvent(*evt);
-  const EditorCommand command = this->CreateCommand(command_name);
+  const EditorCommand command = CreateCommand(command_name);
 
   if (key_event->GetType() == WebInputEvent::Type::kRawKeyDown) {
     // WebKit doesn't have enough information about mode to decide how
diff --git a/third_party/blink/renderer/core/editing/selection_controller.cc b/third_party/blink/renderer/core/editing/selection_controller.cc
index 11a7342e..a0ca233 100644
--- a/third_party/blink/renderer/core/editing/selection_controller.cc
+++ b/third_party/blink/renderer/core/editing/selection_controller.cc
@@ -349,14 +349,14 @@
                 .ToPositionWithAffinity()
           : visible_hit_position;
   const VisibleSelectionInFlatTree& selection =
-      this->Selection().ComputeVisibleSelectionInFlatTree();
+      Selection().ComputeVisibleSelectionInFlatTree();
 
   // Don't restart the selection when the mouse is pressed on an
   // existing selection so we can allow for text dragging.
   if (LocalFrameView* view = frame_->View()) {
     const PhysicalOffset v_point(view->ConvertFromRootFrame(
         FlooredIntPoint(event.Event().PositionInRootFrame())));
-    if (!extend_selection && this->Selection().Contains(v_point)) {
+    if (!extend_selection && Selection().Contains(v_point)) {
       mouse_down_was_single_click_in_selection_ = true;
       if (!event.Event().FromTouch())
         return false;
@@ -602,7 +602,7 @@
   }
 
   // |DispatchSelectStart()| can change document hosted by |frame_|.
-  if (!this->Selection().IsAvailable())
+  if (!Selection().IsAvailable())
     return false;
 
   // TODO(editing-dev): Use of UpdateStyleAndLayout
@@ -1321,8 +1321,7 @@
   DocumentLifecycle::DisallowTransitionScope disallow_transition(
       frame_->GetDocument()->Lifecycle());
 
-  const SelectionInDOMTree& selection =
-      this->Selection().GetSelectionInDOMTree();
+  const SelectionInDOMTree& selection = Selection().GetSelectionInDOMTree();
   if (selection.IsNone()) {
     selection_state_ = SelectionState::kHaveNotStartedSelection;
     return;
diff --git a/third_party/blink/renderer/core/events/pointer_event.cc b/third_party/blink/renderer/core/events/pointer_event.cc
index c63cbb2..ce3d1b4 100644
--- a/third_party/blink/renderer/core/events/pointer_event.cc
+++ b/third_party/blink/renderer/core/events/pointer_event.cc
@@ -171,7 +171,7 @@
     // Assume that time stamps of coalesced events are in ascending order.
     return coalesced_events_[0]->PlatformTimeStamp();
   }
-  return this->PlatformTimeStamp();
+  return PlatformTimeStamp();
 }
 
 void PointerEvent::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/fileapi/file_reader.cc b/third_party/blink/renderer/core/fileapi/file_reader.cc
index 2bbc4db7..93221ba 100644
--- a/third_party/blink/renderer/core/fileapi/file_reader.cc
+++ b/third_party/blink/renderer/core/fileapi/file_reader.cc
@@ -335,7 +335,10 @@
   loading_state_ = kLoadingStateAborted;
 
   DCHECK_NE(kDone, state_);
-  state_ = kDone;
+  // Synchronously cancel the loader before dispatching events. This way we make
+  // sure the FileReader internal state stays consistent even if another load
+  // is started from one of the event handlers, or right after abort returns.
+  Terminate();
 
   base::AutoReset<bool> firing_events(&still_firing_events_, true);
 
@@ -347,15 +350,12 @@
       ThrottlingController::RemoveReader(GetExecutionContext(), this);
 
   FireEvent(event_type_names::kAbort);
+  // TODO(https://crbug.com/1204139): Only fire loadend event if no new load was
+  // started from the abort event handler.
   FireEvent(event_type_names::kLoadend);
 
   // All possible events have fired and we're done, no more pending activity.
   ThrottlingController::FinishReader(GetExecutionContext(), this, final_step);
-
-  // Also synchronously cancel the loader, as script might initiate a new load
-  // right after this method returns, in which case an async termination would
-  // terminate the wrong loader.
-  Terminate();
 }
 
 void FileReader::result(StringOrArrayBuffer& result_attribute) const {
@@ -428,6 +428,8 @@
       ThrottlingController::RemoveReader(GetExecutionContext(), this);
 
   FireEvent(event_type_names::kLoad);
+  // TODO(https://crbug.com/1204139): Only fire loadend event if no new load was
+  // started from the abort event handler.
   FireEvent(event_type_names::kLoadend);
 
   // All possible events have fired and we're done, no more pending activity.
diff --git a/third_party/blink/renderer/core/frame/frame.cc b/third_party/blink/renderer/core/frame/frame.cc
index 106d43d6..fd8c918 100644
--- a/third_party/blink/renderer/core/frame/frame.cc
+++ b/third_party/blink/renderer/core/frame/frame.cc
@@ -222,7 +222,7 @@
 }
 
 ChromeClient& Frame::GetChromeClient() const {
-  if (Page* page = this->GetPage())
+  if (Page* page = GetPage())
     return page->GetChromeClient();
   return GetEmptyChromeClient();
 }
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 7ccd49d..44d6222 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -549,7 +549,7 @@
   DCHECK(this);
   DCHECK(GetPage());
 
-  bool is_local_root = this->IsLocalRoot();
+  bool is_local_root = IsLocalRoot();
 
   if (is_local_root && View())
     View()->SetParentVisible(false);
@@ -976,8 +976,8 @@
 
 void LocalFrame::SetDOMWindow(LocalDOMWindow* dom_window) {
   DCHECK(dom_window);
-  if (this->DomWindow()) {
-    this->DomWindow()->Reset();
+  if (DomWindow()) {
+    DomWindow()->Reset();
     // SystemClipboard and RawSystemClipboard uses HeapMojo wrappers. HeapMojo
     // wrappers uses LocalDOMWindow (ExecutionContext) to reset the mojo
     // objects when the ExecutionContext was destroyed. So when new
@@ -1404,11 +1404,11 @@
       text_zoom_factor_ == text_zoom_factor)
     return;
 
-  Page* page = this->GetPage();
+  Page* page = GetPage();
   if (!page)
     return;
 
-  Document* document = this->GetDocument();
+  Document* document = GetDocument();
   if (!document)
     return;
 
@@ -3171,7 +3171,7 @@
 
 void LocalFrame::NotifyVirtualKeyboardOverlayRect(
     const gfx::Rect& keyboard_rect) {
-  Page* page = this->GetPage();
+  Page* page = GetPage();
   if (!page)
     return;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 8cde8a5..67c16d3b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1053,7 +1053,7 @@
 }
 
 LayoutSVGRoot* LocalFrameView::EmbeddedReplacedContent() const {
-  auto* layout_view = this->GetLayoutView();
+  auto* layout_view = GetLayoutView();
   if (!layout_view)
     return nullptr;
 
@@ -1672,7 +1672,7 @@
   if (!frame_->GetDocument()->IsActive())
     return;
 
-  LayoutView* layout_view = this->GetLayoutView();
+  LayoutView* layout_view = GetLayoutView();
   if (layout_view && layout_view->NeedsLayout()) {
     if (relayout_root)
       relayout_root->MarkContainerChainForLayout(false);
@@ -3321,7 +3321,7 @@
     float maximum_shrink_factor) {
   // Dumping externalRepresentation(m_frame->layoutObject()).ascii() is a good
   // trick to see the state of things before and after the layout
-  if (LayoutView* layout_view = this->GetLayoutView()) {
+  if (LayoutView* layout_view = GetLayoutView()) {
     float page_logical_width = layout_view->StyleRef().IsHorizontalWritingMode()
                                    ? page_size.Width()
                                    : page_size.Height();
diff --git a/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc b/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
index 04f680e2..5bf3eba 100644
--- a/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
+++ b/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
@@ -279,19 +279,19 @@
 
 DOMMatrix* DOMMatrixReadOnly::flipX() {
   DOMMatrix* flip_x = DOMMatrix::Create(this);
-  flip_x->setM11(-this->m11());
-  flip_x->setM12(-this->m12());
-  flip_x->setM13(-this->m13());
-  flip_x->setM14(-this->m14());
+  flip_x->setM11(-m11());
+  flip_x->setM12(-m12());
+  flip_x->setM13(-m13());
+  flip_x->setM14(-m14());
   return flip_x;
 }
 
 DOMMatrix* DOMMatrixReadOnly::flipY() {
   DOMMatrix* flip_y = DOMMatrix::Create(this);
-  flip_y->setM21(-this->m21());
-  flip_y->setM22(-this->m22());
-  flip_y->setM23(-this->m23());
-  flip_y->setM24(-this->m24());
+  flip_y->setM21(-m21());
+  flip_y->setM22(-m22());
+  flip_y->setM23(-m23());
+  flip_y->setM24(-m24());
   return flip_y;
 }
 
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index 5906a3a..6e781a1d 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -114,7 +114,8 @@
 }
 
 CanvasRenderingContext::ContextType CanvasRenderingContext::ContextTypeFromId(
-    const String& id) {
+    const String& id,
+    const ExecutionContext* execution_context) {
   if (id == "2d")
     return kContext2D;
   if (id == "experimental-webgl")
@@ -125,7 +126,8 @@
     return kContextWebgl2;
   if (id == "bitmaprenderer")
     return kContextImageBitmap;
-  if (id == "gpupresent" && RuntimeEnabledFeatures::WebGPUEnabled())
+  if (id == "gpupresent" &&
+      RuntimeEnabledFeatures::WebGPUEnabled(execution_context))
     return kContextGPUPresent;
   return kContextTypeUnknown;
 }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index 5b1cd77..b130b03 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -85,7 +85,9 @@
   void RecordUKMCanvasDrawnToRenderingAPI(
       CanvasRenderingAPI canvasRenderingAPI);
 
-  static ContextType ContextTypeFromId(const String& id);
+  static ContextType ContextTypeFromId(
+      const String& id,
+      const ExecutionContext* execution_context);
   static ContextType ResolveContextTypeAliases(ContextType);
 
   CanvasRenderingContextHost* Host() const { return host_; }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index fd548d85..b2327a95 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -294,17 +294,17 @@
     ExceptionState& exception_state,
     const CanvasRenderingContext* const context) {
   WTF::String object_name = "Canvas";
-  if (this->IsOffscreenCanvas())
+  if (IsOffscreenCanvas())
     object_name = "OffscreenCanvas";
   std::stringstream error_msg;
 
-  if (this->IsOffscreenCanvas() && this->IsNeutered()) {
+  if (IsOffscreenCanvas() && IsNeutered()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "OffscreenCanvas object is detached.");
     return ScriptPromise();
   }
 
-  if (!this->OriginClean()) {
+  if (!OriginClean()) {
     error_msg << "Tainted " << object_name << " may not be exported.";
     exception_state.ThrowSecurityError(error_msg.str().c_str());
     return ScriptPromise();
@@ -316,7 +316,7 @@
   if (RenderingContext())
     RenderingContext()->FinalizeFrame();
 
-  if (!this->IsPaintable() || Size().IsEmpty()) {
+  if (!IsPaintable() || Size().IsEmpty()) {
     error_msg << "The size of " << object_name << " is zero.";
     exception_state.ThrowDOMException(DOMExceptionCode::kIndexSizeError,
                                       error_msg.str().c_str());
@@ -337,7 +337,7 @@
     auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
     CanvasAsyncBlobCreator::ToBlobFunctionType function_type =
         CanvasAsyncBlobCreator::kHTMLCanvasConvertToBlobPromise;
-    if (this->IsOffscreenCanvas()) {
+    if (IsOffscreenCanvas()) {
       function_type =
           CanvasAsyncBlobCreator::kOffscreenCanvasConvertToBlobPromise;
     }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 01e0e2c..4e7fc84 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -292,7 +292,8 @@
     IdentifiabilityMetricBuilder(doc.UkmSourceID())
         .Set(IdentifiableSurface::FromTypeAndToken(
                  IdentifiableSurface::Type::kCanvasRenderingContext,
-                 CanvasRenderingContext::ContextTypeFromId(type)),
+                 CanvasRenderingContext::ContextTypeFromId(
+                     type, GetExecutionContext())),
              !!result)
         .Record(doc.UkmRecorder());
   }
@@ -313,7 +314,7 @@
     const String& type,
     const CanvasContextCreationAttributesCore& attributes) {
   CanvasRenderingContext::ContextType context_type =
-      CanvasRenderingContext::ContextTypeFromId(type);
+      CanvasRenderingContext::ContextTypeFromId(type, GetExecutionContext());
 
   // Unknown type.
   if (context_type == CanvasRenderingContext::kContextTypeUnknown) {
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.cc b/third_party/blink/renderer/core/html/forms/html_option_element.cc
index 11f47dbe..d1e7612 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -438,7 +438,7 @@
   // TODO(crbug.com/1196022) Refine the content that an option can render.
   // Enable the option element to render arbitrary content.
   root->RemoveChildren();
-  Document& document = this->GetDocument();
+  Document& document = GetDocument();
   auto* default_slot = MakeGarbageCollected<HTMLSlotElement>(document);
   root->AppendChild(default_slot);
 }
diff --git a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
index 5c43d477..5e73a48 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_menu_element.cc
@@ -150,7 +150,7 @@
   DCHECK(IsShadowHost(this));
 
   root.EnableNameBasedSlotAssignment();
-  Document& document = this->GetDocument();
+  Document& document = GetDocument();
 
   // TODO(crbug.com/1121840) Where to put the styles for the default elements in
   // the shadow tree? We'd like to have them in the UA styles (html.css), but
diff --git a/third_party/blink/renderer/core/html/html_slot_element.cc b/third_party/blink/renderer/core/html/html_slot_element.cc
index a57d517..0da1f6b4 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.cc
+++ b/third_party/blink/renderer/core/html/html_slot_element.cc
@@ -255,6 +255,19 @@
   }
 }
 
+void HTMLSlotElement::DetachDisplayLockedAssignedNodesLayoutTreeIfNeeded() {
+  // If the assigned node is now under a display locked subtree and its layout
+  // is in 'forced reattach' mode, it means that this node potentially changed
+  // slots into a display locked subtree. We would normally update its layout
+  // tree during a layout tree update phase, but that is skipped in display
+  // locked subtrees. In order to avoid a corrupt layout tree as a result, we
+  // detach the node's layout tree.
+  for (auto& current : assigned_nodes_) {
+    if (current->GetForceReattachLayoutTree())
+      current->DetachLayoutTree();
+  }
+}
+
 void HTMLSlotElement::RecalcFlatTreeChildren() {
   DCHECK(SupportsAssignment());
 
diff --git a/third_party/blink/renderer/core/html/html_slot_element.h b/third_party/blink/renderer/core/html/html_slot_element.h
index bcd8373..c9f7dfe68 100644
--- a/third_party/blink/renderer/core/html/html_slot_element.h
+++ b/third_party/blink/renderer/core/html/html_slot_element.h
@@ -72,9 +72,11 @@
   const HeapVector<Member<Node>> FlattenedAssignedNodes();
 
   void WillRecalcAssignedNodes() { ClearAssignedNodes(); }
-  void DidRecalcAssignedNodes() {
+  void DidRecalcAssignedNodes(bool display_locked_subtree) {
     UpdateFlatTreeNodeDataForAssignedNodes();
     RecalcFlatTreeChildren();
+    if (display_locked_subtree)
+      DetachDisplayLockedAssignedNodesLayoutTreeIfNeeded();
   }
 
   void AttachLayoutTree(AttachContext&) final;
@@ -145,6 +147,7 @@
   void RecalcFlatTreeChildren();
   void UpdateFlatTreeNodeDataForAssignedNodes();
   void ClearAssignedNodesAndFlatTreeChildren();
+  void DetachDisplayLockedAssignedNodesLayoutTreeIfNeeded();
 
   HeapVector<Member<Node>> assigned_nodes_;
   HeapVector<Member<Node>> flat_tree_children_;
diff --git a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
index 72ef7cd7..2b26fab 100644
--- a/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_style_sheet.cc
@@ -707,7 +707,7 @@
 
 std::unique_ptr<protocol::CSS::SourceRange>
 InspectorStyleSheetBase::BuildSourceRangeObject(const SourceRange& range) {
-  const LineEndings* line_endings = this->GetLineEndings();
+  const LineEndings* line_endings = GetLineEndings();
   if (!line_endings)
     return nullptr;
   TextPosition start =
@@ -1520,7 +1520,7 @@
 
   Document* document = style_sheet->OwnerDocument();
   LocalFrame* frame = document ? document->GetFrame() : nullptr;
-  const LineEndings* line_endings = this->GetLineEndings();
+  const LineEndings* line_endings = GetLineEndings();
   TextPosition start = style_sheet->StartPositionInSource();
   TextPosition end = start;
   unsigned text_length = 0;
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index f7f188a..e035b68 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -1023,6 +1023,15 @@
   return SnapSizeToPixel(OffsetHeight(), Location().Y() + ClientTop());
 }
 
+bool LayoutBox::UsesOverlayScrollbars() const {
+  NOT_DESTROYED();
+  if (StyleRef().HasCustomScrollbarStyle())
+    return false;
+  if (GetFrame()->GetPage()->GetScrollbarTheme().UsesOverlayScrollbars())
+    return true;
+  return false;
+}
+
 LayoutUnit LayoutBox::ScrollWidth() const {
   NOT_DESTROYED();
   if (IsScrollContainer())
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index d0e5664b..84e270a 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -31,7 +31,6 @@
 #include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
 #include "third_party/blink/renderer/core/layout/min_max_sizes.h"
 #include "third_party/blink/renderer/core/layout/overflow_model.h"
-#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
 #include "third_party/blink/renderer/platform/graphics/overlay_scrollbar_clip_behavior.h"
@@ -805,14 +804,7 @@
   int PixelSnappedOffsetWidth(const Element*) const final;
   int PixelSnappedOffsetHeight(const Element*) const final;
 
-  bool UsesOverlayScrollbars() const {
-    NOT_DESTROYED();
-    if (StyleRef().HasCustomScrollbarStyle())
-      return false;
-    if (GetFrame()->GetPage()->GetScrollbarTheme().UsesOverlayScrollbars())
-      return true;
-    return false;
-  }
+  bool UsesOverlayScrollbars() const;
 
   // Clamps the left scrollbar size so it is not wider than the content box.
   DISABLE_CFI_PERF LayoutUnit LogicalLeftScrollbarWidth() const {
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 298b122f..5ab66cf9 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1017,11 +1017,17 @@
     scoped_refptr<SecurityOrigin> security_origin =
         SecurityOrigin::Create(navigation_params->url);
 
-    // If `frame_` is provisional, this is largely a no-op other than cleaning
-    // up the initial (and unused) empty document. Otherwise, this unloads the
-    // previous Document and detaches subframes. If `DetachDocument()` returns
-    // false, JS caused `frame_` to be removed, so just return.
+    // If `frame_` is provisional, `DetachDocument()` is largely a no-op other
+    // than cleaning up the initial (and unused) empty document. Otherwise, this
+    // unloads the previous Document and detaches subframes. If
+    // `DetachDocument()` returns false, JS caused `frame_` to be removed, so
+    // just return.
     const bool is_provisional = frame_->IsProvisional();
+    // For an XSLT document, set SentDidFinishLoad now to prevent the
+    // DocumentLoader from reporting an error when detaching the pre-XSLT
+    // document.
+    if (commit_reason == CommitReason::kXSLT && document_loader_)
+      document_loader_->SetSentDidFinishLoad();
     if (!DetachDocument(security_origin.get(), &unload_timing)) {
       DCHECK(!is_provisional);
       return;
diff --git a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
index 03d33f1..40b933f 100644
--- a/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
+++ b/third_party/blink/renderer/core/mobile_metrics/mobile_friendliness_checker.cc
@@ -13,6 +13,7 @@
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/page/viewport_description.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index 146a6f4..c375c9ac 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -286,7 +286,7 @@
     const CanvasContextCreationAttributesCore& attributes) {
   DCHECK_EQ(execution_context, GetTopExecutionContext());
   CanvasRenderingContext::ContextType context_type =
-      CanvasRenderingContext::ContextTypeFromId(id);
+      CanvasRenderingContext::ContextTypeFromId(id, execution_context);
 
   // Unknown type.
   if (context_type == CanvasRenderingContext::kContextTypeUnknown ||
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index eea77bd..6947222 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -135,7 +135,7 @@
 
 HeapVector<Member<Page>> Page::RelatedPages() {
   HeapVector<Member<Page>> result;
-  Page* ptr = this->next_related_page_;
+  Page* ptr = next_related_page_;
   while (ptr != this) {
     result.push_back(ptr);
     ptr = ptr->next_related_page_;
@@ -979,12 +979,12 @@
     // Before: ... -> prev -> this -> next -> ...
     // After: ... -> prev -> next -> ...
     // (this is ok even if |this| is the only element on the list).
-    Page* prev = this->prev_related_page_;
-    Page* next = this->next_related_page_;
+    Page* prev = prev_related_page_;
+    Page* next = next_related_page_;
     next->prev_related_page_ = prev;
     prev->next_related_page_ = next;
-    this->prev_related_page_ = nullptr;
-    this->next_related_page_ = nullptr;
+    prev_related_page_ = nullptr;
+    next_related_page_ = nullptr;
   }
 
   if (scrolling_coordinator_)
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index 8b0ec9ad..de07681c 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -28,7 +28,7 @@
 #include "base/macros.h"
 #include "base/types/pass_key.h"
 #include "third_party/blink/public/mojom/devtools/inspector_issue.mojom-blink-forward.h"
-#include "third_party/blink/public/mojom/frame/frame.mojom-blink.h"
+#include "third_party/blink/public/mojom/frame/text_autosizer_page_info.mojom-blink.h"
 #include "third_party/blink/public/mojom/page/page.mojom-blink.h"
 #include "third_party/blink/public/mojom/page/page_visibility_state.mojom-blink.h"
 #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h"
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_handler.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_handler.cc
index 4bfbeb5..0e92d089 100644
--- a/third_party/blink/renderer/core/page/scrolling/text_fragment_handler.cc
+++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_handler.cc
@@ -52,7 +52,7 @@
 
 // static
 bool TextFragmentHandler::IsOverTextFragment(HitTestResult result) {
-  if (!result.InnerNode()) {
+  if (!result.InnerNode() || !result.InnerNodeFrame()) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
index d411c0c..3d49b74 100644
--- a/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
+++ b/third_party/blink/renderer/core/paint/highlight_painting_utils.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/layout/layout_theme.h"
 #include "third_party/blink/renderer/core/page/focus_controller.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/text_paint_style.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 3598ca66..beb265ef 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -746,7 +746,6 @@
         state.sticky_constraint = std::move(constraint);
       }
 
-      // TODO(crbug.com/1189428): Should this set state.rendering_context_id ?
       OnUpdate(properties_->UpdateStickyTranslation(*context_.current.transform,
                                                     std::move(state)));
     } else {
@@ -855,7 +854,8 @@
 
       // TODO(pdr): There is additional logic in
       // FragmentPaintPropertyTreeBuilder::UpdateTransform that likely needs to
-      // be included here, such as setting rendering context ids, etc.
+      // be included here, such as setting animation_is_axis_aligned, which
+      // may be the only important difference remaining.
       if (RuntimeEnabledFeatures::CompositeSVGEnabled()) {
         state.direct_compositing_reasons =
             direct_compositing_reasons &
@@ -871,8 +871,6 @@
       TransformPaintPropertyNode::AnimationState animation_state;
       animation_state.is_running_animation_on_compositor =
           object_.StyleRef().IsRunningTransformAnimationOnCompositor();
-      // TODO(crbug.com/1189428): Should this set state.rendering_context_id ?
-      // (The comment above already says so!)
       auto effective_change_type = properties_->UpdateTransform(
           *context_.current.transform, std::move(state), animation_state);
       if (effective_change_type ==
@@ -1962,7 +1960,6 @@
       state.flags.flattens_inherited_transform =
           context_.current.should_flatten_inherited_transform;
       state.rendering_context_id = context_.current.rendering_context_id;
-      // TODO(crbug.com/1189428): Should this set state.rendering_context_id ?
       OnUpdate(properties_->UpdateReplacedContentTransform(
           *context_.current.transform, std::move(state)));
     } else {
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.cc b/third_party/blink/renderer/core/script/modulator_impl_base.cc
index fd0acf8..0e585de 100644
--- a/third_party/blink/renderer/core/script/modulator_impl_base.cc
+++ b/third_party/blink/renderer/core/script/modulator_impl_base.cc
@@ -334,7 +334,7 @@
     KURL child_url =
         module_script->ResolveModuleSpecifier(module_request.specifier);
 
-    ModuleType child_module_type = this->ModuleTypeFromRequest(module_request);
+    ModuleType child_module_type = ModuleTypeFromRequest(module_request);
     CHECK_NE(child_module_type, ModuleType::kInvalid);
 
     CHECK(child_url.IsValid())
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index 9190f89..7064ffc 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -409,9 +409,9 @@
   // If the scrollbar does not have its own layer, it must always be
   // invalidated to reflect the new thumb offset, even if the theme did not
   // invalidate any individual part.
-  if (Scrollbar* horizontal_scrollbar = this->HorizontalScrollbar())
+  if (Scrollbar* horizontal_scrollbar = HorizontalScrollbar())
     horizontal_scrollbar->OffsetDidChange(scroll_type);
-  if (Scrollbar* vertical_scrollbar = this->VerticalScrollbar())
+  if (Scrollbar* vertical_scrollbar = VerticalScrollbar())
     vertical_scrollbar->OffsetDidChange(scroll_type);
 
   ScrollOffset delta = GetScrollOffset() - old_offset;
diff --git a/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc b/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc
index bc2a919..629dfac4 100644
--- a/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream_default_controller.cc
@@ -31,12 +31,12 @@
   if (!CanCloseOrEnqueue(this)) {
     // The following code is just to provide a nice exception message.
     const char* errorDescription = nullptr;
-    if (this->is_close_requested_) {
+    if (is_close_requested_) {
       errorDescription =
           "Cannot close a readable stream that has already been requested to "
           "be closed";
     } else {
-      const ReadableStream* stream = this->controlled_readable_stream_;
+      const ReadableStream* stream = controlled_readable_stream_;
       switch (stream->state_) {
         case ReadableStream::kErrored:
           errorDescription = "Cannot close an errored readable stream";
diff --git a/third_party/blink/renderer/core/style/content_data.h b/third_party/blink/renderer/core/style/content_data.h
index 37e2176..ef2746f 100644
--- a/third_party/blink/renderer/core/style/content_data.h
+++ b/third_party/blink/renderer/core/style/content_data.h
@@ -108,7 +108,7 @@
 
  private:
   ContentData* CloneInternal() const override {
-    StyleImage* image = const_cast<StyleImage*>(this->GetImage());
+    StyleImage* image = const_cast<StyleImage*>(GetImage());
     return MakeGarbageCollected<ImageContentData>(image);
   }
 
diff --git a/third_party/blink/renderer/core/timing/background_tracing_helper.cc b/third_party/blink/renderer/core/timing/background_tracing_helper.cc
index f9c414b..0202a46 100644
--- a/third_party/blink/renderer/core/timing/background_tracing_helper.cc
+++ b/third_party/blink/renderer/core/timing/background_tracing_helper.cc
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/timing/performance_mark.h"
 #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
-#include "third_party/blink/renderer/platform/network/network_utils.h"
 #include "third_party/blink/renderer/platform/weborigin/kurl.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
 #include "third_party/blink/renderer/platform/wtf/text/number_parsing_options.h"
@@ -142,12 +141,10 @@
     return;
   }
 
-  // Get the hash of the site (eTLD+1) in an encoded format (friendly for
-  // converting to ASCII, and matching the format in which URLs will be encoded
-  // prior to hashing in the Finch list).
-  String this_site =
-      EncodeWithURLEscapeSequences(network_utils::GetDomainAndRegistry(
-          origin->Host(), network_utils::kIncludePrivateRegistries));
+  // Get the hash of the domain in an encoded format (friendly for converting to
+  // ASCII, and matching the format in which URLs will be encoded prior to
+  // hashing in the Finch list).
+  String this_site = EncodeWithURLEscapeSequences(origin->Domain());
   uint32_t this_site_hash = MD5Hash32(this_site.Ascii());
 
   // Get the allow-list for this site, if there is one.
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
index 235d412..b4fb9ec 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
@@ -104,7 +104,7 @@
     v8::Local<v8::Object> creation_context) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, isolate));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
 
   v8::Local<v8::ArrayBuffer> wrapper;
   {
@@ -118,7 +118,7 @@
 v8::MaybeLocal<v8::Value> DOMArrayBuffer::WrapV2(ScriptState* script_state) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate()));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
 
   v8::Local<v8::ArrayBuffer> wrapper;
   {
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
index 1e22255a..1f87e85 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
@@ -24,7 +24,7 @@
                                        v8::Local<v8::Object> creation_context) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, isolate));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
   v8::Local<v8::Value> v8_buffer = ToV8(buffer(), creation_context, isolate);
   if (v8_buffer.IsEmpty())
     return v8::Local<v8::Object>();
@@ -39,7 +39,7 @@
 v8::MaybeLocal<v8::Value> DOMDataView::WrapV2(ScriptState* script_state) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate()));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
   v8::Local<v8::Value> v8_buffer;
   if (!ToV8Traits<DOMArrayBuffer>::ToV8(script_state, buffer())
            .ToLocal(&v8_buffer)) {
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
index 515f969..abceb42 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
@@ -13,7 +13,7 @@
     v8::Local<v8::Object> creation_context) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, isolate));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
   v8::Local<v8::SharedArrayBuffer> wrapper =
       v8::SharedArrayBuffer::New(isolate, Content()->BackingStore());
   return AssociateWithWrapper(isolate, wrapper_type_info, wrapper);
@@ -23,7 +23,7 @@
     ScriptState* script_state) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate()));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
   v8::Local<v8::SharedArrayBuffer> wrapper = v8::SharedArrayBuffer::New(
       script_state->GetIsolate(), Content()->BackingStore());
   return AssociateWithWrapper(script_state->GetIsolate(), wrapper_type_info,
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
index 2e287f1..66a99d97 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
@@ -27,8 +27,8 @@
     v8::Local<v8::Object> creation_context) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, isolate));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
-  DOMArrayBufferBase* buffer = this->BufferBase();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
+  DOMArrayBufferBase* buffer = BufferBase();
   v8::Local<v8::Value> v8_buffer = ToV8(buffer, creation_context, isolate);
   if (v8_buffer.IsEmpty())
     return v8::Local<v8::Object>();
@@ -54,8 +54,8 @@
     ScriptState* script_state) {
   DCHECK(!DOMDataStore::ContainsWrapper(this, script_state->GetIsolate()));
 
-  const WrapperTypeInfo* wrapper_type_info = this->GetWrapperTypeInfo();
-  DOMArrayBufferBase* buffer = this->BufferBase();
+  const WrapperTypeInfo* wrapper_type_info = GetWrapperTypeInfo();
+  DOMArrayBufferBase* buffer = BufferBase();
   v8::Local<v8::Value> v8_buffer;
   if (!ToV8Traits<DOMArrayBufferBase>::ToV8(script_state, buffer)
            .ToLocal(&v8_buffer)) {
diff --git a/third_party/blink/renderer/core/workers/worker_settings.cc b/third_party/blink/renderer/core/workers/worker_settings.cc
index 15ff38a5..2496c057 100644
--- a/third_party/blink/renderer/core/workers/worker_settings.cc
+++ b/third_party/blink/renderer/core/workers/worker_settings.cc
@@ -8,7 +8,7 @@
 
 WorkerSettings::WorkerSettings(Settings* settings) {
   if (settings)
-    this->CopyFlagValuesFromSettings(settings);
+    CopyFlagValuesFromSettings(settings);
 }
 
 WorkerSettings::WorkerSettings(
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
index 283d820..dd61e1f 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.cc
@@ -307,10 +307,10 @@
   DCHECK(context_provider);
 
   const base::TimeTicks timestamp = base::TimeTicks::Now();
-  const gfx::Size image_size(image->width(), image->height());
   const bool is_opaque = image->CurrentFrameKnownToBeOpaque();
   const media::VideoPixelFormat temp_argb_pixel_format =
       media::VideoPixelFormatFromSkColorType(kN32_SkColorType, is_opaque);
+  const gfx::Size image_size(image->width(), image->height());
   scoped_refptr<media::VideoFrame> temp_argb_frame = frame_pool_.CreateFrame(
       temp_argb_pixel_format, image_size, gfx::Rect(image_size), image_size,
       base::TimeDelta());
@@ -323,13 +323,20 @@
                     kN32_SkColorType == kBGRA_8888_SkColorType,
                 "CanvasCaptureHandler::ReadARGBPixelsAsync supports only "
                 "kRGBA_8888_SkColorType and kBGRA_8888_SkColorType.");
-  // This mapping also matches the behavior of the non-public helper function
-  // media::(anonymous namespace)::GetSkiaAndGlColorTypesForPlane.
-  GLenum format;
-  if (kN32_SkColorType == kRGBA_8888_SkColorType)
-    format = GL_RGBA;
-  else
-    format = GL_BGRA_EXT;
+  SkImageInfo info = SkImageInfo::MakeN32(
+      image_size.width(), image_size.height(),
+      is_opaque ? kOpaque_SkAlphaType : kUnpremul_SkAlphaType);
+  GLuint row_bytes;
+  if (!base::CheckedNumeric<size_t>(info.minRowBytes())
+           .AssignIfValid(&row_bytes)) {
+    DLOG(ERROR) << "Row stride must fit in GLuint (32 bits), given stride: "
+                << info.minRowBytes();
+    return;
+  }
+
+  GrSurfaceOrigin image_origin = image->IsOriginTopLeft()
+                                     ? kTopLeft_GrSurfaceOrigin
+                                     : kBottomLeft_GrSurfaceOrigin;
 
   IncrementOngoingAsyncPixelReadouts();
   gpu::MailboxHolder mailbox_holder = image->GetMailboxHolder();
@@ -337,11 +344,11 @@
   context_provider->RasterInterface()->WaitSyncTokenCHROMIUM(
       mailbox_holder.sync_token.GetConstData());
   context_provider->RasterInterface()->ReadbackARGBPixelsAsync(
-      mailbox_holder.mailbox, mailbox_holder.texture_target, image_size,
-      temp_argb_frame->visible_data(media::VideoFrame::kARGBPlane), format,
+      mailbox_holder.mailbox, mailbox_holder.texture_target, image_origin, info,
+      row_bytes, temp_argb_frame->visible_data(media::VideoFrame::kARGBPlane),
       WTF::Bind(&CanvasCaptureHandler::OnARGBPixelsReadAsync,
                 weak_ptr_factory_.GetWeakPtr(), image, temp_argb_frame,
-                timestamp, !image->IsOriginTopLeft()));
+                timestamp));
 }
 
 void CanvasCaptureHandler::ReadYUVPixelsAsync(
@@ -386,7 +393,7 @@
     scoped_refptr<StaticBitmapImage> image,
     scoped_refptr<media::VideoFrame> temp_argb_frame,
     base::TimeTicks this_frame_ticks,
-    bool flip,
+    GrSurfaceOrigin result_origin,
     bool success) {
   DCHECK_CALLED_ON_VALID_THREAD(main_render_thread_checker_);
   DecrementOngoingAsyncPixelReadouts();
@@ -400,6 +407,7 @@
   // Let |image| fall out of scope after we are done reading.
   const auto color_space = GetImageYUVColorSpace(image);
 
+  bool flip = result_origin == kBottomLeft_GrSurfaceOrigin;
   SendFrame(ConvertToYUVFrame(std::move(temp_argb_frame), flip),
             this_frame_ticks, color_space);
   if (num_ongoing_async_pixel_readouts_ == 0 && deferred_request_refresh_frame_)
diff --git a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h
index a449a99..b435297 100644
--- a/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h
+++ b/third_party/blink/renderer/modules/mediacapturefromelement/canvas_capture_handler.h
@@ -20,6 +20,7 @@
 #include "media/capture/video_capturer_source.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/gpu/GrTypes.h"
 
 class SkImage;
 
@@ -97,7 +98,7 @@
   void OnARGBPixelsReadAsync(scoped_refptr<StaticBitmapImage> image,
                              scoped_refptr<media::VideoFrame> temp_argb_frame,
                              base::TimeTicks this_frame_ticks,
-                             bool flip,
+                             GrSurfaceOrigin result_origin,
                              bool success);
   void OnYUVPixelsReadAsync(scoped_refptr<media::VideoFrame> yuv_frame,
                             base::TimeTicks this_frame_ticks,
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
index 98b3afb..6633824 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
@@ -137,6 +137,11 @@
 }
 
 // static
+const char* AudioDecoderTraits::GetName() {
+  return "AudioDecoder";
+}
+
+// static
 AudioDecoder* AudioDecoder::Create(ScriptState* script_state,
                                    const AudioDecoderInit* init,
                                    ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.h b/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
index dddce48..8fdd64d 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.h
@@ -67,6 +67,7 @@
                                media::MediaLog* media_log);
   static media::StatusOr<OutputType*> MakeOutput(scoped_refptr<MediaOutputType>,
                                                  ExecutionContext*);
+  static const char* GetName();
 };
 
 class MODULES_EXPORT AudioDecoder : public DecoderTemplate<AudioDecoderTraits> {
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
index 9d774bf..290f4f75 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
@@ -134,6 +134,11 @@
   return "AudioEncoder(WebCodecs)";
 }
 
+// static
+const char* AudioEncoderTraits::GetName() {
+  return "AudioEncoder";
+}
+
 AudioEncoder* AudioEncoder::Create(ScriptState* script_state,
                                    const AudioEncoderInit* init,
                                    ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
index b044eb6..1409648 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
@@ -51,6 +51,7 @@
 
   // Can't be a virtual method, because it's used from base ctor.
   static const char* GetNameForDevTools();
+  static const char* GetName();
 };
 
 class MODULES_EXPORT AudioEncoder final
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.cc b/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
index 638a24c..94a44c0d 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
@@ -45,6 +45,11 @@
                                          media::Status status) {
   media_log_->NotifyError(status);
 
+  if (status_code_ == media::StatusCode::kOk) {
+    DCHECK(!status.is_ok());
+    status_code_ = status.code();
+  }
+
   return MakeGarbageCollected<DOMException>(DOMExceptionCode::kOperationError,
                                             error_msg.c_str());
 }
@@ -52,6 +57,11 @@
 DOMException* CodecLogger::MakeException(std::string error_msg,
                                          media::StatusCode code,
                                          const base::Location& location) {
+  if (status_code_ == media::StatusCode::kOk) {
+    DCHECK_NE(code, media::StatusCode::kOk);
+    status_code_ = code;
+  }
+
   return MakeException(error_msg, media::Status(code, error_msg, location));
 }
 
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.h b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
index 6a19b89..80bb6f6 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.h
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
@@ -61,7 +61,12 @@
   // destroyed.
   void Neuter();
 
+  // Records the first media::Status passed to MakeException.
+  media::StatusCode status_code() const { return status_code_; }
+
  private:
+  media::StatusCode status_code_ = media::StatusCode::kOk;
+
   // |parent_media_log_| must be destroyed if ever the ExecutionContext is
   // destroyed, since the blink::MediaInspectorContext* pointer given to
   // InspectorMediaEventHandler might no longer be valid.
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index e934a3e7..b761b27 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/time/time.h"
 #include "media/media_buildflags.h"
 #include "media/video/gpu_video_accelerator_factories.h"
@@ -104,6 +105,11 @@
 template <typename Traits>
 DecoderTemplate<Traits>::~DecoderTemplate() {
   DVLOG(1) << __func__;
+  base::UmaHistogramSparse(
+      String::Format("Blink.WebCodecs.%s.FinalStatus", Traits::GetName())
+          .Ascii()
+          .c_str(),
+      static_cast<int>(logger_->status_code()));
 }
 
 template <typename Traits>
diff --git a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
index 93692dc..e59efd0 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
@@ -11,6 +11,7 @@
 #include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
@@ -66,6 +67,11 @@
 template <typename Traits>
 EncoderBase<Traits>::~EncoderBase() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  base::UmaHistogramSparse(
+      String::Format("Blink.WebCodecs.%s.FinalStatus", Traits::GetName())
+          .Ascii()
+          .c_str(),
+      static_cast<int>(logger_->status_code()));
 }
 
 template <typename Traits>
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
index 98993283..3d4d429 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
@@ -362,6 +362,11 @@
 }
 
 // static
+const char* VideoDecoderTraits::GetName() {
+  return "VideoDecoder";
+}
+
+// static
 VideoDecoder* VideoDecoder::Create(ScriptState* script_state,
                                    const VideoDecoderInit* init,
                                    ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.h b/third_party/blink/renderer/modules/webcodecs/video_decoder.h
index 88df35c..52ab18e 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.h
@@ -77,6 +77,7 @@
                                media::MediaLog* media_log);
   static media::StatusOr<OutputType*> MakeOutput(scoped_refptr<MediaOutputType>,
                                                  ExecutionContext*);
+  static const char* GetName();
 };
 
 class MODULES_EXPORT VideoDecoder : public DecoderTemplate<VideoDecoderTraits> {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
index 2e2a562b..702d157 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
@@ -339,6 +339,11 @@
 }
 
 // static
+const char* VideoEncoderTraits::GetName() {
+  return "VideoEncoder";
+}
+
+// static
 VideoEncoder* VideoEncoder::Create(ScriptState* script_state,
                                    const VideoEncoderInit* init,
                                    ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.h b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
index cbb74fe..f8fceb6c 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
@@ -58,6 +58,7 @@
 
   // Can't be a virtual method, because it's used from base ctor.
   static const char* GetNameForDevTools();
+  static const char* GetName();
 };
 
 class MODULES_EXPORT VideoEncoder final
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.cc b/third_party/blink/renderer/modules/webgpu/gpu.cc
index 3df090d..a777ffc 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu.cc
@@ -22,6 +22,7 @@
 #include "third_party/blink/renderer/modules/webgpu/gpu_supported_features.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/dawn_control_client_holder.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
@@ -176,6 +177,7 @@
 
   if (!dawn_control_client_ || dawn_control_client_->IsContextLost()) {
     ExecutionContext* execution_context = ExecutionContext::From(script_state);
+
     // TODO(natlee@microsoft.com): if GPU process is lost, wait for the GPU
     // process to come back instead of rejecting right away
     std::unique_ptr<WebGraphicsContext3DProvider> context_provider =
@@ -210,6 +212,8 @@
                 WrapPersistent(script_state), WrapPersistent(options),
                 WrapPersistent(resolver)));
 
+  UseCounter::Count(ExecutionContext::From(script_state), WebFeature::kWebGPU);
+
   return promise;
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/idl_member_installer.cc b/third_party/blink/renderer/platform/bindings/idl_member_installer.cc
index 81dbc9c..1f6f792d 100644
--- a/third_party/blink/renderer/platform/bindings/idl_member_installer.cc
+++ b/third_party/blink/renderer/platform/bindings/idl_member_installer.cc
@@ -173,16 +173,19 @@
 }
 
 template <FunctionKind kind, typename Config>
-v8::Local<v8::Function> CreateFunction(v8::Isolate* isolate,
-                                       v8::Local<v8::Context> context,
-                                       const DOMWrapperWorld& world,
-                                       v8::Local<v8::Signature> signature,
-                                       v8::Local<v8::String> name,
-                                       const Config& config) {
+v8::Local<v8::Function> CreateFunction(
+    v8::Isolate* isolate,
+    v8::Local<v8::Context> context,
+    const DOMWrapperWorld& world,
+    v8::Local<v8::Signature> signature,
+    v8::Local<v8::String> name,
+    const Config& config,
+    const v8::CFunction* v8_c_function = nullptr) {
   if (!GetConfigCallback<kind>(config))
     return v8::Local<v8::Function>();
 
-  return CreateFunctionTemplate<kind>(isolate, world, signature, name, config)
+  return CreateFunctionTemplate<kind>(isolate, world, signature, name, config,
+                                      v8_c_function)
       ->GetFunction(context)
       .ToLocalChecked();
 }
@@ -338,7 +341,8 @@
                       v8::Local<v8::Object> prototype_object,
                       v8::Local<v8::Object> interface_object,
                       v8::Local<v8::Signature> signature,
-                      const IDLMemberInstaller::OperationConfig& config) {
+                      const IDLMemberInstaller::OperationConfig& config,
+                      const v8::CFunction* v8_c_function = nullptr) {
   if (!DoesWorldMatch(config, world))
     return;
 
@@ -352,7 +356,7 @@
 
   v8::Local<v8::String> name = V8AtomicString(isolate, config.name);
   v8::Local<v8::Function> func = CreateFunction<FunctionKind::kOperation>(
-      isolate, context, world, signature, name, config);
+      isolate, context, world, signature, name, config, v8_c_function);
 
   v8::Local<v8::Object> target_object;
   switch (location) {
@@ -512,6 +516,23 @@
 }
 
 // static
+void IDLMemberInstaller::InstallOperations(
+    v8::Isolate* isolate,
+    const DOMWrapperWorld& world,
+    v8::Local<v8::Object> instance_object,
+    v8::Local<v8::Object> prototype_object,
+    v8::Local<v8::Object> interface_object,
+    v8::Local<v8::Signature> signature,
+    base::span<const NoAllocDirectCallOperationConfig> configs) {
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  for (const auto& config : configs) {
+    InstallOperation(isolate, context, world, instance_object, prototype_object,
+                     interface_object, signature, config.operation_config,
+                     &config.v8_c_function);
+  }
+}
+
+// static
 void IDLMemberInstaller::InstallExposedConstructs(
     v8::Isolate* isolate,
     const DOMWrapperWorld& world,
diff --git a/third_party/blink/renderer/platform/bindings/idl_member_installer.h b/third_party/blink/renderer/platform/bindings/idl_member_installer.h
index 9316b26..434d246 100644
--- a/third_party/blink/renderer/platform/bindings/idl_member_installer.h
+++ b/third_party/blink/renderer/platform/bindings/idl_member_installer.h
@@ -148,6 +148,14 @@
       v8::Local<v8::Template> interface_template,
       v8::Local<v8::Signature> signature,
       base::span<const NoAllocDirectCallOperationConfig> configs);
+  static void InstallOperations(
+      v8::Isolate* isolate,
+      const DOMWrapperWorld& world,
+      v8::Local<v8::Object> instance_object,
+      v8::Local<v8::Object> prototype_object,
+      v8::Local<v8::Object> interface_object,
+      v8::Local<v8::Signature> signature,
+      base::span<const NoAllocDirectCallOperationConfig> configs);
 
   // Global property reference
   // https://heycam.github.io/webidl/#define-the-global-property-references
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index 63f2786e..c7e8536 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -365,7 +365,8 @@
 
     gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
         gfx::Size(size), ColorParams().GetBufferFormat(),
-        gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, gpu::kNullSurfaceHandle);
+        gfx::BufferUsage::SCANOUT_CPU_READ_WRITE, gpu::kNullSurfaceHandle,
+        nullptr);
     if (!gpu_memory_buffer_)
       return;
 
diff --git a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
index 2f93e8cf..7200f09 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.cc
@@ -1703,7 +1703,7 @@
       // round-trip to the browser process here.
       gpu_memory_buffer = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
           gfx::Size(size), buffer_format, gfx::BufferUsage::SCANOUT,
-          gpu::kNullSurfaceHandle);
+          gpu::kNullSurfaceHandle, nullptr);
 
       if (gpu_memory_buffer) {
         back_buffer_mailbox = sii->CreateSharedImage(
diff --git a/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc b/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
index 00925bf..cf0ea2b5 100644
--- a/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu_memory_buffer_image_copy.cc
@@ -31,7 +31,7 @@
 
     gpu_memory_buffer_ = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
         gfx::Size(width, height), gfx::BufferFormat::RGBA_8888,
-        gfx::BufferUsage::SCANOUT, gpu::kNullSurfaceHandle);
+        gfx::BufferUsage::SCANOUT, gpu::kNullSurfaceHandle, nullptr);
     if (!gpu_memory_buffer_)
       return false;
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index ed7f199f..7dc97f5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2272,6 +2272,7 @@
     // --enable-experimental-web-platform-features flag.
     {
       name: "WebGPU",
+      origin_trial_feature_name: "WebGPU",
     },
     {
       // Requires both WebGPU and --enable-experimental-web-platform-features
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
index 16774115..f6a8bb47 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater.py
@@ -197,6 +197,10 @@
         #         config3: AnotherSimpleTestResult
         #     }
         # }
+
+        self.add_results_for_configs_without_results(
+            test_expectations, self.configs_with_no_results)
+
         # And then we merge results for different platforms that had the same results.
         for test_name, platform_result in test_expectations.iteritems():
             # platform_result is a dict mapping platforms to results.
@@ -215,14 +219,60 @@
         exp_lines_dict = self.write_to_test_expectations(test_expectations)
         return rebaselined_tests, exp_lines_dict
 
+    def add_results_for_configs_without_results(self, test_expectations,
+                                                configs_with_no_results):
+        # Handle any platforms with missing results.
+        def os_name(port_name):
+            # Port names are typically "os-os_version". So we can grab the os by
+            # taking everything up to the '-'
+            if '-' not in port_name:
+                return port_name
+            return port_name[:port_name.rfind('-')]
+
+        # When a config has no results, we try to guess at what its results are
+        # based on other results. We prefer to use results from other builds on
+        # the same OS, but fallback to all other builders otherwise (eg: there
+        # is usually only one Linux).
+        # In both cases, we union the results across the other builders (whether
+        # same OS or all builders), so we are usually over-expecting.
+        for config_no_result in configs_with_no_results:
+            _log.warning("No results for %s, inheriting from other builds" %
+                         config_no_result)
+            for test_name in test_expectations:
+                # The union of all other actual statuses is used when there is
+                # no similar OS to inherit from (eg: no results on Linux, and
+                # inheriting from Mac and Win).
+                union_actual_all = set()
+                # The union of statuses on the same OS is used when there are
+                # multiple versions of the same OS with results (eg: no results
+                # on Mac10.12, and inheriting from Mac10.15 and Mac11)
+                union_actual_sameos = set()
+                config_result_dict = test_expectations[test_name]
+                for config in config_result_dict.keys():
+                    result = config_result_dict[config]
+                    union_actual_all.add(result.actual)
+                    if os_name(config.port_name) == os_name(
+                            config_no_result.port_name):
+                        union_actual_sameos.add(result.actual)
+
+                statuses = union_actual_sameos or union_actual_all
+                union_result = SimpleTestResult(expected="",
+                                                actual=" ".join(statuses),
+                                                bug=self.UMBRELLA_BUG)
+                _log.debug("Inheriting result for test %s on config %s. "
+                           "Same-os? %s Result: %s." %
+                           (test_name, config_no_result,
+                            len(union_actual_sameos) > 0, union_result))
+                test_expectations[test_name][config_no_result] = union_result
+
     def get_issue_number(self):
         """Returns current CL number. Can be replaced in unit tests."""
         return self.git_cl.get_issue_number()
 
     def get_latest_try_jobs(self):
         """Returns the latest finished try jobs as Build objects."""
-        return self.git_cl.latest_try_jobs(
-            builder_names=self._get_try_bots(), patchset=self.patchset)
+        return self.git_cl.latest_try_jobs(builder_names=self._get_try_bots(),
+                                           patchset=self.patchset)
 
     def get_failing_results_dicts(self, build):
         """Returns a list of nested dicts of failing test results.
@@ -338,10 +388,14 @@
                 continue
             test_dict[test_name] = {
                 config:
-                SimpleTestResult(
-                    expected=result.expected_results(),
-                    actual=result.actual_results(),
-                    bug=self.UMBRELLA_BUG)
+                # Note: we omit `expected` so that existing expectation lines
+                # don't prevent us from merging current results across platform.
+                # Eg: if a test FAILs everywhere, it should not matter that it
+                # has a pre-existing TIMEOUT expectation on Win7. This code is
+                # not currently capable of updating that existing expectation.
+                SimpleTestResult(expected="",
+                                 actual=result.actual_results(),
+                                 bug=self.UMBRELLA_BUG)
             }
         return test_dict
 
@@ -527,15 +581,6 @@
             |test_name|.
         """
         lines = []
-        # The ports with no results are generally ports of builders that
-        # failed, maybe for unrelated reasons. It is possible to have multiple
-        # builders using the same port, where one gets results while the other
-        # does not.
-        # At this point, we add ports with no results to the list of platforms
-        # because we're guessing that this new expectation might be
-        # cross-platform and should also apply to any ports that we weren't able
-        # to get results for.
-        configs = tuple(set(configs) | set(self.configs_with_no_results))
 
         expectations = '[ %s ]' % \
             ' '.join(self.get_expectations(result, test_name))
diff --git a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
index c533a0f..9e968ab4 100644
--- a/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/wpt_expectations_updater_unittest.py
@@ -252,17 +252,16 @@
         updater = WPTExpectationsUpdater(host)
         results = updater.get_failing_results_dicts(
             Build('MOCK Try Mac10.10', 123))
-        self.assertEqual(
-            results, [{
-                'external/wpt/x/failing-test.html': {
-                    DesktopConfig(port_name='test-mac-mac10.10'):
-                    SimpleTestResult(
-                        actual='IMAGE',
-                        expected='PASS',
-                        bug='crbug.com/626703',
-                    ),
-                },
-            }])
+        self.assertEqual(results, [{
+            'external/wpt/x/failing-test.html': {
+                DesktopConfig(port_name='test-mac-mac10.10'):
+                SimpleTestResult(
+                    actual='IMAGE',
+                    expected='',
+                    bug='crbug.com/626703',
+                ),
+            },
+        }])
 
     def test_get_failing_results_dict_non_wpt_test(self):
         host = self.mock_host()
@@ -325,25 +324,25 @@
         results = updater.get_failing_results_dicts(
             Build('MOCK Try Trusty', 123))
         self.assertEqual(len(results), 2)
-        self.assertEqual(
-            results, [{
-                'external/wpt/x/failing-test.html': {
-                    DesktopConfig('test-linux-trusty'):
-                    SimpleTestResult(
-                        actual='IMAGE',
-                        expected='PASS',
-                        bug='crbug.com/626703',
-                    ),
-                }}, {
-                'external/wpt/y/webdriver-fail.html': {
-                    DesktopConfig('test-linux-trusty'):
-                    SimpleTestResult(
-                        actual='FAIL',
-                        expected='PASS',
-                        bug='crbug.com/626703',
-                    ),
-                },
-            }])
+        self.assertEqual(results, [{
+            'external/wpt/x/failing-test.html': {
+                DesktopConfig('test-linux-trusty'):
+                SimpleTestResult(
+                    actual='IMAGE',
+                    expected='',
+                    bug='crbug.com/626703',
+                ),
+            }
+        }, {
+            'external/wpt/y/webdriver-fail.html': {
+                DesktopConfig('test-linux-trusty'):
+                SimpleTestResult(
+                    actual='FAIL',
+                    expected='',
+                    bug='crbug.com/626703',
+                ),
+            },
+        }])
 
     def test_merge_same_valued_keys_all_match(self):
         updater = WPTExpectationsUpdater(self.mock_host())
@@ -748,17 +747,18 @@
         updater.port_name = lambda b: b.builder_name
         self.assertEqual(
             updater.generate_failing_results_dict(
-                Build(builder_name='test-mac-mac10.10', build_number=1), web_test_list),
-            {
-                'external/wpt/test/name.html': {
-                    DesktopConfig(port_name='test-mac-mac10.10'):
-                    SimpleTestResult(
-                        expected='bar',
-                        actual='foo',
-                        bug='crbug.com/626703',
-                    )
+                Build(builder_name='test-mac-mac10.10', build_number=1),
+                web_test_list), {
+                    'external/wpt/test/name.html': {
+                        DesktopConfig(port_name='test-mac-mac10.10'):
+                        SimpleTestResult(
+                            expected='',
+                            actual='foo',
+                            bug='crbug.com/626703',
+                        )
+                    }
                 }
-            })
+        )
 
     def test_no_expectations_to_write(self):
         host = self.mock_host()
@@ -1258,34 +1258,6 @@
                 ['crbug.com/test external/wpt/x-manual.html [ Skip ]']
             })
 
-    def test_one_platform_has_no_results(self):
-        # In this example, there is a failure that has been observed on
-        # Linux and one Mac port, but the other Mac port has no results at all.
-        # The specifiers are "filled in" and the failure is assumed to apply
-        # to all Mac platforms.
-        host = self.mock_host()
-        updater = WPTExpectationsUpdater(host)
-        results = {
-            'external/wpt/x.html': {
-                (
-                    DesktopConfig(port_name='test-linux-precise'),
-                    DesktopConfig(port_name='test-linux-trusty'),
-                    DesktopConfig(port_name='test-mac-mac10.11'),
-                ):
-                SimpleTestResult(
-                    expected='PASS', actual='TEXT', bug='crbug.com/test')
-            }
-        }
-        updater.configs_with_no_results = [
-            DesktopConfig(port_name='test-mac-mac10.10')]
-        self.assertEqual(
-            updater.create_line_dict(results), {
-                'external/wpt/x.html': [
-                    'crbug.com/test [ Linux ] external/wpt/x.html [ Failure ]',
-                    'crbug.com/test [ Mac ] external/wpt/x.html [ Failure ]',
-                ]
-            })
-
     def test_same_platform_one_without_results(self):
         # In this example, there are two configs using the same platform
         # (Mac10.10), and one of them has no results while the other one does.
@@ -1438,3 +1410,153 @@
                     'crbug.com/test [ Win7 ] external/wpt/x.html [ Failure ]',
                 ]
             })
+
+    def test_inheriting_results(self):
+        # We make sure that platforms that have no results are able to inherit
+        # results from other builds.
+        host = self.mock_host()
+        # Reset the fake list of try builders to use 3 Macs, 2 Wins and 1 Linux.
+        host.builders = BuilderList({
+            'MOCK Try Mac10.10': {
+                'port_name': 'test-mac-mac10.10',
+                'specifiers': ['Mac10.10', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Mac10.11': {
+                'port_name': 'test-mac-mac10.11',
+                'specifiers': ['Mac10.11', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Mac10.12': {
+                'port_name': 'test-mac-mac10.12',
+                'specifiers': ['Mac10.12', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Trusty': {
+                'port_name': 'test-linux-trusty',
+                'specifiers': ['Trusty', 'Release'],
+                'master': 'tryserver.blink',
+                'has_webdriver_tests': True,
+                'is_try_builder': True,
+            },
+            'MOCK Try Win10': {
+                'port_name': 'test-win-win10',
+                'specifiers': ['Win10', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Win7': {
+                'port_name': 'test-win-win7',
+                'specifiers': ['Win7', 'Release'],
+                'is_try_builder': True,
+            },
+        })
+        updater = WPTExpectationsUpdater(host)
+        test_name = 'external/wpt/x.html'
+        results = {
+            test_name: {
+                (DesktopConfig('test-win-win7')):
+                SimpleTestResult(expected='',
+                                 actual='TIMEOUT',
+                                 bug='crbug.com/626703'),
+                (DesktopConfig('test-mac-mac10.10')):
+                SimpleTestResult(expected='',
+                                 actual='CRASH',
+                                 bug='crbug.com/626703'),
+                (DesktopConfig('test-mac-mac10.11')):
+                SimpleTestResult(expected='',
+                                 actual='FAILURE',
+                                 bug='crbug.com/626703')
+            }
+        }
+
+        # Win10 will inherit the result from win7
+        missing_config = DesktopConfig('test-win-win10')
+        tmp_results = copy.deepcopy(results)
+        updater.add_results_for_configs_without_results(
+            tmp_results, [missing_config])
+        inherited_result = tmp_results[test_name][missing_config]
+        self.assertEqual("TIMEOUT", inherited_result.actual)
+
+        # Mac10.12 will inherit the union of results from Mac10.10 and Mac10.11
+        missing_config = DesktopConfig('test-mac-mac10.12')
+        tmp_results = copy.deepcopy(results)
+        updater.add_results_for_configs_without_results(
+            tmp_results, [missing_config])
+        inherited_result = tmp_results[test_name][missing_config]
+        self.assertEqual("FAILURE CRASH", inherited_result.actual)
+
+        # Linux will inherit all results from Mac and Win since there is no
+        # other Linux result to take.
+        missing_config = DesktopConfig('test-linux-trusty')
+        tmp_results = copy.deepcopy(results)
+        updater.add_results_for_configs_without_results(
+            tmp_results, [missing_config])
+        inherited_result = tmp_results[test_name][missing_config]
+        self.assertEqual("FAILURE CRASH TIMEOUT", inherited_result.actual)
+
+    def test_inheriting_results_dedupe(self):
+        # In this test we make sure that we dedupe the inherited results.
+        host = self.mock_host()
+        # Set up a fake list of try builders.
+        # This uses 3 Macs, 2 Wins and 1 Linux.
+        host.builders = BuilderList({
+            'MOCK Try Mac10.10': {
+                'port_name': 'test-mac-mac10.10',
+                'specifiers': ['Mac10.10', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Mac10.11': {
+                'port_name': 'test-mac-mac10.11',
+                'specifiers': ['Mac10.11', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Mac10.12': {
+                'port_name': 'test-mac-mac10.12',
+                'specifiers': ['Mac10.12', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Trusty': {
+                'port_name': 'test-linux-trusty',
+                'specifiers': ['Trusty', 'Release'],
+                'master': 'tryserver.blink',
+                'has_webdriver_tests': True,
+                'is_try_builder': True,
+            },
+            'MOCK Try Win10': {
+                'port_name': 'test-win-win10',
+                'specifiers': ['Win10', 'Release'],
+                'is_try_builder': True,
+            },
+            'MOCK Try Win7': {
+                'port_name': 'test-win-win7',
+                'specifiers': ['Win7', 'Release'],
+                'is_try_builder': True,
+            },
+        })
+        updater = WPTExpectationsUpdater(host)
+        test_name = 'external/wpt/x.html'
+        results = {
+            test_name: {
+                (DesktopConfig('test-win-win7')):
+                SimpleTestResult(expected='',
+                                 actual='TIMEOUT',
+                                 bug='crbug.com/626703'),
+                (DesktopConfig('test-mac-mac10.10')):
+                SimpleTestResult(expected='',
+                                 actual='TIMEOUT',
+                                 bug='crbug.com/626703'),
+                (DesktopConfig('test-mac-mac10.11')):
+                SimpleTestResult(expected='',
+                                 actual='FAILURE',
+                                 bug='crbug.com/626703')
+            }
+        }
+
+        # Linux will inherit all results from Mac and Win since there is no
+        # other Linux result to take. The results are deduped so we should not
+        # get two TIMEOUT statuses in the result.
+        missing_config = DesktopConfig('test-linux-trusty')
+        updater.add_results_for_configs_without_results(
+            results, [missing_config])
+        inherited_result = results[test_name][missing_config]
+        self.assertEqual("FAILURE TIMEOUT", inherited_result.actual)
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0cd44fad..f2ab9c1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -4193,6 +4193,12 @@
 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/descendant-static-position-001.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/descendant-static-position-002.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/descendant-static-position-003.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-002.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-003.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-002.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-003.html [ Pass ]
+virtual/layout-ng-grid/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-004.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-011.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-012.html [ Pass ]
 virtual/layout-ng-grid/external/wpt/css/css-grid/alignment/grid-alignment-implies-size-change-013.html [ Pass ]
@@ -4348,9 +4354,9 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/animated-svg-as-image-same-image.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/ems-display-none.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/exs-display-none.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/filter-repaint.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/filter-width-update.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/hit-test-unclosed-subpaths.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/paint/invalidation/svg/filter-repaint.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/paint/invalidation/svg/filter-width-update.svg [ Failure Crash ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/paint/invalidation/svg/hit-test-unclosed-subpaths.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/inner-svg-change-viewPort-relative.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/js-late-clipPath-and-object-creation.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/js-late-clipPath-creation.svg [ Failure ]
@@ -4372,9 +4378,9 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/text-selection-text-05-t.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/text-selection-update.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/text-viewbox-rescale.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/text-xy-updates-SVGList.xhtml [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/paint/invalidation/svg/text-xy-updates-SVGList.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/transform-text-element.html [ Pass Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/tspan-dynamic-positioning.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/paint/invalidation/svg/tspan-dynamic-positioning.svg [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/use-detach.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/use-event-handler-on-use-element.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/paint/invalidation/svg/window.svg [ Failure ]
@@ -4436,20 +4442,20 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/alignment-baseline-modes.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/anchor-on-use.svg [ Pass Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/broken-internal-references.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/clip-mask-negative-scale.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/clip-path-referencing-use.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/clip-path-referencing-use2.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/container-opacity-clip-viewBox.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/coords-relative-units-transforms.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/clip-mask-negative-scale.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/clip-path-referencing-use.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/clip-path-referencing-use2.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/container-opacity-clip-viewBox.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/coords-relative-units-transforms.svg [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/dominant-baseline-hanging.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/dominant-baseline-modes.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/empty-clip-path.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/empty-clip-path.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/empty-mask.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Discrete.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Gamma.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Linear.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Table.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/fill-fallback.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Discrete.svg [ Failure Timeout ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Gamma.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Linear.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/feComponentTransfer-Table.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/fill-fallback.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/focus-ring.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/focus-ring-text.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/font-face-cascade-order.svg [ Failure ]
@@ -4459,54 +4465,54 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/getscreenctm-in-scrollable-svg-area.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/getSubStringLength.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/glyph-transformation-with-hkern.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/gradient-cycle-detection.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/gradient-deep-referencing.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/gradient-cycle-detection.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/gradient-deep-referencing.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/gradient-userSpaceOnUse-with-percentage.svg [ Pass Skip ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/gradient-with-1d-boundingbox.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/image-small-width-height.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/image-with-transform-clip-filter.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/image-small-width-height.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/image-with-transform-clip-filter.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/invalid-css.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/invalid-fill-hex.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/invalid-gradient-with-xlink.svg [ Failure Crash ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/invalid-lengthlist.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/invalid-lengthlist.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/invalid-stroke-hex.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/junk-data.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/linking-a-03-b-transform.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/junk-data.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/linking-a-03-b-transform.svg [ Failure Timeout ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/linking-uri-01-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/marker-default-width-height.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/marker-overflow-clip.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/missing-xlink.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/marker-overflow-clip.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/missing-xlink.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-container.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-container-standalone.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-root.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/mouse-move-on-svg-root-standalone.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/path-textPath-simulation.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-cycle-detection.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-deep-referencing.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/pattern-cycle-detection.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/pattern-deep-referencing.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-incorrect-tiling.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-rotate.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-rotate-gaps.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-userSpaceOnUse-userToBaseTransform.xhtml [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/pattern-rotate.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/pattern-rotate-gaps.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/pattern-userSpaceOnUse-userToBaseTransform.xhtml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pattern-with-transformation.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pointer-events-path.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/pointer-events-path.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pointer-events-text.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/pointer-events-text-css-transform.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/preserve-aspect-ratio-syntax.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/preserve-aspect-ratio-syntax.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/recursive-clippath.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/recursive-filter.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/recursive-gradient.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/recursive-mask.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/recursive-pattern.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/selectSubString.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/shape-rendering.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/shape-rendering.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/shapes-supporting-markers.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/stroke-fallback.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/stroke-fallback.svg [ Failure Timeout ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/stroke-width-large.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/style-attribute-font-size.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/SVGMatrix-interface.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/style-attribute-font-size.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/SVGMatrix-interface.svg [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/svg-root-with-opacity.html [ Pass Failure ]
 crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/text-clip.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-decoration-visibility.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/text-decoration-visibility.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-dom-01-f.svg [ Failure Crash ]
 crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/text-filter.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-hit-test.svg [ Failure ]
@@ -4523,17 +4529,17 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-whitespace-handling.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/text-x-dy-lists.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/transformed-outlines.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-dynamic-append.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-dynamic-append.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-event-handler-on-referenced-element.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-events-crash.svg [ Failure ]
 crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-font-face-crash.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-modify-container-in-target.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-modify-target-container.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-modify-target-symbol.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-modify-container-in-target.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-modify-target-container.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-modify-target-symbol.svg [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-on-text.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-property-changes-through-dom.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/use-property-changes-through-svg-dom.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/viewbox-syntax.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-property-changes-through-dom.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/use-property-changes-through-svg-dom.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/custom/viewbox-syntax.svg [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/visibility-collapse.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/zoomed-alignment-baseline.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/custom/zoomed-baseline-shift.html [ Failure ]
@@ -4553,18 +4559,18 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-rotate-attr.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-textLength-attr.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-transform-attr.html [ Pass Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-x-attr.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-y-attr.html [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-x-attr.html [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-dom-y-attr.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-dx-prop.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-dy-prop.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-lengthAdjust-prop.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-rotate-prop.html [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-rotate-prop.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-textLength-prop.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-transform-prop.html [ Pass Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-x-prop.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-y-prop.html [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-x-prop.html [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/dynamic-updates/SVGTextElement-svgdom-y-prop.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/feComponentTransfer-style-crash.xhtml [ Pass ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/feComposite.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/filters/feComposite.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/feDisplacementMap.svg [ Pass Timeout ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/filter-on-filter-for-text.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/filters/filter-on-tspan.svg [ Failure ]
@@ -4579,7 +4585,7 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hittest/text-small-font-size-and-viewbox.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hittest/text-with-multiple-tspans.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hittest/text-with-text-path.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/dynamic/002.xml [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/hixie/dynamic/002.xml [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/hixie/error/002.xml [ Failure ]
 crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/hixie/error/010.xml [ Failure ]
 crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/hixie/error/011.xml [ Failure ]
@@ -4606,7 +4612,7 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-embedded-direction.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-getcharnumatpos.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-getsubstringlength.html [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-reorder-value-lists.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/text/bidi-reorder-value-lists.svg [ Failure Timeout ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-text-anchor-direction.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-text-query.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/bidi-tspans.svg [ Failure ]
@@ -4615,7 +4621,7 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/combining-character-queries.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/degenerate-text-path.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/empty-text-node-crash.html [ Pass Crash ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/font-size-below-point-five-2.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/text/font-size-below-point-five-2.svg [ Failure Crash ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/getcharnumatposition-multiple-fragments.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/getextentofchar-nonbmp.html [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/text/lengthAdjust-text-metrics.html [ Failure ]
@@ -4802,7 +4808,7 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/animate-elem-83-t.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/animate-elem-84-t.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/animate-elem-85-t.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/color-prof-01-f.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/color-prof-01-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/color-prop-01-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/color-prop-02-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/color-prop-03-t.svg [ Failure ]
@@ -4824,8 +4830,8 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-color-01-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-composite-02-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-comptran-01-b.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-conv-01-f.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-diffuse-01-f.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-conv-01-f.svg [ Failure Crash ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-diffuse-01-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-displace-01-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-example-01-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/filters-gauss-01-b.svg [ Failure ]
@@ -4996,10 +5002,10 @@
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/text-tspan-01-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/text-ws-01-t.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/text-ws-02-t.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/types-basicDOM-01-b.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/W3C-SVG-1.1/types-basicDOM-01-b.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/color-prop-05-t.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/coords-dom-01-f.svg [ Failure ]
-crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/coords-dom-02-f.svg [ Failure ]
+crbug.com/1179585 [ Mac ] virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/coords-dom-02-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/coords-dom-03-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/coords-dom-04-f.svg [ Failure ]
 crbug.com/1179585 virtual/layout_ng_svg_text/svg/W3C-SVG-1.1-SE/coords-units-03-b.svg [ Failure ]
@@ -7041,6 +7047,10 @@
 crbug.com/1180491 [ Linux ] external/wpt/storage-access-api/storageAccess.testdriver.sub.html [ Pass Failure ]
 crbug.com/1180491 [ Linux ] virtual/storage-access-api/external/wpt/storage-access-api/storageAccess.testdriver.sub.html [ Pass Failure ]
 
+# Sheriff 2021-02-23
+crbug.com/1181466 external/wpt/css/css-scroll-snap/snap-after-relayout/resnap-to-focused.html [ Pass Failure ]
+crbug.com/1181466 virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-relayout/resnap-to-focused.html [ Pass Failure ]
+
 # Sheriff 2021-02-24
 crbug.com/1181667 [ Linux ] external/wpt/css/selectors/focus-visible-011.html [ Pass Failure ]
 crbug.com/1045052 [ Linux ] virtual/split-http-cache-not-site-per-process/http/tests/devtools/isolated-code-cache/stale-revalidation-test.js [ Pass Failure Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/element-reassigned-to-skipped-slot.html b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/element-reassigned-to-skipped-slot.html
new file mode 100644
index 0000000..a47b463
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/element-reassigned-to-skipped-slot.html
@@ -0,0 +1,68 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Content Visibility: element reslotting</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
+<link rel="match" href="container-ref.html">
+<meta name="assert" content="element is correctly removed when slot assignment takes it into a skipped slot">
+
+<script src="/common/reftest-wait.js"></script>
+
+<script>
+window.customElements.define("my-element", class extends HTMLElement {
+  connectedCallback() {
+      if (this.shadowRoot) {
+        this.computeEdges_();
+        return;
+      }
+
+      this.attachShadow({ mode: 'open' }).innerHTML = `
+        <style>
+          slot[name=locked] {
+            display: block;
+            content-visibility: hidden;
+          }
+        </style>
+        <slot name=unlocked></slot>
+        <slot name=locked></slot>
+      `;
+  }
+});
+</script>
+
+<style>
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+
+div {
+  width: 50px;
+  height: 50px;
+}
+.composited { will-change: transform; }
+#one { background: green; }
+#two { background: red; }
+</style>
+
+<div id=container>
+  <my-element>
+    <div id=one slot=unlocked>hello</div>
+    <div id=two slot=locked>world</div>
+  </my-element>
+</div>
+
+<script>
+// Ensure everything is loaded and rendered.
+onload = () =>
+  requestAnimationFrame(() =>
+  requestAnimationFrame(() =>
+  requestAnimationFrame(() => {
+    // Reslot the element and composite the other one.
+    one.slot = "locked";
+    two.classList.add("composited");
+    requestAnimationFrame(takeScreenshot);
+})));
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/element-reassigned-to-slot-in-skipped-subtree.html b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/element-reassigned-to-slot-in-skipped-subtree.html
new file mode 100644
index 0000000..103de97
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/element-reassigned-to-slot-in-skipped-subtree.html
@@ -0,0 +1,70 @@
+<!doctype HTML>
+<html class="reftest-wait">
+<meta charset="utf8">
+<title>CSS Content Visibility: element reslotting</title>
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-contain/#content-visibility">
+<link rel="match" href="container-ref.html">
+<meta name="assert" content="element is correctly removed when slot assignment takes it into a skipped slot">
+
+<script src="/common/reftest-wait.js"></script>
+
+<script>
+window.customElements.define("my-element", class extends HTMLElement {
+  connectedCallback() {
+      if (this.shadowRoot) {
+        this.computeEdges_();
+        return;
+      }
+
+      this.attachShadow({ mode: 'open' }).innerHTML = `
+        <style>
+          #locked {
+            display: block;
+            content-visibility: hidden;
+          }
+        </style>
+        <slot name=unlocked></slot>
+        <div id=locked>
+          <slot name=locked></slot>
+        </div>
+      `;
+  }
+});
+</script>
+
+<style>
+#container {
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+
+div {
+  width: 50px;
+  height: 50px;
+}
+.composited { will-change: transform; }
+#one { background: green; }
+#two { background: red; }
+</style>
+
+<div id=container>
+  <my-element>
+    <div id=one slot=unlocked>hello</div>
+    <div id=two slot=locked>world</div>
+  </my-element>
+</div>
+
+<script>
+// Ensure everything is loaded and rendered.
+onload = () =>
+  requestAnimationFrame(() =>
+  requestAnimationFrame(() =>
+  requestAnimationFrame(() => {
+    // Reslot the element and composite the other one.
+    one.slot = "locked";
+    two.classList.add("composited");
+    requestAnimationFrame(takeScreenshot);
+})));
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-002-ref.html
index 4197dc5..ddb89d1 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-002-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-002-ref.html
@@ -35,11 +35,11 @@
       width: 8px;
     }
     .big   .alignStart  { margin-top:  0px; }
-    .big   .alignCenter { margin-top: 13px; }
-    .big   .alignEnd    { margin-top: 26px; }
+    .big   .alignCenter { margin-top: 12px; }
+    .big   .alignEnd    { margin-top: 24px; }
     .small .alignStart  { margin-top:  0px; }
-    .small .alignCenter { margin-top: -1px; }
-    .small .alignEnd    { margin-top: -2px; }
+    .small .alignCenter { margin-top: -2px; }
+    .small .alignEnd    { margin-top: -4px; }
   </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-003-ref.html
index 50239d5f..dde6f967 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-003-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-003-ref.html
@@ -38,11 +38,11 @@
     .small > .container > * { margin-left: -4px; }
 
     .big   .alignStart  { margin-top:  0px; }
-    .big   .alignCenter { margin-top: 13px; }
-    .big   .alignEnd    { margin-top: 26px; }
+    .big   .alignCenter { margin-top: 12px; }
+    .big   .alignEnd    { margin-top: 24px; }
     .small .alignStart  { margin-top:  0px; }
-    .small .alignCenter { margin-top: -1px; }
-    .small .alignEnd    { margin-top: -2px; }
+    .small .alignCenter { margin-top: -2px; }
+    .small .alignEnd    { margin-top: -4px; }
   </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004-ref.html
index 50239d5f..dde6f967 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004-ref.html
@@ -38,11 +38,11 @@
     .small > .container > * { margin-left: -4px; }
 
     .big   .alignStart  { margin-top:  0px; }
-    .big   .alignCenter { margin-top: 13px; }
-    .big   .alignEnd    { margin-top: 26px; }
+    .big   .alignCenter { margin-top: 12px; }
+    .big   .alignEnd    { margin-top: 24px; }
     .small .alignStart  { margin-top:  0px; }
-    .small .alignCenter { margin-top: -1px; }
-    .small .alignEnd    { margin-top: -2px; }
+    .small .alignCenter { margin-top: -2px; }
+    .small .alignEnd    { margin-top: -4px; }
   </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-002-ref.html
index a5e8aad..cb0fb93 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-002-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-002-ref.html
@@ -36,11 +36,11 @@
       height: 8px;
     }
     .big   .alignStart  { margin-left:  0px; }
-    .big   .alignCenter { margin-left: 13px; }
-    .big   .alignEnd    { margin-left: 26px; }
+    .big   .alignCenter { margin-left: 12px; }
+    .big   .alignEnd    { margin-left: 24px; }
     .small .alignStart  { margin-left:  0px; }
-    .small .alignCenter { margin-left: -1px; }
-    .small .alignEnd    { margin-left: -2px; }
+    .small .alignCenter { margin-left: -2px; }
+    .small .alignEnd    { margin-left: -4px; }
   </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-003-ref.html
index 22dd4bfb..602d0a5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-003-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-003-ref.html
@@ -36,11 +36,11 @@
       height: 8px;
     }
     .big   .alignStart  { margin-left: 24px; }
-    .big   .alignCenter { margin-left: 11px; }
-    .big   .alignEnd    { margin-left: -2px; }
+    .big   .alignCenter { margin-left: 12px; }
+    .big   .alignEnd    { margin-left:  0px; }
     .small .alignStart  { margin-left: -4px; }
-    .small .alignCenter { margin-left: -3px; }
-    .small .alignEnd    { margin-left: -2px; }
+    .small .alignCenter { margin-left: -2px; }
+    .small .alignEnd    { margin-left:  0px; }
   </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-004-ref.html
index fde33cf..ebf46d76 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-004-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-004-ref.html
@@ -36,11 +36,11 @@
       height: 8px;
     }
     .big   .alignStart  { margin-left: 24px; }
-    .big   .alignCenter { margin-left: 11px; }
-    .big   .alignEnd    { margin-left: -2px; }
+    .big   .alignCenter { margin-left: 12px; }
+    .big   .alignEnd    { margin-left:  0px; }
     .small .alignStart  { margin-left: -4px; }
-    .small .alignCenter { margin-left: -3px; }
-    .small .alignEnd    { margin-left: -2px; }
+    .small .alignCenter { margin-left: -2px; }
+    .small .alignEnd    { margin-left:  0px; }
   </style>
 </head>
 <body>
diff --git a/third_party/blink/web_tests/virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-relayout/resnap-to-focused-expected.txt b/third_party/blink/web_tests/virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-relayout/resnap-to-focused-expected.txt
new file mode 100644
index 0000000..0b8f95e4
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/external/wpt/css/css-scroll-snap/snap-after-relayout/resnap-to-focused-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL Resnap to focused element after relayout assert_equals: After resize, should snap to row 4. expected 4 but got 3
+Harness: the test ran to completion.
+
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 5c10cf8..b653bbe 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -500,6 +500,7 @@
     'chromium.linux': {
       'Cast Audio Linux': 'cast_audio_release_bot',
       'Cast Linux': 'cast_release_bot',
+      'Cast Linux Debug': 'cast_debug_bot',
       'Deterministic Fuchsia (dbg)': 'debug_bot_fuchsia',
       'Deterministic Linux (dbg)': {
         'local': 'debug_bot_local_build',
@@ -967,6 +968,7 @@
     'tryserver.chromium.linux': {
       'cast_shell_audio_linux': 'cast_audio_release_trybot',
       'cast_shell_linux': 'cast_release_trybot',
+      'cast_shell_linux_dbg': 'cast_debug_bot',
       'cast-binary-size': 'cast_binary_size',
       'chromium_presubmit': 'presubmit',
       'fuchsia_arm64': 'release_trybot_fuchsia_arm64',
@@ -1698,6 +1700,10 @@
       'cast', 'release_trybot',
     ],
 
+    'cast_debug_bot': [
+      'cast', 'debug_bot',
+    ],
+
     'cast_binary_size': [
       'cast', 'minimal_symbols', 'release_bot',
     ],
diff --git a/tools/mb/mb_config_expectations/chromium.linux.json b/tools/mb/mb_config_expectations/chromium.linux.json
index e6ab3100..d2b52e1 100644
--- a/tools/mb/mb_config_expectations/chromium.linux.json
+++ b/tools/mb/mb_config_expectations/chromium.linux.json
@@ -18,6 +18,15 @@
       "use_goma": true
     }
   },
+  "Cast Linux Debug": {
+    "gn_args": {
+      "is_chromecast": true,
+      "is_component_build": true,
+      "is_debug": true,
+      "symbol_level": 1,
+      "use_goma": true
+    }
+  },
   "Deterministic Fuchsia (dbg)": {
     "gn_args": {
       "is_component_build": true,
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
index 0618138..89dd51b 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.linux.json
@@ -29,6 +29,15 @@
       "use_goma": true
     }
   },
+  "cast_shell_linux_dbg": {
+    "gn_args": {
+      "is_chromecast": true,
+      "is_component_build": true,
+      "is_debug": true,
+      "symbol_level": 1,
+      "use_goma": true
+    }
+  },
   "fuchsia-arm64-cast": {
     "gn_args": {
       "dcheck_always_on": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6cc7b81..ed95d010 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -74,6 +74,11 @@
   <int value="3" label="both valid and invalid entries"/>
 </enum>
 
+<enum name="AcceptCHFrameRestart">
+  <int value="0" label="Frame present"/>
+  <int value="1" label="Navigation restarted"/>
+</enum>
+
 <enum name="AccessibilityAndroidAnimationsEnabled">
   <obsolete>
     Removed from code 2020/10.
@@ -32341,6 +32346,7 @@
   <int value="3885" label="RTCPeerConnectionOfferAllowExtmapMixedFalse"/>
   <int value="3886" label="NewCanvas2DAPI"/>
   <int value="3887" label="ServiceWorkerSubresourceFilter"/>
+  <int value="3888" label="WebGPU"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -44331,6 +44337,7 @@
   <int value="-2047822258" label="enable-avfoundation"/>
   <int value="-2047190657" label="SyncUserConsentSeparateType:enabled"/>
   <int value="-2047071651" label="NearbySharing:enabled"/>
+  <int value="-2046434739" label="enable-unsafe-webgpu-service"/>
   <int value="-2044939529" label="CrostiniResetLxdDb:disabled"/>
   <int value="-2043331707" label="PasswordScriptsFetching:disabled"/>
   <int value="-2043128632" label="enable-tab-switcher-in-document-mode"/>
@@ -47083,6 +47090,7 @@
   <int value="348449023"
       label="DesktopPWAsLocalUpdatingThrottlePersistence:enabled"/>
   <int value="348854923" label="v8-cache-strategies-for-cache-storage"/>
+  <int value="349469694" label="WebGPUService:disabled"/>
   <int value="350399958" label="ModuleScriptsImportMetaUrl:disabled"/>
   <int value="350635266" label="TabGroupsContinuationAndroid:enabled"/>
   <int value="351005753" label="enable-experimental-accessibility-autoclick"/>
@@ -47811,6 +47819,7 @@
   <int value="1000587036" label="OfflinePagesDescriptiveFailStatus:disabled"/>
   <int value="1000630368" label="lacros-stability"/>
   <int value="1000706989" label="AutomaticTabDiscarding:disabled"/>
+  <int value="1002247842" label="WebGPUService:enabled"/>
   <int value="1002585107" label="emphasize-titles-in-omnibox-dropdown"/>
   <int value="1003002105" label="MaterialDesignBookmarks:disabled"/>
   <int value="1003081697" label="DesktopPWAsUnifiedInstall:enabled"/>
@@ -76630,6 +76639,7 @@
   <int value="6" label="Tab Destroyed"/>
   <int value="7" label="Tab Hidden"/>
   <int value="8" label="Offline Available"/>
+  <int value="9" label="Accessibility Not Supported"/>
 </enum>
 
 <enum name="TabDiscardingEvents">
diff --git a/tools/metrics/histograms/histograms_xml/blink/histograms.xml b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
index 3a97183..3556054 100644
--- a/tools/metrics/histograms/histograms_xml/blink/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
@@ -2949,6 +2949,23 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.WebCodecs.{CodecApi}.FinalStatus" enum="MediaStatusCode"
+    expires_after="2022-02-01">
+  <owner>dalecurtis@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    The final media::StatusCode for each {CodecApi} instance. The final status
+    code is either StatusCode::kOk or the first fatal error encountered.
+    Recorded once upon destruction for each codec type.
+  </summary>
+  <token key="CodecApi">
+    <variant name="AudioDecoder"/>
+    <variant name="AudioEncoder"/>
+    <variant name="VideoDecoder"/>
+    <variant name="VideoEncoder"/>
+  </token>
+</histogram>
+
 <histogram name="Blink.WindowOpen.FromAdState" enum="WindowOpenFromAdState"
     expires_after="2019-08-30">
   <owner>yaoxia@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml b/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
index 7c04617..c774cab 100644
--- a/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
@@ -1275,8 +1275,9 @@
 </histogram>
 
 <histogram name="Enterprise.PolicyUserVerification"
-    enum="EnterprisePolicyUserVerification" expires_after="M92">
+    enum="EnterprisePolicyUserVerification" expires_after="M100">
   <owner>poromov@chromium.org</owner>
+  <owner>chromeos-commercial-stability@google.com</owner>
   <summary>Tracking the results of policy user verification.</summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index 43b63ffe..37b7bdc6 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -16517,7 +16517,15 @@
   <suffix name="UrlUws" label=""/>
   <affected-histogram name="SafeBrowsing.V4Database.Size"/>
   <affected-histogram
-      name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount"/>
+      name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount">
+    <obsolete>
+      Removed in M92 in favor of
+      SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount2, which has a larger
+      maximum count.
+    </obsolete>
+  </affected-histogram>
+  <affected-histogram
+      name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount2"/>
   <affected-histogram
       name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Result"/>
   <affected-histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Time"/>
@@ -19185,6 +19193,8 @@
   <suffix name="RemovedByNavigation" label="Removed by navigation"/>
   <suffix name="RemovedByPullToRefresh" label="Removed by pull to refresh"/>
   <suffix name="RemovedBySnackBar" label="Removed by SnackBar"/>
+  <suffix name="RemovedOnAccessibilityNotSupported"
+      label="Removed on accessibility not supported"/>
   <suffix name="RemovedOnLoad" label="Removed on load"/>
   <suffix name="RemovedOnOfflineAvailable"
       label="Removed on offline page available"/>
diff --git a/tools/metrics/histograms/histograms_xml/nearby/histograms.xml b/tools/metrics/histograms/histograms_xml/nearby/histograms.xml
index 7753cc8e..472a0c3e 100644
--- a/tools/metrics/histograms/histograms_xml/nearby/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/nearby/histograms.xml
@@ -342,20 +342,6 @@
   </token>
 </histogram>
 
-<histogram name="Nearby.Share.Connection.EstablishOutgoingConnection.Success"
-    enum="BooleanSuccess" expires_after="2021-08-19">
-  <owner>nohle@chromium.org</owner>
-  <owner>nearby-share-chromeos-eng@google.com</owner>
-  <summary>
-    Records whether an attempt to establish a connection to a receiving device
-    succeeded or failed; cancellations are ignored. Emitted after the user
-    selects a device to send a payload to but before the payload begins its
-    transfer. Because the sending device initiates the connection--via the
-    Nearby Connections library--this metric is only emitted for outgoing
-    transfers.
-  </summary>
-</histogram>
-
 <histogram name="Nearby.Share.Connection.EstablishOutgoingConnectionStatus"
     enum="NearbyShareFinalStatus" expires_after="2021-10-25">
   <owner>nohle@chromium.org</owner>
@@ -1105,44 +1091,6 @@
   </token>
 </histogram>
 
-<histogram name="Nearby.Share.Transfer.Success" enum="BooleanSuccess"
-    expires_after="2021-08-19">
-  <owner>nohle@chromium.org</owner>
-  <owner>nearby-share-chromeos-eng@google.com</owner>
-  <summary>
-    Records the success/failure of a Nearby Share transfer. Emitted when a
-    transfer completes successfully or definitively fails, but not for
-    indeterminate states like cancellation.
-  </summary>
-</histogram>
-
-<histogram
-    name="Nearby.Share.Transfer.Success.{Direction}.{ShareTargetType}.{ContactStatus}"
-    enum="BooleanSuccess" expires_after="2021-08-19">
-  <owner>nohle@chromium.org</owner>
-  <owner>nearby-share-chromeos-eng@google.com</owner>
-  <summary>
-    Records the success/failure of a Nearby Share {Direction} transfer with
-    {ShareTargetType} using {ContactStatus}. Emitted when a transfer completes
-    successfully or definitively fails, but not for indeterminate states like
-    cancellation.
-  </summary>
-  <token key="Direction">
-    <variant name="Receive" summary="incoming (receive)"/>
-    <variant name="Send" summary="outgoing (send)"/>
-  </token>
-  <token key="ShareTargetType">
-    <variant name="Laptop" summary="a laptop"/>
-    <variant name="Phone" summary="a phone"/>
-    <variant name="Tablet" summary="a tablet"/>
-    <variant name="UnknownDeviceType" summary="an unknown device type"/>
-  </token>
-  <token key="ContactStatus">
-    <variant name="Contact" summary="contact-based sharing"/>
-    <variant name="NonContact" summary="non-contact-based sharing"/>
-  </token>
-</histogram>
-
 <histogram
     name="Nearby.Share.Transfer.TotalSize{Direction}{ShareTargetType}{UpgradedMedium}{PayloadStatus}"
     units="KB" expires_after="2021-08-19">
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index e967745..7b7a4b11 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -2723,6 +2723,18 @@
   </summary>
 </histogram>
 
+<histogram name="ClientHints.AcceptCHFrame" enum="AcceptCHFrameRestart"
+    expires_after="2021-08-22">
+  <owner>aarontag@chromium.org</owner>
+  <owner>miketaylr@chromium.org</owner>
+  <summary>
+    Emitted on two related events: First, when an request that is eligable to be
+    restarted receives an ACCEPT_CH HTTP2/HTTP3 frame via a TLS connection with
+    the relevent ALPS setting. Next, if that frame causes that request to be
+    restarted.
+  </summary>
+</histogram>
+
 <histogram name="ClientHints.CriticalCHRestart" enum="CriticalCHRestart"
     expires_after="2021-10-25">
   <owner>aarontag@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
index 52d8e2e3..af3e0aa 100644
--- a/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/safe_browsing/histograms.xml
@@ -1586,6 +1586,10 @@
 
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount"
     units="entries" expires_after="2021-08-29">
+  <obsolete>
+    Removed in M92 and replaced with
+    SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount2
+  </obsolete>
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
@@ -1593,6 +1597,16 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.V4ProcessFullUpdate.AdditionsHashesCount2"
+    units="entries" expires_after="2021-08-29">
+  <owner>xinghuilu@chromium.org</owner>
+  <owner>chrome-safebrowsing-alerts@google.com</owner>
+  <summary>
+    Records the number of additions hashes in a SafeBrowsing list full update.
+    This was added in M92 to accomodate a larger maximum size.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.V4ProcessFullUpdate.ApplyUpdate.Result"
     enum="SafeBrowsingV4ApplyUpdateResult" expires_after="2021-09-05">
   <owner>vakh@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/software/histograms.xml b/tools/metrics/histograms/histograms_xml/software/histograms.xml
index 461151d..b700c5c4 100644
--- a/tools/metrics/histograms/histograms_xml/software/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/software/histograms.xml
@@ -22,106 +22,129 @@
 <histograms>
 
 <histogram name="SoftwareReporter.Cleaner.ChromePromptChannelError"
-    enum="SoftwareReporterChromePromptChannelError" expires_after="M90">
+    enum="SoftwareReporterChromePromptChannelError" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Keeps a tally of IPC related errors between chrome_cleaner and chrome. The
     first two bytes of the bucket represent the error category and the two last
-    bytes represent the actual error code.
+    bytes represent the actual error code. Warning: this histogram was expired
+    in M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.CleaningTime" units="ms"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time between sending the user's response to the Chrome Cleaner process
     and the Cleaner process terminating. This histogram is logged only for
-    successfully completed runs of the cleaner.
+    successfully completed runs of the cleaner. Warning: this histogram was
+    expired in M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.CleanupResult"
-    enum="SoftwareReporterCleanupResult" expires_after="M90">
+    enum="SoftwareReporterCleanupResult" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The final status of the Chrome Cleanup Tool run (e.g. succeeded, failed,
-    reboot required).
+    reboot required). Warning: this histogram was expired in M91 and extended in
+    M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.DownloadStatus"
-    enum="SoftwareReporterCleanerDownloadStatus" expires_after="M90">
+    enum="SoftwareReporterCleanerDownloadStatus" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     An indication if an attempt to download the Chrome Cleanup Tool succeeded or
-    the reason why it failed.
+    the reason why it failed. Warning: this histogram was expired in M91 and
+    extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.DownloadStatusErrorCode"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M90">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The HTTP response or net error code when trying to download the Chrome
-    Cleanup Tool.
+    Cleanup Tool. Warning: this histogram was expired in M91 and extended in
+    M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.HasRebooted" enum="BooleanRebooted"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether a reboot happened since the software reporter cleaner tool has
-    finished execution with a pre-reboot exit code.
+    finished execution with a pre-reboot exit code. Warning: this histogram was
+    expired in M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.RebootResponse" enum="Boolean"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates that the user accepted to initiate a reboot to complete a run of
     the Chrome Cleanup Tool. This is logged the user starts a reboot from the
     cleanup card in the Settings page. We can't track when the reboot is not
     initiated, because it can happen at any moment in the future (there is no
-    prompt blocking the user).
+    prompt blocking the user). Warning: this histogram was expired in M91 and
+    extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.RunningTime" units="ms"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
-  <summary>How long it took to run the software reporter cleaner tool.</summary>
+  <summary>
+    How long it took to run the software reporter cleaner tool. Warning: this
+    histogram was expired in M91 and extended in M92; data may be missing in
+    M91.
+  </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.ScanningTime" units="ms"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The time between launching the Chrome Cleaner process and the cleaner having
     scanned the user's machine and Chrome receiving an IPC call with the
-    results.
+    results. Warning: this histogram was expired in M91 and extended in M92;
+    data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.Cleaner.SettingsPageActiveOnRebootRequired"
     enum="SoftwareReporterCleanerSettingsPageActiveOnRebootRequired"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates if the Settings page was the active tab once a cleanup finished
-    with reboot required.
+    with reboot required. Warning: this histogram was expired in M91 and
+    extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
@@ -137,32 +160,40 @@
 </histogram>
 
 <histogram name="SoftwareReporter.CleanerLogsAcceptance" enum="BooleanAccepted"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether the user accepted to upload logs from Chrome Cleanup Tool. This is
     logged when the user accepts the Chrome prompt to start a cleanup either
     from the prompt dialog or from the cleanup card in the Settings page.
+    Warning: this histogram was expired in M91 and extended in M92; data may be
+    missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.CleanupCard" enum="Boolean"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether the Chrome Cleanup Tool card in the Settings page has been shown.
+    Warning: this histogram was expired in M91 and extended in M92; data may be
+    missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.CleanupStarted"
-    enum="SoftwareReporterCleanupStarted" expires_after="M90">
+    enum="SoftwareReporterCleanupStarted" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates where the user started a cleanup from (e.g. from the prompt dialog
-    or the settings page).
+    or the settings page). Warning: this histogram was expired in M91 and
+    extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
@@ -187,8 +218,9 @@
 </histogram>
 
 <histogram name="SoftwareReporter.IPCDisconnected"
-    enum="SoftwareReporterIPCDisconnected" expires_after="M90">
+    enum="SoftwareReporterIPCDisconnected" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates when the IPC with the Chrome Cleanup Tool process is disconnected
@@ -197,7 +229,8 @@
     terminated normally); failures are logged when the IPC is disconnected, but
     communication between Chrome and the cleaner process is still required (e.g.
     while Chrome is waiting for scanning results or when the cleaner is waiting
-    for the user's response from Chrome).
+    for the user's response from Chrome). Warning: this histogram was expired in
+    M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
@@ -209,33 +242,40 @@
 </histogram>
 
 <histogram name="SoftwareReporter.LogsUploadEnabled"
-    enum="SoftwareReporterLogsUploadEnabled" expires_after="M90">
+    enum="SoftwareReporterLogsUploadEnabled" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     If logs uploads are enabled in the Software Reporter or the reason why it is
-    disabled. Recorded before each run of the Software Reporter.
+    disabled. Recorded before each run of the Software Reporter. Warning: this
+    histogram was expired in M91 and extended in M92; data may be missing in
+    M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.LogsUploadResult"
-    enum="SoftwareReporterLogsUploadResult" expires_after="M90">
+    enum="SoftwareReporterLogsUploadResult" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The result of the most recent Software Reporter logs upload. Written by the
     Software Reporter in the registry and sent by Chrome after the reporter
-    finishes.
+    finishes. Warning: this histogram was expired in M91 and extended in M92;
+    data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.LogsUploadResultRegistryError"
-    enum="SoftwareReporterLogsUploadResultRegistryError" expires_after="M90">
+    enum="SoftwareReporterLogsUploadResultRegistryError" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Error encountered when reading the software reporter logs upload result from
-    the registry.
+    the registry. Warning: this histogram was expired in M91 and extended in
+    M92; data may be missing in M91.
   </summary>
 </histogram>
 
@@ -273,83 +313,102 @@
 </histogram>
 
 <histogram name="SoftwareReporter.NoPromptReason"
-    enum="SoftwareReporterNoPromptReason" expires_after="M90">
+    enum="SoftwareReporterNoPromptReason" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The reason why the user has not been prompted to run the Chrome Cleanup Tool
-    (e.g. no unwanted software found, user recently prompted).
+    (e.g. no unwanted software found, user recently prompted). Warning: this
+    histogram was expired in M91 and extended in M92; data may be missing in
+    M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.NumberOfFilesToDelete" units="counts"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
-    The number of files to be deleted by the Chrome Cleanup Tool.
+    The number of files to be deleted by the Chrome Cleanup Tool. Warning: this
+    histogram was expired in M91 and extended in M92; data may be missing in
+    M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.OnDemandUpdateRequired"
-    enum="BooleanRequired" expires_after="M90">
+    enum="BooleanRequired" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether an on-demand update of the Software Reporter component was needed
-    for a user-initiated cleanup.
+    for a user-initiated cleanup. Warning: this histogram was expired in M91 and
+    extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.OnDemandUpdateSucceeded"
-    enum="BooleanSuccess" expires_after="M90">
+    enum="BooleanSuccess" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether an on-demand update of the Software Reporter component succeeded,
-    when needed for a user-initiated cleanup.
+    when needed for a user-initiated cleanup. Warning: this histogram was
+    expired in M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.PostCleanupSettingsReset" units="counts"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The number of profiles whose settings will be reset once a successful
-    cleanup completes.
+    cleanup completes. Warning: this histogram was expired in M91 and extended
+    in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.PromptDialogResponse"
-    enum="SoftwareReporterPromptDialogResponse" expires_after="M90">
+    enum="SoftwareReporterPromptDialogResponse" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     The user response on the modal Chrome Cleanup Tool prompt dialog (e.g.
-    accepted, cancelled).
+    accepted, cancelled). Warning: this histogram was expired in M91 and
+    extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.PromptShownWithType"
-    enum="SoftwareReporterPromptShownWithType" expires_after="M90">
+    enum="SoftwareReporterPromptShownWithType" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether the user has been prompted to run the Chrome Cleanup Tool and which
     type of prompt has been shown.
 
-    This is logged once a prompt is presented to the user.
+    This is logged once a prompt is presented to the user. Warning: this
+    histogram was expired in M91 and extended in M92; data may be missing in
+    M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.ReporterSequenceType"
-    enum="SoftwareReporterSequenceType" expires_after="M90">
+    enum="SoftwareReporterSequenceType" expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Indicates the type of a reporter sequence run on the user system. Logged
-    when the sequence is scheduled to start.
+    when the sequence is scheduled to start. Warning: this histogram was expired
+    in M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
@@ -364,23 +423,27 @@
 </histogram>
 
 <histogram name="SoftwareReporter.ScannerLogsAcceptance" enum="BooleanAccepted"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Whether the user accepted to upload scanner logs from Chrome Cleanup Tool.
     This is logged when the user starts a scan for the Settings page and is only
-    logged for user-initiated cleanups.
+    logged for user-initiated cleanups. Warning: this histogram was expired in
+    M91 and extended in M92; data may be missing in M91.
   </summary>
 </histogram>
 
 <histogram name="SoftwareReporter.TaggedProfileForResetting" enum="Boolean"
-    expires_after="M90">
+    expires_after="M95">
   <owner>drubery@chromium.org</owner>
+  <owner>bdea@chromium.org</owner>
   <owner>chrome-safebrowsing-alerts@google.com</owner>
   <summary>
     Registers when a profile is tagged for settings reset before a run of the
-    Chrome Cleanup Tool.
+    Chrome Cleanup Tool. Warning: this histogram was expired in M91 and extended
+    in M92; data may be missing in M91.
   </summary>
 </histogram>
 
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index f13019c..e97f3b4 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "4b81d55c6b8006ceb165784d1105c8ac1deba9ef",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/424d8898471d8c779a0378ff6fb9117e1de3095e/trace_processor_shell.exe"
+            "hash": "01c343f9df8a5ef5ec58b96c1793a9301b83f55d",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/a6c4cea7f7ff399ed4f69ec8ca30153ff60264fa/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "c63fa49d9ea416ed8b61fac1a67db9863aa26d42",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/4d5d1738db08c3bd5e381f9a9cdc307532eb3831/trace_processor_shell"
+            "hash": "fd71c24d8e85705c4a5abf1ac3c6e6405e9874c8",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/a6c4cea7f7ff399ed4f69ec8ca30153ff60264fa/trace_processor_shell"
         },
         "linux": {
-            "hash": "9fe3d92d9ef490078bd8467d383c4bea516fd75b",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/4d5d1738db08c3bd5e381f9a9cdc307532eb3831/trace_processor_shell"
+            "hash": "69e538301b42ba89cbe14e061ce227422140a206",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/a6c4cea7f7ff399ed4f69ec8ca30153ff60264fa/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/ax_relative_bounds.h b/ui/accessibility/ax_relative_bounds.h
index de780d5..6c09bc60 100644
--- a/ui/accessibility/ax_relative_bounds.h
+++ b/ui/accessibility/ax_relative_bounds.h
@@ -7,8 +7,8 @@
 
 #include <stdint.h>
 
+#include <iosfwd>
 #include <memory>
-#include <ostream>
 
 #include "ui/accessibility/ax_base_export.h"
 #include "ui/accessibility/ax_enums.mojom-forward.h"
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils.cc b/ui/accessibility/platform/inspect/ax_inspect_utils.cc
index 4a190d1..401cc29 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils.cc
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils.cc
@@ -12,13 +12,19 @@
 
 constexpr char kConstValuePrefix[] = "_const_";
 constexpr char kSetKeyPrefixDictAttr[] = "_setkey_";
+constexpr char kOrderedKeyPrefixDictAttr[] = "_index_";
 
-std::string AX_EXPORT AXMakeConst(const std::string& value) {
+std::string AXMakeConst(const std::string& value) {
   return kConstValuePrefix + value;
 }
 
-std::string AX_EXPORT AXMakeSetKey(const std::string& value) {
-  return kSetKeyPrefixDictAttr + value;
+std::string AXMakeSetKey(const std::string& key_name) {
+  return kSetKeyPrefixDictAttr + key_name;
+}
+
+std::string AXMakeOrderedKey(const std::string& key_name, int position) {
+  // Works for single-diget orders.
+  return kOrderedKeyPrefixDictAttr + base::NumberToString(position) + key_name;
 }
 
 std::string AXFormatValue(const base::Value& value) {
@@ -60,17 +66,26 @@
   // dictionary is exposed as {value1, ..., valueN}.
   if (value.is_dict()) {
     const std::string setkey_prefix(kSetKeyPrefixDictAttr);
+    const std::string orderedkey_prefix(kOrderedKeyPrefixDictAttr);
+
     std::string output;
     for (const auto& item : value.DictItems()) {
       if (!output.empty()) {
         output += ", ";
       }
-      // Some of the dictionary's keys should not be appended to the output, so
-      // that the dictionary can also be used as a set. Such keys start with
-      // the _setkey_ prefix.
       if (base::StartsWith(item.first, setkey_prefix,
                            base::CompareCase::SENSITIVE)) {
+        // Some of the dictionary's keys should not be appended to the output,
+        // so that the dictionary can also be used as a set. Such keys start
+        // with the _setkey_ prefix.
         output += AXFormatValue(item.second);
+      } else if (base::StartsWith(item.first, orderedkey_prefix,
+                                  base::CompareCase::SENSITIVE)) {
+        // Process ordered dictionaries. Remove order number from keys before
+        // formatting.
+        std::string key = item.first;
+        key.erase(0, orderedkey_prefix.length() + 1);
+        output += key + ": " + AXFormatValue(item.second);
       } else {
         output += item.first + ": " + AXFormatValue(item.second);
       }
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils.h b/ui/accessibility/platform/inspect/ax_inspect_utils.h
index 162f96a..bd1b817 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils.h
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils.h
@@ -24,11 +24,19 @@
  * Constructs a key for a formattable set represented by dictionary. It adds
  * the _setkey_ prefix to a string key.  Refers to FormatValue.
  */
-std::string AX_EXPORT AXMakeSetKey(const std::string& value);
+std::string AX_EXPORT AXMakeSetKey(const std::string& key_name);
 
 /**
- * Formats a `base::Value` into a human readable string that can be used for
- * automated testing and tooling.
+ * Constructs an ordered key for a formattable dictionary by appending position
+ * number to a string key for sorting. It makes the keys to be traversed
+ * according to their position when the dictionary is formatted. Refers to
+ * FormatValue.
+ */
+std::string AX_EXPORT AXMakeOrderedKey(const std::string& key_name,
+                                       int position);
+
+/**
+ * Formats a value.
  */
 std::string AX_EXPORT AXFormatValue(const base::Value& value);
 
diff --git a/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc b/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc
index 8ce568f..2e1d571 100644
--- a/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc
+++ b/ui/accessibility/platform/inspect/ax_inspect_utils_unittest.cc
@@ -55,4 +55,11 @@
   EXPECT_EQ(AXFormatValue(set), std::string("{:1, 2, down}"));
 }
 
+TEST(AXInspectUtilsTest, FormatOrderedDict) {
+  base::Value ordered_dict(base::Value::Type::DICTIONARY);
+  ordered_dict.SetIntPath(AXMakeOrderedKey("w", 0), 40);
+  ordered_dict.SetIntPath(AXMakeOrderedKey("h", 1), 30);
+  EXPECT_EQ(AXFormatValue(ordered_dict), std::string("{w: 40, h: 30}"));
+}
+
 }  // namespace ui
diff --git a/ui/android/java/res/values-night/colors.xml b/ui/android/java/res/values-night/colors.xml
index 54c5c19..034b3990 100644
--- a/ui/android/java/res/values-night/colors.xml
+++ b/ui/android/java/res/values-night/colors.xml
@@ -84,4 +84,5 @@
     <color name="menu_chip_background_color">@color/menu_chip_background_color_light</color>
     <color name="menu_chip_background_color_disabled">@color/menu_chip_background_color_disabled_light</color>
     <color name="iph_highlight_blue">@color/iph_highlight_blue_light</color>
+    <color name="rating_star_yellow">@color/rating_star_yellow_light</color>
 </resources>
diff --git a/ui/android/java/res/values/color_palette.xml b/ui/android/java/res/values/color_palette.xml
index fb7228f..142e434 100644
--- a/ui/android/java/res/values/color_palette.xml
+++ b/ui/android/java/res/values/color_palette.xml
@@ -54,6 +54,9 @@
     <color name="white_alpha_50">#80FFFFFF</color>
     <color name="white_alpha_70">#B3FFFFFF</color>
 
+    <color name="modern_yellow_300">#FDD663</color>
+    <color name="modern_yellow_500">#FBBC04</color>
+
     <!-- Old color palette -->
     <color name="google_red_300">#F28B82</color>
     <color name="google_red_600">#D93025</color>
diff --git a/ui/android/java/res/values/semantic_colors_adaptive.xml b/ui/android/java/res/values/semantic_colors_adaptive.xml
index a34339b..0454f7f 100644
--- a/ui/android/java/res/values/semantic_colors_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_adaptive.xml
@@ -121,4 +121,5 @@
     <color name="menu_chip_background_color">@color/menu_chip_background_color_dark</color>
     <color name="menu_chip_background_color_disabled">@color/menu_chip_background_color_disabled_dark</color>
     <color name="iph_highlight_blue">@color/iph_highlight_blue_dark</color>
+    <color name="rating_star_yellow">@color/rating_star_yellow_dark</color>
 </resources>
diff --git a/ui/android/java/res/values/semantic_colors_non_adaptive.xml b/ui/android/java/res/values/semantic_colors_non_adaptive.xml
index ec4b394..13557293 100644
--- a/ui/android/java/res/values/semantic_colors_non_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_non_adaptive.xml
@@ -139,4 +139,8 @@
     <!-- Settings -->
     <color name="preference_highlighted_bg_color_dark">@color/modern_blue_600_alpha_12</color>
     <color name="preference_highlighted_bg_color_light">@color/white_alpha_24</color>
+
+    <!-- Rating star colors -->
+    <color name="rating_star_yellow_dark">@color/modern_yellow_500</color>
+    <color name="rating_star_yellow_light">@color/modern_yellow_300</color>
 </resources>
diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h
index 82d35fe..11ce126 100644
--- a/ui/base/clipboard/clipboard.h
+++ b/ui/base/clipboard/clipboard.h
@@ -58,6 +58,8 @@
                                                    uint32_t fragment_end)>;
   using ReadSvgCallback = base::OnceCallback<void(std::u16string result)>;
   using ReadRTFCallback = base::OnceCallback<void(std::string result)>;
+  using ReadPngCallback =
+      base::OnceCallback<void(const std::vector<uint8_t>& result)>;
   using ReadImageCallback = base::OnceCallback<void(const SkBitmap& result)>;
   using ReadCustomDataCallback =
       base::OnceCallback<void(std::u16string result)>;
@@ -180,6 +182,11 @@
                        const DataTransferEndpoint* data_dst,
                        ReadRTFCallback callback) const;
 
+  // Reads a png from the clipboard, if available.
+  virtual void ReadPng(ClipboardBuffer buffer,
+                       const DataTransferEndpoint* data_dst,
+                       ReadPngCallback callback) const = 0;
+
   // Reads an image from the clipboard, if available.
   virtual void ReadImage(ClipboardBuffer buffer,
                          const DataTransferEndpoint* data_dst,
diff --git a/ui/base/clipboard/clipboard_android.cc b/ui/base/clipboard/clipboard_android.cc
index dd32059..f8391c2 100644
--- a/ui/base/clipboard/clipboard_android.cc
+++ b/ui/base/clipboard/clipboard_android.cc
@@ -18,6 +18,7 @@
 #include "base/containers/contains.h"
 #include "base/lazy_instance.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/task/post_task.h"
@@ -63,6 +64,7 @@
 
 constexpr char kPngExtension[] = ".png";
 
+using ReadPngCallback = ClipboardAndroid::ReadPngCallback;
 using ReadImageCallback = ClipboardAndroid::ReadImageCallback;
 
 // Fetching image data from Java.
@@ -571,6 +573,16 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardAndroid::ReadPng(ClipboardBuffer buffer,
+                               const DataTransferEndpoint* data_dst,
+                               ReadPngCallback callback) const {
+  DCHECK(CalledOnValidThread());
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardAndroid::ReadImage(ClipboardBuffer buffer,
                                  const DataTransferEndpoint* data_dst,
                                  ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/clipboard_android.h b/ui/base/clipboard/clipboard_android.h
index c8e23a49..870ee34 100644
--- a/ui/base/clipboard/clipboard_android.h
+++ b/ui/base/clipboard/clipboard_android.h
@@ -88,6 +88,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/clipboard/clipboard_mac.h b/ui/base/clipboard/clipboard_mac.h
index e517301..f45d165 100644
--- a/ui/base/clipboard/clipboard_mac.h
+++ b/ui/base/clipboard/clipboard_mac.h
@@ -62,6 +62,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/clipboard/clipboard_mac.mm b/ui/base/clipboard/clipboard_mac.mm
index 5dedaf7..76b08ef 100644
--- a/ui/base/clipboard/clipboard_mac.mm
+++ b/ui/base/clipboard/clipboard_mac.mm
@@ -17,6 +17,7 @@
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/no_destructor.h"
+#include "base/notreached.h"
 #include "base/stl_util.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -279,6 +280,15 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardMac::ReadPng(ClipboardBuffer buffer,
+                           const DataTransferEndpoint* data_dst,
+                           ReadPngCallback callback) const {
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardMac::ReadImage(ClipboardBuffer buffer,
                              const DataTransferEndpoint* data_dst,
                              ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/clipboard_non_backed.cc b/ui/base/clipboard/clipboard_non_backed.cc
index c6009c5..10a78445 100644
--- a/ui/base/clipboard/clipboard_non_backed.cc
+++ b/ui/base/clipboard/clipboard_non_backed.cc
@@ -604,6 +604,14 @@
 #endif
 }
 
+void ClipboardNonBacked::ReadPng(ClipboardBuffer buffer,
+                                 const DataTransferEndpoint* data_dst,
+                                 ReadPngCallback callback) const {
+  DCHECK(CalledOnValidThread());
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
 void ClipboardNonBacked::ReadImage(ClipboardBuffer buffer,
                                    const DataTransferEndpoint* data_dst,
                                    ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/clipboard_non_backed.h b/ui/base/clipboard/clipboard_non_backed.h
index 6c6e3f16..17403e75 100644
--- a/ui/base/clipboard/clipboard_non_backed.h
+++ b/ui/base/clipboard/clipboard_non_backed.h
@@ -81,6 +81,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/clipboard/clipboard_ozone.cc b/ui/base/clipboard/clipboard_ozone.cc
index 36480a5..a306891 100644
--- a/ui/base/clipboard/clipboard_ozone.cc
+++ b/ui/base/clipboard/clipboard_ozone.cc
@@ -15,6 +15,7 @@
 #include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
@@ -464,6 +465,14 @@
 }
 
 // TODO(crbug.com/1103194): |data_dst| should be supported.
+void ClipboardOzone::ReadPng(ClipboardBuffer buffer,
+                             const DataTransferEndpoint* data_dst,
+                             ReadPngCallback callback) const {
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
+// TODO(crbug.com/1103194): |data_dst| should be supported.
 void ClipboardOzone::ReadImage(ClipboardBuffer buffer,
                                const DataTransferEndpoint* data_dst,
                                ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/clipboard_ozone.h b/ui/base/clipboard/clipboard_ozone.h
index fda6985..730cf5d 100644
--- a/ui/base/clipboard/clipboard_ozone.h
+++ b/ui/base/clipboard/clipboard_ozone.h
@@ -55,6 +55,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc
index 619b5be..d1f1092 100644
--- a/ui/base/clipboard/clipboard_win.cc
+++ b/ui/base/clipboard/clipboard_win.cc
@@ -502,6 +502,15 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardWin::ReadPng(ClipboardBuffer buffer,
+                           const DataTransferEndpoint* data_dst,
+                           ReadPngCallback callback) const {
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardWin::ReadImage(ClipboardBuffer buffer,
                              const DataTransferEndpoint* data_dst,
                              ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/clipboard_win.h b/ui/base/clipboard/clipboard_win.h
index f4931b8..33b6abc 100644
--- a/ui/base/clipboard/clipboard_win.h
+++ b/ui/base/clipboard/clipboard_win.h
@@ -61,6 +61,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/clipboard/clipboard_x11.cc b/ui/base/clipboard/clipboard_x11.cc
index 3c83fe8..2807d6e8 100644
--- a/ui/base/clipboard/clipboard_x11.cc
+++ b/ui/base/clipboard/clipboard_x11.cc
@@ -14,6 +14,7 @@
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/notreached.h"
 #include "base/strings/utf_string_conversions.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/base/clipboard/clipboard_buffer.h"
@@ -207,6 +208,15 @@
 
 // |data_dst| is not used. It's only passed to be consistent with other
 // platforms.
+void ClipboardX11::ReadPng(ClipboardBuffer buffer,
+                           const DataTransferEndpoint* data_dst,
+                           ReadPngCallback callback) const {
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
+// |data_dst| is not used. It's only passed to be consistent with other
+// platforms.
 void ClipboardX11::ReadImage(ClipboardBuffer buffer,
                              const DataTransferEndpoint* data_dst,
                              ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/clipboard_x11.h b/ui/base/clipboard/clipboard_x11.h
index 286c15e..88a5ed96 100644
--- a/ui/base/clipboard/clipboard_x11.h
+++ b/ui/base/clipboard/clipboard_x11.h
@@ -55,6 +55,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/clipboard/test/test_clipboard.cc b/ui/base/clipboard/test/test_clipboard.cc
index d904a145..444c186df 100644
--- a/ui/base/clipboard/test/test_clipboard.cc
+++ b/ui/base/clipboard/test/test_clipboard.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 #include "base/memory/ptr_util.h"
+#include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -229,6 +230,13 @@
     *result = it->second;
 }
 
+void TestClipboard::ReadPng(ClipboardBuffer buffer,
+                            const DataTransferEndpoint* data_dst,
+                            ReadPngCallback callback) const {
+  // TODO(crbug.com/1201018): Implement this.
+  NOTIMPLEMENTED();
+}
+
 void TestClipboard::ReadImage(ClipboardBuffer buffer,
                               const DataTransferEndpoint* data_dst,
                               ReadImageCallback callback) const {
diff --git a/ui/base/clipboard/test/test_clipboard.h b/ui/base/clipboard/test/test_clipboard.h
index 4e205cf6..a4749c2 100644
--- a/ui/base/clipboard/test/test_clipboard.h
+++ b/ui/base/clipboard/test/test_clipboard.h
@@ -66,6 +66,9 @@
   void ReadRTF(ClipboardBuffer buffer,
                const DataTransferEndpoint* data_dst,
                std::string* result) const override;
+  void ReadPng(ClipboardBuffer buffer,
+               const DataTransferEndpoint* data_dst,
+               ReadPngCallback callback) const override;
   void ReadImage(ClipboardBuffer buffer,
                  const DataTransferEndpoint* data_dst,
                  ReadImageCallback callback) const override;
diff --git a/ui/base/x/x11_gl_egl_utility.cc b/ui/base/x/x11_gl_egl_utility.cc
index bf92de82..ae54288c 100644
--- a/ui/base/x/x11_gl_egl_utility.cc
+++ b/ui/base/x/x11_gl_egl_utility.cc
@@ -21,6 +21,10 @@
 #define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348F
 #endif
 
+#ifndef EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE
+#define EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE 0x3487
+#endif
+
 #ifndef EGL_ANGLE_platform_angle
 #define EGL_ANGLE_platform_angle 1
 #define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348F
@@ -35,9 +39,12 @@
 
 void GetPlatformExtraDisplayAttribs(EGLenum platform_type,
                                     std::vector<EGLAttrib>* attributes) {
-  // ANGLE_NULL doesn't use the visual, and may run without X11 where we can't
-  // get it anyway.
-  if (platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) {
+  // ANGLE_NULL and SwiftShader backends don't use the visual,
+  // and may run without X11 where we can't get it anyway.
+  if ((platform_type != EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE) &&
+      (std::find(attributes->begin(), attributes->end(),
+                 EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE) ==
+       attributes->end())) {
     x11::VisualId visual_id;
     XVisualManager::GetInstance()->ChooseVisualForWindow(
         true, &visual_id, nullptr, nullptr, nullptr);
diff --git a/ui/events/ozone/evdev/event_device_info.cc b/ui/events/ozone/evdev/event_device_info.cc
index e41dbdef..69eaa50 100644
--- a/ui/events/ozone/evdev/event_device_info.cc
+++ b/ui/events/ozone/evdev/event_device_info.cc
@@ -577,6 +577,7 @@
       {0x18d1, 0x503c},  // Google, Masterball PID (krane)
       {0x18d1, 0x503d},  // Google, Magnemite PID (kodama)
       {0x18d1, 0x5044},  // Google, Moonball PID (kakadu)
+      {0x18d1, 0x504c},  // Google, Zed PID (coachz)
       {0x18d1, 0x5050},  // Google, Don PID (katsu)
       {0x1fd2, 0x8103},  // LG, Internal TouchScreen PID
   };
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h
index 464b747..fe3b1c5c 100644
--- a/ui/gfx/color_space.h
+++ b/ui/gfx/color_space.h
@@ -7,7 +7,7 @@
 
 #include <stdint.h>
 
-#include <ostream>
+#include <iosfwd>
 #include <string>
 #include <vector>
 
diff --git a/ui/gl/gl_implementation.cc b/ui/gl/gl_implementation.cc
index b12af22..0204c54 100644
--- a/ui/gl/gl_implementation.cc
+++ b/ui/gl/gl_implementation.cc
@@ -182,7 +182,8 @@
 }
 
 GLImplementationParts GetSoftwareGLForTestsImplementation() {
-#if defined(OS_WIN)
+#if defined(OS_WIN) || \
+    (defined(OS_LINUX) && !defined(OS_FUCHSIA) && !defined(USE_OZONE))
   return GetSoftwareGLImplementation();
 #else
   return GetLegacySoftwareGLImplementation();
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index e62dfd9..5df8ae4 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -296,7 +296,7 @@
     }
   }
 
-  // Notify about mouse changes.
+  // Notify about keyboard changes.
   GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
 
   // TODO(msisov): wl_touch doesn't expose the display it belongs to. Thus, it's
@@ -372,19 +372,25 @@
     connection->compositor_ = wl::Bind<wl_compositor>(
         registry, name, std::min(version, kMaxCompositorVersion));
     connection->compositor_version_ = version;
-    if (!connection->compositor_)
+    if (!connection->compositor_) {
       LOG(ERROR) << "Failed to bind to wl_compositor global";
+      return;
+    }
   } else if (!connection->subcompositor_ &&
              strcmp(interface, "wl_subcompositor") == 0) {
     connection->subcompositor_ = wl::Bind<wl_subcompositor>(registry, name, 1);
-    if (!connection->subcompositor_)
+    if (!connection->subcompositor_) {
       LOG(ERROR) << "Failed to bind to wl_subcompositor global";
+      return;
+    }
   } else if (!connection->shm_ && strcmp(interface, "wl_shm") == 0) {
     wl::Object<wl_shm> shm =
         wl::Bind<wl_shm>(registry, name, std::min(version, kMaxShmVersion));
     connection->shm_ = std::make_unique<WaylandShm>(shm.release(), connection);
-    if (!connection->shm_)
+    if (!connection->shm_) {
       LOG(ERROR) << "Failed to bind to wl_shm global";
+      return;
+    }
   } else if (!connection->seat_ && strcmp(interface, "wl_seat") == 0) {
     connection->seat_ =
         wl::Bind<wl_seat>(registry, name, std::min(version, kMaxSeatVersion));
@@ -423,7 +429,7 @@
       return;
     }
 
-    wl::Object<wl_output> output = wl::Bind<wl_output>(registry, name, version);
+    auto output = wl::Bind<wl_output>(registry, name, version);
     if (!output) {
       LOG(ERROR) << "Failed to bind to wl_output global";
       return;
@@ -437,9 +443,8 @@
                                                           output.release());
   } else if (!connection->data_device_manager_ &&
              strcmp(interface, "wl_data_device_manager") == 0) {
-    wl::Object<wl_data_device_manager> data_device_manager =
-        wl::Bind<wl_data_device_manager>(
-            registry, name, std::min(version, kMaxDeviceManagerVersion));
+    auto data_device_manager = wl::Bind<wl_data_device_manager>(
+        registry, name, std::min(version, kMaxDeviceManagerVersion));
     if (!data_device_manager) {
       LOG(ERROR) << "Failed to bind to wl_data_device_manager global";
       return;
@@ -450,25 +455,35 @@
     connection->CreateDataObjectsIfReady();
   } else if (!connection->gtk_primary_selection_device_manager_ &&
              strcmp(interface, "gtk_primary_selection_device_manager") == 0) {
-    wl::Object<::gtk_primary_selection_device_manager> manager =
-        wl::Bind<::gtk_primary_selection_device_manager>(
-            registry, name,
-            std::min(version, kMaxGtkPrimarySelectionDeviceManagerVersion));
+    auto manager = wl::Bind<::gtk_primary_selection_device_manager>(
+        registry, name,
+        std::min(version, kMaxGtkPrimarySelectionDeviceManagerVersion));
+    if (!manager) {
+      LOG(ERROR) << "Failed to bind gtk_primary_selection_device_manager";
+      return;
+    }
     connection->gtk_primary_selection_device_manager_ =
         std::make_unique<GtkPrimarySelectionDeviceManager>(manager.release(),
                                                            connection);
   } else if (!connection->gtk_shell1_ && strcmp(interface, "gtk_shell1") == 0 &&
              version >= kMinGtkShell1Version) {
-    wl::Object<::gtk_shell1> gtk_shell1 = wl::Bind<::gtk_shell1>(
+    auto gtk_shell1 = wl::Bind<::gtk_shell1>(
         registry, name, std::min(version, kMaxGtkShell1Version));
+    if (!gtk_shell1) {
+      LOG(ERROR) << "Failed to bind gtk_shell1";
+      return;
+    }
     connection->gtk_shell1_ = std::make_unique<GtkShell1>(gtk_shell1.release());
   } else if (!connection->zwp_primary_selection_device_manager_ &&
              strcmp(interface, "zwp_primary_selection_device_manager_v1") ==
                  0) {
-    wl::Object<zwp_primary_selection_device_manager_v1> manager =
-        wl::Bind<zwp_primary_selection_device_manager_v1>(
-            registry, name,
-            std::min(version, kMaxGtkPrimarySelectionDeviceManagerVersion));
+    auto manager = wl::Bind<zwp_primary_selection_device_manager_v1>(
+        registry, name,
+        std::min(version, kMaxGtkPrimarySelectionDeviceManagerVersion));
+    if (!manager) {
+      LOG(ERROR) << "Failed to bind zwp_primary_selection_device_manager_v1";
+      return;
+    }
     connection->zwp_primary_selection_device_manager_ =
         std::make_unique<ZwpPrimarySelectionDeviceManager>(manager.release(),
                                                            connection);
@@ -478,21 +493,36 @@
     connection->linux_explicit_synchronization_ =
         wl::Bind<zwp_linux_explicit_synchronization_v1>(
             registry, name, std::min(version, kMaxExplicitSyncVersion));
+    if (!connection->linux_explicit_synchronization_) {
+      LOG(ERROR) << "Failed to bind zwp_linux_explicit_synchronization_v1";
+      return;
+    }
   } else if (!connection->zwp_dmabuf_ &&
              (strcmp(interface, "zwp_linux_dmabuf_v1") == 0)) {
-    wl::Object<zwp_linux_dmabuf_v1> zwp_linux_dmabuf =
-        wl::Bind<zwp_linux_dmabuf_v1>(
-            registry, name, std::min(version, kMaxLinuxDmabufVersion));
+    auto zwp_linux_dmabuf = wl::Bind<zwp_linux_dmabuf_v1>(
+        registry, name, std::min(version, kMaxLinuxDmabufVersion));
+    if (!zwp_linux_dmabuf) {
+      LOG(ERROR) << "Failed to bind zwp_linux_dmabuf_v1";
+      return;
+    }
     connection->zwp_dmabuf_ = std::make_unique<WaylandZwpLinuxDmabuf>(
         zwp_linux_dmabuf.release(), connection);
   } else if (!connection->presentation_ &&
              (strcmp(interface, "wp_presentation") == 0)) {
     connection->presentation_ = wl::Bind<wp_presentation>(
         registry, name, std::min(version, kMaxWpPresentationVersion));
+    if (!connection->presentation_) {
+      LOG(ERROR) << "Failed to bind wp_presentation";
+      return;
+    }
   } else if (!connection->viewporter_ &&
              (strcmp(interface, "wp_viewporter") == 0)) {
     connection->viewporter_ = wl::Bind<wp_viewporter>(
         registry, name, std::min(version, kMaxWpViewporterVersion));
+    if (!connection->viewporter_) {
+      LOG(ERROR) << "Failed to bind wp_viewporter";
+      return;
+    }
   } else if (!connection->zcr_cursor_shapes_ &&
              strcmp(interface, "zcr_cursor_shapes_v1") == 0) {
     auto zcr_cursor_shapes = wl::Bind<zcr_cursor_shapes_v1>(
@@ -546,6 +576,10 @@
     connection->xdg_decoration_manager_ =
         wl::Bind<struct zxdg_decoration_manager_v1>(
             registry, name, std::min(version, kMaxXdgDecorationVersion));
+    if (!connection->xdg_decoration_manager_) {
+      LOG(ERROR) << "Failed to bind zxdg_decoration_manager_v1";
+      return;
+    }
   } else if (!connection->extended_drag_v1_ &&
              strcmp(interface, "zcr_extended_drag_v1") == 0) {
     connection->extended_drag_v1_ = wl::Bind<zcr_extended_drag_v1>(
diff --git a/ui/touch_selection/DEPS b/ui/touch_selection/DEPS
index 5b83034..b6852ea 100644
--- a/ui/touch_selection/DEPS
+++ b/ui/touch_selection/DEPS
@@ -2,6 +2,7 @@
   "+ui/aura",
   "+ui/aura_extra",
   "+ui/base",
+  "+ui/compositor",
   "+ui/events",
   "+ui/gfx",
   "+ui/resources"
diff --git a/ui/touch_selection/touch_handle_drawable_aura.cc b/ui/touch_selection/touch_handle_drawable_aura.cc
index fb2e8f8..f98d664 100644
--- a/ui/touch_selection/touch_handle_drawable_aura.cc
+++ b/ui/touch_selection/touch_handle_drawable_aura.cc
@@ -10,6 +10,7 @@
 #include "ui/base/cursor/cursor.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/resource/resource_bundle.h"
+#include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/rect_conversions.h"
diff --git a/ui/views/controls/views_text_services_context_menu_base.cc b/ui/views/controls/views_text_services_context_menu_base.cc
index 62fac34..ba7f725a 100644
--- a/ui/views/controls/views_text_services_context_menu_base.cc
+++ b/ui/views/controls/views_text_services_context_menu_base.cc
@@ -54,11 +54,8 @@
     *accelerator = ui::Accelerator(ui::VKEY_SPACE,
                                    ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN);
     return true;
-#elif BUILDFLAG(IS_CHROMEOS_ASH)
-    *accelerator = ui::Accelerator(ui::VKEY_SPACE,
-                                   ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
-    return true;
 #else
+    // TODO(crbug.com/887660): Add accelerator key for Chrome OS.
     return false;
 #endif
   }
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
index 4f9172e..56a8ac45 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc
@@ -622,7 +622,10 @@
   if (IsFullscreen() == fullscreen)
     return;
 
+  auto weak_ptr = GetWeakPtr();
   platform_window()->ToggleFullscreen();
+  if (!weak_ptr)
+    return;
 
   // The state must change synchronously to let media react on fullscreen
   // changes.
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
index 86a6610..4dcb1abd 100644
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
@@ -466,7 +466,10 @@
 }
 
 void DesktopWindowTreeHostWin::SetFullscreen(bool fullscreen) {
+  auto weak_ptr = GetWeakPtr();
   message_handler_->SetFullscreen(fullscreen);
+  if (!weak_ptr)
+    return;
   // TODO(sky): workaround for ScopedFullscreenVisibility showing window
   // directly. Instead of this should listen for visibility changes and then
   // update window.
diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc
index 4952fbd..21845e0 100644
--- a/ui/views/widget/widget.cc
+++ b/ui/views/widget/widget.cc
@@ -760,7 +760,10 @@
   if (IsFullscreen() == fullscreen)
     return;
 
+  auto weak_ptr = GetWeakPtr();
   native_widget_->SetFullscreen(fullscreen);
+  if (!weak_ptr)
+    return;
 
   if (non_client_view_)
     non_client_view_->InvalidateLayout();
diff --git a/ui/views/widget/widget_unittest.cc b/ui/views/widget/widget_unittest.cc
index c11baa36..c8a4a0622 100644
--- a/ui/views/widget/widget_unittest.cc
+++ b/ui/views/widget/widget_unittest.cc
@@ -3289,6 +3289,41 @@
             IsNativeWindowVisible(top_level_widget->GetNativeWindow()));
 }
 
+// Used to delete the widget when the supplied bounds changes.
+class DestroyingWidgetBoundsObserver : public WidgetObserver {
+ public:
+  explicit DestroyingWidgetBoundsObserver(std::unique_ptr<Widget> widget)
+      : widget_(std::move(widget)) {
+    widget_->AddObserver(this);
+  }
+
+  // There are no assertions here as not all platforms call
+  // OnWidgetBoundsChanged() when going fullscreen.
+  ~DestroyingWidgetBoundsObserver() override = default;
+
+  // WidgetObserver:
+  void OnWidgetBoundsChanged(Widget* widget,
+                             const gfx::Rect& new_bounds) override {
+    widget_->RemoveObserver(this);
+    widget_.reset();
+  }
+
+ private:
+  std::unique_ptr<Widget> widget_;
+};
+
+// Deletes a Widget when the bounds change as part of toggling fullscreen.
+// This is a regression test for https://crbug.com/1197436 .
+TEST_F(DesktopWidgetTest, DeleteInSetFullscreen) {
+  std::unique_ptr<Widget> widget = std::make_unique<Widget>();
+  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW);
+  params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  widget->Init(std::move(params));
+  Widget* w = widget.get();
+  DestroyingWidgetBoundsObserver destroyer(std::move(widget));
+  w->SetFullscreen(true);
+}
+
 namespace {
 
 class FullscreenAwareFrame : public views::NonClientFrameView {
diff --git a/ui/views/win/fullscreen_handler.cc b/ui/views/win/fullscreen_handler.cc
index 8791362..708d28f 100644
--- a/ui/views/win/fullscreen_handler.cc
+++ b/ui/views/win/fullscreen_handler.cc
@@ -70,6 +70,7 @@
 
   fullscreen_ = fullscreen;
 
+  auto ref = weak_ptr_factory_.GetWeakPtr();
   if (fullscreen_) {
     // Set new window style and size.
     SetWindowLong(hwnd_, GWL_STYLE,
@@ -102,6 +103,8 @@
                  new_rect.height(),
                  SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
   }
+  if (!ref)
+    return;
 
   MarkFullscreen(fullscreen);
 }
diff --git a/ui/views/win/fullscreen_handler.h b/ui/views/win/fullscreen_handler.h
index fe17c7f..c76ef18 100644
--- a/ui/views/win/fullscreen_handler.h
+++ b/ui/views/win/fullscreen_handler.h
@@ -11,6 +11,7 @@
 #include <map>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 
 namespace gfx {
 class Rect;
@@ -54,6 +55,8 @@
   // Used to mark a window as fullscreen.
   Microsoft::WRL::ComPtr<ITaskbarList2> task_bar_list_;
 
+  base::WeakPtrFactory<FullscreenHandler> weak_ptr_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(FullscreenHandler);
 };
 
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index 73970de..91dd04b1 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -899,7 +899,10 @@
 
 void HWNDMessageHandler::SetFullscreen(bool fullscreen) {
   background_fullscreen_hack_ = false;
+  auto ref = msg_handler_weak_factory_.GetWeakPtr();
   fullscreen_handler()->SetFullscreen(fullscreen);
+  if (!ref)
+    return;
 
   // Add the fullscreen window to the fullscreen window map which is used to
   // handle window activations.
@@ -1403,8 +1406,10 @@
   // Ignore size changes due to fullscreen windows losing activation.
   if (background_fullscreen_hack_ && !sent_window_size_changing_)
     return;
-  gfx::Size s = GetClientAreaBounds().size();
-  delegate_->HandleClientSizeChanged(s);
+  auto ref = msg_handler_weak_factory_.GetWeakPtr();
+  delegate_->HandleClientSizeChanged(GetClientAreaBounds().size());
+  if (!ref)
+    return;
 
   current_window_size_message_++;
   sent_window_size_changing_ = false;
@@ -2936,8 +2941,11 @@
 void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) {
   TRACE_EVENT0("ui", "HWNDMessageHandler::OnWindowPosChanged");
 
+  base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
   if (DidClientAreaSizeChange(window_pos))
     ClientAreaSizeChanged();
+  if (!ref)
+    return;
   if (window_pos->flags & SWP_FRAMECHANGED)
     SetDwmFrameExtension(DwmFrameState::kOn);
   if (window_pos->flags & SWP_SHOWWINDOW) {
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java
index 8e3e2c08..5990416 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/AutofillTest.java
@@ -19,7 +19,6 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.test.util.CallbackHelper;
-import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.net.test.util.TestWebServer;
@@ -79,7 +78,6 @@
      */
     @Test
     @SmallTest
-    @DisabledTest(message = "https://crbug.com/1201631")
     public void testBasicAutofill() throws Throwable {
         final String data = "<html><head></head><body><form action='a.html' name='formname'>"
                 + "<label>User Name:</label>"