diff --git a/.clang-tidy b/.clang-tidy
index 3477d88..c7515c8 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,5 +1,4 @@
 ---
----
   Checks:          '-*,
                     bugprone-string-integer-assignment,
                     bugprone-unused-raii,
@@ -25,6 +24,8 @@
                     modernize-use-transparent-functors,
                     readability-redundant-member-init'
   CheckOptions:
+    - key:          modernize-use-default-member-init.UseAssignment
+      value:        1
     # This relaxes modernize-use-emplace in some cases; we might want to make it
     # more aggressive in the future. See discussion on
     # https://groups.google.com/a/chromium.org/g/cxx/c/noMMTNYiM0w .
diff --git a/DEPS b/DEPS
index aaa31148..c5f60ca 100644
--- a/DEPS
+++ b/DEPS
@@ -206,11 +206,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': 'be8004d2fb6c8575d7da7c580811639654a9d255',
+  'skia_revision': '260029435baaefe42548c0be0fa408e6f8dee42a',
   # 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': '992b13db26c29db225f812aa1dfc637940e99cb8',
+  'v8_revision': '93ed90e664430291b53e3ee3f26d4dbc0e3c7804',
   # 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.
@@ -218,7 +218,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': '07ea804e620132517b6af0ef92fe85ea737d0c27',
+  'angle_revision': 'c69ef8bf34ca3d7c1e8a4e187280e5b06e21e605',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -226,7 +226,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '2f56cfe7fc9eb21eab44ed0f9bcfa3538afe1305',
+  'pdfium_revision': '1105ef875a9375fc4e7030161817412f1db283c2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -269,7 +269,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': '5d73338a50dcee6d9f6a4e92afcf41e281715779',
+  'catapult_revision': '91f276e903b3495d5fd4ad74e289e0893787d9a1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -277,7 +277,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': 'b055d47b4a3a91dc67e13f5be947299278e29496',
+  'devtools_frontend_revision': '69e28b68cab8dc34cdbd8c6ec3503546a57875df',
   # 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.
@@ -912,7 +912,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4761cf50d032aa5d1e0c51f47d20fa43380c01fd',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '4c45ca0dd90a390224b1031d068900f3e4c3f5c5',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1497,7 +1497,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '37d41eea047799ccca2f09c941870c26ee3ffc0a',
+    Var('webrtc_git') + '/src.git' + '@' + '1f7eab68c0ce267b935f9ea3544d3105d7fd19dc',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1569,7 +1569,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@0c7a073b194f0e80b5dacda0f5d8dd1c8b322d8f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8216043b1fc4e7c0160ed64a18c273a1874faba3',
     'condition': 'checkout_src_internal',
   },
 
@@ -1588,7 +1588,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'vBjX1UUxt2Xw11p044tIxezzzvpljMVlif9zIUpKokcC',
+        'version': 'wHmgVwhdw29vHqkyUXbbeSCuBeOGOHCSL0B8dd3273MC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 47bace3..9c2f3d5b 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -1366,6 +1366,7 @@
     'build/android/test_wrapper/logdog_wrapper.pydeps',
     'build/lacros/lacros_resource_sizes.pydeps',
     'build/protoc_java.pydeps',
+    'chrome/android/monochrome/scripts/monochrome_python_tests.pydeps',
     'chrome/test/chromedriver/log_replay/client_replay_unittest.pydeps',
     'chrome/test/chromedriver/test/run_py_tests.pydeps',
     'components/cronet/tools/generate_javadoc.pydeps',
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 8b23b50d..e017dd49 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -300,8 +300,6 @@
     "window_animation_types.h",
     "window_backdrop.cc",
     "window_backdrop.h",
-    "window_pin_type.cc",
-    "window_pin_type.h",
     "window_properties.cc",
     "window_properties.h",
   ]
diff --git a/ash/public/cpp/window_properties.cc b/ash/public/cpp/window_properties.cc
index 2c3531a..6889994 100644
--- a/ash/public/cpp/window_properties.cc
+++ b/ash/public/cpp/window_properties.cc
@@ -6,7 +6,6 @@
 
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/window_backdrop.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
@@ -14,7 +13,6 @@
 #include "ui/aura/window.h"
 #include "ui/wm/core/window_properties.h"
 
-DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(ASH_PUBLIC_EXPORT, ash::WindowPinType)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(ASH_PUBLIC_EXPORT,
                                        chromeos::WindowStateType)
 DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(ASH_PUBLIC_EXPORT, ash::WindowBackdrop*)
@@ -33,7 +31,6 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kExcludeInMruKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInOverviewKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInShelfKey, false)
-DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideShelfWhenFullscreenKey, true)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsDeferredTabDraggingTargetWindowKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsDraggingTabsKey, false)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideInDeskMiniViewKey, false)
@@ -71,9 +68,6 @@
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(base::string16,
                                    kWindowOverviewTitleKey,
                                    nullptr)
-DEFINE_UI_CLASS_PROPERTY_KEY(WindowPinType,
-                             kWindowPinTypeKey,
-                             WindowPinType::kNone)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowPositionManagedTypeKey, false)
 
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kWindowPipTypeKey, false)
diff --git a/ash/public/cpp/window_properties.h b/ash/public/cpp/window_properties.h
index 319edcb9..5012133b 100644
--- a/ash/public/cpp/window_properties.h
+++ b/ash/public/cpp/window_properties.h
@@ -31,8 +31,6 @@
 
 namespace ash {
 
-enum class WindowPinType;
-
 class WindowBackdrop;
 
 // Shell-specific window property keys for use by ash and its clients.
@@ -80,11 +78,6 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
     kHideInShelfKey;
 
-// Whether the shelf should be hidden when this window is put into fullscreen.
-// Exposed because some windows want to explicitly opt-out of this.
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<bool>* const
-    kHideShelfWhenFullscreenKey;
-
 // If true, the window is the target window for the tab-dragged window. The key
 // is used by overview to show a highlight indication to indicate which overview
 // window the dragged tabs will merge into when the user releases the pointer.
@@ -206,14 +199,6 @@
 ASH_PUBLIC_EXPORT extern const aura::WindowProperty<base::string16*>* const
     kWindowOverviewTitleKey;
 
-// A property key to store ash::WindowPinType for a window.
-// When setting this property to PINNED or TRUSTED_PINNED, the window manager
-// will try to fullscreen the window and pin it on the top of the screen. If the
-// window manager failed to do it, the property will be restored to NONE. When
-// setting this property to NONE, the window manager will restore the window.
-ASH_PUBLIC_EXPORT extern const aura::WindowProperty<WindowPinType>* const
-    kWindowPinTypeKey;
-
 // A property key to indicate whether ash should perform auto management of
 // window positions; when you open a second browser, ash will move the two to
 // minimize overlap.
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 3f6fc60..eaa21d4 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -34,7 +34,6 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/test/shell_test_api.h"
 #include "ash/public/cpp/wallpaper_controller_observer.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
@@ -81,6 +80,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "components/prefs/pref_service.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/window_parenting_client.h"
@@ -109,6 +109,8 @@
 namespace ash {
 namespace {
 
+using ::chromeos::kHideShelfWhenFullscreenKey;
+
 void PressHomeButton() {
   Shell::Get()->app_list_controller()->ToggleAppList(
       display::Screen::GetScreen()->GetPrimaryDisplay().id(),
diff --git a/ash/system/phonehub/phone_hub_notification_controller_unittest.cc b/ash/system/phonehub/phone_hub_notification_controller_unittest.cc
index b9506f58..4f208835 100644
--- a/ash/system/phonehub/phone_hub_notification_controller_unittest.cc
+++ b/ash/system/phonehub/phone_hub_notification_controller_unittest.cc
@@ -136,6 +136,11 @@
   notification_manager_.RemoveNotificationsInternal(base::flat_set<int64_t>(
       {kPhoneHubNotificationId1, kPhoneHubNotificationId2}));
   EXPECT_FALSE(message_center_->NotificationCount());
+
+  // Attempt removing the same notifications again and expect nothing to happen.
+  notification_manager_.RemoveNotificationsInternal(base::flat_set<int64_t>(
+      {kPhoneHubNotificationId1, kPhoneHubNotificationId2}));
+  EXPECT_FALSE(message_center_->NotificationCount());
 }
 
 TEST_F(PhoneHubNotificationControllerTest, CloseByUser) {
diff --git a/ash/wm/fullscreen_window_finder_unittest.cc b/ash/wm/fullscreen_window_finder_unittest.cc
index 20eb0a3..6fcd99f 100644
--- a/ash/wm/fullscreen_window_finder_unittest.cc
+++ b/ash/wm/fullscreen_window_finder_unittest.cc
@@ -6,10 +6,10 @@
 
 #include <memory>
 
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window.h"
 #include "ui/base/ui_base_types.h"
@@ -17,6 +17,9 @@
 
 namespace ash {
 
+using ::chromeos::kWindowPinTypeKey;
+using ::chromeos::WindowPinType;
+
 class FullscreenWindowFinderTest : public AshTestBase {
  public:
   FullscreenWindowFinderTest() = default;
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 6da6761..1f4c7d6 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_animation_types.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
@@ -30,6 +29,7 @@
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "chromeos/ui/base/window_pin_type.h"
 #include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "ui/aura/client/aura_constants.h"
@@ -52,7 +52,10 @@
 namespace ash {
 namespace {
 
+using ::chromeos::kHideShelfWhenFullscreenKey;
 using ::chromeos::kImmersiveIsActive;
+using ::chromeos::kWindowPinTypeKey;
+using ::chromeos::WindowPinType;
 using ::chromeos::WindowStateType;
 
 bool IsTabletModeEnabled() {
@@ -115,13 +118,13 @@
   return WM_EVENT_NORMAL;
 }
 
-WMEventType WMEventTypeFromWindowPinType(ash::WindowPinType type) {
+WMEventType WMEventTypeFromWindowPinType(chromeos::WindowPinType type) {
   switch (type) {
-    case ash::WindowPinType::kNone:
+    case chromeos::WindowPinType::kNone:
       return WM_EVENT_NORMAL;
-    case ash::WindowPinType::kPinned:
+    case chromeos::WindowPinType::kPinned:
       return WM_EVENT_PIN;
-    case ash::WindowPinType::kTrustedPinned:
+    case chromeos::WindowPinType::kTrustedPinned:
       return WM_EVENT_TRUSTED_PIN;
   }
   NOTREACHED() << "No WMEvent defined for the window pin type:" << type;
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index 8e8075bc..a1df5057 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -21,6 +21,7 @@
 #include "ui/gfx/animation/tween.h"
 
 namespace chromeos {
+enum class WindowPinType;
 enum class WindowStateType;
 }
 
@@ -32,7 +33,6 @@
 class ClientControlledState;
 class LockWindowState;
 class TabletModeWindowState;
-enum class WindowPinType;
 class WindowState;
 class WindowStateDelegate;
 class WindowStateObserver;
@@ -395,7 +395,7 @@
   ui::WindowShowState GetShowState() const;
 
   // Return the window's current pin type.
-  WindowPinType GetPinType() const;
+  chromeos::WindowPinType GetPinType() const;
 
   // Sets the window's bounds in screen coordinates.
   void SetBoundsInScreen(const gfx::Rect& bounds_in_screen);
diff --git a/base/allocator/partition_allocator/partition_alloc.cc b/base/allocator/partition_allocator/partition_alloc.cc
index 5fce1b5..3a06ecc 100644
--- a/base/allocator/partition_allocator/partition_alloc.cc
+++ b/base/allocator/partition_allocator/partition_alloc.cc
@@ -631,7 +631,7 @@
 #endif
       }
 
-      slot_span->freelist_head = head;
+      slot_span->SetFreelistHead(head);
       if (back)
         back->next = internal::PartitionFreelistEntry::Encode(nullptr);
 
diff --git a/base/allocator/partition_allocator/partition_alloc.h b/base/allocator/partition_allocator/partition_alloc.h
index 90c948f2..1a905b46 100644
--- a/base/allocator/partition_allocator/partition_alloc.h
+++ b/base/allocator/partition_allocator/partition_alloc.h
@@ -423,7 +423,7 @@
     internal::PartitionFreelistEntry* new_head =
         internal::EncodedPartitionFreelistEntry::Decode(
             slot_span->freelist_head->next);
-    slot_span->freelist_head = new_head;
+    slot_span->SetFreelistHead(new_head);
     slot_span->num_allocated_slots++;
 
     PA_DCHECK(slot_span->bucket == bucket);
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 1b8e704..a5c2a45 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -617,40 +617,29 @@
   EXPECT_TRUE(bucket->empty_slot_spans_head);
 }
 
-// TODO(bartekn): Clean up page/slot-span confusion below here.
-
 // Test a large series of allocations that cross more than one underlying
-// 64KB super page allocation.
+// super page.
 TEST_F(PartitionAllocTest, MultiPageAllocs) {
-  // Need to consider the number of slot span per 1 Super Page.
-  // The number of pages needed should be calculated by using the number.
-  // Because if the number of left partition pages is smaller than
-  // the number of pages per slot span, a new super page will be allocated.
   size_t num_pages_per_slot_span = GetNumPagesPerSlotSpan(kTestAllocSize);
-  // 1 SuperPage has 2 guard PartitionPages.
-  size_t num_slot_span_needed =
+  // 1 super page has 2 guard partition pages.
+  size_t num_slot_spans_needed =
       (NumPartitionPagesPerSuperPage() - NumPartitionPagesPerTagBitmap() - 2) /
       num_pages_per_slot_span;
 
-  // This is guaranteed to cross a super page boundary because the first
-  // partition page "slot" will be taken up by a guard page.
-  size_t num_pages_needed = num_slot_span_needed * num_pages_per_slot_span;
+  // We need one more slot span in order to cross super page boundary.
+  ++num_slot_spans_needed;
 
-  // The super page should begin and end in a guard so we one less page in
-  // order to allocate a single page in the new super page.
-  ++num_pages_needed;
-
-  EXPECT_GT(num_pages_needed, 1u);
-  auto pages = std::make_unique<SlotSpan*[]>(num_pages_needed);
+  EXPECT_GT(num_slot_spans_needed, 1u);
+  auto slot_spans = std::make_unique<SlotSpan*[]>(num_slot_spans_needed);
   uintptr_t first_super_page_base = 0;
   size_t i;
-  for (i = 0; i < num_pages_needed; ++i) {
-    pages[i] = GetFullSlotSpan(kTestAllocSize);
-    void* storage_ptr = SlotSpan::ToPointer(pages[i]);
+  for (i = 0; i < num_slot_spans_needed; ++i) {
+    slot_spans[i] = GetFullSlotSpan(kTestAllocSize);
+    void* storage_ptr = SlotSpan::ToPointer(slot_spans[i]);
     if (!i)
       first_super_page_base =
           reinterpret_cast<uintptr_t>(storage_ptr) & kSuperPageBaseMask;
-    if (i == num_pages_needed - 1) {
+    if (i == num_slot_spans_needed - 1) {
       uintptr_t second_super_page_base =
           reinterpret_cast<uintptr_t>(storage_ptr) & kSuperPageBaseMask;
       uintptr_t second_super_page_offset =
@@ -661,8 +650,8 @@
                 second_super_page_offset);
     }
   }
-  for (i = 0; i < num_pages_needed; ++i)
-    FreeFullSlotSpan(allocator.root(), pages[i]);
+  for (i = 0; i < num_slot_spans_needed; ++i)
+    FreeFullSlotSpan(allocator.root(), slot_spans[i]);
 }
 
 // Test the generic allocation functions that can handle arbitrary sizes and
@@ -807,7 +796,7 @@
   allocator.root()->Free(ptr4);
 
 #if DCHECK_IS_ON()
-  // |PartitionPage::Free| must poison the slot's contents with |kFreedByte|.
+  // |SlotSpanMetadata::Free| must poison the slot's contents with |kFreedByte|.
   EXPECT_EQ(kFreedByte,
             *(reinterpret_cast<unsigned char*>(new_ptr) + (size - 1)));
 #endif
@@ -1033,7 +1022,7 @@
   allocator.root()->Free(ptr2);
 }
 
-// Tests the handing out of freelists for partial pages.
+// Tests the handing out of freelists for partial slot spans.
 TEST_F(PartitionAllocTest, PartialPageFreelists) {
   size_t big_size = SystemPageSize() - kExtraAllocSize;
   size_t bucket_index = SizeToIndex(big_size + kExtraAllocSize);
@@ -1082,7 +1071,7 @@
       SlotSpan::FromPointer(PartitionPointerAdjustSubtract(true, ptr5));
   EXPECT_EQ(1, slot_span2->num_allocated_slots);
 
-  // Churn things a little whilst there's a partial page freelist.
+  // Churn things a little whilst there's a partial slot span freelist.
   allocator.root()->Free(ptr);
   ptr = allocator.root()->Alloc(big_size, type_name);
   void* ptr6 = allocator.root()->Alloc(big_size, type_name);
@@ -1112,9 +1101,11 @@
   total_slots =
       (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) /
       (medium_size + kExtraAllocSize);
-  size_t first_page_slots = SystemPageSize() / (medium_size + kExtraAllocSize);
-  EXPECT_EQ(2u, first_page_slots);
-  EXPECT_EQ(total_slots - first_page_slots, slot_span->num_unprovisioned_slots);
+  size_t first_slot_span_slots =
+      SystemPageSize() / (medium_size + kExtraAllocSize);
+  EXPECT_EQ(2u, first_slot_span_slots);
+  EXPECT_EQ(total_slots - first_slot_span_slots,
+            slot_span->num_unprovisioned_slots);
 
   allocator.root()->Free(ptr);
 
@@ -1130,8 +1121,9 @@
   total_slots =
       (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) /
       (small_size + kExtraAllocSize);
-  first_page_slots = SystemPageSize() / (small_size + kExtraAllocSize);
-  EXPECT_EQ(total_slots - first_page_slots, slot_span->num_unprovisioned_slots);
+  first_slot_span_slots = SystemPageSize() / (small_size + kExtraAllocSize);
+  EXPECT_EQ(total_slots - first_slot_span_slots,
+            slot_span->num_unprovisioned_slots);
 
   allocator.root()->Free(ptr);
   EXPECT_TRUE(slot_span->freelist_head);
@@ -1149,10 +1141,11 @@
   total_slots =
       (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) /
       (very_small_size + kExtraAllocSize);
-  first_page_slots =
+  first_slot_span_slots =
       (SystemPageSize() + very_small_size + kExtraAllocSize - 1) /
       (very_small_size + kExtraAllocSize);
-  EXPECT_EQ(total_slots - first_page_slots, slot_span->num_unprovisioned_slots);
+  EXPECT_EQ(total_slots - first_slot_span_slots,
+            slot_span->num_unprovisioned_slots);
 
   allocator.root()->Free(ptr);
   EXPECT_TRUE(slot_span->freelist_head);
@@ -1174,25 +1167,25 @@
   allocator.root()->Free(ptr);
 
   // And then make sure than exactly the page size only faults one page.
-  size_t pageSize = SystemPageSize() - kExtraAllocSize;
-  ptr = allocator.root()->Alloc(pageSize, type_name);
+  size_t page_size = SystemPageSize() - kExtraAllocSize;
+  ptr = allocator.root()->Alloc(page_size, type_name);
   EXPECT_TRUE(ptr);
   slot_span = SlotSpan::FromPointer(PartitionPointerAdjustSubtract(true, ptr));
   EXPECT_EQ(1, slot_span->num_allocated_slots);
   EXPECT_TRUE(slot_span->freelist_head);
   total_slots =
       (slot_span->bucket->num_system_pages_per_slot_span * SystemPageSize()) /
-      (pageSize + kExtraAllocSize);
+      (page_size + kExtraAllocSize);
   EXPECT_EQ(total_slots - 2, slot_span->num_unprovisioned_slots);
   allocator.root()->Free(ptr);
 }
 
 // Test some of the fragmentation-resistant properties of the allocator.
-TEST_F(PartitionAllocTest, PageRefilling) {
+TEST_F(PartitionAllocTest, SlotSpanRefilling) {
   PartitionRoot<ThreadSafe>::Bucket* bucket =
       &allocator.root()->buckets[test_bucket_index_];
 
-  // Grab two full pages and a non-full page.
+  // Grab two full slot spans and a non-full slot span.
   auto* slot_span1 = GetFullSlotSpan(kTestAllocSize);
   auto* slot_span2 = GetFullSlotSpan(kTestAllocSize);
   void* ptr = allocator.root()->Alloc(kTestAllocSize, type_name);
@@ -1213,7 +1206,7 @@
   allocator.root()->Free(ptr2);
 
   // If we perform two allocations from the same bucket now, we expect to
-  // refill both the nearly full pages.
+  // refill both the nearly full slot spans.
   (void)allocator.root()->Alloc(kTestAllocSize, type_name);
   (void)allocator.root()->Alloc(kTestAllocSize, type_name);
   EXPECT_EQ(1, slot_span->num_allocated_slots);
@@ -1337,7 +1330,8 @@
   }
 }
 
-// Tests that pages in the free page cache do get freed as appropriate.
+// Tests that slot spans in the free slot span cache do get freed as
+// appropriate.
 TEST_F(PartitionAllocTest, FreeCache) {
   EXPECT_EQ(0U, allocator.root()->total_size_of_committed_pages_for_testing());
 
@@ -1362,7 +1356,7 @@
 
   CycleFreeCache(kTestAllocSize);
 
-  // Flushing the cache should have really freed the unused page.
+  // Flushing the cache should have really freed the unused slot spans.
   EXPECT_FALSE(slot_span->freelist_head);
   EXPECT_EQ(-1, slot_span->empty_cache_index);
   EXPECT_EQ(0, slot_span->num_allocated_slots);
@@ -1374,13 +1368,13 @@
   EXPECT_EQ(expected_size,
             allocator.root()->total_size_of_committed_pages_for_testing());
 
-  // Check that an allocation works ok whilst in this state (a free'd page
-  // as the active pages head).
+  // Check that an allocation works ok whilst in this state (a free'd slot span
+  // as the active slot spans head).
   ptr = allocator.root()->Alloc(big_size, type_name);
   EXPECT_FALSE(bucket->empty_slot_spans_head);
   allocator.root()->Free(ptr);
 
-  // Also check that a page that is bouncing immediately between empty and
+  // Also check that a slot span that is bouncing immediately between empty and
   // used does not get freed.
   for (size_t i = 0; i < kMaxFreeableSpans * 2; ++i) {
     ptr = allocator.root()->Alloc(big_size, type_name);
@@ -1392,8 +1386,8 @@
             allocator.root()->total_size_of_committed_pages_for_testing());
 }
 
-// Tests for a bug we had with losing references to free pages.
-TEST_F(PartitionAllocTest, LostFreePagesBug) {
+// Tests for a bug we had with losing references to free slot spans.
+TEST_F(PartitionAllocTest, LostFreeSlotSpansBug) {
   size_t size = PartitionPageSize() - kExtraAllocSize;
 
   void* ptr = allocator.root()->Alloc(size, type_name);
@@ -1448,8 +1442,8 @@
   CycleFreeCache(kTestAllocSize);
 
   // We're now set up to trigger a historical bug by scanning over the active
-  // pages list. The current code gets into a different state, but we'll keep
-  // the test as being an interesting corner case.
+  // slot spans list. The current code gets into a different state, but we'll
+  // keep the test as being an interesting corner case.
   ptr = allocator.root()->Alloc(size, type_name);
   EXPECT_TRUE(ptr);
   allocator.root()->Free(ptr);
@@ -1558,8 +1552,8 @@
   allocator.root()->Free(ptr);
   allocator.root()->Free(ptr2);
   // This is not an immediate double-free so our immediate detection won't
-  // fire. However, it does take the "refcount" of the partition page to -1,
-  // which is illegal and should be trapped.
+  // fire. However, it does take the "refcount" of the to -1, which is illegal
+  // and should be trapped.
   EXPECT_DEATH(allocator.root()->Free(ptr), "");
 }
 
@@ -1682,7 +1676,7 @@
     }
   }
 
-  // This test checks for correct empty page list accounting.
+  // This test checks for correct empty slot span list accounting.
   {
     size_t size = PartitionPageSize() - kExtraAllocSize;
     void* ptr1 = allocator.root()->Alloc(size, type_name);
@@ -1935,22 +1929,22 @@
   EXPECT_EQ(slot_span3, bucket->active_slot_spans_head);
 
   // Free up the 2nd slot in each slot span.
-  // This leaves the active list containing 3 pages, each with 1 used and 1
-  // free slot. The active page will be the one containing ptr1.
+  // This leaves the active list containing 3 slot spans, each with 1 used and 1
+  // free slot. The active slot span will be the one containing ptr1.
   allocator.root()->Free(ptr6);
   allocator.root()->Free(ptr4);
   allocator.root()->Free(ptr2);
   EXPECT_EQ(slot_span1, bucket->active_slot_spans_head);
 
-  // Empty the middle page in the active list.
+  // Empty the middle slot span in the active list.
   allocator.root()->Free(ptr3);
   EXPECT_EQ(slot_span1, bucket->active_slot_spans_head);
 
-  // Empty the first page in the active list -- also the current page.
+  // Empty the first slot span in the active list -- also the current slot span.
   allocator.root()->Free(ptr1);
 
-  // A good choice here is to re-fill the third page since the first two are
-  // empty. We used to fail that.
+  // A good choice here is to re-fill the third slot span since the first two
+  // are empty. We used to fail that.
   void* ptr7 = allocator.root()->Alloc(size, type_name);
   EXPECT_EQ(ptr6, ptr7);
   EXPECT_EQ(slot_span3, bucket->active_slot_spans_head);
@@ -2322,7 +2316,7 @@
 
 TEST_F(PartitionAllocTest, Bug_897585) {
   // Need sizes big enough to be direct mapped and a delta small enough to
-  // allow re-use of the page when cookied. These numbers fall out of the
+  // allow re-use of the slot span when cookied. These numbers fall out of the
   // test case in the indicated bug.
   size_t kInitialSize = 983040;
   size_t kDesiredSize = 983100;
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index c749f47..069ebae 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -92,8 +92,8 @@
   PA_DCHECK(!page->slot_span_metadata.num_unprovisioned_slots);
   PA_DCHECK(!page->slot_span_metadata.empty_cache_index);
   page->slot_span_metadata.bucket = &metadata->bucket;
-  page->slot_span_metadata.freelist_head =
-      reinterpret_cast<PartitionFreelistEntry*>(slot);
+  page->slot_span_metadata.SetFreelistHead(
+      reinterpret_cast<PartitionFreelistEntry*>(slot));
 
   auto* next_entry = reinterpret_cast<PartitionFreelistEntry*>(slot);
   next_entry->next = PartitionFreelistEntry::Encode(nullptr);
@@ -116,7 +116,7 @@
   map_extent->prev_extent = nullptr;
   root->direct_map_list = map_extent;
 
-  return reinterpret_cast<SlotSpanMetadata<thread_safe>*>(page);
+  return &page->slot_span_metadata;
 }
 
 }  // namespace
@@ -471,7 +471,7 @@
   if (LIKELY(num_new_freelist_entries)) {
     char* freelist_pointer = first_freelist_pointer;
     auto* entry = reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
-    slot_span->freelist_head = entry;
+    slot_span->SetFreelistHead(entry);
     while (--num_new_freelist_entries) {
       freelist_pointer += size;
       auto* next_entry =
@@ -481,7 +481,7 @@
     }
     entry->next = PartitionFreelistEntry::Encode(nullptr);
   } else {
-    slot_span->freelist_head = nullptr;
+    slot_span->SetFreelistHead(nullptr);
   }
   return return_object;
 }
@@ -673,7 +673,7 @@
     PartitionFreelistEntry* entry = new_slot_span->freelist_head;
     PartitionFreelistEntry* new_head =
         EncodedPartitionFreelistEntry::Decode(entry->next);
-    new_slot_span->freelist_head = new_head;
+    new_slot_span->SetFreelistHead(new_head);
     new_slot_span->num_allocated_slots++;
     return entry;
   }
diff --git a/base/allocator/partition_allocator/partition_page.cc b/base/allocator/partition_allocator/partition_page.cc
index e6eb3ef..f87fa30f 100644
--- a/base/allocator/partition_allocator/partition_page.cc
+++ b/base/allocator/partition_allocator/partition_page.cc
@@ -175,7 +175,7 @@
   // Pulling this trick enables us to use a singly-linked list for all
   // cases, which is critical in keeping the slot span metadata structure down
   // to 32 bytes in size.
-  freelist_head = nullptr;
+  SetFreelistHead(nullptr);
   num_unprovisioned_slots = 0;
   PA_DCHECK(is_decommitted());
   PA_DCHECK(bucket);
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
index da6b03f..ad66db6 100644
--- a/base/allocator/partition_allocator/partition_page.h
+++ b/base/allocator/partition_allocator/partition_page.h
@@ -119,6 +119,8 @@
   ALWAYS_INLINE void SetRawSize(size_t raw_size);
   ALWAYS_INLINE size_t GetRawSize() const;
 
+  ALWAYS_INLINE void SetFreelistHead(PartitionFreelistEntry* new_head);
+
   // Returns size of the region used within a slot. The used region comprises
   // of actual allocated data, extras and possibly empty space.
   ALWAYS_INLINE size_t GetUtilizedSlotSize() const {
@@ -256,8 +258,8 @@
 template <bool thread_safe>
 ALWAYS_INLINE SlotSpanMetadata<thread_safe>*
 SlotSpanMetadata<thread_safe>::FromPointerNoAlignmentCheck(void* ptr) {
-  return reinterpret_cast<SlotSpanMetadata*>(
-      PartitionPage<thread_safe>::FromPointerNoAlignmentCheck(ptr));
+  return &PartitionPage<thread_safe>::FromPointerNoAlignmentCheck(ptr)
+              ->slot_span_metadata;
 }
 
 // See the comment for |FromPointer|.
@@ -361,6 +363,15 @@
 }
 
 template <bool thread_safe>
+ALWAYS_INLINE void SlotSpanMetadata<thread_safe>::SetFreelistHead(
+    PartitionFreelistEntry* new_head) {
+  PA_DCHECK(!new_head ||
+            (reinterpret_cast<uintptr_t>(this) & kSuperPageBaseMask) ==
+                (reinterpret_cast<uintptr_t>(new_head) & kSuperPageBaseMask));
+  freelist_head = new_head;
+}
+
+template <bool thread_safe>
 ALWAYS_INLINE DeferredUnmap SlotSpanMetadata<thread_safe>::Free(void* ptr) {
 #if DCHECK_IS_ON()
   auto* root = PartitionRoot<thread_safe>::FromSlotSpan(this);
@@ -375,7 +386,7 @@
             ptr != EncodedPartitionFreelistEntry::Decode(freelist_head->next));
   auto* entry = static_cast<internal::PartitionFreelistEntry*>(ptr);
   entry->next = internal::PartitionFreelistEntry::Encode(freelist_head);
-  freelist_head = entry;
+  SetFreelistHead(entry);
   --num_allocated_slots;
   if (UNLIKELY(num_allocated_slots <= 0)) {
     return FreeSlowPath();
diff --git a/base/callback_list.h b/base/callback_list.h
index 78bea71..1778541 100644
--- a/base/callback_list.h
+++ b/base/callback_list.h
@@ -5,7 +5,6 @@
 #ifndef BASE_CALLBACK_LIST_H_
 #define BASE_CALLBACK_LIST_H_
 
-#include <algorithm>
 #include <list>
 #include <memory>
 #include <utility>
@@ -17,6 +16,7 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/memory/weak_ptr.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 
 // OVERVIEW:
@@ -163,8 +163,8 @@
   // Returns whether the list of registered callbacks is empty (from an external
   // perspective -- meaning no remaining callbacks are live).
   bool empty() const {
-    return std::all_of(callbacks_.cbegin(), callbacks_.cend(),
-                       [](const auto& callback) { return callback.is_null(); });
+    return ranges::all_of(
+        callbacks_, [](const auto& callback) { return callback.is_null(); });
   }
 
   // Calls all registered callbacks that are not canceled beforehand. If any
diff --git a/base/command_line.cc b/base/command_line.cc
index 440484b..6f0a3471 100644
--- a/base/command_line.cc
+++ b/base/command_line.cc
@@ -4,13 +4,13 @@
 
 #include "base/command_line.h"
 
-#include <algorithm>
 #include <ostream>
 
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_split.h"
@@ -411,8 +411,7 @@
   // Gather all arguments after the last switch (may include kSwitchTerminator).
   StringVector args(argv_.begin() + begin_args_, argv_.end());
   // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
-  auto switch_terminator =
-      std::find(args.begin(), args.end(), kSwitchTerminator);
+  auto switch_terminator = ranges::find(args, kSwitchTerminator);
   if (switch_terminator != args.end())
     args.erase(switch_terminator);
   return args;
diff --git a/base/containers/checked_range_unittest.cc b/base/containers/checked_range_unittest.cc
index 28e813b..a84496d 100644
--- a/base/containers/checked_range_unittest.cc
+++ b/base/containers/checked_range_unittest.cc
@@ -8,6 +8,7 @@
 #include <initializer_list>
 #include <type_traits>
 
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -151,9 +152,9 @@
   std::vector<int> vector = {3, 1, 4, 2, 5};
   CheckedContiguousRange<std::vector<int>> range(vector);
 
-  EXPECT_FALSE(std::is_sorted(vector.begin(), vector.end()));
+  EXPECT_FALSE(ranges::is_sorted(vector));
   std::sort(range.data(), range.data() + range.size());
-  EXPECT_TRUE(std::is_sorted(vector.begin(), vector.end()));
+  EXPECT_TRUE(ranges::is_sorted(vector));
 }
 
 TEST(CheckedContiguousRange, DataSizeEmpty_Constexpr) {
diff --git a/base/containers/circular_deque.h b/base/containers/circular_deque.h
index 4fc61c2..e8782a7 100644
--- a/base/containers/circular_deque.h
+++ b/base/containers/circular_deque.h
@@ -14,6 +14,7 @@
 #include "base/check_op.h"
 #include "base/containers/vector_buffer.h"
 #include "base/macros.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/template_util.h"
 
@@ -1097,7 +1098,7 @@
 // Implementations of base::Erase[If] (see base/stl_util.h).
 template <class T, class Value>
 size_t Erase(circular_deque<T>& container, const Value& value) {
-  auto it = std::remove(container.begin(), container.end(), value);
+  auto it = ranges::remove(container, value);
   size_t removed = std::distance(it, container.end());
   container.erase(it, container.end());
   return removed;
@@ -1105,7 +1106,7 @@
 
 template <class T, class Predicate>
 size_t EraseIf(circular_deque<T>& container, Predicate pred) {
-  auto it = std::remove_if(container.begin(), container.end(), pred);
+  auto it = ranges::remove_if(container, pred);
   size_t removed = std::distance(it, container.end());
   container.erase(it, container.end());
   return removed;
diff --git a/base/containers/flat_map_unittest.cc b/base/containers/flat_map_unittest.cc
index 3512528..3cf18cd 100644
--- a/base/containers/flat_map_unittest.cc
+++ b/base/containers/flat_map_unittest.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "base/test/move_only_int.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -214,7 +215,7 @@
   base::flat_map<MoveOnlyInt, int> map;
   for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
     map.insert_or_assign(MoveOnlyInt(i), i);
-    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+    EXPECT_TRUE(ranges::is_sorted(map));
   }
 }
 
@@ -249,7 +250,7 @@
   base::flat_map<MoveOnlyInt, int> map;
   for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
     map.insert_or_assign(map.end(), MoveOnlyInt(i), i);
-    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+    EXPECT_TRUE(ranges::is_sorted(map));
   }
 }
 
@@ -294,7 +295,7 @@
   base::flat_map<MoveOnlyInt, int> map;
   for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
     map.try_emplace(MoveOnlyInt(i), i);
-    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+    EXPECT_TRUE(ranges::is_sorted(map));
   }
 }
 
@@ -340,7 +341,7 @@
   base::flat_map<MoveOnlyInt, int> map;
   for (int i : {3, 1, 5, 6, 8, 7, 0, 9, 4, 2}) {
     map.try_emplace(map.end(), MoveOnlyInt(i), i);
-    EXPECT_TRUE(std::is_sorted(map.begin(), map.end()));
+    EXPECT_TRUE(ranges::is_sorted(map));
   }
 }
 
diff --git a/base/containers/flat_tree.h b/base/containers/flat_tree.h
index 1d779f2c..cee0a3b 100644
--- a/base/containers/flat_tree.h
+++ b/base/containers/flat_tree.h
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/template_util.h"
 
@@ -756,11 +757,10 @@
 void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::replace(
     underlying_type&& body) {
   // Ensure that |body| is sorted and has no repeated elements.
-  DCHECK(std::is_sorted(body.begin(), body.end(), value_comp()));
-  DCHECK(std::adjacent_find(body.begin(), body.end(),
-                            [this](const auto& lhs, const auto& rhs) {
-                              return !value_comp()(lhs, rhs);
-                            }) == body.end());
+  DCHECK(ranges::is_sorted(body, value_comp()));
+  DCHECK(ranges::adjacent_find(body, [this](const auto& lhs, const auto& rhs) {
+           return !value_comp()(lhs, rhs);
+         }) == body.end());
   impl_.body_ = std::move(body);
 }
 
@@ -886,7 +886,7 @@
   const KeyTypeOrK<K>& key_ref = key;
 
   KeyValueCompare key_value(impl_.get_key_comp());
-  return std::lower_bound(begin(), end(), key_ref, key_value);
+  return ranges::lower_bound(*this, key_ref, key_value);
 }
 
 template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
@@ -907,7 +907,7 @@
   const KeyTypeOrK<K>& key_ref = key;
 
   KeyValueCompare key_value(impl_.get_key_comp());
-  return std::upper_bound(begin(), end(), key_ref, key_value);
+  return ranges::upper_bound(*this, key_ref, key_value);
 }
 
 // ----------------------------------------------------------------------------
@@ -981,7 +981,7 @@
     base::internal::flat_tree<Key, Value, GetKeyFromValue, KeyCompare>&
         container,
     Predicate pred) {
-  auto it = std::remove_if(container.begin(), container.end(), pred);
+  auto it = ranges::remove_if(container, pred);
   size_t removed = std::distance(it, container.end());
   container.erase(it, container.end());
   return removed;
diff --git a/base/containers/flat_tree_unittest.cc b/base/containers/flat_tree_unittest.cc
index 8eab5b6..24b0895 100644
--- a/base/containers/flat_tree_unittest.cc
+++ b/base/containers/flat_tree_unittest.cc
@@ -35,6 +35,7 @@
 #include <string>
 #include <vector>
 
+#include "base/ranges/algorithm.h"
 #include "base/template_util.h"
 #include "base/test/move_only_int.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -218,8 +219,8 @@
   Tree cont({{0, 0}, {1, 0}, {0, 1}, {2, 0}, {0, 2}, {1, 1}});
 
   auto AllOfSecondsAreZero = [&cont] {
-    return std::all_of(cont.begin(), cont.end(),
-                       [](const Pair& elem) { return elem.second == 0; });
+    return ranges::all_of(cont,
+                          [](const Pair& elem) { return elem.second == 0; });
   };
 
   EXPECT_TRUE(AllOfSecondsAreZero()) << "constructor should be stable";
@@ -975,11 +976,11 @@
 TEST(FlatTree, KeyComp) {
   ReversedTree cont({1, 2, 3, 4, 5});
 
-  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.key_comp()));
+  EXPECT_TRUE(ranges::is_sorted(cont, cont.key_comp()));
   int new_elements[] = {6, 7, 8, 9, 10};
   std::copy(std::begin(new_elements), std::end(new_elements),
             std::inserter(cont, cont.end()));
-  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.key_comp()));
+  EXPECT_TRUE(ranges::is_sorted(cont, cont.key_comp()));
 }
 
 // value_compare value_comp() const
@@ -987,11 +988,11 @@
 TEST(FlatTree, ValueComp) {
   ReversedTree cont({1, 2, 3, 4, 5});
 
-  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.value_comp()));
+  EXPECT_TRUE(ranges::is_sorted(cont, cont.value_comp()));
   int new_elements[] = {6, 7, 8, 9, 10};
   std::copy(std::begin(new_elements), std::end(new_elements),
             std::inserter(cont, cont.end()));
-  EXPECT_TRUE(std::is_sorted(cont.begin(), cont.end(), cont.value_comp()));
+  EXPECT_TRUE(ranges::is_sorted(cont, cont.value_comp()));
 }
 
 // ----------------------------------------------------------------------------
diff --git a/base/containers/span_unittest.cc b/base/containers/span_unittest.cc
index 58f4eaf..f75dc6a0 100644
--- a/base/containers/span_unittest.cc
+++ b/base/containers/span_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <stdint.h>
 
-#include <algorithm>
 #include <iterator>
 #include <memory>
 #include <string>
@@ -14,6 +13,7 @@
 #include <vector>
 
 #include "base/containers/checked_iterators.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -1407,7 +1407,7 @@
   int array[] = {5, 4, 3, 2, 1};
 
   span<int> dynamic_span = array;
-  std::sort(dynamic_span.begin(), dynamic_span.end());
+  ranges::sort(dynamic_span);
   EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5));
   std::sort(dynamic_span.rbegin(), dynamic_span.rend());
   EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1));
@@ -1415,7 +1415,7 @@
   span<int, 5> static_span = array;
   std::sort(static_span.rbegin(), static_span.rend(), std::greater<>());
   EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5));
-  std::sort(static_span.begin(), static_span.end(), std::greater<>());
+  ranges::sort(static_span, std::greater<>());
   EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1));
 }
 
diff --git a/base/containers/unique_ptr_adapters_unittest.cc b/base/containers/unique_ptr_adapters_unittest.cc
index 8a107f32..da2d719 100644
--- a/base/containers/unique_ptr_adapters_unittest.cc
+++ b/base/containers/unique_ptr_adapters_unittest.cc
@@ -4,10 +4,10 @@
 
 #include "base/containers/unique_ptr_adapters.h"
 
-#include <algorithm>
 #include <memory>
 #include <vector>
 
+#include "base/ranges/algorithm.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -76,19 +76,19 @@
   v.push_back(std::move(foo_ptr2));
 
   {
-    auto iter = std::find_if(v.begin(), v.end(), UniquePtrMatcher<Foo>(foo1));
+    auto iter = ranges::find_if(v, UniquePtrMatcher<Foo>(foo1));
     ASSERT_TRUE(iter != v.end());
     EXPECT_EQ(foo1, iter->get());
   }
 
   {
-    auto iter = std::find_if(v.begin(), v.end(), UniquePtrMatcher<Foo>(foo2));
+    auto iter = ranges::find_if(v, UniquePtrMatcher<Foo>(foo2));
     ASSERT_TRUE(iter != v.end());
     EXPECT_EQ(foo2, iter->get());
   }
 
   {
-    auto iter = std::find_if(v.begin(), v.end(), MatchesUniquePtr(foo2));
+    auto iter = ranges::find_if(v, MatchesUniquePtr(foo2));
     ASSERT_TRUE(iter != v.end());
     EXPECT_EQ(foo2, iter->get());
   }
@@ -110,22 +110,19 @@
   v.push_back(std::move(foo_ptr2));
 
   {
-    auto iter = std::find_if(v.begin(), v.end(),
-                             UniquePtrMatcher<Foo, TestDeleter>(foo1));
+    auto iter = ranges::find_if(v, UniquePtrMatcher<Foo, TestDeleter>(foo1));
     ASSERT_TRUE(iter != v.end());
     EXPECT_EQ(foo1, iter->get());
   }
 
   {
-    auto iter = std::find_if(v.begin(), v.end(),
-                             UniquePtrMatcher<Foo, TestDeleter>(foo2));
+    auto iter = ranges::find_if(v, UniquePtrMatcher<Foo, TestDeleter>(foo2));
     ASSERT_TRUE(iter != v.end());
     EXPECT_EQ(foo2, iter->get());
   }
 
   {
-    auto iter = std::find_if(v.begin(), v.end(),
-                             MatchesUniquePtr<Foo, TestDeleter>(foo2));
+    auto iter = ranges::find_if(v, MatchesUniquePtr<Foo, TestDeleter>(foo2));
     ASSERT_TRUE(iter != v.end());
     EXPECT_EQ(foo2, iter->get());
   }
diff --git a/base/debug/activity_analyzer.cc b/base/debug/activity_analyzer.cc
index 143174a..c7c2dec 100644
--- a/base/debug/activity_analyzer.cc
+++ b/base/debug/activity_analyzer.cc
@@ -4,7 +4,6 @@
 
 #include "base/debug/activity_analyzer.h"
 
-#include <algorithm>
 #include <utility>
 
 #include "base/check_op.h"
@@ -14,6 +13,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 
@@ -398,7 +398,7 @@
   }
 
   // Reverse the list of PIDs so that they get popped in the order found.
-  std::reverse(process_ids_.begin(), process_ids_.end());
+  ranges::reverse(process_ids_);
 }
 
 }  // namespace debug
diff --git a/base/debug/task_trace.cc b/base/debug/task_trace.cc
index bea5f1b..08f32d2 100644
--- a/base/debug/task_trace.cc
+++ b/base/debug/task_trace.cc
@@ -4,13 +4,13 @@
 
 #include "base/debug/task_trace.h"
 
+#include "base/ranges/algorithm.h"
 #include "build/build_config.h"
 
 #if defined(OS_ANDROID)
 #include <android/log.h>
 #endif  // OS_ANDROID
 
-#include <algorithm>
 #include <iostream>
 #include <sstream>
 
@@ -55,8 +55,7 @@
     return;
   std::array<const void*, PendingTask::kTaskBacktraceLength + 1> task_trace;
   task_trace[0] = current_task->posted_from.program_counter();
-  std::copy(current_task->task_backtrace.begin(),
-            current_task->task_backtrace.end(), task_trace.begin() + 1);
+  ranges::copy(current_task->task_backtrace, task_trace.begin() + 1);
   size_t length = 0;
   while (length < task_trace.size() && task_trace[length])
     ++length;
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index c687e65..1cef7f4 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <stddef.h>
 
-#include <algorithm>
 #include <utility>
 #include <vector>
 
@@ -14,6 +13,7 @@
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/metrics/field_trial.h"
 #include "base/metrics/persistent_memory_allocator.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -39,7 +39,7 @@
 std::string SortFeatureListString(const std::string& feature_list) {
   std::vector<base::StringPiece> features =
       FeatureList::SplitFeatureListString(feature_list);
-  std::sort(features.begin(), features.end());
+  ranges::sort(features);
   return JoinString(features, ",");
 }
 
diff --git a/base/hash/sha1_perftest.cc b/base/hash/sha1_perftest.cc
index 547cd58..72d46cb1 100644
--- a/base/hash/sha1_perftest.cc
+++ b/base/hash/sha1_perftest.cc
@@ -6,11 +6,11 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <algorithm>
 #include <string>
 #include <vector>
 
 #include "base/rand_util.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/time/time.h"
@@ -48,7 +48,7 @@
       utime[i] = TimeTicks::Now() - start;
       total_test_time += utime[i];
     }
-    std::sort(utime.begin(), utime.end());
+    ranges::sort(utime);
   }
 
   reporter.AddResult(kMetricRuntime, total_test_time.InMicrosecondsF());
@@ -67,8 +67,8 @@
 
   // Convert to a comma-separated string so we can report every data point.
   std::vector<std::string> rate_strings(utime.size());
-  std::transform(utime.cbegin(), utime.cend(), rate_strings.begin(),
-                 [rate](const auto& t) { return NumberToString(rate(t)); });
+  ranges::transform(utime, rate_strings.begin(),
+                    [rate](const auto& t) { return NumberToString(rate(t)); });
   reporter.AddResultList(kMetricThroughput, JoinString(rate_strings, ","));
 }
 
diff --git a/base/i18n/break_iterator_unittest.cc b/base/i18n/break_iterator_unittest.cc
index f7c3e8e..0501424b 100644
--- a/base/i18n/break_iterator_unittest.cc
+++ b/base/i18n/break_iterator_unittest.cc
@@ -7,6 +7,7 @@
 #include <stddef.h>
 #include <vector>
 
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -414,8 +415,7 @@
   sentence_breaks.push_back(24);
   sentence_breaks.push_back(42);
   for (size_t i = 0; i < str.size(); i++) {
-    if (std::find(sentence_breaks.begin(), sentence_breaks.end(), i) !=
-        sentence_breaks.end()) {
+    if (ranges::find(sentence_breaks, i) != sentence_breaks.end()) {
       EXPECT_TRUE(iter.IsSentenceBoundary(i)) << " at index=" << i;
     } else {
       EXPECT_FALSE(iter.IsSentenceBoundary(i)) << " at index=" << i;
diff --git a/base/immediate_crash_unittest.cc b/base/immediate_crash_unittest.cc
index 6b20278..b27c78e 100644
--- a/base/immediate_crash_unittest.cc
+++ b/base/immediate_crash_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/optional.h"
 #include "base/path_service.h"
+#include "base/ranges/algorithm.h"
 #include "base/scoped_native_library.h"
 #include "base/strings/string_number_conversions.h"
 #include "build/build_config.h"
@@ -207,7 +208,7 @@
   ASSERT_NO_FATAL_FAILURE(GetTestFunctionInstructions(&body));
   SCOPED_TRACE(HexEncode(body.data(), body.size() * sizeof(Instruction)));
 
-  auto it = std::find(body.begin(), body.end(), kRet);
+  auto it = ranges::find(body, kRet);
   ASSERT_NE(body.end(), it) << "Failed to find return opcode";
   it++;
 
diff --git a/base/json/json_parser.cc b/base/json/json_parser.cc
index fcb479c..5724ecaf 100644
--- a/base/json/json_parser.cc
+++ b/base/json/json_parser.cc
@@ -13,6 +13,7 @@
 #include "base/macros.h"
 #include "base/notreached.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
@@ -441,7 +442,7 @@
   ConsumeChar();  // Closing '}'.
   // Reverse |dict_storage| to keep the last of elements with the same key in
   // the input.
-  std::reverse(dict_storage.begin(), dict_storage.end());
+  ranges::reverse(dict_storage);
   return Value(Value::DictStorage(std::move(dict_storage)));
 }
 
diff --git a/base/memory/platform_shared_memory_region_unittest.cc b/base/memory/platform_shared_memory_region_unittest.cc
index 928ef8c..8588f9d 100644
--- a/base/memory/platform_shared_memory_region_unittest.cc
+++ b/base/memory/platform_shared_memory_region_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/check.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "base/process/process_metrics.h"
+#include "base/ranges/algorithm.h"
 #include "base/system/sys_info.h"
 #include "base/test/gtest_util.h"
 #include "base/test/test_shared_memory_util.h"
@@ -250,11 +251,10 @@
   ASSERT_TRUE(base::debug::ReadProcMaps(&proc_maps));
   std::vector<base::debug::MappedMemoryRegion> regions;
   ASSERT_TRUE(base::debug::ParseProcMaps(proc_maps, &regions));
-  auto it =
-      std::find_if(regions.begin(), regions.end(),
-                   [addr](const base::debug::MappedMemoryRegion& region) {
-                     return region.start == reinterpret_cast<uintptr_t>(addr);
-                   });
+  auto it = ranges::find_if(
+      regions, [addr](const base::debug::MappedMemoryRegion& region) {
+        return region.start == reinterpret_cast<uintptr_t>(addr);
+      });
   ASSERT_TRUE(it != regions.end());
   // PROT_READ may imply PROT_EXEC on some architectures, so just check that
   // permissions don't contain PROT_WRITE bit.
diff --git a/base/memory/shared_memory_mapping_unittest.cc b/base/memory/shared_memory_mapping_unittest.cc
index 48fba74..796e869 100644
--- a/base/memory/shared_memory_mapping_unittest.cc
+++ b/base/memory/shared_memory_mapping_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <stdint.h>
 
-#include <algorithm>
 #include <limits>
 
 #include "base/containers/span.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/writable_shared_memory_region.h"
+#include "base/ranges/algorithm.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -67,7 +67,7 @@
   span<const uint32_t> read_span = read_mapping_.GetMemoryAsSpan<uint32_t>();
   ASSERT_EQ(2u, read_span.size());
 
-  std::fill(write_span.begin(), write_span.end(), 0);
+  ranges::fill(write_span, 0);
   EXPECT_EQ(0u, read_span[0]);
   EXPECT_EQ(0u, read_span[1]);
 
@@ -92,7 +92,7 @@
   span<const uint32_t> read_span_2 = read_mapping_.GetMemoryAsSpan<uint32_t>(1);
   ASSERT_EQ(1u, read_span_2.size());
 
-  std::fill(write_span.begin(), write_span.end(), 0);
+  ranges::fill(write_span, 0);
   EXPECT_EQ(0u, read_span[0]);
   EXPECT_EQ(0u, read_span[1]);
   EXPECT_EQ(0u, read_span_2[0]);
@@ -103,7 +103,7 @@
   EXPECT_EQ(0x08070605u, read_span[1]);
   EXPECT_EQ(0x04030201u, read_span_2[0]);
 
-  std::fill(write_span_2.begin(), write_span_2.end(), 0);
+  ranges::fill(write_span_2, 0);
   EXPECT_EQ(0u, read_span[0]);
   EXPECT_EQ(0x08070605u, read_span[1]);
   EXPECT_EQ(0u, read_span_2[0]);
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 6ae1afd..99524406 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -13,7 +13,6 @@
 #include <limits.h>
 #include <math.h>
 
-#include <algorithm>
 #include <string>
 #include <utility>
 
@@ -29,6 +28,7 @@
 #include "base/metrics/sample_vector.h"
 #include "base/metrics/statistics_recorder.h"
 #include "base/pickle.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -1245,8 +1245,8 @@
     std::vector<int> ranges = *custom_ranges_;
     ranges.push_back(0);  // Ensure we have a zero value.
     ranges.push_back(HistogramBase::kSampleType_MAX);
-    std::sort(ranges.begin(), ranges.end());
-    ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end());
+    ranges::sort(ranges);
+    ranges.erase(ranges::unique(ranges), ranges.end());
 
     BucketRanges* bucket_ranges = new BucketRanges(ranges.size());
     for (uint32_t i = 0; i < ranges.size(); i++) {
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index 58a442d..b8cacab8 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -16,6 +16,7 @@
 #include "base/metrics/metrics_hashes.h"
 #include "base/metrics/persistent_histogram_allocator.h"
 #include "base/metrics/record_histogram_checker.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/values.h"
@@ -366,7 +367,7 @@
 
 // static
 StatisticsRecorder::Histograms StatisticsRecorder::Sort(Histograms histograms) {
-  std::sort(histograms.begin(), histograms.end(), &HistogramNameLesser);
+  ranges::sort(histograms, &HistogramNameLesser);
   return histograms;
 }
 
@@ -376,12 +377,12 @@
     const std::string& query) {
   // Need a C-string query for comparisons against C-string histogram name.
   const char* const query_string = query.c_str();
-  histograms.erase(std::remove_if(histograms.begin(), histograms.end(),
-                                  [query_string](const HistogramBase* const h) {
-                                    return !strstr(h->histogram_name(),
-                                                   query_string);
-                                  }),
-                   histograms.end());
+  histograms.erase(
+      ranges::remove_if(histograms,
+                        [query_string](const HistogramBase* const h) {
+                          return !strstr(h->histogram_name(), query_string);
+                        }),
+      histograms.end());
   return histograms;
 }
 
@@ -389,10 +390,11 @@
 StatisticsRecorder::Histograms StatisticsRecorder::NonPersistent(
     Histograms histograms) {
   histograms.erase(
-      std::remove_if(histograms.begin(), histograms.end(),
-                     [](const HistogramBase* const h) {
-                       return (h->flags() & HistogramBase::kIsPersistent) != 0;
-                     }),
+      ranges::remove_if(histograms,
+                        [](const HistogramBase* const h) {
+                          return (h->flags() & HistogramBase::kIsPersistent) !=
+                                 0;
+                        }),
       histograms.end());
   return histograms;
 }
diff --git a/base/observer_list.h b/base/observer_list.h
index 28369a25..834ee64e 100644
--- a/base/observer_list.h
+++ b/base/observer_list.h
@@ -17,6 +17,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/notreached.h"
 #include "base/observer_list_internal.h"
+#include "base/ranges/algorithm.h"
 #include "base/sequence_checker.h"
 #include "base/stl_util.h"
 
@@ -279,9 +280,8 @@
   // not in this list.
   void RemoveObserver(const ObserverType* obs) {
     DCHECK(obs);
-    const auto it =
-        std::find_if(observers_.begin(), observers_.end(),
-                     [obs](const auto& o) { return o.IsEqual(obs); });
+    const auto it = ranges::find_if(
+        observers_, [obs](const auto& o) { return o.IsEqual(obs); });
     if (it == observers_.end())
       return;
 
@@ -300,9 +300,9 @@
     // probably DCHECK, but some client code currently does pass null.
     if (obs == nullptr)
       return false;
-    return std::find_if(observers_.begin(), observers_.end(),
-                        [obs](const auto& o) { return o.IsEqual(obs); }) !=
-           observers_.end();
+    return ranges::find_if(observers_, [obs](const auto& o) {
+             return o.IsEqual(obs);
+           }) != observers_.end();
   }
 
   // Removes all the observers from this list.
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc
index a50f09b..70898d3 100644
--- a/base/process/process_metrics_unittest.cc
+++ b/base/process/process_metrics_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/macros.h"
 #include "base/memory/shared_memory_mapping.h"
 #include "base/memory/writable_shared_memory_region.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -787,13 +788,13 @@
 
   // Should have at least the test runner thread and the thread spawned above.
   EXPECT_GE(prev_thread_times.size(), 2u);
-  EXPECT_TRUE(std::any_of(
-      prev_thread_times.begin(), prev_thread_times.end(),
+  EXPECT_TRUE(ranges::any_of(
+      prev_thread_times,
       [&thread1](const std::pair<PlatformThreadId, base::TimeDelta>& entry) {
         return entry.first == thread1.GetThreadId();
       }));
-  EXPECT_TRUE(std::any_of(
-      prev_thread_times.begin(), prev_thread_times.end(),
+  EXPECT_TRUE(ranges::any_of(
+      prev_thread_times,
       [](const std::pair<PlatformThreadId, base::TimeDelta>& entry) {
         return entry.first == base::PlatformThread::CurrentId();
       }));
@@ -809,16 +810,16 @@
 
   // The stopped thread may still be reported until the kernel cleans it up.
   EXPECT_GE(prev_thread_times.size(), 1u);
-  EXPECT_TRUE(std::any_of(
-      current_thread_times.begin(), current_thread_times.end(),
+  EXPECT_TRUE(ranges::any_of(
+      current_thread_times,
       [](const std::pair<PlatformThreadId, base::TimeDelta>& entry) {
         return entry.first == base::PlatformThread::CurrentId();
       }));
 
   // Reported times should not decrease.
   for (const auto& entry : current_thread_times) {
-    auto prev_it = std::find_if(
-        prev_thread_times.begin(), prev_thread_times.end(),
+    auto prev_it = ranges::find_if(
+        prev_thread_times,
         [&entry](
             const std::pair<PlatformThreadId, base::TimeDelta>& prev_entry) {
           return entry.first == prev_entry.first;
@@ -868,8 +869,8 @@
 
   // Reported times should not decrease.
   for (const auto& entry : current_thread_times) {
-    auto prev_it = std::find_if(
-        prev_thread_times.begin(), prev_thread_times.end(),
+    auto prev_it = ranges::find_if(
+        prev_thread_times,
         [&entry](const ProcessMetrics::ThreadTimeInState& prev_entry) {
           return entry.thread_id == prev_entry.thread_id &&
                  entry.core_type == prev_entry.core_type &&
diff --git a/base/profiler/arm_cfi_table.cc b/base/profiler/arm_cfi_table.cc
index a9593b4..04f9737 100644
--- a/base/profiler/arm_cfi_table.cc
+++ b/base/profiler/arm_cfi_table.cc
@@ -4,7 +4,7 @@
 
 #include "base/profiler/arm_cfi_table.h"
 
-#include <algorithm>
+#include "base/ranges/algorithm.h"
 
 namespace base {
 
@@ -91,8 +91,7 @@
   // Find the required function address in UNW_INDEX as the last function lower
   // or equal to |address| (the value right before the result of upper_bound(),
   // if any).
-  auto func_it = std::upper_bound(function_addresses_.begin(),
-                                  function_addresses_.end(), address);
+  auto func_it = ranges::upper_bound(function_addresses_, address);
   // If no function comes before |address|, no CFI entry  is returned.
   if (func_it == function_addresses_.begin())
     return nullopt;
@@ -157,4 +156,4 @@
   return last_frame_entry;
 }
 
-}  // namespace base
\ No newline at end of file
+}  // namespace base
diff --git a/base/profiler/metadata_recorder_unittest.cc b/base/profiler/metadata_recorder_unittest.cc
index 4aff812..33332c1 100644
--- a/base/profiler/metadata_recorder_unittest.cc
+++ b/base/profiler/metadata_recorder_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "base/profiler/metadata_recorder.h"
 
+#include "base/ranges/algorithm.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -213,7 +214,7 @@
   }
 
   MetadataRecorder::ItemArray items_arr;
-  std::copy(items_set.begin(), items_set.end(), items_arr.begin());
+  ranges::copy(items_set, items_arr.begin());
 
   MetadataRecorder::ItemArray recorder_items;
   size_t recorder_item_count =
diff --git a/base/profiler/module_cache.cc b/base/profiler/module_cache.cc
index 9c72296..b36ba46 100644
--- a/base/profiler/module_cache.cc
+++ b/base/profiler/module_cache.cc
@@ -4,10 +4,11 @@
 
 #include "base/profiler/module_cache.h"
 
-#include <algorithm>
 #include <iterator>
 #include <utility>
 
+#include "base/ranges/algorithm.h"
+
 namespace base {
 
 namespace {
@@ -69,8 +70,8 @@
   //
   // stable_partition is O(m*log(r)) where m is the number of current modules
   // and r is the number of modules to remove. insert and erase are both O(r).
-  auto first_module_defunct_modules = std::stable_partition(
-      non_native_modules_.begin(), non_native_modules_.end(),
+  auto first_module_defunct_modules = ranges::stable_partition(
+      non_native_modules_,
       [&defunct_modules_set](const std::unique_ptr<const Module>& module) {
         return defunct_modules_set.find(module.get()) ==
                defunct_modules_set.end();
diff --git a/base/profiler/stack_sampler_impl.cc b/base/profiler/stack_sampler_impl.cc
index a05b10e..dd58645 100644
--- a/base/profiler/stack_sampler_impl.cc
+++ b/base/profiler/stack_sampler_impl.cc
@@ -16,6 +16,7 @@
 #include "base/profiler/stack_copier.h"
 #include "base/profiler/suspendable_thread_delegate.h"
 #include "base/profiler/unwinder.h"
+#include "base/ranges/algorithm.h"
 #include "build/build_config.h"
 
 // IMPORTANT NOTE: Some functions within this implementation are invoked while
@@ -169,11 +170,10 @@
   do {
     // Choose an authoritative unwinder for the current module. Use the first
     // unwinder that thinks it can unwind from the current frame.
-    auto unwinder =
-        std::find_if(unwinders.begin(), unwinders.end(),
-                     [&stack](const std::unique_ptr<Unwinder>& unwinder) {
-                       return unwinder->CanUnwindFrom(stack.back());
-                     });
+    auto unwinder = ranges::find_if(
+        unwinders, [&stack](const std::unique_ptr<Unwinder>& unwinder) {
+          return unwinder->CanUnwindFrom(stack.back());
+        });
     if (unwinder == unwinders.end())
       return stack;
 
diff --git a/base/profiler/stack_sampling_profiler_unittest.cc b/base/profiler/stack_sampling_profiler_unittest.cc
index 7951dfe..7ed7e4d 100644
--- a/base/profiler/stack_sampling_profiler_unittest.cc
+++ b/base/profiler/stack_sampling_profiler_unittest.cc
@@ -5,7 +5,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <algorithm>
 #include <cstdlib>
 #include <memory>
 #include <set>
@@ -26,6 +25,7 @@
 #include "base/profiler/stack_sampling_profiler.h"
 #include "base/profiler/stack_sampling_profiler_test_util.h"
 #include "base/profiler/unwinder.h"
+#include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "base/scoped_native_library.h"
 #include "base/stl_util.h"
@@ -270,10 +270,10 @@
     const std::vector<std::unique_ptr<TestProfilerInfo>>& infos) {
   // Map unique_ptrs to something that WaitMany can accept.
   std::vector<WaitableEvent*> sampling_completed_rawptrs(infos.size());
-  std::transform(infos.begin(), infos.end(), sampling_completed_rawptrs.begin(),
-                 [](const std::unique_ptr<TestProfilerInfo>& info) {
-                   return &info.get()->completed;
-                 });
+  ranges::transform(infos, sampling_completed_rawptrs.begin(),
+                    [](const std::unique_ptr<TestProfilerInfo>& info) {
+                      return &info.get()->completed;
+                    });
   // Wait for one profiler to finish.
   return WaitableEvent::WaitMany(sampling_completed_rawptrs.data(),
                                  sampling_completed_rawptrs.size());
diff --git a/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
index 0cae9f7..ea7ff8d3 100644
--- a/base/sampling_heap_profiler/poisson_allocation_sampler.cc
+++ b/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -4,7 +4,6 @@
 
 #include "base/sampling_heap_profiler/poisson_allocation_sampler.h"
 
-#include <algorithm>
 #include <atomic>
 #include <cmath>
 #include <memory>
@@ -17,6 +16,7 @@
 #include "base/no_destructor.h"
 #include "base/partition_alloc_buildflags.h"
 #include "base/rand_util.h"
+#include "base/ranges/algorithm.h"
 #include "build/build_config.h"
 
 #if defined(OS_APPLE) || defined(OS_ANDROID)
@@ -571,8 +571,7 @@
 void PoissonAllocationSampler::AddSamplesObserver(SamplesObserver* observer) {
   ScopedMuteThreadSamples no_reentrancy_scope;
   AutoLock lock(mutex_);
-  DCHECK(std::find(observers_.begin(), observers_.end(), observer) ==
-         observers_.end());
+  DCHECK(ranges::find(observers_, observer) == observers_.end());
   observers_.push_back(observer);
   InstallAllocatorHooksOnce();
   g_running = !observers_.empty();
@@ -582,7 +581,7 @@
     SamplesObserver* observer) {
   ScopedMuteThreadSamples no_reentrancy_scope;
   AutoLock lock(mutex_);
-  auto it = std::find(observers_.begin(), observers_.end(), observer);
+  auto it = ranges::find(observers_, observer);
   DCHECK(it != observers_.end());
   observers_.erase(it);
   g_running = !observers_.empty();
diff --git a/base/scoped_observer.h b/base/scoped_observer.h
index 0e3a107e..f97265f 100644
--- a/base/scoped_observer.h
+++ b/base/scoped_observer.h
@@ -7,10 +7,10 @@
 
 #include <stddef.h>
 
-#include <algorithm>
 #include <vector>
 
 #include "base/check.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 
 // ScopedObserver is used to keep track of the set of sources an object has
@@ -56,7 +56,7 @@
 
   // Remove the object passed to the constructor as an observer from |source|.
   void Remove(Source* source) {
-    auto it = std::find(sources_.begin(), sources_.end(), source);
+    auto it = base::ranges::find(sources_, source);
     DCHECK(it != sources_.end());
     sources_.erase(it);
     (source->*RemoveObsFn)(observer_);
diff --git a/base/strings/abseil_string_conversions.cc b/base/strings/abseil_string_conversions.cc
index 7e594efa..9933b5f 100644
--- a/base/strings/abseil_string_conversions.cc
+++ b/base/strings/abseil_string_conversions.cc
@@ -4,10 +4,10 @@
 
 #include "base/strings/abseil_string_conversions.h"
 
-#include <algorithm>
 #include <vector>
 
 #include "base/containers/span.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "third_party/abseil-cpp/absl/strings/string_view.h"
 
@@ -16,16 +16,14 @@
 std::vector<absl::string_view> StringPiecesToStringViews(
     span<const StringPiece> pieces) {
   std::vector<absl::string_view> views(pieces.size());
-  std::transform(pieces.begin(), pieces.end(), views.begin(),
-                 &StringPieceToStringView);
+  ranges::transform(pieces, views.begin(), &StringPieceToStringView);
   return views;
 }
 
 std::vector<StringPiece> StringViewsToStringPieces(
     span<const absl::string_view> views) {
   std::vector<StringPiece> pieces(views.size());
-  std::transform(views.begin(), views.end(), pieces.begin(),
-                 &StringViewToStringPiece);
+  ranges::transform(views, pieces.begin(), &StringViewToStringPiece);
   return pieces;
 }
 
diff --git a/base/strings/string_util_internal.h b/base/strings/string_util_internal.h
index 3ec97a7a..7229524c 100644
--- a/base/strings/string_util_internal.h
+++ b/base/strings/string_util_internal.h
@@ -9,6 +9,7 @@
 
 #include "base/logging.h"
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_piece.h"
 #include "base/third_party/icu/icu_utf.h"
 
@@ -580,8 +581,7 @@
             ReplacementOffset r_offset(index,
                                        static_cast<int>(formatted.size()));
             r_offsets.insert(
-                std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
-                                 &CompareParameter),
+                ranges::upper_bound(r_offsets, r_offset, &CompareParameter),
                 r_offset);
           }
           if (index < substitutions)
diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc
index ab36f8d..adf6411 100644
--- a/base/synchronization/waitable_event_posix.cc
+++ b/base/synchronization/waitable_event_posix.cc
@@ -4,13 +4,13 @@
 
 #include <stddef.h>
 
-#include <algorithm>
 #include <limits>
 #include <vector>
 
 #include "base/check_op.h"
 #include "base/debug/activity_tracker.h"
 #include "base/optional.h"
+#include "base/ranges/algorithm.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
@@ -265,7 +265,7 @@
 
   DCHECK_EQ(count, waitables.size());
 
-  sort(waitables.begin(), waitables.end(), cmp_fst_addr);
+  ranges::sort(waitables, cmp_fst_addr);
 
   // The set of waitables must be distinct. Since we have just sorted by
   // address, we can check this cheaply by comparing pairs of consecutive
diff --git a/base/task/common/checked_lock_impl.cc b/base/task/common/checked_lock_impl.cc
index 8b41e95c..cef737a 100644
--- a/base/task/common/checked_lock_impl.cc
+++ b/base/task/common/checked_lock_impl.cc
@@ -4,12 +4,12 @@
 
 #include "base/task/common/checked_lock_impl.h"
 
-#include <algorithm>
 #include <unordered_map>
 #include <vector>
 
 #include "base/check_op.h"
 #include "base/lazy_instance.h"
+#include "base/ranges/algorithm.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/task/common/checked_lock.h"
 #include "base/threading/platform_thread.h"
@@ -44,8 +44,7 @@
 
   void RecordRelease(const CheckedLockImpl* const lock) {
     LockVector* acquired_locks = GetAcquiredLocksOnCurrentThread();
-    const auto iter_at_lock =
-        std::find(acquired_locks->begin(), acquired_locks->end(), lock);
+    const auto iter_at_lock = ranges::find(*acquired_locks, lock);
     DCHECK(iter_at_lock != acquired_locks->end());
     acquired_locks->erase(iter_at_lock);
   }
diff --git a/base/task/common/operations_controller_unittest.cc b/base/task/common/operations_controller_unittest.cc
index 0e110ed1..e7174252 100644
--- a/base/task/common/operations_controller_unittest.cc
+++ b/base/task/common/operations_controller_unittest.cc
@@ -8,6 +8,7 @@
 #include <cstdint>
 #include <utility>
 
+#include "base/ranges/algorithm.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/simple_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -124,8 +125,7 @@
       }
       if (!was_started)
         continue;
-      if (std::any_of(tokens.begin(), tokens.end(),
-                      [](const auto& token) { return !token; })) {
+      if (ranges::any_of(tokens, [](const auto& token) { return !token; })) {
         break;
       }
     }
diff --git a/base/task/common/task_annotator.cc b/base/task/common/task_annotator.cc
index fe3c1e8..528105d 100644
--- a/base/task/common/task_annotator.cc
+++ b/base/task/common/task_annotator.cc
@@ -11,6 +11,7 @@
 #include "base/debug/alias.h"
 #include "base/hash/md5.h"
 #include "base/no_destructor.h"
+#include "base/ranges/algorithm.h"
 #include "base/sys_byteorder.h"
 #include "base/threading/thread_local.h"
 #include "base/trace_event/base_tracing.h"
@@ -148,8 +149,7 @@
   task_backtrace.back() = reinterpret_cast<void*>(0x0d00d1d1d178119);
 
   task_backtrace[1] = pending_task->posted_from.program_counter();
-  std::copy(pending_task->task_backtrace.begin(),
-            pending_task->task_backtrace.end(), task_backtrace.begin() + 2);
+  ranges::copy(pending_task->task_backtrace, task_backtrace.begin() + 2);
   task_backtrace[kStackTaskTraceSnapshotSize - 2] =
       reinterpret_cast<void*>(pending_task->ipc_hash);
   debug::Alias(&task_backtrace);
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index d16889c..71bb0cf 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -18,6 +18,7 @@
 #include "base/no_destructor.h"
 #include "base/optional.h"
 #include "base/rand_util.h"
+#include "base/ranges/algorithm.h"
 #include "base/task/sequence_manager/real_time_domain.h"
 #include "base/task/sequence_manager/task_time_observer.h"
 #include "base/task/sequence_manager/thread_controller_impl.h"
@@ -542,8 +543,7 @@
     case Settings::TaskLogging::kEnabledWithBacktrace: {
       std::array<const void*, PendingTask::kTaskBacktraceLength + 1> task_trace;
       task_trace[0] = task->posted_from.program_counter();
-      std::copy(task->task_backtrace.begin(), task->task_backtrace.end(),
-                task_trace.begin() + 1);
+      ranges::copy(task->task_backtrace, task_trace.begin() + 1);
       size_t length = 0;
       while (length < task_trace.size() && task_trace[length])
         ++length;
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index 7dc1149..b3a468c 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/common/scoped_defer_task_posting.h"
 #include "base/task/sequence_manager/sequence_manager_impl.h"
@@ -1418,7 +1419,7 @@
 
   // If we deleted something, re-enforce the heap property.
   if (task_deleted)
-    std::make_heap(queue_.c.begin(), queue_.c.end(), queue_.comp);
+    ranges::make_heap(queue_.c, queue_.comp);
 }
 
 Value TaskQueueImpl::DelayedIncomingQueue::AsValue(TimeTicks now) const {
diff --git a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
index 1e5d8ce..5f9f593 100644
--- a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
+++ b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
@@ -4,7 +4,6 @@
 
 #include "base/task/thread_pool/pooled_single_thread_task_runner_manager.h"
 
-#include <algorithm>
 #include <memory>
 #include <string>
 #include <utility>
@@ -12,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/memory/ptr_util.h"
+#include "base/ranges/algorithm.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
@@ -719,7 +719,7 @@
     if (workers_.empty())
       return;
 
-    auto worker_iter = std::find(workers_.begin(), workers_.end(), worker);
+    auto worker_iter = ranges::find(workers_, worker);
     DCHECK(worker_iter != workers_.end());
     worker_to_destroy = std::move(*worker_iter);
     workers_.erase(worker_iter);
diff --git a/base/task/thread_pool/thread_group_impl.cc b/base/task/thread_pool/thread_group_impl.cc
index c911c47..487f158 100644
--- a/base/task/thread_pool/thread_group_impl.cc
+++ b/base/task/thread_pool/thread_group_impl.cc
@@ -22,6 +22,7 @@
 #include "base/metrics/histogram.h"
 #include "base/numerics/clamped_math.h"
 #include "base/optional.h"
+#include "base/ranges/algorithm.h"
 #include "base/sequence_token.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -82,10 +83,10 @@
 // Only used in DCHECKs.
 bool ContainsWorker(const std::vector<scoped_refptr<WorkerThread>>& workers,
                     const WorkerThread* worker) {
-  auto it = std::find_if(workers.begin(), workers.end(),
-                         [worker](const scoped_refptr<WorkerThread>& i) {
-                           return i.get() == worker;
-                         });
+  auto it =
+      ranges::find_if(workers, [worker](const scoped_refptr<WorkerThread>& i) {
+        return i.get() == worker;
+      });
   return it != workers.end();
 }
 
@@ -720,8 +721,7 @@
   outer_->idle_workers_stack_.Remove(worker);
 
   // Remove the worker from |workers_|.
-  auto worker_iter =
-      std::find(outer_->workers_.begin(), outer_->workers_.end(), worker);
+  auto worker_iter = ranges::find(outer_->workers_, worker);
   DCHECK(worker_iter != outer_->workers_.end());
   outer_->workers_.erase(worker_iter);
 }
diff --git a/base/task/thread_pool/worker_thread_stack.cc b/base/task/thread_pool/worker_thread_stack.cc
index 75bc5c6..824dfc0 100644
--- a/base/task/thread_pool/worker_thread_stack.cc
+++ b/base/task/thread_pool/worker_thread_stack.cc
@@ -4,9 +4,8 @@
 
 #include "base/task/thread_pool/worker_thread_stack.h"
 
-#include <algorithm>
-
 #include "base/check_op.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/task/thread_pool/worker_thread.h"
 
@@ -47,7 +46,7 @@
 void WorkerThreadStack::Remove(const WorkerThread* worker) {
   DCHECK(!IsEmpty());
   DCHECK_NE(worker, stack_.back());
-  auto it = std::find(stack_.begin(), stack_.end(), worker);
+  auto it = ranges::find(stack_, worker);
   DCHECK(it != stack_.end());
   DCHECK_NE(TimeTicks(), (*it)->GetLastUsedTime());
   stack_.erase(it);
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index a62b35b..1985601 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -30,6 +30,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
+#include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/pattern.h"
@@ -695,7 +696,7 @@
   CHECK_GT(runner_count_, 0u);
   tests_to_run_ = test_names;
   // Reverse test order to avoid coping the whole vector when removing tests.
-  std::reverse(tests_to_run_.begin(), tests_to_run_.end());
+  ranges::reverse(tests_to_run_);
   runners_done_ = 0;
   task_runners_.clear();
   for (size_t i = 0; i < runner_count_; i++) {
@@ -1591,7 +1592,7 @@
 
     std::mt19937 randomizer;
     randomizer.seed(shuffle_seed);
-    std::shuffle(tests_.begin(), tests_.end(), randomizer);
+    ranges::shuffle(tests_, randomizer);
 
     fprintf(stdout, "Randomizing with seed %u\n", shuffle_seed);
     fflush(stdout);
diff --git a/base/test/scoped_feature_list.cc b/base/test/scoped_feature_list.cc
index e6fec8d..7ada1d5 100644
--- a/base/test/scoped_feature_list.cc
+++ b/base/test/scoped_feature_list.cc
@@ -4,12 +4,12 @@
 
 #include "base/test/scoped_feature_list.h"
 
-#include <algorithm>
 #include <utility>
 #include <vector>
 
 #include "base/memory/ptr_util.h"
 #include "base/metrics/field_trial_param_associator.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -71,10 +71,10 @@
 // with GetFeatureName() and also could be without parameters.
 bool ContainsFeature(const std::vector<StringPiece>& feature_vector,
                      StringPiece feature_name) {
-  auto iter = std::find_if(feature_vector.begin(), feature_vector.end(),
-                           [&feature_name](const StringPiece& a) {
-                             return GetFeatureName(a) == feature_name;
-                           });
+  auto iter =
+      ranges::find_if(feature_vector, [&feature_name](const StringPiece& a) {
+        return GetFeatureName(a) == feature_name;
+      });
   return iter != feature_vector.end();
 }
 
diff --git a/base/test/trace_event_analyzer.cc b/base/test/trace_event_analyzer.cc
index 13af95dd..f59c7aaa 100644
--- a/base/test/trace_event_analyzer.cc
+++ b/base/test/trace_event_analyzer.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "base/strings/pattern.h"
 #include "base/trace_event/trace_buffer.h"
@@ -775,7 +776,7 @@
   raw_events_.clear();
   if (!ParseEventsFromJson(json_events, &raw_events_))
     return false;
-  std::stable_sort(raw_events_.begin(), raw_events_.end());
+  base::ranges::stable_sort(raw_events_);
   ParseMetadata();
   return true;
 }
@@ -964,7 +965,7 @@
     deltas.push_back(delta);
   }
 
-  std::sort(deltas.begin(), deltas.end());
+  base::ranges::sort(deltas);
 
   if (options) {
     if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
@@ -980,8 +981,8 @@
   for (size_t i = 0; i < num_deltas; ++i)
     delta_sum += deltas[i];
 
-  stats->min_us = *std::min_element(deltas.begin(), deltas.end());
-  stats->max_us = *std::max_element(deltas.begin(), deltas.end());
+  stats->min_us = *base::ranges::min_element(deltas);
+  stats->max_us = *base::ranges::max_element(deltas);
   stats->mean_us = delta_sum / static_cast<double>(num_deltas);
 
   double sum_mean_offsets_squared = 0.0;
diff --git a/base/threading/hang_watcher.cc b/base/threading/hang_watcher.cc
index a84299b..03413dd 100644
--- a/base/threading/hang_watcher.cc
+++ b/base/threading/hang_watcher.cc
@@ -4,7 +4,6 @@
 
 #include "base/threading/hang_watcher.h"
 
-#include <algorithm>
 #include <atomic>
 #include <utility>
 
@@ -16,6 +15,7 @@
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
+#include "base/ranges/algorithm.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
@@ -413,10 +413,10 @@
 
   // Sort |hung_watch_state_copies_| by order of decreasing hang severity so the
   // most severe hang is first in the list.
-  std::sort(hung_watch_state_copies_.begin(), hung_watch_state_copies_.end(),
-            [](const WatchStateCopy& lhs, const WatchStateCopy& rhs) {
-              return lhs.deadline < rhs.deadline;
-            });
+  ranges::sort(hung_watch_state_copies_,
+               [](const WatchStateCopy& lhs, const WatchStateCopy& rhs) {
+                 return lhs.deadline < rhs.deadline;
+               });
 }
 
 HangWatcher::WatchStateSnapShot::WatchStateSnapShot(
@@ -476,8 +476,8 @@
   // atomically. This is fine. Detecting a hang is generally best effort and
   // if a thread resumes from hang in the time it takes to move on to
   // capturing then its ID will be absent from the crash keys.
-  bool any_thread_hung = std::any_of(
-      watch_states_.cbegin(), watch_states_.cend(),
+  bool any_thread_hung = ranges::any_of(
+      watch_states_,
       [this, now](const std::unique_ptr<internal::HangWatchState>& state) {
         uint64_t flags;
         base::TimeTicks deadline;
@@ -595,12 +595,12 @@
   internal::HangWatchState* current_hang_watch_state =
       internal::HangWatchState::GetHangWatchStateForCurrentThread()->Get();
 
-  auto it =
-      std::find_if(watch_states_.cbegin(), watch_states_.cend(),
-                   [current_hang_watch_state](
-                       const std::unique_ptr<internal::HangWatchState>& state) {
-                     return state.get() == current_hang_watch_state;
-                   });
+  auto it = ranges::find_if(
+      watch_states_,
+      [current_hang_watch_state](
+          const std::unique_ptr<internal::HangWatchState>& state) {
+        return state.get() == current_hang_watch_state;
+      });
 
   // Thread should be registered to get unregistered.
   DCHECK(it != watch_states_.end());
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index b7728b9..8244eb7 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -4,7 +4,6 @@
 
 #include "base/trace_event/trace_log.h"
 
-#include <algorithm>
 #include <cmath>
 #include <limits>
 #include <memory>
@@ -22,6 +21,7 @@
 #include "base/no_destructor.h"
 #include "base/process/process.h"
 #include "base/process/process_metrics.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -778,8 +778,7 @@
 void TraceLog::RemoveEnabledStateObserver(EnabledStateObserver* listener) {
   AutoLock lock(observers_lock_);
   enabled_state_observers_.erase(
-      std::remove(enabled_state_observers_.begin(),
-                  enabled_state_observers_.end(), listener),
+      ranges::remove(enabled_state_observers_, listener),
       enabled_state_observers_.end());
 }
 
diff --git a/base/values.cc b/base/values.cc
index 2f82767b..d8d0357f 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -6,7 +6,6 @@
 
 #include <string.h>
 
-#include <algorithm>
 #include <cmath>
 #include <new>
 #include <ostream>
@@ -18,6 +17,7 @@
 #include "base/json/json_writer.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
+#include "base/ranges/algorithm.h"
 #include "base/stl_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -1554,7 +1554,7 @@
 }
 
 bool ListValue::Remove(const Value& value, size_t* index) {
-  auto it = std::find(list().begin(), list().end(), value);
+  auto it = ranges::find(list(), value);
 
   if (it == list().end())
     return false;
@@ -1626,7 +1626,7 @@
 }
 
 ListValue::const_iterator ListValue::Find(const Value& value) const {
-  return std::find(GetList().begin(), GetList().end(), value);
+  return ranges::find(GetList(), value);
 }
 
 void ListValue::Swap(ListValue* other) {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a3a6fbc..a6d7a75d 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20201017.2.1
+0.20201018.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a3a6fbc..a6d7a75d 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20201017.2.1
+0.20201018.3.1
diff --git a/build/print_python_deps.py b/build/print_python_deps.py
index fd29c09..ab00d0c 100755
--- a/build/print_python_deps.py
+++ b/build/print_python_deps.py
@@ -12,6 +12,7 @@
 """
 
 import argparse
+import fnmatch
 import os
 import pipes
 import sys
@@ -63,12 +64,13 @@
   return ' '.join(pipes.quote(x) for x in args)
 
 
-def _FindPythonInDirectory(directory):
+def _FindPythonInDirectory(directory, allow_test):
   """Returns an iterable of all non-test python files in the given directory."""
   files = []
   for root, _dirnames, filenames in os.walk(directory):
     for filename in filenames:
-      if filename.endswith('.py') and not filename.endswith('_test.py'):
+      if filename.endswith('.py') and (allow_test
+                                       or not filename.endswith('_test.py')):
         yield os.path.join(root, filename)
 
 
@@ -99,6 +101,8 @@
 
 def _ImportModuleByPath(module_path):
   """Imports a module by its source file."""
+  # Replace the path entry for print_python_deps.py with the one for the given
+  # module.
   sys.path[0] = os.path.dirname(module_path)
   if sys.version_info[0] == 2:
     import imp  # Python 2 only, since it's deprecated in Python 3.
@@ -146,8 +150,17 @@
     options.output = options.module + 'deps'
     options.root = os.path.dirname(options.module)
 
-  target_version = _GetTargetPythonVersion(options.module)
+  modules = [options.module]
+  if os.path.isdir(options.module):
+    modules = list(_FindPythonInDirectory(options.module, allow_test=True))
+  if not modules:
+    parser.error('Input directory does not contain any python files!')
+
+  target_versions = [_GetTargetPythonVersion(m) for m in modules]
+  target_version = target_versions[0]
   assert target_version in [2, 3]
+  assert all(v == target_version for v in target_versions)
+
   current_version = sys.version_info[0]
 
   # Trybots run with vpython as default Python, but with a different config
@@ -167,10 +180,11 @@
     vpython_to_use = {2: 'vpython', 3: 'vpython3'}[target_version]
     os.execvp(vpython_to_use, [vpython_to_use] + sys.argv + ['--did-relaunch'])
 
-  # Replace the path entry for print_python_deps.py with the one for the given
-  # module.
+  paths_set = set()
   try:
-    _ImportModuleByPath(options.module)
+    for module in modules:
+      _ImportModuleByPath(module)
+      paths_set.update(ComputePythonDependencies())
   except Exception:
     # Output extra diagnostics when loading the script fails.
     sys.stderr.write('Error running print_python_deps.py.\n')
@@ -179,9 +193,10 @@
     sys.stderr.write('python={}\n'.format(sys.executable))
     raise
 
-  paths_set = ComputePythonDependencies()
   for path in options.whitelists:
-    paths_set.update(os.path.abspath(p) for p in _FindPythonInDirectory(path))
+    paths_set.update(
+        os.path.abspath(p)
+        for p in _FindPythonInDirectory(path, allow_test=False))
 
   paths = [os.path.relpath(p, options.root) for p in paths_set]
 
diff --git a/chrome/VERSION b/chrome/VERSION
index d9eb55b..d740630c 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=88
 MINOR=0
-BUILD=4296
+BUILD=4297
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 3126777..cf0d5f51 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -297,6 +297,7 @@
   "java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerHost.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/LayoutProvider.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java",
+  "java/src/org/chromium/chrome/browser/compositor/layouts/LayoutStateProvider.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/OverviewModeBehavior.java",
   "java/src/org/chromium/chrome/browser/compositor/layouts/OverviewModeController.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 6972de7..81ae043f 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -269,6 +269,7 @@
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantGenericUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java",
+    "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantKeyboardIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantNavigationIntegrationTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayIntegrationTest.java",
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java
new file mode 100644
index 0000000..26aaa82
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInputActionIntegrationTest.java
@@ -0,0 +1,334 @@
+package org.chromium.chrome.browser.autofill_assistant;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementExists;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getElementValue;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntil;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
+
+import android.support.test.InstrumentationRegistry;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ChipType;
+import org.chromium.chrome.browser.autofill_assistant.proto.ClickProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ClickType;
+import org.chromium.chrome.browser.autofill_assistant.proto.DropdownSelectStrategy;
+import org.chromium.chrome.browser.autofill_assistant.proto.KeyboardValueFillStrategy;
+import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto.Choice;
+import org.chromium.chrome.browser.autofill_assistant.proto.SelectOptionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SelectorProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SelectorProto.Filter;
+import org.chromium.chrome.browser.autofill_assistant.proto.SetFormFieldValueProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SetFormFieldValueProto.KeyPress;
+import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
+import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
+import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Tests autofill assistant's input actions such as keyboard and clicking.
+ */
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@RunWith(ChromeJUnit4ClassRunner.class)
+public class AutofillAssistantInputActionIntegrationTest {
+    @Rule
+    public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
+
+    private static final String TEST_PAGE = "/components/test/data/autofill_assistant/html/"
+            + "autofill_assistant_target_website.html";
+
+    @Before
+    public void setUp() throws Exception {
+        AutofillAssistantPreferencesUtil.setInitialPreferences(true);
+        mTestRule.startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent(
+                InstrumentationRegistry.getTargetContext(),
+                mTestRule.getTestServer().getURL(TEST_PAGE)));
+    }
+
+    @Test
+    @MediumTest
+    public void fillFormFieldWithText() throws Exception {
+        ArrayList<ActionProto> list = new ArrayList<>();
+
+        SelectorProto element_set_value =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#input1"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSetFormValue(
+                                 SetFormFieldValueProto.newBuilder()
+                                         .setElement(element_set_value)
+                                         .addValue(KeyPress.newBuilder().setText("Set Value"))
+                                         .setFillStrategy(KeyboardValueFillStrategy.SET_VALUE))
+                         .build());
+        SelectorProto element_keystrokes =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#input2"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSetFormValue(
+                                 SetFormFieldValueProto.newBuilder()
+                                         .setElement(element_keystrokes)
+                                         .addValue(KeyPress.newBuilder().setText("Keystrokes"))
+                                         .setFillStrategy(
+                                                 KeyboardValueFillStrategy.SIMULATE_KEY_PRESSES))
+                         .build());
+        SelectorProto element_keystrokes_select =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#input3"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSetFormValue(
+                                 SetFormFieldValueProto.newBuilder()
+                                         .setElement(element_keystrokes_select)
+                                         .addValue(
+                                                 KeyPress.newBuilder().setText("Keystrokes Select"))
+                                         .setFillStrategy(
+                                                 KeyboardValueFillStrategy
+                                                         .SIMULATE_KEY_PRESSES_SELECT_VALUE))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Finished")
+                                            .addChoices(Choice.newBuilder()))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath(TEST_PAGE)
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                list);
+
+        runScript(script);
+
+        waitUntilViewMatchesCondition(withText("Finished"), isCompletelyDisplayed());
+        assertThat(getElementValue(mTestRule.getWebContents(), "input1"), is("Set Value"));
+        assertThat(getElementValue(mTestRule.getWebContents(), "input2"), is("Keystrokes"));
+        assertThat(getElementValue(mTestRule.getWebContents(), "input3"), is("Keystrokes Select"));
+    }
+
+    @Test
+    @MediumTest
+    public void clearFormFieldFromText() throws Exception {
+        ArrayList<ActionProto> list = new ArrayList<>();
+
+        SelectorProto element_set_value =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#input1"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSetFormValue(
+                                 SetFormFieldValueProto.newBuilder()
+                                         .setElement(element_set_value)
+                                         .addValue(KeyPress.newBuilder().setText(""))
+                                         .setFillStrategy(KeyboardValueFillStrategy.SET_VALUE))
+                         .build());
+        SelectorProto element_keystrokes =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#input2"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSetFormValue(
+                                 SetFormFieldValueProto.newBuilder()
+                                         .setElement(element_keystrokes)
+                                         .addValue(KeyPress.newBuilder().setText(""))
+                                         .setFillStrategy(
+                                                 KeyboardValueFillStrategy.SIMULATE_KEY_PRESSES))
+                         .build());
+        SelectorProto element_keystrokes_select =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#input3"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSetFormValue(
+                                 SetFormFieldValueProto.newBuilder()
+                                         .setElement(element_keystrokes_select)
+                                         .addValue(KeyPress.newBuilder().setText(""))
+                                         .setFillStrategy(
+                                                 KeyboardValueFillStrategy
+                                                         .SIMULATE_KEY_PRESSES_SELECT_VALUE))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Finished")
+                                            .addChoices(Choice.newBuilder()))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath(TEST_PAGE)
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                list);
+
+        assertThat(getElementValue(mTestRule.getWebContents(), "input1"), is("helloworld1"));
+        assertThat(getElementValue(mTestRule.getWebContents(), "input2"), is("helloworld2"));
+        assertThat(getElementValue(mTestRule.getWebContents(), "input3"), is("helloworld3"));
+
+        runScript(script);
+
+        waitUntilViewMatchesCondition(withText("Finished"), isCompletelyDisplayed());
+        assertThat(getElementValue(mTestRule.getWebContents(), "input1"), is(""));
+        assertThat(getElementValue(mTestRule.getWebContents(), "input2"), is(""));
+        assertThat(getElementValue(mTestRule.getWebContents(), "input3"), is(""));
+    }
+
+    @Test
+    @MediumTest
+    public void selectOptionFromDropdown() throws Exception {
+        ArrayList<ActionProto> list = new ArrayList<>();
+
+        SelectorProto element = (SelectorProto) SelectorProto.newBuilder()
+                                        .addFilters(Filter.newBuilder().setCssSelector("#select"))
+                                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSelectOption(
+                                 SelectOptionProto.newBuilder()
+                                         .setElement(element)
+                                         .setSelectedOption("one")
+                                         .setSelectStrategy(DropdownSelectStrategy.VALUE_MATCH))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Value Match")
+                                            .addChoices(Choice.newBuilder().setChip(
+                                                    ChipProto.newBuilder()
+                                                            .setType(ChipType.HIGHLIGHTED_ACTION)
+                                                            .setText("Continue"))))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSelectOption(
+                                 SelectOptionProto.newBuilder()
+                                         .setElement(element)
+                                         .setSelectedOption("Three")
+                                         .setSelectStrategy(DropdownSelectStrategy.LABEL_MATCH))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Label Match")
+                                            .addChoices(Choice.newBuilder().setChip(
+                                                    ChipProto.newBuilder()
+                                                            .setType(ChipType.HIGHLIGHTED_ACTION)
+                                                            .setText("Continue"))))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setSelectOption(SelectOptionProto.newBuilder()
+                                                  .setElement(element)
+                                                  .setSelectedOption("Zürich")
+                                                  .setSelectStrategy(
+                                                          DropdownSelectStrategy.LABEL_STARTS_WITH))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Label Starts With")
+                                            .addChoices(Choice.newBuilder()))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath(TEST_PAGE)
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                list);
+
+        runScript(script);
+
+        waitUntilViewMatchesCondition(withText("Value Match"), isCompletelyDisplayed());
+        assertThat(getElementValue(mTestRule.getWebContents(), "select"), is("one"));
+        onView(withText("Continue")).perform(click());
+
+        waitUntilViewMatchesCondition(withText("Label Match"), isCompletelyDisplayed());
+        assertThat(getElementValue(mTestRule.getWebContents(), "select"), is("three"));
+        onView(withText("Continue")).perform(click());
+
+        waitUntilViewMatchesCondition(withText("Label Starts With"), isCompletelyDisplayed());
+        assertThat(getElementValue(mTestRule.getWebContents(), "select"), is("two"));
+    }
+
+    @Test
+    @MediumTest
+    public void clickingOnElementToHide() {
+        ArrayList<ActionProto> list = new ArrayList<>();
+
+        SelectorProto element_click =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#touch_area_one"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setClick(ClickProto.newBuilder()
+                                           .setElementToClick(element_click)
+                                           .setClickType(ClickType.CLICK))
+                         .build());
+        SelectorProto element_tap =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#touch_area_five"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setClick(ClickProto.newBuilder()
+                                           .setElementToClick(element_tap)
+                                           .setClickType(ClickType.TAP))
+                         .build());
+        SelectorProto element_js =
+                (SelectorProto) SelectorProto.newBuilder()
+                        .addFilters(Filter.newBuilder().setCssSelector("#touch_area_six"))
+                        .build();
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setClick(ClickProto.newBuilder()
+                                           .setElementToClick(element_js)
+                                           .setClickType(ClickType.JAVASCRIPT))
+                         .build());
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Finished")
+                                            .addChoices(Choice.newBuilder()))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath(TEST_PAGE)
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Done")))
+                        .build(),
+                list);
+
+        runScript(script);
+
+        waitUntilViewMatchesCondition(withText("Finished"), isCompletelyDisplayed());
+        waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_one"));
+        waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_five"));
+        waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_six"));
+    }
+
+    private void runScript(AutofillAssistantTestScript script) {
+        AutofillAssistantTestService testService =
+                new AutofillAssistantTestService(Collections.singletonList(script));
+        startAutofillAssistant(mTestRule.getActivity(), testService);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java
index 2aa29a70..61b926f 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/bar_component/KeyboardAccessoryModernViewTest.java
@@ -158,6 +158,11 @@
         public void addOnInitializedCallback(Callback<Boolean> callback) {
             assert false : "Implement addOnInitializedCallback if you need it.";
         }
+
+        @Override
+        public void injectTracker(Tracker tracker) {
+            assert false : "This should only be called on a production tracker";
+        }
     }
 
     @Before
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 3d17a880..c9179a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -64,6 +64,7 @@
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromePhone;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManagerChromeTablet;
+import org.chromium.chrome.browser.compositor.layouts.LayoutStateProvider;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
 import org.chromium.chrome.browser.compositor.layouts.OverviewModeController;
 import org.chromium.chrome.browser.compositor.layouts.phone.StackLayout;
@@ -286,6 +287,9 @@
 
     private NextTabPolicySupplier mNextTabPolicySupplier;
 
+    private final OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderOneshotSupplier =
+            new OneshotSupplierImpl<>();
+    // TODO(crbug.com/1108496): Removed after all usages has been migrated to LayoutStateProvider.
     private final OneshotSupplierImpl<OverviewModeBehavior> mOverviewModeBehaviorSupplier =
             new OneshotSupplierImpl<>();
     private OverviewModeController mOverviewModeController;
@@ -565,7 +569,7 @@
             }
             mLayoutManager = new LayoutManagerChromePhone(compositorViewHolder, mContentContainer,
                     mStartSurfaceSupplier.get(), getTabContentManagerSupplier(),
-                    mOverviewModeBehaviorSupplier);
+                    mOverviewModeBehaviorSupplier, mLayoutStateProviderOneshotSupplier);
             mOverviewModeController = mLayoutManager;
         }
     }
@@ -575,9 +579,9 @@
 
         try (TraceEvent e = TraceEvent.scoped(
                      "ChromeTabbedActivity.setupCompositorContentPreNativeForTablet")) {
-            mLayoutManager =
-                    new LayoutManagerChromeTablet(getCompositorViewHolder(), mContentContainer,
-                            getTabContentManagerSupplier(), mOverviewModeBehaviorSupplier);
+            mLayoutManager = new LayoutManagerChromeTablet(getCompositorViewHolder(),
+                    mContentContainer, getTabContentManagerSupplier(),
+                    mOverviewModeBehaviorSupplier, mLayoutStateProviderOneshotSupplier);
             mOverviewModeController = mLayoutManager;
         }
     }
@@ -615,7 +619,7 @@
                 }
 
                 Layout activeLayout = mLayoutManager.getActiveLayout();
-                if (activeLayout instanceof StackLayout && !activeLayout.isHiding()) {
+                if (activeLayout instanceof StackLayout && !activeLayout.isStartingToHide()) {
                     RecordUserAction.record("MobileToolbarStackViewButtonInStackView");
                 } else if (!isInOverviewMode()) {
                     RecordUserAction.record("MobileToolbarStackViewButtonInBrowsingView");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
index 7142f40..eb59c8e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
@@ -22,6 +22,7 @@
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabLaunchType;
@@ -127,9 +128,7 @@
      */
     public void requestOpenSheet(String url, String title, boolean isIncognito) {
         mUrl = url;
-        Profile profile = isIncognito ? Profile.getLastUsedRegularProfile().getOffTheRecordProfile()
-                                      : Profile.getLastUsedRegularProfile();
-
+        Profile profile = getProfile(isIncognito);
         if (mMediator == null) {
             float topControlsHeight =
                     mContext.getResources().getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
@@ -139,7 +138,7 @@
         }
         if (mWebContents == null) {
             assert mSheetContent == null;
-            createWebContents(isIncognito);
+            createWebContents(profile);
             mSheetObserver = new EmptyBottomSheetObserver() {
                 private int mCloseReason;
 
@@ -208,11 +207,18 @@
         if (tracker.isInitialized()) tracker.notifyEvent(EventConstants.EPHEMERAL_TAB_USED);
     }
 
-    private void createWebContents(boolean incognito) {
+    private Profile getProfile(boolean isIncognito) {
+        if (!isIncognito) return Profile.getLastUsedRegularProfile();
+        Profile otrProfile = IncognitoUtils.getNonPrimaryOTRProfileFromWindowAndroid(mWindow);
+        return (otrProfile == null) ? Profile.getLastUsedRegularProfile().getPrimaryOTRProfile()
+                                    : otrProfile;
+    }
+
+    private void createWebContents(Profile profile) {
         assert mWebContents == null;
 
         // Creates an initially hidden WebContents which gets shown when the panel is opened.
-        mWebContents = WebContentsFactory.createWebContents(incognito, true);
+        mWebContents = WebContentsFactory.createWebContents(profile, true);
 
         mContentView = ContentView.createContentView(
                 mContext, null /* eventOffsetHandler */, mWebContents);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
index 504b63c..361325cc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/Layout.java
@@ -114,7 +114,10 @@
     protected LayoutTab[] mLayoutTabs;
 
     // True means that the layout is going to hide as soon as the animation finishes.
-    private boolean mIsHiding;
+    private boolean mIsStartingToHide;
+
+    // True means that the layout is going to show as soon as the animation finishes.
+    private boolean mIsStartingToShow;
 
     // The next id to show when the layout is hidden, or TabBase#INVALID_TAB_ID if no change.
     protected int mNextTabId = Tab.INVALID_TAB_ID;
@@ -416,15 +419,22 @@
      */
     public void startHiding(int nextTabId, boolean hintAtTabSelection) {
         mUpdateHost.startHiding(nextTabId, hintAtTabSelection);
-        mIsHiding = true;
+        mIsStartingToHide = true;
         mNextTabId = nextTabId;
     }
 
     /**
      * @return True is the layout is in the process of hiding itself.
      */
-    public boolean isHiding() {
-        return mIsHiding;
+    public boolean isStartingToHide() {
+        return mIsStartingToHide;
+    }
+
+    /**
+     * @return True is the layout is in the process of showing itself.
+     */
+    public boolean isStartingToShow() {
+        return mIsStartingToShow;
     }
 
     /**
@@ -438,6 +448,7 @@
      * To be called when the transition into the layout is done.
      */
     public void doneShowing() {
+        mIsStartingToShow = false;
         mUpdateHost.doneShowing();
     }
 
@@ -446,7 +457,7 @@
      * This is currently called by the renderer when all the animation are done while hiding.
      */
     public void doneHiding() {
-        mIsHiding = false;
+        mIsStartingToHide = false;
         if (mNextTabId != Tab.INVALID_TAB_ID) {
             TabModel model = mTabModelSelector.getModelForTabId(mNextTabId);
             if (model != null) {
@@ -477,7 +488,9 @@
      * @param animate Whether to play an entry animation.
      */
     public void show(long time, boolean animate) {
-        mIsHiding = false;
+        // TODO(crbug.com/1108496): Remove after LayoutManager explicitly hide the old layout.
+        mIsStartingToHide = false;
+        mIsStartingToShow = true;
         mNextTabId = Tab.INVALID_TAB_ID;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
index 85731fee..a1770db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManager.java
@@ -20,6 +20,7 @@
 import org.chromium.base.TraceEvent;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsUtils;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager;
@@ -80,7 +81,7 @@
  * includes lifecycle managment like showing/hiding this {@link Layout}.
  */
 public class LayoutManager implements LayoutUpdateHost, LayoutProvider,
-                                      TabModelSelector.CloseAllTabsDelegate {
+                                      TabModelSelector.CloseAllTabsDelegate, LayoutStateProvider {
     /** Sampling at 60 fps. */
     private static final long FRAME_DELTA_TIME_MS = 16;
 
@@ -118,6 +119,9 @@
     private TabModelObserver mTabModelFilterObserver;
 
     // External Observers
+    private final ObserverList<LayoutStateObserver> mLayoutObservers = new ObserverList<>();
+    // TODO(crbug.com/1108496): Remove after all SceneChangeObserver migrates to
+    // LayoutStateObserver.
     private final ObserverList<SceneChangeObserver> mSceneChangeObservers = new ObserverList<>();
 
     // Current Layout State
@@ -165,6 +169,9 @@
     /** A map of {@link SceneOverlay} to its position relative to the others. */
     private Map<Class, Integer> mOverlayOrderMap = new HashMap<>();
 
+    /** The supplier used to supply the LayoutStateProvider. */
+    private final OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderOneshotSupplier;
+
     /**
      * Protected class to handle {@link TabModelObserver} related tasks. Extending classes will
      * need to override any related calls to add new functionality */
@@ -241,14 +248,18 @@
      * @param host A {@link LayoutManagerHost} instance.
      * @param contentContainer A {@link ViewGroup} for Android views to be bound to.
      * @param tabContentManagerSupplier Supplier of the {@link TabContentManager} instance.
+     * @param layoutStateProviderOneshotSupplier Supplier used to supply the {@link
+     *         LayoutStateProvider}.
      */
     public LayoutManager(LayoutManagerHost host, ViewGroup contentContainer,
-            ObservableSupplier<TabContentManager> tabContentManagerSupplier) {
+            ObservableSupplier<TabContentManager> tabContentManagerSupplier,
+            OneshotSupplierImpl<LayoutStateProvider> layoutStateProviderOneshotSupplier) {
         mHost = host;
         mPxToDp = 1.f / mHost.getContext().getResources().getDisplayMetrics().density;
         mAndroidViewShownSupplier = new ObservableSupplierImpl<>();
         mAndroidViewShownSupplier.set(true);
         mTabContentManagerSupplier = tabContentManagerSupplier;
+        mLayoutStateProviderOneshotSupplier = layoutStateProviderOneshotSupplier;
 
         mContext = host.getContext();
         LayoutRenderHost renderHost = host.getLayoutRenderHost();
@@ -262,7 +273,7 @@
                 StripLayoutHelperManager.class,
                 StatusIndicatorCoordinator.getSceneOverlayClass(),
                 ContextualSearchPanel.class};
-        // clang-format off
+        // clang-format on
 
         for (int i = 0; i < overlayOrder.length; i++) mOverlayOrderMap.put(overlayOrder[i], i);
 
@@ -274,6 +285,8 @@
         mOverlayPanelManager = new OverlayPanelManager();
 
         mFrameRequestSupplier = new CompositorModelChangeProcessor.FrameRequestSupplier(this);
+
+        mLayoutStateProviderOneshotSupplier.set(this);
     }
 
     /**
@@ -418,9 +431,16 @@
         //  system.
         final Layout layout = getActiveLayout();
 
-        if (layout != null && layout.onUpdate(timeMs, dtMs) && layout.isHiding()
-                && areAnimatorsComplete) {
-            layout.doneHiding();
+        // TODO(crbug.com/1070281): Layout itself should decide when it's done hiding and done
+        //  showing.
+        if (layout != null && layout.onUpdate(timeMs, dtMs) && areAnimatorsComplete) {
+            if (layout.isStartingToHide()) {
+                layout.doneHiding();
+            } else if (layout.isStartingToShow()) {
+                // TODO(crbug.com/1108496): Call layout.doneShowing() here after all Layout have
+                //  been consolidated into the new Layout System to avoid Layout tests become flaky,
+                //  especially the StartSurfaceLayoutTest.
+            }
         }
 
         // TODO(1100332): Once overlays are MVC, this should no longer be needed.
@@ -883,10 +903,18 @@
     public void startHiding(int nextTabId, boolean hintAtTabSelection) {
         requestUpdate();
         if (hintAtTabSelection) {
+            notifyObserversOnTabSelectionHinted(nextTabId);
+
+            // TODO(crbug.com/1108496): Remove after migrates to LayoutStateObserver.
             for (SceneChangeObserver observer : mSceneChangeObservers) {
                 observer.onTabSelectionHinted(nextTabId);
             }
         }
+
+        Layout layoutBeingHidden = getActiveLayout();
+        notifyObserversLayoutStartedHiding(layoutBeingHidden.getLayoutType(),
+                shouldShowToolbarAnimationOnHide(layoutBeingHidden, nextTabId),
+                shouldDelayHideAnimation(layoutBeingHidden));
     }
 
     @Override
@@ -894,11 +922,19 @@
         // TODO: If next layout is default layout clear caches (should this be a sub layout thing?)
 
         assert mNextActiveLayout != null : "Need to have a next active layout.";
-        if (mNextActiveLayout != null) startShowing(mNextActiveLayout, true);
+        if (mNextActiveLayout != null) {
+            // Notify LayoutObservers the active layout is finished hiding.
+            notifyObserversLayoutFinishedHiding(getActiveLayout().getLayoutType());
+
+            startShowing(mNextActiveLayout, true);
+        }
     }
 
     @Override
-    public void doneShowing() {}
+    public void doneShowing() {
+        // Notify LayoutObservers the active layout is finished showing.
+        notifyObserversLayoutFinishedShowing(getActiveLayout().getLayoutType());
+    }
 
     /**
      * Should be called by control logic to show a new {@link Layout}.
@@ -920,6 +956,8 @@
             if (oldLayout != null) {
                 oldLayout.forceAnimationToFinish();
                 oldLayout.detachViews();
+
+                // TODO(crbug.com/1108496): hide oldLayout if it's not hidden.
             }
             layout.contextChanged(mHost.getContext());
             layout.attachViews(mContentContainer);
@@ -949,10 +987,14 @@
                 getActiveLayout().canHostBeFocusable());
         mHost.requestRender();
 
+        // TODO(crbug.com/1108496): Remove after migrates to LayoutStateObserver#onStartedShowing.
         // Notify observers about the new scene.
         for (SceneChangeObserver observer : mSceneChangeObservers) {
             observer.onSceneChange(getActiveLayout());
         }
+
+        notifyObserversLayoutStartedShowing(
+                layout.getLayoutType(), shouldShowToolbarAnimationOnShow(animate));
     }
 
     /**
@@ -1068,4 +1110,74 @@
     protected void switchToTab(Tab tab, int lastTabId) {
         tabSelected(tab.getId(), lastTabId, tab.isIncognito());
     }
+
+    // LayoutStateProvider implementation.
+    @Override
+    public boolean isLayoutVisible(int layoutType) {
+        return getActiveLayout().getLayoutType() == layoutType;
+    }
+
+    @Override
+    public void addObserver(LayoutStateObserver listener) {
+        mLayoutObservers.addObserver(listener);
+    }
+
+    @Override
+    public void removeObserver(LayoutStateObserver listener) {
+        mLayoutObservers.removeObserver(listener);
+    }
+
+    protected final void notifyObserversLayoutStartedShowing(
+            @Layout.LayoutType int layoutType, boolean showToolbar) {
+        mLayoutStateProviderOneshotSupplier.onAvailable((unused) -> {
+            for (LayoutStateObserver observer : mLayoutObservers) {
+                observer.onStartedShowing(layoutType, showToolbar);
+            }
+        });
+    }
+
+    protected final void notifyObserversLayoutFinishedShowing(@Layout.LayoutType int layoutType) {
+        mLayoutStateProviderOneshotSupplier.onAvailable((unused) -> {
+            for (LayoutStateObserver observer : mLayoutObservers) {
+                observer.onFinishedShowing(layoutType);
+            }
+        });
+    }
+
+    protected final void notifyObserversLayoutStartedHiding(
+            @Layout.LayoutType int layoutType, boolean showToolbar, boolean delayAnimation) {
+        mLayoutStateProviderOneshotSupplier.onAvailable((unused) -> {
+            for (LayoutStateObserver observer : mLayoutObservers) {
+                observer.onStartedHiding(layoutType, showToolbar, delayAnimation);
+            }
+        });
+    }
+
+    protected final void notifyObserversLayoutFinishedHiding(@Layout.LayoutType int layoutType) {
+        mLayoutStateProviderOneshotSupplier.onAvailable((unused) -> {
+            for (LayoutStateObserver observer : mLayoutObservers) {
+                observer.onFinishedHiding(layoutType);
+            }
+        });
+    }
+
+    protected final void notifyObserversOnTabSelectionHinted(int tabId) {
+        mLayoutStateProviderOneshotSupplier.onAvailable((unused) -> {
+            for (LayoutStateObserver observer : mLayoutObservers) {
+                observer.onTabSelectionHinted(tabId);
+            }
+        });
+    }
+
+    protected boolean shouldShowToolbarAnimationOnShow(boolean isAnimate) {
+        return false;
+    }
+
+    protected boolean shouldShowToolbarAnimationOnHide(Layout layoutBeingHidden, int nextTabId) {
+        return false;
+    }
+
+    protected boolean shouldDelayHideAnimation(Layout layoutBeingHidden) {
+        return false;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index 8238e9b..305773d4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -88,8 +88,10 @@
     public LayoutManagerChrome(LayoutManagerHost host, ViewGroup contentContainer,
             boolean createOverviewLayout, @Nullable StartSurface startSurface,
             ObservableSupplier<TabContentManager> tabContentManagerSupplier,
-            OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier) {
-        super(host, contentContainer, tabContentManagerSupplier);
+            OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier,
+            OneshotSupplierImpl<LayoutStateProvider> layoutStateProviderOneshotSupplier) {
+        super(host, contentContainer, tabContentManagerSupplier,
+                layoutStateProviderOneshotSupplier);
         Context context = host.getContext();
         LayoutRenderHost renderHost = host.getLayoutRenderHost();
 
@@ -259,6 +261,7 @@
 
         Layout layoutBeingShown = getActiveLayout();
 
+        // TODO(crbug.com/1108496): Remove after migrates to LayoutStateObserver.
         // Check if we should notify OverviewModeObservers.
         if (isOverviewLayout(layoutBeingShown)) {
             boolean showToolbar = animate && (!mEnableAnimations
@@ -271,6 +274,7 @@
     public void startHiding(int nextTabId, boolean hintAtTabSelection) {
         super.startHiding(nextTabId, hintAtTabSelection);
 
+        // TODO(crbug.com/1108496): Remove after migrates to LayoutStateObserver.
         Layout layoutBeingHidden = getActiveLayout();
         if (isOverviewLayout(layoutBeingHidden)) {
             boolean showToolbar = true;
@@ -289,6 +293,7 @@
     public void doneShowing() {
         super.doneShowing();
 
+        // TODO(crbug.com/1108496): Remove after migrates to LayoutStateObserver.
         if (isOverviewLayout(getActiveLayout())) {
             notifyObserversFinishedShowing();
         }
@@ -305,12 +310,34 @@
 
         super.doneHiding();
 
+        // TODO(crbug.com/1108496): Remove after migrates to Observer.
         if (isOverviewLayout(layoutBeingHidden)) {
             notifyObserversFinishedHiding();
         }
     }
 
     @Override
+    protected boolean shouldDelayHideAnimation(Layout layoutBeingHidden) {
+        return mEnableAnimations && layoutBeingHidden == mOverviewLayout && mCreatingNtp;
+    }
+
+    @Override
+    protected boolean shouldShowToolbarAnimationOnShow(boolean isAnimate) {
+        return isAnimate
+                && (!mEnableAnimations || getTabModelSelector().getCurrentModel().getCount() <= 0);
+    }
+
+    @Override
+    protected boolean shouldShowToolbarAnimationOnHide(Layout layoutBeingHidden, int nextTabId) {
+        boolean showAnimation = true;
+        if (mEnableAnimations && layoutBeingHidden == mOverviewLayout) {
+            final LayoutTab tab = layoutBeingHidden.getLayoutTab(nextTabId);
+            showAnimation = tab == null || !tab.showToolbar();
+        }
+        return showAnimation;
+    }
+
+    @Override
     protected void tabCreated(int id, int sourceId, @TabLaunchType int launchType,
             boolean incognito, boolean willBeSelected, float originX, float originY) {
         Tab newTab = TabModelUtils.getTabById(getTabModelSelector().getModel(incognito), id);
@@ -394,7 +421,7 @@
     @Override
     public void hideOverview(boolean animate) {
         Layout activeLayout = getActiveLayout();
-        if (activeLayout != null && !activeLayout.isHiding()) {
+        if (activeLayout != null && !activeLayout.isStartingToHide()) {
             if (animate) {
                 activeLayout.onTabSelecting(time(), Tab.INVALID_TAB_ID);
             } else {
@@ -423,7 +450,7 @@
     @Override
     public boolean overviewVisible() {
         Layout activeLayout = getActiveLayout();
-        return isOverviewLayout(activeLayout) && !activeLayout.isHiding();
+        return isOverviewLayout(activeLayout) && !activeLayout.isStartingToHide();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
index 43f9d355..7dbb7eb7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
@@ -41,9 +41,10 @@
     public LayoutManagerChromePhone(LayoutManagerHost host, ViewGroup contentContainer,
             StartSurface startSurface,
             ObservableSupplier<TabContentManager> tabContentManagerSupplier,
-            OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier) {
+            OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier,
+            OneshotSupplierImpl<LayoutStateProvider> layoutStateProviderOneshotSupplier) {
         super(host, contentContainer, true, startSurface, tabContentManagerSupplier,
-                overviewModeBehaviorSupplier);
+                overviewModeBehaviorSupplier, layoutStateProviderOneshotSupplier);
     }
 
     @Override
@@ -130,7 +131,7 @@
 
     @Override
     protected void tabCreating(int sourceId, String url, boolean isIncognito) {
-        if (!getActiveLayout().isHiding() && overlaysHandleTabCreating()
+        if (!getActiveLayout().isStartingToHide() && overlaysHandleTabCreating()
                 && getActiveLayout().handlesTabCreating()) {
             // If the current layout in the foreground, let it handle the tab creation animation.
             // This check allows us to switch from the StackLayout to the SimpleAnimationLayout
@@ -138,7 +139,7 @@
             getActiveLayout().onTabCreating(sourceId);
         } else if (animationsEnabled()) {
             if (!overviewVisible()) {
-                if (getActiveLayout() != null && getActiveLayout().isHiding()) {
+                if (getActiveLayout() != null && getActiveLayout().isStartingToHide()) {
                     setNextLayout(mSimpleAnimationLayout);
                     // The method Layout#doneHiding() will automatically show the next layout.
                     getActiveLayout().doneHiding();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
index a0f4ff5..8f24eee4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -37,9 +37,10 @@
      */
     public LayoutManagerChromeTablet(LayoutManagerHost host, ViewGroup contentContainer,
             ObservableSupplier<TabContentManager> tabContentManagerSupplier,
-            OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier) {
+            OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier,
+            OneshotSupplierImpl<LayoutStateProvider> layoutStateProviderOneshotSupplier) {
         super(host, contentContainer, false, null, tabContentManagerSupplier,
-                overviewModeBehaviorSupplier);
+                overviewModeBehaviorSupplier, layoutStateProviderOneshotSupplier);
 
         mTabStripLayoutHelperManager = new StripLayoutHelperManager(
                 host.getContext(), this, mHost.getLayoutRenderHost(), () -> mTitleCache);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutStateProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutStateProvider.java
new file mode 100644
index 0000000..bb1fcf32
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutStateProvider.java
@@ -0,0 +1,71 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.compositor.layouts;
+
+/**
+ * Exposes the current {@link Layout} state as well as a way to listen to {@link Layout} state
+ * changes.
+ */
+public interface LayoutStateProvider {
+    /**
+     * An observer that is notified when the {@link Layout} state changes.
+     */
+    interface LayoutStateObserver {
+        // TODO(crbug.com/1108496): Reiterate to see whether the showToolbar param is needed.
+        /**
+         * Called when Layout starts showing.
+         * @param layoutType LayoutType of the started showing Layout.
+         * @param showToolbar Whether or not to show the normal toolbar when animating into the
+         */
+        default void onStartedShowing(@Layout.LayoutType int layoutType, boolean showToolbar) {}
+
+        /**
+         * Called when Layout finishes showing.
+         * @param layoutType LayoutType of the finished showing Layout.
+         */
+        default void onFinishedShowing(@Layout.LayoutType int layoutType) {}
+
+        // TODO(crbug.com/1108496): Reiterate to see whether the showToolbar and delayAnimation
+        // param is needed.
+        /**
+         * Called when Layout starts hiding.
+         * @param layoutType LayoutType of the started hiding Layout.
+         * @param showToolbar    Whether or not to show the normal toolbar when animating out of
+         *                       showing Layout.
+         * @param delayAnimation Whether or not to delay any related animations until after Layout
+         */
+        default void onStartedHiding(
+                @Layout.LayoutType int layoutType, boolean showToolbar, boolean delayAnimation) {}
+
+        /**
+         * Called when Layout finishes hiding.
+         * @param layoutType LayoutType of the finished hiding Layout.
+         */
+        default void onFinishedHiding(@Layout.LayoutType int layoutType) {}
+
+        /**
+         * Called when a layout wants to hint that a new tab might be selected soon. This is not
+         * called every time a tab is selected.
+         * @param tabId The id of the tab that might be selected soon.
+         */
+        default void onTabSelectionHinted(int tabId) {}
+    }
+
+    /**
+     * @return Whether or not the {@link Layout} is visible.
+     * @param layoutType whether the {@link Layout} give {@link Layout.LayoutType} is visible.
+     */
+    boolean isLayoutVisible(@Layout.LayoutType int layoutType);
+
+    /**
+     * @param listener Registers {@code listener} for all layout status changes.
+     */
+    void addObserver(LayoutStateObserver listener);
+
+    /**
+     * @param listener Unregisters {@code listener} for all layout status changes.
+     */
+    void removeObserver(LayoutStateObserver listener);
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
index 234227a7..841faaa2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
@@ -1727,7 +1727,7 @@
             mStackTabs = new StackTab[count];
 
             final boolean isIncognito = mTabList.isIncognito();
-            final boolean needTitle = !mLayout.isHiding();
+            final boolean needTitle = !mLayout.isStartingToHide();
             for (int i = 0; i < count; ++i) {
                 Tab tab = mTabList.getTabAt(i);
                 int tabId = tab != null ? tab.getId() : Tab.INVALID_TAB_ID;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCompositorContentInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCompositorContentInitializer.java
index 8d8f1ba..4561f996 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCompositorContentInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabCompositorContentInitializer.java
@@ -9,6 +9,7 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
 import org.chromium.chrome.browser.compositor.layouts.LayoutManager;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
@@ -67,8 +68,8 @@
     @Override
     public void onFinishNativeInitialization() {
         ViewGroup contentContainer = mActivity.findViewById(android.R.id.content);
-        LayoutManager layoutDriver = new LayoutManager(
-                mCompositorViewHolder.get(), contentContainer, mTabContentManagerSupplier);
+        LayoutManager layoutDriver = new LayoutManager(mCompositorViewHolder.get(),
+                contentContainer, mTabContentManagerSupplier, new OneshotSupplierImpl<>());
 
         mCompositorViewHolderInitializer.initializeCompositorContent(layoutDriver,
                 mActivity.findViewById(org.chromium.chrome.R.id.url_bar), contentContainer,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
index e1e9fbd..7220633 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ChromePaymentRequestService.java
@@ -413,7 +413,8 @@
             // request.show() is called.
             mPaymentUiService.calculateWhetherShouldSkipShowingPaymentRequestUi(mIsUserGestureShow,
                     mURLPaymentMethodIdentifiersSupported,
-                    mPaymentRequestService.skipUiForNonUrlPaymentMethodIdentifiers());
+                    mPaymentRequestService.skipUiForNonUrlPaymentMethodIdentifiers(),
+                    mPaymentOptions);
             if (!buildUI(chromeActivity)) return;
             if (!mPaymentUiService.shouldSkipShowingPaymentRequestUi()
                     && mSkipToGPayHelper == null) {
@@ -1570,7 +1571,8 @@
             assert mIsFinishedQueryingPaymentApps;
             mPaymentUiService.calculateWhetherShouldSkipShowingPaymentRequestUi(mIsUserGestureShow,
                     mURLPaymentMethodIdentifiersSupported,
-                    mPaymentRequestService.skipUiForNonUrlPaymentMethodIdentifiers());
+                    mPaymentRequestService.skipUiForNonUrlPaymentMethodIdentifiers(),
+                    mPaymentOptions);
             if (!buildUI(chromeActivity)) return;
             if (!mPaymentUiService.shouldSkipShowingPaymentRequestUi()
                     && mSkipToGPayHelper == null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
index ab2f297..1c758e9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
@@ -270,6 +270,7 @@
     // Implement WebContentsObserver:
     @Override
     public void didFailLoad(boolean isMainFrame, int errorCode, String failingUrl) {
+        if (!isMainFrame) return;
         mHandler.post(() -> {
             mCloseReason = CloseReason.FAIL_LOAD;
             mHider.run();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
index e5cba31..3d416d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentUiService.java
@@ -1385,14 +1385,14 @@
     }
 
     /**
+     * @param options The payment options specified in the payment request.
      * @return true when there is exactly one available payment app which can provide all requested
      * information including shipping address and payer's contact information whenever needed.
      */
-    public boolean onlySingleAppCanProvideAllRequiredInformation() {
+    public boolean onlySingleAppCanProvideAllRequiredInformation(PaymentOptions options) {
         assert mPaymentMethodsSection != null;
 
-        if (mParams.hasClosed()) return false;
-        if (!PaymentOptionsUtils.requestAnyInformation(mParams.getPaymentOptions())) {
+        if (!PaymentOptionsUtils.requestAnyInformation(options)) {
             return mPaymentMethodsSection.getSize() == 1
                     && !((PaymentApp) mPaymentMethodsSection.getItem(0)).isAutofillInstrument();
         }
@@ -1401,11 +1401,10 @@
         int sectionSize = mPaymentMethodsSection.getSize();
         for (int i = 0; i < sectionSize; i++) {
             PaymentApp app = (PaymentApp) mPaymentMethodsSection.getItem(i);
-            if ((!mParams.getPaymentOptions().requestShipping || app.handlesShippingAddress())
-                    && (!mParams.getPaymentOptions().requestPayerName || app.handlesPayerName())
-                    && (!mParams.getPaymentOptions().requestPayerPhone || app.handlesPayerPhone())
-                    && (!mParams.getPaymentOptions().requestPayerEmail
-                            || app.handlesPayerEmail())) {
+            if ((!options.requestShipping || app.handlesShippingAddress())
+                    && (!options.requestPayerName || app.handlesPayerName())
+                    && (!options.requestPayerPhone || app.handlesPayerPhone())
+                    && (!options.requestPayerEmail || app.handlesPayerEmail())) {
                 // There is more than one available app that can provide all merchant requested
                 // information information.
                 if (anAppCanProvideAllInfo) return false;
@@ -1448,10 +1447,11 @@
      *         identifier is specified in payment request.
      * @param skipUiForNonUrlPaymentMethodIdentifiers True when skip UI is available for non-url
      *         based payment method identifiers (e.g., basic-card).
+     * @param options The payment options specified in the payment request.
      */
     public void calculateWhetherShouldSkipShowingPaymentRequestUi(boolean isUserGestureShow,
             boolean urlPaymentMethodIdentifiersSupported,
-            boolean skipUiForNonUrlPaymentMethodIdentifiers) {
+            boolean skipUiForNonUrlPaymentMethodIdentifiers, PaymentOptions options) {
         assert mPaymentMethodsSection != null;
         PaymentApp selectedApp = (PaymentApp) mPaymentMethodsSection.getSelectedItem();
 
@@ -1465,7 +1465,7 @@
                 // the payment request UI, thus can't be skipped.
                 && (urlPaymentMethodIdentifiersSupported || skipUiForNonUrlPaymentMethodIdentifiers)
                 && mPaymentMethodsSection.getSize() >= 1
-                && onlySingleAppCanProvideAllRequiredInformation()
+                && onlySingleAppCanProvideAllRequiredInformation(options)
                 // Skip to payment app only if it can be pre-selected.
                 && selectedApp != null
                 // Skip to payment app only if user gesture is provided when it is required to
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 16e95ab..c53d58a1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -4,6 +4,17 @@
 
 package org.chromium.chrome.browser.banners;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+
+import static org.chromium.chrome.test.util.ViewUtils.waitForView;
+
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.app.Instrumentation.ActivityMonitor;
@@ -18,9 +29,12 @@
 import android.support.test.uiautomator.UiSelector;
 import android.view.View;
 
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.matcher.RootMatchers;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
+import org.hamcrest.Matcher;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
@@ -36,6 +50,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -46,6 +61,7 @@
 import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
 import org.chromium.chrome.browser.engagement.SiteEngagementService;
+import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.infobar.InfoBarContainer;
@@ -53,6 +69,8 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabUtils;
+import org.chromium.chrome.browser.ui.appmenu.AppMenuCoordinator;
+import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport;
 import org.chromium.chrome.browser.webapps.WebappDataStorage;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -61,6 +79,9 @@
 import org.chromium.chrome.test.util.browser.TabLoadObserver;
 import org.chromium.chrome.test.util.browser.TabTitleObserver;
 import org.chromium.chrome.test.util.browser.webapps.WebappTestPage;
+import org.chromium.components.feature_engagement.CppWrappedTestTracker;
+import org.chromium.components.feature_engagement.EventConstants;
+import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.infobars.InfoBar;
 import org.chromium.components.infobars.InfoBarAnimationListener;
 import org.chromium.components.infobars.InfoBarUiItem;
@@ -82,7 +103,7 @@
  * Tests the app banners.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "use-java-proxy-tracker"})
 public class AppBannerManagerTest {
     @Rule
     public ChromeTabbedActivityTestRule mTabbedActivityTestRule =
@@ -94,6 +115,12 @@
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
+    // A callback that fires when the IPH system sends an event.
+    private final CallbackHelper mOnEventCallback = new CallbackHelper();
+
+    // The ID of the last event received.
+    private String mLastNotifyEvent;
+
     private static final String NATIVE_APP_MANIFEST_WITH_ID =
             "/chrome/test/data/banners/play_app_manifest.json";
 
@@ -183,6 +210,7 @@
     private PackageManager mPackageManager;
     private EmbeddedTestServer mTestServer;
     private UiDevice mUiDevice;
+    private CppWrappedTestTracker mTracker;
 
     @Before
     public void setUp() throws Exception {
@@ -198,8 +226,20 @@
         mTabbedActivityTestRule.startMainActivityOnBlankPage();
         // Must be set after native has loaded.
         mDetailsDelegate = new MockAppDetailsDelegate();
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> { AppBannerManager.setAppDetailsDelegate(mDetailsDelegate); });
+        mTracker = new CppWrappedTestTracker(FeatureConstants.PWA_INSTALL_AVAILABLE_FEATURE) {
+            @Override
+            public void notifyEvent(String event) {
+                super.notifyEvent(event);
+                mOnEventCallback.notifyCalled();
+            }
+        };
+
+        ThreadUtils.runOnUiThreadBlocking(() -> {
+            AppBannerManager.setAppDetailsDelegate(mDetailsDelegate);
+
+            Profile profile = Profile.getLastUsedRegularProfile();
+            TrackerFactory.getTrackerForProfile(profile).injectTracker(mTracker);
+        });
 
         AppBannerManager.ignoreChromeChannelForTesting();
         AppBannerManager.setTotalEngagementForTesting(10);
@@ -209,7 +249,9 @@
 
     @After
     public void tearDown() {
-        mTestServer.stopAndDestroyServer();
+        if (mTestServer != null) {
+            mTestServer.stopAndDestroyServer();
+        }
     }
 
     private void resetEngagementForUrl(final String url, final double engagement) {
@@ -656,4 +698,43 @@
                         WEB_APP_MANIFEST_WITH_UNSUPPORTED_PLATFORM, "call_stashed_prompt_on_click"),
                 false);
     }
+
+    @Test
+    @MediumTest
+    @Feature({"AppBanners"})
+    @CommandLineFlags.Add("enable-features=" + ChromeFeatureList.INSTALLABLE_AMBIENT_BADGE_INFOBAR)
+    public void testInProductHelp() throws Exception {
+        // Visit a site that is a PWA. The ambient badge should show.
+        String webBannerUrl = WebappTestPage.getServiceWorkerUrl(mTestServer);
+        resetEngagementForUrl(webBannerUrl, 10);
+
+        InfoBarContainer container = mTabbedActivityTestRule.getInfoBarContainer();
+        final InfobarListener listener = new InfobarListener();
+        container.addAnimationListener(listener);
+
+        Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
+        new TabLoadObserver(tab).fullyLoadUrl(webBannerUrl);
+        waitUntilAmbientBadgeInfoBarAppears(mTabbedActivityTestRule);
+
+        waitForHelpBubble(withText(R.string.iph_pwa_install_available_text)).perform(click());
+        assertThat(mTracker.wasDismissed(), is(true));
+
+        int callCount = mOnEventCallback.getCallCount();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            AppMenuCoordinator coordinator = mTabbedActivityTestRule.getAppMenuCoordinator();
+            AppMenuTestSupport.showAppMenu(coordinator, null, false);
+            AppMenuTestSupport.callOnItemClick(coordinator, R.id.add_to_homescreen_id);
+        });
+        mOnEventCallback.waitForCallback(callCount, 1);
+
+        assertThat(mTracker.getLastEvent(), is(EventConstants.PWA_INSTALL_MENU_SELECTED));
+    }
+
+    private ViewInteraction waitForHelpBubble(Matcher<View> matcher) {
+        View mainDecorView = mTabbedActivityTestRule.getActivity().getWindow().getDecorView();
+        return onView(isRoot())
+                .inRoot(RootMatchers.withDecorView(not(is(mainDecorView))))
+                .check(waitForView(matcher));
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
index aa29d42b..9afeeca 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerTest.java
@@ -10,7 +10,6 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.support.test.InstrumentationRegistry;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
@@ -28,10 +27,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.base.Log;
 import org.chromium.base.MathUtils;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.UiThreadTest;
+import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
@@ -68,6 +69,8 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
+import java.util.concurrent.TimeoutException;
+
 /**
  * Unit tests for {@link org.chromium.chrome.browser.compositor.layouts.LayoutManagerChrome}
  */
@@ -92,6 +95,13 @@
 
     private float mDpToPx;
 
+    private OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderSupplier;
+
+    class LayoutObserverCallbackHelper extends CallbackHelper {
+        @Layout.LayoutType
+        public int layoutType;
+    }
+
     private void initializeMotionEvent() {
         mProperties[0] = new PointerProperties();
         mProperties[0].id = 0;
@@ -172,8 +182,14 @@
                 new ObservableSupplierImpl<>();
         OneshotSupplierImpl<OverviewModeBehavior> overviewModeBehaviorSupplier =
                 new OneshotSupplierImpl<>();
+
+        if (mLayoutStateProviderSupplier == null) {
+            mLayoutStateProviderSupplier = new OneshotSupplierImpl<>();
+        }
+
         mManagerPhone = new LayoutManagerChromePhone(layoutManagerHost, container, null,
-                tabContentManagerSupplier, overviewModeBehaviorSupplier);
+                tabContentManagerSupplier, overviewModeBehaviorSupplier,
+                mLayoutStateProviderSupplier);
         tabContentManagerSupplier.set(tabContentManager);
         mManager = mManagerPhone;
         CompositorAnimationHandler.setTestingMode(true);
@@ -641,6 +657,207 @@
         verifyStartSurfaceLayoutEnable(TabListCoordinator.TabListMode.LIST);
     }
 
+    @Test
+    @MediumTest
+    public void testLayoutObserverNotification_ShowAndHide_ToolbarSwipe() throws TimeoutException {
+        LayoutObserverCallbackHelper startedShowingCallback = new LayoutObserverCallbackHelper();
+        LayoutObserverCallbackHelper finishedShowingCallback = new LayoutObserverCallbackHelper();
+        LayoutObserverCallbackHelper startedHidingCallback = new LayoutObserverCallbackHelper();
+        LayoutObserverCallbackHelper finishedHidingCallback = new LayoutObserverCallbackHelper();
+
+        setUpShowAndHideLayoutObserverNotification(startedShowingCallback, finishedShowingCallback,
+                startedHidingCallback, finishedHidingCallback);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            performToolbarSideSwipe(ScrollDirection.RIGHT);
+            Assert.assertEquals(
+                    Layout.LayoutType.TOOLBAR_SWIPE, mManager.getActiveLayout().getLayoutType());
+            Assert.assertTrue(mLayoutStateProviderSupplier.get().isLayoutVisible(
+                    Layout.LayoutType.TOOLBAR_SWIPE));
+        });
+
+        // The |startedShowingCallback| callCount 0 is reserved for the default layout during
+        // initialization. Because LayoutManager does not explicitly hide the old layout when a new
+        // layout is forced to show, the callCount for |finishedShowingCallback|,
+        // |startedHidingCallback|, and |finishedHidingCallback| are still 0.
+        // TODO(crbug.com/1108496): update the callCount when LayoutManager explicitly hide the old
+        // layout.
+        startedShowingCallback.waitForCallback(1);
+        Assert.assertEquals(Layout.LayoutType.TOOLBAR_SWIPE, startedShowingCallback.layoutType);
+
+        // TODO(crbug.com/1108496): Enable the following two lines after layout.doneShowing() is
+        //  always call when trying to show a Layout.
+        // finishedShowingCallback.waitForCallback(0);
+        // Assert.assertEquals(Layout.LayoutType.TOOLBAR_SWIPE, finishedShowingCallback.layoutType);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            finishToolbarSideSwipe();
+            Assert.assertEquals(
+                    Layout.LayoutType.BROWSING, mManager.getActiveLayout().getLayoutType());
+            Assert.assertTrue(
+                    mLayoutStateProviderSupplier.get().isLayoutVisible(Layout.LayoutType.BROWSING));
+        });
+
+        startedHidingCallback.waitForCallback(0);
+        Assert.assertEquals(Layout.LayoutType.TOOLBAR_SWIPE, startedHidingCallback.layoutType);
+
+        finishedHidingCallback.waitForCallback(0);
+        Assert.assertEquals(Layout.LayoutType.TOOLBAR_SWIPE, finishedHidingCallback.layoutType);
+
+        startedShowingCallback.waitForCallback(2);
+        Assert.assertEquals(Layout.LayoutType.BROWSING, startedShowingCallback.layoutType);
+
+        // TODO(crbug.com/1108496): Enable the following two lines after layout.doneShowing() is
+        //  always call when trying to show a Layout.
+        // finishedShowingCallback.waitForCallback(1);
+        // Assert.assertEquals(Layout.LayoutType.BROWSING, finishedShowingCallback.layoutType);
+    }
+
+    @Test
+    @MediumTest
+    public void testLayoutObserverNotification_ShowAndHide_TabSwitcher() throws TimeoutException {
+        LayoutObserverCallbackHelper startedShowingCallback = new LayoutObserverCallbackHelper();
+        LayoutObserverCallbackHelper finishedShowingCallback = new LayoutObserverCallbackHelper();
+        LayoutObserverCallbackHelper startedHidingCallback = new LayoutObserverCallbackHelper();
+        LayoutObserverCallbackHelper finishedHidingCallback = new LayoutObserverCallbackHelper();
+
+        setUpShowAndHideLayoutObserverNotification(startedShowingCallback, finishedShowingCallback,
+                startedHidingCallback, finishedHidingCallback);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mManager.showOverview(true);
+
+            Assert.assertTrue(
+                    "layoutManager is way too long to end motion", simulateTime(mManager, 1000));
+            Assert.assertEquals(
+                    Layout.LayoutType.TAB_SWITCHER, mManager.getActiveLayout().getLayoutType());
+            Assert.assertTrue(mLayoutStateProviderSupplier.get().isLayoutVisible(
+                    Layout.LayoutType.TAB_SWITCHER));
+        });
+
+        // The |startedShowingCallback| callCount 0 is reserved for the default layout during
+        // initialization. Because LayoutManager does not explicitly hide the old layout when a new
+        // layout is forced to show, the callCount for |finishedShowingCallback|,
+        // |startedHidingCallback|, and |finishedHidingCallback| are still 0.
+        // TODO(crbug.com/1108496): update the callCount when LayoutManager explicitly hide the old
+        // layout.
+        startedShowingCallback.waitForCallback(1);
+        Assert.assertEquals(Layout.LayoutType.TAB_SWITCHER, startedShowingCallback.layoutType);
+
+        finishedShowingCallback.waitForCallback(0);
+        Assert.assertEquals(Layout.LayoutType.TAB_SWITCHER, finishedShowingCallback.layoutType);
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mManagerPhone.hideOverview(true);
+            Assert.assertTrue(
+                    "layoutManager is way too long to end motion", simulateTime(mManager, 1000));
+
+            Assert.assertTrue(
+                    mLayoutStateProviderSupplier.get().isLayoutVisible(Layout.LayoutType.BROWSING));
+        });
+
+        startedHidingCallback.waitForCallback(0);
+        Assert.assertEquals(Layout.LayoutType.TAB_SWITCHER, startedHidingCallback.layoutType);
+
+        finishedHidingCallback.waitForCallback(0);
+        Assert.assertEquals(Layout.LayoutType.TAB_SWITCHER, finishedHidingCallback.layoutType);
+
+        startedShowingCallback.waitForCallback(2);
+        Assert.assertEquals(Layout.LayoutType.BROWSING, startedShowingCallback.layoutType);
+
+        // TODO(crbug.com/1108496): Enable the following two lines after layout.doneShowing() is
+        //  always call when trying to show a Layout.
+        // finishedShowingCallback.waitForCallback(1);
+        // Assert.assertEquals(Layout.LayoutType.BROWSING, finishedShowingCallback.layoutType);
+    }
+
+    private void setUpShowAndHideLayoutObserverNotification(
+            LayoutObserverCallbackHelper startedShowingCallback,
+            LayoutObserverCallbackHelper finishedShowingCallback,
+            LayoutObserverCallbackHelper startedHidingCallback,
+            LayoutObserverCallbackHelper finishedHidingCallback) throws TimeoutException {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mLayoutStateProviderSupplier = new OneshotSupplierImpl<>();
+
+            mLayoutStateProviderSupplier.onAvailable((layoutStateProvider) -> {
+                layoutStateProvider.addObserver(new LayoutStateProvider.LayoutStateObserver() {
+                    @Override
+                    public void onStartedShowing(int layoutType, boolean showToolbar) {
+                        Log.d(TAG, "Started to show: " + layoutType);
+                        startedShowingCallback.layoutType = layoutType;
+                        startedShowingCallback.notifyCalled();
+                    }
+
+                    @Override
+                    public void onFinishedShowing(int layoutType) {
+                        Log.d(TAG, "finished to show: " + layoutType);
+                        finishedShowingCallback.layoutType = layoutType;
+                        finishedShowingCallback.notifyCalled();
+                    }
+
+                    @Override
+                    public void onStartedHiding(
+                            int layoutType, boolean showToolbar, boolean delayAnimation) {
+                        Log.d(TAG, "Started to hide: " + layoutType);
+                        startedHidingCallback.layoutType = layoutType;
+                        startedHidingCallback.notifyCalled();
+                    }
+
+                    @Override
+                    public void onFinishedHiding(int layoutType) {
+                        Log.d(TAG, "finished to hide: " + layoutType);
+                        finishedHidingCallback.layoutType = layoutType;
+                        finishedHidingCallback.notifyCalled();
+                    }
+                });
+            });
+
+            initializeLayoutManagerPhone(2, 0);
+            Assert.assertEquals(
+                    Layout.LayoutType.BROWSING, mManager.getActiveLayout().getLayoutType());
+        });
+
+        startedShowingCallback.waitForCallback(0);
+        Assert.assertEquals(Layout.LayoutType.BROWSING, startedShowingCallback.layoutType);
+    }
+
+    @Test
+    @MediumTest
+    public void testLayoutObserverNotification_TabSelectionHinted() throws TimeoutException {
+        CallbackHelper tabSelectionHintedCallback = new CallbackHelper();
+
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mLayoutStateProviderSupplier = new OneshotSupplierImpl<>();
+
+            mLayoutStateProviderSupplier.onAvailable((layoutStateProvider) -> {
+                layoutStateProvider.addObserver(new LayoutStateProvider.LayoutStateObserver() {
+                    @Override
+                    public void onTabSelectionHinted(int tabId) {
+                        Log.d(TAG, "onTabSelectionHinted");
+                        tabSelectionHintedCallback.notifyCalled();
+                    }
+                });
+            });
+
+            initializeLayoutManagerPhone(2, 0);
+            mManager.showOverview(true);
+
+            Assert.assertTrue(
+                    "layoutManager is way too long to end motion", simulateTime(mManager, 1000));
+            Assert.assertEquals(
+                    Layout.LayoutType.TAB_SWITCHER, mManager.getActiveLayout().getLayoutType());
+
+            mManagerPhone.hideOverview(true);
+            Assert.assertTrue(
+                    "layoutManager is way too long to end motion", simulateTime(mManager, 1000));
+
+            Assert.assertEquals(
+                    Layout.LayoutType.BROWSING, mManager.getActiveLayout().getLayoutType());
+        });
+
+        tabSelectionHintedCallback.waitForCallback(0);
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -721,6 +938,7 @@
         final int finalId = model.getTabAt(finalIndex).getId();
 
         performToolbarSideSwipe(direction);
+        finishToolbarSideSwipe();
 
         Assert.assertEquals("Unexpected model change after side swipe", model.isIncognito(),
                 mTabModelSelector.isIncognitoSelected());
@@ -751,14 +969,21 @@
         // TODO(mdjones): Update implementation of EdgeSwipeHandler to work this way by default.
         eventHandler.swipeUpdated(deltaX, 0.f, deltaX, 0.f, deltaX, 0.f);
         eventHandler.swipeUpdated(deltaX, 0.f, deltaX, 0.f, deltaX, 0.f);
-        eventHandler.swipeFinished();
-
         Assert.assertTrue("LayoutManager#getActiveLayout() should be ToolbarSwipeLayout",
                 mManager.getActiveLayout() instanceof ToolbarSwipeLayout);
         Assert.assertTrue("LayoutManager took too long to finish the animations",
                 simulateTime(mManager, 1000));
     }
 
+    private void finishToolbarSideSwipe() {
+        final EdgeSwipeHandler eventHandler = mManager.getToolbarSwipeHandler();
+        Assert.assertNotNull("LayoutManager#getToolbarSwipeHandler() returned null", eventHandler);
+
+        eventHandler.swipeFinished();
+        Assert.assertTrue("LayoutManager took too long to finish the animations",
+                simulateTime(mManager, 1000));
+    }
+
     @Override
     public Tab createTab(int id, boolean incognito) {
         return MockTab.createAndInitialize(id, incognito);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
index b6a89b2ab..e59151a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
@@ -223,6 +223,9 @@
     @Test
     @MediumTest
     public void testDismissCollapsedSheetWithDismissButton() {
+        MetricsUtils.HistogramDelta accountConsistencyHistogram =
+                new HistogramDelta("Signin.AccountConsistencyPromoAction",
+                        AccountConsistencyPromoAction.DISMISSED_BUTTON);
         buildAndShowCollapsedBottomSheet();
         onView(withText(PROFILE_DATA1.getAccountName())).check(matches(isDisplayed()));
         BottomSheetController controller = getBottomSheetController();
@@ -232,6 +235,7 @@
         Assert.assertFalse(controller.isSheetOpen());
         verify(mAccountPickerDelegateMock).onDismiss();
         Assert.assertEquals(0, mFakeProfileDataSource.getNumberOfObservers());
+        Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
     }
 
     @Test
@@ -386,6 +390,9 @@
     @Test
     @MediumTest
     public void testSignInGeneralError() {
+        MetricsUtils.HistogramDelta accountConsistencyHistogram =
+                new HistogramDelta("Signin.AccountConsistencyPromoAction",
+                        AccountConsistencyPromoAction.GENERIC_ERROR_SHOWN);
         // Throws a connection error during the sign-in action
         doAnswer(invocation -> {
             Callback<GoogleServiceAuthError> onSignInErrorCallback = invocation.getArgument(1);
@@ -409,11 +416,15 @@
         onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
         onView(withId(R.id.account_picker_signin_spinner_view)).check(matches(not(isDisplayed())));
         onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
+        Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
     }
 
     @Test
     @MediumTest
     public void testSignInAuthError() {
+        MetricsUtils.HistogramDelta accountConsistencyHistogram =
+                new HistogramDelta("Signin.AccountConsistencyPromoAction",
+                        AccountConsistencyPromoAction.AUTH_ERROR_SHOWN);
         CoreAccountInfo coreAccountInfo =
                 mAccountManagerTestRule.toCoreAccountInfo(PROFILE_DATA1.getAccountName());
         // Throws an auth error during the sign-in action
@@ -437,6 +448,7 @@
         onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
         onView(withId(R.id.account_picker_signin_spinner_view)).check(matches(not(isDisplayed())));
         onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
+        Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
     }
 
     @Test
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/SceneOverlayTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/SceneOverlayTest.java
index efbbd4e9..e2d5e24 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/SceneOverlayTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/SceneOverlayTest.java
@@ -5,6 +5,8 @@
 package org.chromium.chrome.browser.compositor.layouts;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -21,6 +23,7 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.OneshotSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
@@ -53,6 +56,9 @@
     @Mock
     private ObservableSupplier<TabContentManager> mTabContentManagerSupplier;
 
+    @Mock
+    private OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderOneshotSupplier;
+
     private LayoutManager mLayoutManager;
 
     @Before
@@ -62,9 +68,10 @@
         when(mLayoutManagerHost.getContext()).thenReturn(mContext);
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+        doNothing().when(mLayoutStateProviderOneshotSupplier).set(any());
 
-        mLayoutManager =
-                new LayoutManager(mLayoutManagerHost, mContainerView, mTabContentManagerSupplier);
+        mLayoutManager = new LayoutManager(mLayoutManagerHost, mContainerView,
+                mTabContentManagerSupplier, mLayoutStateProviderOneshotSupplier);
     }
 
     @Test
diff --git a/chrome/android/monochrome/BUILD.gn b/chrome/android/monochrome/BUILD.gn
index a76b89db..0252d05 100644
--- a/chrome/android/monochrome/BUILD.gn
+++ b/chrome/android/monochrome/BUILD.gn
@@ -2,22 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/android/config.gni")
+import("//build/config/python.gni")
 
-group("monochrome_public_apk_checker") {
+python_library("monochrome_public_apk_checker") {
   testonly = true
+  pydeps_file =
+      "//chrome/android/monochrome/scripts/monochrome_python_tests.pydeps"
   data_deps = [
     "//android_webview:system_webview_apk",
     "//chrome/android:chrome_public_apk",
     "//chrome/android:monochrome_public_apk",
   ]
-
-  data = [
-    "scripts/",
-    "//testing/scripts/common.py",
-    "//testing/scripts/run_isolated_script_test.py",
-    "//third_party/catapult/devil/",
-    "//third_party/catapult/third_party/typ/",
-    "//testing/xvfb.py",
-  ]
 }
diff --git a/chrome/android/monochrome/scripts/monochrome_android_manifest_test.py b/chrome/android/monochrome/scripts/monochrome_android_manifest_test.py
index dd9dcfbd..7f29074 100755
--- a/chrome/android/monochrome/scripts/monochrome_android_manifest_test.py
+++ b/chrome/android/monochrome/scripts/monochrome_android_manifest_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python2.7
 #
 # Copyright 2020 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/chrome/android/monochrome/scripts/monochrome_dexdump_test.py b/chrome/android/monochrome/scripts/monochrome_dexdump_test.py
index 91f4404..12aef8a9 100755
--- a/chrome/android/monochrome/scripts/monochrome_dexdump_test.py
+++ b/chrome/android/monochrome/scripts/monochrome_dexdump_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python2.7
 #
 # Copyright 2020 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/chrome/android/monochrome/scripts/monochrome_python_tests.pydeps b/chrome/android/monochrome/scripts/monochrome_python_tests.pydeps
new file mode 100644
index 0000000..1694d42
--- /dev/null
+++ b/chrome/android/monochrome/scripts/monochrome_python_tests.pydeps
@@ -0,0 +1,61 @@
+# Generated by running:
+#   build/print_python_deps.py chrome/android/monochrome/scripts
+build/android/devil_chromium.py
+build/android/pylib/__init__.py
+build/android/pylib/constants/__init__.py
+build/android/pylib/constants/host_paths.py
+chrome/android/monochrome/scripts/monochrome_android_manifest_test.py
+chrome/android/monochrome/scripts/monochrome_apk_checker_test.py
+chrome/android/monochrome/scripts/monochrome_dexdump_test.py
+chrome/android/monochrome/scripts/monochrome_python_tests.py
+third_party/catapult/common/py_utils/py_utils/__init__.py
+third_party/catapult/common/py_utils/py_utils/cloud_storage.py
+third_party/catapult/common/py_utils/py_utils/cloud_storage_global_lock.py
+third_party/catapult/common/py_utils/py_utils/lock.py
+third_party/catapult/dependency_manager/dependency_manager/__init__.py
+third_party/catapult/dependency_manager/dependency_manager/archive_info.py
+third_party/catapult/dependency_manager/dependency_manager/base_config.py
+third_party/catapult/dependency_manager/dependency_manager/cloud_storage_info.py
+third_party/catapult/dependency_manager/dependency_manager/dependency_info.py
+third_party/catapult/dependency_manager/dependency_manager/dependency_manager_util.py
+third_party/catapult/dependency_manager/dependency_manager/exceptions.py
+third_party/catapult/dependency_manager/dependency_manager/local_path_info.py
+third_party/catapult/dependency_manager/dependency_manager/manager.py
+third_party/catapult/dependency_manager/dependency_manager/uploader.py
+third_party/catapult/devil/devil/__init__.py
+third_party/catapult/devil/devil/android/__init__.py
+third_party/catapult/devil/devil/android/constants/__init__.py
+third_party/catapult/devil/devil/android/constants/chrome.py
+third_party/catapult/devil/devil/android/ndk/__init__.py
+third_party/catapult/devil/devil/android/ndk/abis.py
+third_party/catapult/devil/devil/android/sdk/__init__.py
+third_party/catapult/devil/devil/android/sdk/build_tools.py
+third_party/catapult/devil/devil/android/sdk/keyevent.py
+third_party/catapult/devil/devil/android/sdk/version_codes.py
+third_party/catapult/devil/devil/base_error.py
+third_party/catapult/devil/devil/constants/__init__.py
+third_party/catapult/devil/devil/constants/exit_codes.py
+third_party/catapult/devil/devil/devil_env.py
+third_party/catapult/devil/devil/utils/__init__.py
+third_party/catapult/devil/devil/utils/cmd_helper.py
+third_party/catapult/devil/devil/utils/lazy/__init__.py
+third_party/catapult/devil/devil/utils/lazy/weak_constant.py
+third_party/catapult/devil/devil/utils/reraiser_thread.py
+third_party/catapult/devil/devil/utils/timeout_retry.py
+third_party/catapult/devil/devil/utils/watchdog_timer.py
+third_party/catapult/third_party/typ/typ/__init__.py
+third_party/catapult/third_party/typ/typ/arg_parser.py
+third_party/catapult/third_party/typ/typ/artifacts.py
+third_party/catapult/third_party/typ/typ/expectations_parser.py
+third_party/catapult/third_party/typ/typ/fakes/__init__.py
+third_party/catapult/third_party/typ/typ/fakes/host_fake.py
+third_party/catapult/third_party/typ/typ/host.py
+third_party/catapult/third_party/typ/typ/json_results.py
+third_party/catapult/third_party/typ/typ/pool.py
+third_party/catapult/third_party/typ/typ/printer.py
+third_party/catapult/third_party/typ/typ/python_2_3_compat.py
+third_party/catapult/third_party/typ/typ/runner.py
+third_party/catapult/third_party/typ/typ/stats.py
+third_party/catapult/third_party/typ/typ/test_case.py
+third_party/catapult/third_party/typ/typ/version.py
+third_party/catapult/third_party/zipfile/zipfile_2_7_13.py
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 475a305d..e4a28ea 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4351,6 +4351,7 @@
       "//chromeos/services/secure_channel/public/mojom",
       "//chromeos/settings",
       "//chromeos/strings",
+      "//chromeos/ui/base",
       "//chromeos/ui/vector_icons",
       "//components/arc",
       "//components/arc:arc_base",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0e94e95..cdfc5f7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3787,6 +3787,15 @@
     {"files-filters-in-recents", flag_descriptions::kFiltersInRecentsName,
      flag_descriptions::kFiltersInRecentsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFiltersInRecents)},
+    {"files-js-modules", flag_descriptions::kFilesJsModulesName,
+     flag_descriptions::kFilesJsModulesDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kFilesJsModules)},
+    {"audio-player-js-modules", flag_descriptions::kAudioPlayerJsModulesName,
+     flag_descriptions::kAudioPlayerJsModulesDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kAudioPlayerJsModules)},
+    {"video-player-js-modules", flag_descriptions::kVideoPlayerJsModulesName,
+     flag_descriptions::kVideoPlayerJsModulesDescription, kOsCrOS,
+     FEATURE_VALUE_TYPE(chromeos::features::kVideoPlayerJsModules)},
     {"files-ng", flag_descriptions::kFilesNGName,
      flag_descriptions::kFilesNGDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(chromeos::features::kFilesNG)},
diff --git a/chrome/browser/android/externalauth/BUILD.gn b/chrome/browser/android/externalauth/BUILD.gn
new file mode 100644
index 0000000..d311a83
--- /dev/null
+++ b/chrome/browser/android/externalauth/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+# We can't use android_library here since gn doesn't allow those with empty
+# sources. Use java_group until we have files in this module.
+java_group("java") {
+  # Empty module for a three sided patch.
+}
diff --git a/chrome/browser/android/externalauth/OWNERS b/chrome/browser/android/externalauth/OWNERS
new file mode 100644
index 0000000..2df71ef
--- /dev/null
+++ b/chrome/browser/android/externalauth/OWNERS
@@ -0,0 +1,3 @@
+file://chrome/android/OWNERS
+
+peconn@chromium.org
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index b3f3285..febd4d4 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -253,14 +253,14 @@
   return false;
 }
 
-bool AppBannerManager::ShouldDeferToRelatedApplication() const {
+bool AppBannerManager::ShouldDeferToRelatedNonWebApp() const {
   for (const auto& related_app : manifest_.related_applications) {
     if (manifest_.prefer_related_applications &&
-        IsSupportedAppPlatform(
+        IsSupportedNonWebAppPlatform(
             related_app.platform.value_or(base::string16()))) {
       return true;
     }
-    if (IsRelatedAppInstalled(related_app))
+    if (IsRelatedNonWebAppInstalled(related_app))
       return true;
   }
   return false;
@@ -397,7 +397,7 @@
     return;
   }
 
-  if (ShouldDeferToRelatedApplication()) {
+  if (ShouldDeferToRelatedNonWebApp()) {
     SetInstallableWebAppCheckResult(
         InstallableWebAppCheckResult::kByUserRequest);
     Stop(PREFER_RELATED_APPLICATIONS);
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index 853d6d1..007083d 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -195,9 +195,9 @@
   // GetAppIdentifier() must return a valid value for this method to work.
   bool CheckIfShouldShowBanner();
 
-  // Returns whether the site would prefer a related application be installed
-  // instead of the PWA or a related application is already installed.
-  bool ShouldDeferToRelatedApplication() const;
+  // Returns whether the site would prefer a related non-web app be installed
+  // instead of the PWA or a related non-web app is already installed.
+  bool ShouldDeferToRelatedNonWebApp() const;
 
   // Return a string identifying this app for metrics.
   virtual std::string GetAppIdentifier();
@@ -219,11 +219,14 @@
   bool ShouldBypassEngagementChecks() const;
 
   // Returns whether installation of apps from |platform| is supported on the
-  // current device.
-  virtual bool IsSupportedAppPlatform(const base::string16& platform) const = 0;
+  // current device and the platform delivers apps considered replacements for
+  // web apps.
+  virtual bool IsSupportedNonWebAppPlatform(
+      const base::string16& platform) const = 0;
 
-  // Returns whether |related_app| is already installed.
-  virtual bool IsRelatedAppInstalled(
+  // Returns whether |related_app| is already installed and considered a
+  // replacement for the manifest's web app.
+  virtual bool IsRelatedNonWebAppInstalled(
       const blink::Manifest::RelatedApplication& related_app) const = 0;
 
   // Returns whether the current page is already installed as a web app, or
diff --git a/chrome/browser/banners/app_banner_manager_android.cc b/chrome/browser/banners/app_banner_manager_android.cc
index 7e04d1d2..ddf34a91 100644
--- a/chrome/browser/banners/app_banner_manager_android.cc
+++ b/chrome/browser/banners/app_banner_manager_android.cc
@@ -551,13 +551,13 @@
     infobar_service->RemoveInfoBar(ambient_badge_infobar);
 }
 
-bool AppBannerManagerAndroid::IsSupportedAppPlatform(
+bool AppBannerManagerAndroid::IsSupportedNonWebAppPlatform(
     const base::string16& platform) const {
   // TODO(https://crbug.com/949430): Implement for Android apps.
   return false;
 }
 
-bool AppBannerManagerAndroid::IsRelatedAppInstalled(
+bool AppBannerManagerAndroid::IsRelatedNonWebAppInstalled(
     const blink::Manifest::RelatedApplication& related_app) const {
   // TODO(https://crbug.com/949430): Implement for Android apps.
   return false;
diff --git a/chrome/browser/banners/app_banner_manager_android.h b/chrome/browser/banners/app_banner_manager_android.h
index 03e5af9d..a4a3b3d 100644
--- a/chrome/browser/banners/app_banner_manager_android.h
+++ b/chrome/browser/banners/app_banner_manager_android.h
@@ -97,8 +97,9 @@
   void MaybeShowAmbientBadge() override;
   base::WeakPtr<AppBannerManager> GetWeakPtr() override;
   void InvalidateWeakPtrs() override;
-  bool IsSupportedAppPlatform(const base::string16& platform) const override;
-  bool IsRelatedAppInstalled(
+  bool IsSupportedNonWebAppPlatform(
+      const base::string16& platform) const override;
+  bool IsRelatedNonWebAppInstalled(
       const blink::Manifest::RelatedApplication& related_app) const override;
 
  private:
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index 8cd83ac..69a21436 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -128,11 +128,12 @@
 
   void InvalidateWeakPtrs() override { weak_factory_.InvalidateWeakPtrs(); }
 
-  bool IsSupportedAppPlatform(const base::string16& platform) const override {
+  bool IsSupportedNonWebAppPlatform(
+      const base::string16& platform) const override {
     return base::EqualsASCII(platform, "chrome_web_store");
   }
 
-  bool IsRelatedAppInstalled(
+  bool IsRelatedNonWebAppInstalled(
       const blink::Manifest::RelatedApplication& related_app) const override {
     // Corresponds to the id listed in manifest_listing_related_chrome_app.json.
     return base::EqualsASCII(related_app.platform.value_or(base::string16()),
diff --git a/chrome/browser/banners/app_banner_manager_desktop.cc b/chrome/browser/banners/app_banner_manager_desktop.cc
index 0aefdede..666fbed 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/app_banner_manager_desktop.cc
@@ -103,7 +103,7 @@
   weak_factory_.InvalidateWeakPtrs();
 }
 
-bool AppBannerManagerDesktop::IsSupportedAppPlatform(
+bool AppBannerManagerDesktop::IsSupportedNonWebAppPlatform(
     const base::string16& platform) const {
   if (base::EqualsASCII(platform, kPlatformChromeWebStore))
     return true;
@@ -119,7 +119,7 @@
   return false;
 }
 
-bool AppBannerManagerDesktop::IsRelatedAppInstalled(
+bool AppBannerManagerDesktop::IsRelatedNonWebAppInstalled(
     const blink::Manifest::RelatedApplication& related_app) const {
   if (!related_app.id || related_app.id->empty() || !related_app.platform ||
       related_app.platform->empty()) {
diff --git a/chrome/browser/banners/app_banner_manager_desktop.h b/chrome/browser/banners/app_banner_manager_desktop.h
index fcb53e1c..0a05e0a 100644
--- a/chrome/browser/banners/app_banner_manager_desktop.h
+++ b/chrome/browser/banners/app_banner_manager_desktop.h
@@ -57,8 +57,9 @@
   // AppBannerManager overrides.
   base::WeakPtr<AppBannerManager> GetWeakPtr() override;
   void InvalidateWeakPtrs() override;
-  bool IsSupportedAppPlatform(const base::string16& platform) const override;
-  bool IsRelatedAppInstalled(
+  bool IsSupportedNonWebAppPlatform(
+      const base::string16& platform) const override;
+  bool IsRelatedNonWebAppInstalled(
       const blink::Manifest::RelatedApplication& related_app) const override;
 
   // Called when the web app install initiated by a banner has completed.
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index d986e54..c8273ee 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -853,6 +853,8 @@
     "borealis/borealis_installer_factory.h",
     "borealis/borealis_installer_impl.cc",
     "borealis/borealis_installer_impl.h",
+    "borealis/borealis_launch_watcher.cc",
+    "borealis/borealis_launch_watcher.h",
     "borealis/borealis_prefs.cc",
     "borealis/borealis_prefs.h",
     "borealis/borealis_service.cc",
@@ -3262,6 +3264,7 @@
     "borealis/borealis_context_manager_unittest.cc",
     "borealis/borealis_features_unittest.cc",
     "borealis/borealis_installer_unittest.cc",
+    "borealis/borealis_launch_watcher_unittest.cc",
     "borealis/borealis_task_unittest.cc",
     "camera_mic/vm_camera_mic_manager_unittest.cc",
     "cert_provisioning/cert_provisioning_invalidator_unittest.cc",
diff --git a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
index d2a2b48..c153095 100644
--- a/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_launcher.cc
@@ -7,9 +7,9 @@
 #include <memory>
 #include <string>
 
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "components/arc/arc_util.h"
 #include "components/arc/metrics/arc_metrics_constants.h"
 #include "ui/aura/env.h"
@@ -82,8 +82,8 @@
     return false;
   // Stop observing as target window is already found.
   StopObserving();
-  window->SetProperty(ash::kWindowPinTypeKey,
-                      ash::WindowPinType::kTrustedPinned);
+  window->SetProperty(chromeos::kWindowPinTypeKey,
+                      chromeos::WindowPinType::kTrustedPinned);
   if (delegate_)
     delegate_->OnAppWindowLaunched();
   return true;
diff --git a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc
index 8ff62f5..82fd671 100644
--- a/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc
+++ b/chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.cc
@@ -5,7 +5,6 @@
 #include <chrome/browser/chromeos/app_mode/web_app/web_kiosk_app_launcher.h>
 #include <memory>
 
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "base/bind.h"
 #include "base/logging.h"
@@ -18,6 +17,7 @@
 #include "chrome/browser/web_applications/components/web_app_data_retriever.h"
 #include "chrome/browser/web_applications/components/web_app_url_loader.h"
 #include "chrome/browser/web_applications/web_app_install_task.h"
+#include "chromeos/ui/base/window_pin_type.h"
 #include "components/account_id/account_id.h"
 #include "ui/aura/window.h"
 #include "ui/base/page_transition_types.h"
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager.cc b/chrome/browser/chromeos/arc/session/arc_session_manager.cc
index a3d7732b..d25ee76 100644
--- a/chrome/browser/chromeos/arc/session/arc_session_manager.cc
+++ b/chrome/browser/chromeos/arc/session/arc_session_manager.cc
@@ -447,7 +447,7 @@
     : arc_session_runner_(std::move(arc_session_runner)),
       adb_sideloading_availability_delegate_(
           std::move(adb_sideloading_availability_delegate)),
-      attempt_user_exit_callback_(base::Bind(chrome::AttemptUserExit)),
+      attempt_user_exit_callback_(base::BindRepeating(chrome::AttemptUserExit)),
       property_files_source_dir_(base::FilePath(
           IsArcVmEnabled() ? kPropertyFilesPathVm : kPropertyFilesPath)),
       property_files_dest_dir_(
@@ -1208,8 +1208,8 @@
   arc_session_runner_->RequestStartMiniInstance();
 
   terms_of_service_negotiator_->StartNegotiation(
-      base::Bind(&ArcSessionManager::OnTermsOfServiceNegotiated,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&ArcSessionManager::OnTermsOfServiceNegotiated,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcSessionManager::OnTermsOfServiceNegotiated(bool accepted) {
@@ -1266,8 +1266,8 @@
   android_management_checker_ = std::make_unique<ArcAndroidManagementChecker>(
       profile_, false /* retry_on_error */);
   android_management_checker_->StartCheck(
-      base::Bind(&ArcSessionManager::OnAndroidManagementChecked,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&ArcSessionManager::OnAndroidManagementChecked,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcSessionManager::OnAndroidManagementChecked(
@@ -1316,8 +1316,8 @@
   android_management_checker_ = std::make_unique<ArcAndroidManagementChecker>(
       profile_, true /* retry_on_error */);
   android_management_checker_->StartCheck(
-      base::Bind(&ArcSessionManager::OnBackgroundAndroidManagementChecked,
-                 weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&ArcSessionManager::OnBackgroundAndroidManagementChecked,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ArcSessionManager::OnBackgroundAndroidManagementChecked(
@@ -1546,7 +1546,7 @@
 }
 
 void ArcSessionManager::SetAttemptUserExitCallbackForTesting(
-    const base::Closure& callback) {
+    const base::RepeatingClosure& callback) {
   DCHECK(!callback.is_null());
   attempt_user_exit_callback_ = callback;
 }
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager.h b/chrome/browser/chromeos/arc/session/arc_session_manager.h
index ff6fbf9..6000a15 100644
--- a/chrome/browser/chromeos/arc/session/arc_session_manager.h
+++ b/chrome/browser/chromeos/arc/session/arc_session_manager.h
@@ -250,7 +250,8 @@
   void SetArcSessionRunnerForTesting(
       std::unique_ptr<ArcSessionRunner> arc_session_runner);
   ArcSessionRunner* GetArcSessionRunnerForTesting();
-  void SetAttemptUserExitCallbackForTesting(const base::Closure& callback);
+  void SetAttemptUserExitCallbackForTesting(
+      const base::RepeatingClosure& callback);
 
   // Returns whether the Play Store app is requested to be launched by this
   // class. Should be used only for tests.
@@ -418,7 +419,7 @@
   base::TimeTicks sign_in_start_time_;
   // The time when ARC was about to start.
   base::TimeTicks arc_start_time_;
-  base::Closure attempt_user_exit_callback_;
+  base::RepeatingClosure attempt_user_exit_callback_;
 
   ArcAppIdProviderImpl app_id_provider_;
 
diff --git a/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc b/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc
index 94ebc39..ea246baf 100644
--- a/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/chromeos/arc/session/arc_session_manager_unittest.cc
@@ -1282,7 +1282,8 @@
   // and not invoked then, including TearDown().
   bool terminated = false;
   arc_session_manager()->SetAttemptUserExitCallbackForTesting(
-      base::Bind([](bool* terminated) { *terminated = true; }, &terminated));
+      base::BindRepeating([](bool* terminated) { *terminated = true; },
+                          &terminated));
 
   arc_session_manager()->OnProvisioningFinished(
       ProvisioningResult::CHROME_SERVER_COMMUNICATION_ERROR, nullptr);
diff --git a/chrome/browser/chromeos/borealis/borealis_context.cc b/chrome/browser/chromeos/borealis/borealis_context.cc
index ee0e4013c..b3c5397 100644
--- a/chrome/browser/chromeos/borealis/borealis_context.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context.cc
@@ -6,6 +6,7 @@
 
 namespace borealis {
 
+BorealisContext::~BorealisContext() = default;
 BorealisContext::BorealisContext() = default;
 BorealisContext::BorealisContext(Profile* profile) : profile_(profile) {}
 
diff --git a/chrome/browser/chromeos/borealis/borealis_context.h b/chrome/browser/chromeos/borealis/borealis_context.h
index a1ccf05..51ee38b1 100644
--- a/chrome/browser/chromeos/borealis/borealis_context.h
+++ b/chrome/browser/chromeos/borealis/borealis_context.h
@@ -19,7 +19,7 @@
  public:
   BorealisContext(const BorealisContext&) = delete;
   BorealisContext& operator=(const BorealisContext&) = delete;
-  ~BorealisContext() = default;
+  ~BorealisContext();
 
   static BorealisContext* CreateBorealisContextForTesting() {
     return new BorealisContext();
@@ -31,9 +31,14 @@
   bool borealis_running() const { return borealis_running_; }
   void set_borealis_running(bool success) { borealis_running_ = success; }
 
-  const std::string& vm_name() { return vm_name_; }
+  const std::string& vm_name() const { return vm_name_; }
   void set_vm_name(std::string vm_name) { vm_name_ = std::move(vm_name); }
 
+  const std::string& container_name() const { return container_name_; }
+  void set_container_name(std::string container_name) {
+    container_name_ = std::move(container_name);
+  }
+
   const std::string& root_path() const { return root_path_; }
   void set_root_path(std::string path) { root_path_ = std::move(path); }
 
@@ -49,6 +54,7 @@
   Profile* profile_ = nullptr;
   bool borealis_running_ = false;
   std::string vm_name_;
+  std::string container_name_;
   std::string root_path_;
   base::FilePath disk_path_;
 };
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager.cc b/chrome/browser/chromeos/borealis/borealis_context_manager.cc
index 0b2f6391..d3b4492 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager.cc
@@ -35,7 +35,7 @@
   return failure_reason_;
 }
 
-const BorealisContext& BorealisContextManager::Result::Success() {
+const BorealisContext& BorealisContextManager::Result::Success() const {
   DCHECK(Ok());
   return *ctx_;
 }
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager.h b/chrome/browser/chromeos/borealis/borealis_context_manager.h
index 4d4e149..78313ded 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager.h
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager.h
@@ -17,7 +17,12 @@
 class BorealisContextManager : public KeyedService {
  public:
   // A list of possible outcomes for an attempt to startup borealis.
-  enum Status { kSuccess = 0, kMountFailed = 1 };
+  enum Status {
+    kSuccess = 0,
+    kMountFailed = 1,
+    kDiskImageFailed = 2,
+    kStartVmFailed = 3,
+  };
 
   // An attempt to launch borealis. If the launch succeeds, holds a reference to
   // the context created for that launch, otherwise holds an error.
@@ -42,7 +47,7 @@
 
     // In the event of a successful launch, returns a handle to the context
     // created for that launch.
-    const BorealisContext& Success();
+    const BorealisContext& Success() const;
 
    private:
     Status status_;
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc
index b4576476..f555519e 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.cc
@@ -4,10 +4,30 @@
 
 #include "chrome/browser/chromeos/borealis/borealis_context_manager_impl.h"
 
+#include <ostream>
+
 #include "base/logging.h"
 #include "chrome/browser/chromeos/borealis/borealis_context_manager.h"
 #include "chrome/browser/chromeos/borealis/borealis_task.h"
 
+namespace {
+
+std::ostream& operator<<(std::ostream& stream,
+                         borealis::BorealisContextManager::Status status) {
+  switch (status) {
+    case borealis::BorealisContextManager::kSuccess:
+      return stream << "Success";
+    case borealis::BorealisContextManager::kMountFailed:
+      return stream << "Mount Failed";
+    case borealis::BorealisContextManager::kDiskImageFailed:
+      return stream << "Disk Image Failed";
+    case borealis::BorealisContextManager::kStartVmFailed:
+      return stream << "Start VM Failed";
+  }
+}
+
+}  // namespace
+
 namespace borealis {
 
 BorealisContextManagerImpl::BorealisContextManagerImpl(Profile* profile)
@@ -24,7 +44,7 @@
   if (!is_borealis_starting_) {
     is_borealis_starting_ = true;
     task_queue_ = GetTasks();
-    NextTask(/*should_continue=*/true);
+    NextTask();
   }
 }
 
@@ -34,6 +54,7 @@
   task_queue.push(std::make_unique<MountDlc>());
   task_queue.push(std::make_unique<CreateDiskImage>());
   task_queue.push(std::make_unique<StartBorealisVm>());
+  task_queue.push(std::make_unique<AwaitBorealisStartup>());
   return task_queue;
 }
 
@@ -41,17 +62,7 @@
   callback_queue_.push(std::move(callback));
 }
 
-void BorealisContextManagerImpl::NextTask(bool should_continue) {
-  if (!should_continue) {
-    // TODO(b/168425531): Error handling should be expanded to give more
-    // information about which task failed, why it failed and what should happen
-    // as a result.
-    // For now just use some random error.
-    startup_status_ = kMountFailed;
-    startup_error_ = "A task failed when trying to start Borealis.";
-    OnQueueComplete();
-    return;
-  }
+void BorealisContextManagerImpl::NextTask() {
   if (task_queue_.empty()) {
     context_.set_borealis_running(true);
     is_borealis_running_ = true;
@@ -62,10 +73,23 @@
   current_task_ = std::move(task_queue_.front());
   task_queue_.pop();
   current_task_->Run(&context_,
-                     base::BindOnce(&BorealisContextManagerImpl::NextTask,
+                     base::BindOnce(&BorealisContextManagerImpl::TaskCallback,
                                     weak_factory_.GetWeakPtr()));
 }
 
+void BorealisContextManagerImpl::TaskCallback(Status status,
+                                              std::string error) {
+  startup_status_ = status;
+  if (startup_status_ == kSuccess) {
+    NextTask();
+    return;
+  }
+  startup_error_ = error;
+  LOG(ERROR) << "Startup failed: failure=" << startup_status_
+             << " message=" << startup_error_;
+  OnQueueComplete();
+}
+
 void BorealisContextManagerImpl::OnQueueComplete() {
   is_borealis_starting_ = false;
   while (!callback_queue_.empty()) {
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h
index a57e826..2927bb4d 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager_impl.h
@@ -33,7 +33,8 @@
 
  private:
   void AddCallback(ResultCallback callback);
-  void NextTask(bool should_continue);
+  void NextTask();
+  void TaskCallback(Status status, std::string error);
   void OnQueueComplete();
 
   // Returns the result of the startup (i.e. the context if it succeeds, or an
diff --git a/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc b/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc
index e2c6a67..0717f73 100644
--- a/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc
+++ b/chrome/browser/chromeos/borealis/borealis_context_manager_unittest.cc
@@ -21,19 +21,27 @@
 namespace {
 
 MATCHER(IsSuccessResult, "") {
-  return arg.Ok();
+  return arg.Ok() && arg.Success().vm_name() == "test_vm_name";
 }
 
 MATCHER(IsFailureResult, "") {
-  return !arg.Ok();
+  return !arg.Ok() && arg.Failure() == BorealisContextManager::kStartVmFailed &&
+         arg.FailureReason() == "Something went wrong!";
 }
 
 class MockTask : public BorealisTask {
  public:
   explicit MockTask(bool success) : success_(success) {}
   void Run(BorealisContext* context,
-           base::OnceCallback<void(bool)> callback) override {
-    std::move(callback).Run(/*should_continue=*/success_);
+           BorealisTask::CompletionStatusCallback callback) override {
+    if (success_) {
+      context->set_vm_name("test_vm_name");
+      std::move(callback).Run(BorealisContextManager::kSuccess, "");
+    } else {
+      // Just use a random error.
+      std::move(callback).Run(BorealisContextManager::kStartVmFailed,
+                              "Something went wrong!");
+    }
   }
   bool success_ = true;
 };
@@ -110,6 +118,18 @@
   EXPECT_FALSE(tasks.empty());
 }
 
+TEST_F(BorealisContextManagerTest, NoTasksImpliesSuccess) {
+  testing::StrictMock<ResultCallbackHandler> callback_expectation;
+
+  BorealisContextManagerImplForTesting context_manager(
+      profile_.get(), /*tasks=*/0, /*success=*/true);
+  EXPECT_CALL(callback_expectation, Callback(testing::_))
+      .WillOnce(testing::Invoke(
+          [](BorealisContextManager::Result result) { result.Ok(); }));
+  context_manager.StartBorealis(callback_expectation.GetCallback());
+  task_environment_.RunUntilIdle();
+}
+
 TEST_F(BorealisContextManagerTest, StartupSucceedsForSuccessfulTask) {
   testing::StrictMock<ResultCallbackHandler> callback_expectation;
 
diff --git a/chrome/browser/chromeos/borealis/borealis_launch_watcher.cc b/chrome/browser/chromeos/borealis/borealis_launch_watcher.cc
new file mode 100644
index 0000000..071fd05
--- /dev/null
+++ b/chrome/browser/chromeos/borealis/borealis_launch_watcher.cc
@@ -0,0 +1,59 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/borealis/borealis_launch_watcher.h"
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+
+namespace borealis {
+
+BorealisLaunchWatcher::BorealisLaunchWatcher(Profile* profile,
+                                             std::string vm_name)
+    : owner_id_(chromeos::ProfileHelper::GetUserIdHashFromProfile(profile)),
+      vm_name_(vm_name) {
+  chromeos::DBusThreadManager::Get()->GetCiceroneClient()->AddObserver(this);
+}
+
+BorealisLaunchWatcher::~BorealisLaunchWatcher() {
+  chromeos::DBusThreadManager::Get()->GetCiceroneClient()->RemoveObserver(this);
+}
+
+void BorealisLaunchWatcher::AwaitLaunch(OnLaunchCallback callback) {
+  if (container_started_signal_) {
+    std::move(callback).Run(container_started_signal_->container_name());
+  } else {
+    if (callback_queue_.empty()) {
+      base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE,
+          base::BindOnce(&BorealisLaunchWatcher::TimeoutCallback,
+                         weak_factory_.GetWeakPtr()),
+          timeout_);
+    }
+    callback_queue_.push(std::move(callback));
+  }
+}
+
+void BorealisLaunchWatcher::OnContainerStarted(
+    const vm_tools::cicerone::ContainerStartedSignal& signal) {
+  if (signal.owner_id() == owner_id_ && signal.vm_name() == vm_name_ &&
+      !container_started_signal_) {
+    container_started_signal_ = signal;
+    while (!callback_queue_.empty()) {
+      std::move(callback_queue_.front())
+          .Run(container_started_signal_->container_name());
+      callback_queue_.pop();
+    }
+  }
+}
+
+void BorealisLaunchWatcher::TimeoutCallback() {
+  while (!callback_queue_.empty()) {
+    std::move(callback_queue_.front()).Run(base::nullopt);
+    callback_queue_.pop();
+  }
+}
+
+}  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_launch_watcher.h b/chrome/browser/chromeos/borealis/borealis_launch_watcher.h
new file mode 100644
index 0000000..ba19e75
--- /dev/null
+++ b/chrome/browser/chromeos/borealis/borealis_launch_watcher.h
@@ -0,0 +1,58 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_LAUNCH_WATCHER_H_
+#define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_LAUNCH_WATCHER_H_
+
+#include "base/callback.h"
+#include "base/containers/queue.h"
+#include "base/memory/weak_ptr.h"
+#include "chromeos/dbus/cicerone_client.h"
+
+class Profile;
+
+namespace borealis {
+
+// Watches to see if a specified VM (from a given owner id and vm name) was
+// started.
+class BorealisLaunchWatcher : public chromeos::CiceroneClient::Observer {
+ public:
+  using OnLaunchCallback =
+      base::OnceCallback<void(base::Optional<std::string>)>;
+
+  BorealisLaunchWatcher(Profile* profile, std::string vm_name);
+  BorealisLaunchWatcher(const BorealisLaunchWatcher&) = delete;
+  BorealisLaunchWatcher() = delete;
+  BorealisLaunchWatcher& operator=(const BorealisLaunchWatcher&) = delete;
+  ~BorealisLaunchWatcher() override;
+
+  // Adds a callback to be run when the VM is launched, the callback will be run
+  // immediately if the VM has already been launched. If no event has been
+  // observed after |timeout_|, then the callback is run anyway. If successful
+  // the callback will be run with the name of the container that started,
+  // otherwise it will be run with nullopt.
+  void AwaitLaunch(OnLaunchCallback callback);
+
+  void SetTimeoutForTesting(base::TimeDelta time) { timeout_ = time; }
+
+ private:
+  void TimeoutCallback();
+
+  // CiceroneClient::Observer:
+  void OnContainerStarted(
+      const vm_tools::cicerone::ContainerStartedSignal& signal) override;
+
+  std::string owner_id_;
+  std::string vm_name_;
+  base::TimeDelta timeout_ = base::TimeDelta::FromMilliseconds(5000);
+  base::Optional<vm_tools::cicerone::ContainerStartedSignal>
+      container_started_signal_;
+  base::queue<OnLaunchCallback> callback_queue_;
+
+  base::WeakPtrFactory<BorealisLaunchWatcher> weak_factory_{this};
+};
+
+}  // namespace borealis
+
+#endif  // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_LAUNCH_WATCHER_H_
diff --git a/chrome/browser/chromeos/borealis/borealis_launch_watcher_unittest.cc b/chrome/browser/chromeos/borealis/borealis_launch_watcher_unittest.cc
new file mode 100644
index 0000000..2b3dffe
--- /dev/null
+++ b/chrome/browser/chromeos/borealis/borealis_launch_watcher_unittest.cc
@@ -0,0 +1,163 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/borealis/borealis_launch_watcher.h"
+
+#include <memory>
+
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/fake_cicerone_client.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace borealis {
+namespace {
+
+class CallbackForTestingExpectation {
+ public:
+  base::OnceCallback<void(base::Optional<std::string>)> GetCallback() {
+    return base::BindOnce(&CallbackForTestingExpectation::Callback,
+                          base::Unretained(this));
+  }
+  MOCK_METHOD(void, Callback, (base::Optional<std::string>), ());
+};
+
+class BorealisLaunchWatcherTest : public testing::Test {
+ public:
+  BorealisLaunchWatcherTest() = default;
+  BorealisLaunchWatcherTest(const BorealisLaunchWatcherTest&) = delete;
+  BorealisLaunchWatcherTest& operator=(const BorealisLaunchWatcherTest&) =
+      delete;
+  ~BorealisLaunchWatcherTest() override = default;
+
+ protected:
+  void SetUp() override {
+    chromeos::DBusThreadManager::Initialize();
+    fake_cicerone_client_ = static_cast<chromeos::FakeCiceroneClient*>(
+        chromeos::DBusThreadManager::Get()->GetCiceroneClient());
+  }
+
+  void TearDown() override {
+    chromeos::DBusThreadManager::Shutdown();
+    profile_.reset();
+  }
+
+  // Owned by DBusThreadManager
+  chromeos::FakeCiceroneClient* fake_cicerone_client_ = nullptr;
+
+  std::unique_ptr<TestingProfile> profile_;
+  content::BrowserTaskEnvironment task_environment_;
+
+ private:
+  void CreateProfile() {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetProfileName("defaultprofile");
+    profile_ = profile_builder.Build();
+  }
+};
+
+TEST_F(BorealisLaunchWatcherTest, VmStartsCallbackRan) {
+  testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
+  BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
+  vm_tools::cicerone::ContainerStartedSignal signal;
+  signal.set_owner_id(
+      chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
+  signal.set_vm_name("FooVm");
+  signal.set_container_name("FooContainer");
+
+  EXPECT_CALL(callback_expectation,
+              Callback(base::Optional<std::string>("FooContainer")));
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+  fake_cicerone_client_->NotifyContainerStarted(std::move(signal));
+
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(BorealisLaunchWatcherTest, VmTimesOutCallbackRan) {
+  testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
+  BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
+  watcher.SetTimeoutForTesting(base::TimeDelta::FromMilliseconds(0));
+
+  EXPECT_CALL(callback_expectation,
+              Callback(base::Optional<std::string>(base::nullopt)));
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(BorealisLaunchWatcherTest, VmAlreadyStartedCallbackRan) {
+  testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
+  BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
+  vm_tools::cicerone::ContainerStartedSignal signal;
+  signal.set_owner_id(
+      chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
+  signal.set_vm_name("FooVm");
+  signal.set_container_name("FooContainer");
+
+  EXPECT_CALL(callback_expectation,
+              Callback(base::Optional<std::string>("FooContainer")));
+  fake_cicerone_client_->NotifyContainerStarted(std::move(signal));
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(BorealisLaunchWatcherTest, VmStartsMultipleCallbacksRan) {
+  testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
+  BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
+  vm_tools::cicerone::ContainerStartedSignal signal;
+  signal.set_owner_id(
+      chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
+  signal.set_vm_name("FooVm");
+  signal.set_container_name("FooContainer");
+
+  EXPECT_CALL(callback_expectation,
+              Callback(base::Optional<std::string>("FooContainer")))
+      .Times(2);
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+  fake_cicerone_client_->NotifyContainerStarted(std::move(signal));
+
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(BorealisLaunchWatcherTest, VmTimesOutMultipleCallbacksRan) {
+  testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
+  BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
+  watcher.SetTimeoutForTesting(base::TimeDelta::FromMilliseconds(0));
+
+  EXPECT_CALL(callback_expectation,
+              Callback(base::Optional<std::string>(base::nullopt)))
+      .Times(2);
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+
+  task_environment_.RunUntilIdle();
+}
+
+TEST_F(BorealisLaunchWatcherTest, OtherVmsStartBorealisTimesOutCallbackRan) {
+  testing::StrictMock<CallbackForTestingExpectation> callback_expectation;
+  BorealisLaunchWatcher watcher(profile_.get(), "FooVm");
+  watcher.SetTimeoutForTesting(base::TimeDelta::FromMilliseconds(0));
+  vm_tools::cicerone::ContainerStartedSignal signal1;
+  signal1.set_owner_id("not-the-owner");
+  signal1.set_vm_name("FooVm");
+  vm_tools::cicerone::ContainerStartedSignal signal2;
+  signal2.set_owner_id(
+      chromeos::ProfileHelper::GetUserIdHashFromProfile(profile_.get()));
+  signal2.set_vm_name("not-FooVm");
+
+  EXPECT_CALL(callback_expectation,
+              Callback(base::Optional<std::string>(base::nullopt)));
+  fake_cicerone_client_->NotifyContainerStarted(std::move(signal1));
+  fake_cicerone_client_->NotifyContainerStarted(std::move(signal2));
+  watcher.AwaitLaunch(callback_expectation.GetCallback());
+
+  task_environment_.RunUntilIdle();
+}
+
+}  // namespace
+}  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_task.cc b/chrome/browser/chromeos/borealis/borealis_task.cc
index 7aa848f3..14f5333 100644
--- a/chrome/browser/chromeos/borealis/borealis_task.cc
+++ b/chrome/browser/chromeos/borealis/borealis_task.cc
@@ -3,13 +3,16 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/borealis/borealis_task.h"
+#include <string>
 
 #include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
 #include "chrome/browser/chromeos/borealis/borealis_context.h"
 #include "chrome/browser/chromeos/borealis/borealis_util.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/constants/chromeos_features.h"
+#include "chromeos/dbus/concierge/concierge_service.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 
 namespace borealis {
@@ -31,12 +34,12 @@
     CompletionStatusCallback callback,
     const chromeos::DlcserviceClient::InstallResult& install_result) {
   if (install_result.error != dlcservice::kErrorNone) {
-    LOG(ERROR) << "Mounting the DLC for Borealis failed: "
-               << install_result.error;
-    std::move(callback).Run(false);
+    std::move(callback).Run(
+        BorealisContextManager::kMountFailed,
+        "Mounting the DLC for Borealis failed: " + install_result.error);
   } else {
     context->set_root_path(install_result.root_path);
-    std::move(callback).Run(true);
+    std::move(callback).Run(BorealisContextManager::kSuccess, "");
   }
 }
 
@@ -67,22 +70,23 @@
     CompletionStatusCallback callback,
     base::Optional<vm_tools::concierge::CreateDiskImageResponse> response) {
   if (!response) {
-    LOG(ERROR) << "Failed to create disk image for Borealis. Empty response.";
     context->set_disk_path(base::FilePath());
-    std::move(callback).Run(false);
+    std::move(callback).Run(
+        BorealisContextManager::kDiskImageFailed,
+        "Failed to create disk image for Borealis: Empty response.");
     return;
   }
 
   if (response->status() != vm_tools::concierge::DISK_STATUS_EXISTS &&
       response->status() != vm_tools::concierge::DISK_STATUS_CREATED) {
-    LOG(ERROR) << "Failed to create disk image for Borealis: "
-               << response->failure_reason();
     context->set_disk_path(base::FilePath());
-    std::move(callback).Run(false);
+    std::move(callback).Run(BorealisContextManager::kDiskImageFailed,
+                            "Failed to create disk image for Borealis: " +
+                                response->failure_reason());
     return;
   }
   context->set_disk_path(base::FilePath(response->disk_path()));
-  std::move(callback).Run(true);
+  std::move(callback).Run(BorealisContextManager::kSuccess, "");
 }
 
 StartBorealisVm::StartBorealisVm() = default;
@@ -126,24 +130,29 @@
     CompletionStatusCallback callback,
     base::Optional<vm_tools::concierge::StartVmResponse> response) {
   if (!response) {
-    LOG(ERROR) << "Failed to start Borealis VM. Empty response.";
-    std::move(callback).Run(false);
+    std::move(callback).Run(BorealisContextManager::kStartVmFailed,
+                            "Failed to start Borealis VM: Empty response.");
     return;
   }
 
-  if (response->status() == vm_tools::concierge::VM_STATUS_RUNNING) {
-    std::move(callback).Run(true);
+  if (response->status() == vm_tools::concierge::VM_STATUS_RUNNING ||
+      response->status() == vm_tools::concierge::VM_STATUS_STARTING) {
+    std::move(callback).Run(BorealisContextManager::kSuccess, "");
     return;
   }
 
-  if (response->status() == vm_tools::concierge::VM_STATUS_FAILURE ||
-      response->status() == vm_tools::concierge::VM_STATUS_UNKNOWN) {
-    LOG(ERROR) << "Failed to start Borealis VM: " << response->failure_reason();
-    std::move(callback).Run(false);
-    return;
-  }
-
-  DCHECK_EQ(response->status(), vm_tools::concierge::VM_STATUS_STARTING);
-  std::move(callback).Run(true);
+  std::move(callback).Run(
+      BorealisContextManager::kStartVmFailed,
+      "Failed to start Borealis VM: " + response->failure_reason() + " (code " +
+          base::NumberToString(response->status()) + ")");
 }
+
+void AwaitBorealisStartup::Run(BorealisContext* context,
+                               CompletionStatusCallback callback) {
+  // TODO(b/170696557): Refactor to use the LaunchWatcher which is not finished
+  // yet. In our case the name is hard-coded, so we can use that.
+  context->set_container_name("penguin");
+  std::move(callback).Run(BorealisContextManager::kSuccess, "");
+}
+
 }  // namespace borealis
diff --git a/chrome/browser/chromeos/borealis/borealis_task.h b/chrome/browser/chromeos/borealis/borealis_task.h
index 8be30e0..0287cd5 100644
--- a/chrome/browser/chromeos/borealis/borealis_task.h
+++ b/chrome/browser/chromeos/borealis/borealis_task.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_TASK_H_
 
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/borealis/borealis_context_manager.h"
 #include "chromeos/dbus/concierge_client.h"
 #include "chromeos/dbus/dlcservice/dlcservice_client.h"
 
@@ -17,9 +18,11 @@
 // Borealis Context Manager.
 class BorealisTask {
  public:
-  // Callback to be run when the task completes. The |bool| should reflect
-  // if the task succeeded (true) or failed (false).
-  using CompletionStatusCallback = base::OnceCallback<void(bool)>;
+  // Callback to be run when the task completes. The |status| should reflect
+  // if the task succeeded with kSuccess and an empty string. If it fails, a
+  // different status should be used, and an error string provided.
+  using CompletionStatusCallback =
+      base::OnceCallback<void(BorealisContextManager::Status, std::string)>;
   BorealisTask() = default;
   BorealisTask(const BorealisTask&) = delete;
   BorealisTask& operator=(const BorealisTask&) = delete;
@@ -76,6 +79,13 @@
   base::WeakPtrFactory<StartBorealisVm> weak_factory_{this};
 };
 
+// Waits for the startup daemon to signal completion.
+class AwaitBorealisStartup : public BorealisTask {
+ public:
+  void Run(BorealisContext* context,
+           CompletionStatusCallback callback) override;
+};
+
 }  // namespace borealis
 
 #endif  // CHROME_BROWSER_CHROMEOS_BOREALIS_BOREALIS_TASK_H_
diff --git a/chrome/browser/chromeos/borealis/borealis_task_unittest.cc b/chrome/browser/chromeos/borealis/borealis_task_unittest.cc
index 68b4c3d3..ebad5fe8 100644
--- a/chrome/browser/chromeos/borealis/borealis_task_unittest.cc
+++ b/chrome/browser/chromeos/borealis/borealis_task_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "chrome/browser/chromeos/borealis/borealis_context.h"
+#include "chrome/browser/chromeos/borealis/borealis_context_manager.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/dlcservice/fake_dlcservice_client.h"
@@ -14,18 +15,24 @@
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+using ::testing::_;
+using ::testing::StrNe;
+
 namespace borealis {
 
 namespace {
 
 class CallbackForTesting {
  public:
-  base::OnceCallback<void(bool)> GetCallback() {
+  BorealisTask::CompletionStatusCallback GetCallback() {
     return base::BindOnce(&CallbackForTesting::Callback,
                           base::Unretained(this));
   }
 
-  MOCK_METHOD(void, Callback, (bool), ());
+  MOCK_METHOD(void,
+              Callback,
+              (BorealisContextManager::Status, std::string),
+              ());
 };
 
 class BorealisTasksTest : public testing::Test {
@@ -33,6 +40,10 @@
   BorealisTasksTest() = default;
   ~BorealisTasksTest() override = default;
 
+  // Disallow copy and assign.
+  BorealisTasksTest(const BorealisTasksTest&) = delete;
+  BorealisTasksTest& operator=(const BorealisTasksTest&) = delete;
+
  protected:
   void SetUp() override {
     chromeos::DBusThreadManager::Initialize();
@@ -67,10 +78,6 @@
     profile_builder.SetProfileName("defaultprofile");
     profile_ = profile_builder.Build();
   }
-
-  // Disallow copy and assign.
-  BorealisTasksTest(const BorealisTasksTest&) = delete;
-  BorealisTasksTest& operator=(const BorealisTasksTest&) = delete;
 };
 
 TEST_F(BorealisTasksTest, MountDlcSucceedsAndCallbackRanWithResults) {
@@ -79,7 +86,7 @@
   EXPECT_EQ(context_->root_path(), "");
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(true));
+  EXPECT_CALL(callback, Callback(BorealisContextManager::kSuccess, _));
 
   MountDlc task;
   task.Run(context_, callback.GetCallback());
@@ -97,7 +104,7 @@
   EXPECT_EQ(context_->disk_path(), base::FilePath());
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(true));
+  EXPECT_CALL(callback, Callback(BorealisContextManager::kSuccess, _));
 
   CreateDiskImage task;
   task.Run(context_, callback.GetCallback());
@@ -117,7 +124,7 @@
   EXPECT_EQ(context_->disk_path(), base::FilePath());
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(true));
+  EXPECT_CALL(callback, Callback(BorealisContextManager::kSuccess, _));
 
   CreateDiskImage task;
   task.Run(context_, callback.GetCallback());
@@ -133,7 +140,7 @@
   fake_concierge_client_->set_start_vm_response(std::move(response));
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(true));
+  EXPECT_CALL(callback, Callback(BorealisContextManager::kSuccess, _));
 
   StartBorealisVm task;
   task.Run(context_, callback.GetCallback());
@@ -149,7 +156,7 @@
   fake_concierge_client_->set_start_vm_response(std::move(response));
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(true));
+  EXPECT_CALL(callback, Callback(BorealisContextManager::kSuccess, _));
 
   StartBorealisVm task;
   task.Run(context_, callback.GetCallback());
@@ -164,7 +171,8 @@
 TEST_P(BorealisTasksTestDlc, MountDlcFailsAndCallbackRanWithResults) {
   fake_dlcservice_client_->set_install_error(GetParam());
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(false));
+  EXPECT_CALL(callback,
+              Callback(BorealisContextManager::kMountFailed, StrNe("")));
 
   MountDlc task;
   task.Run(context_, callback.GetCallback());
@@ -192,7 +200,8 @@
   EXPECT_EQ(context_->disk_path(), base::FilePath());
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(false));
+  EXPECT_CALL(callback,
+              Callback(BorealisContextManager::kDiskImageFailed, StrNe("")));
 
   CreateDiskImage task;
   task.Run(context_, callback.GetCallback());
@@ -223,7 +232,8 @@
   fake_concierge_client_->set_start_vm_response(std::move(response));
 
   testing::StrictMock<CallbackForTesting> callback;
-  EXPECT_CALL(callback, Callback(false));
+  EXPECT_CALL(callback,
+              Callback(BorealisContextManager::kStartVmFailed, StrNe("")));
 
   StartBorealisVm task;
   task.Run(context_, callback.GetCallback());
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
index edf9f82d..bf19a78 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.h
@@ -277,10 +277,6 @@
 
   ResponseAction Run() override;
   void RestartCallback(crostini::CrostiniResult);
-
- private:
-  std::string source_path_;
-  std::string mount_label_;
 };
 
 // Implements the chrome.fileManagerPrivate.importCrostiniImage method.
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener.cc b/chrome/browser/chromeos/file_manager/devtools_listener.cc
index 9f06b925..7c06f46 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener.cc
+++ b/chrome/browser/chromeos/file_manager/devtools_listener.cc
@@ -247,6 +247,9 @@
   if (!navigated_)
     return;
 
+  if (VLOG_IS_ON(2))
+    VLOG(2) << SpanToStringPiece(message);
+
   std::unique_ptr<base::DictionaryValue> value = base::DictionaryValue::From(
       base::JSONReader::ReadDeprecated(SpanToStringPiece(message)));
   CHECK(value);
diff --git a/chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc b/chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc
index 464199f..816355d18 100644
--- a/chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/devtools_listener_browsertest.cc
@@ -33,9 +33,6 @@
   void SetUpOnMainThread() override {
     process_id_ = base::GetUniqueIdForProcess().GetUnsafeValue();
     content::DevToolsAgentHost::AddObserver(this);
-
-    base::ScopedAllowBlockingForTesting allow_blocking;
-    CHECK(tmp_dir_.CreateUniqueTempDir());
   }
 
   bool ShouldForceDevToolsAgentHostCreation() override { return true; }
@@ -62,9 +59,10 @@
     LOG(FATAL) << "Host crashed: " << DevToolsListener::HostString(host);
   }
 
-  void CollectCodeCoverage() {
+  void CollectCodeCoverage(const std::string test_name) {
     base::ScopedAllowBlockingForTesting allow_blocking;
 
+    CHECK(tmp_dir_.CreateUniqueTempDir());
     base::FilePath coverage_store =
         tmp_dir_.GetPath().AppendASCII("devtools_listener_browser_test");
     CHECK(base::CreateDirectory(coverage_store));
@@ -72,7 +70,7 @@
     for (auto& agent : devtools_agent_) {
       auto* host = agent.first;
       EXPECT_TRUE(agent.second->HasCoverage(host));
-      agent.second->GetCoverage(host, coverage_store, "fake_test_name");
+      agent.second->GetCoverage(host, coverage_store, test_name);
       agent.second->Detach(host);
     }
 
@@ -81,10 +79,12 @@
 
  private:
   base::ScopedTempDir tmp_dir_;
+
+  using DevToolsAgentMap =  // agent hosts: have a unique devtools listener
+      std::map<content::DevToolsAgentHost*, std::unique_ptr<DevToolsListener>>;
+
+  DevToolsAgentMap devtools_agent_;
   uint32_t process_id_ = 0;
-  std::map<content::DevToolsAgentHost*, std::unique_ptr<DevToolsListener>>
-      devtools_agent_;
-  base::FilePath coverage_store_;
 
   DISALLOW_COPY_AND_ASSIGN(DevToolsListenerBrowserTest);
 };
@@ -106,7 +106,7 @@
   content::DevToolsAgentHost::RemoveObserver(this);
   content::RunAllTasksUntilIdle();
 
-  CollectCodeCoverage();
+  CollectCodeCoverage("CanCollectCodeCoverage");
 
   content::DevToolsAgentHost::DetachAllClients();
   content::RunAllTasksUntilIdle();
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index c275354..e4d3bb0e 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -1051,6 +1051,7 @@
       "//chromeos/settings",
       "//chromeos/system",
       "//chromeos/tpm",
+      "//chromeos/ui/base",
       "//components/arc",
       "//components/constrained_window",
       "//components/drive",
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index 0fba941..592b1ae 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -33,9 +33,9 @@
 #include "ui/display/test/test_screen.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "chrome/browser/chromeos/policy/dlp/mock_dlp_content_manager.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #endif
 
 namespace extensions {
@@ -991,7 +991,7 @@
 
   // In locked fullscreen mode we should not be able to create any tabs.
   browser_window()->GetNativeWindow()->SetProperty(
-      ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
+      chromeos::kWindowPinTypeKey, chromeos::WindowPinType::kTrustedPinned);
 
   EXPECT_EQ(tabs_constants::kLockedFullscreenModeNewTabError,
             extension_function_test_utils::RunFunctionAndReturnError(
diff --git a/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc b/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
index 4ed2273..a4ddb8c9 100644
--- a/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_util_chromeos.cc
@@ -5,8 +5,6 @@
 #include "chrome/browser/extensions/api/tabs/tabs_util.h"
 
 #include "ash/public/cpp/assistant/assistant_state.h"
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
@@ -17,6 +15,8 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "content/public/browser/devtools_agent_host.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/aura/window.h"
@@ -32,9 +32,9 @@
   aura::Window* window = browser->window()->GetNativeWindow();
   // TRUSTED_PINNED is used here because that one locks the window fullscreen
   // without allowing the user to exit (as opposed to regular PINNED).
-  window->SetProperty(
-      ash::kWindowPinTypeKey,
-      locked ? ash::WindowPinType::kTrustedPinned : ash::WindowPinType::kNone);
+  window->SetProperty(chromeos::kWindowPinTypeKey,
+                      locked ? chromeos::WindowPinType::kTrustedPinned
+                             : chromeos::WindowPinType::kNone);
 
   // Update the set of available browser commands.
   browser->command_controller()->LockedFullscreenStateChanged();
diff --git a/chrome/browser/extensions/window_open_apitest.cc b/chrome/browser/extensions/window_open_apitest.cc
index f05793ef..b5c2205b 100644
--- a/chrome/browser/extensions/window_open_apitest.cc
+++ b/chrome/browser/extensions/window_open_apitest.cc
@@ -40,12 +40,12 @@
 #include "ui/base/base_window.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/extensions/window_controller.h"
 #include "chrome/browser/extensions/window_controller_list.h"
 #include "chrome/browser/ui/browser_command_controller.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/window.h"
 #endif
 
@@ -377,14 +377,14 @@
   return controller->window()->GetNativeWindow();
 }
 
-ash::WindowPinType GetCurrentWindowPinType() {
-  ash::WindowPinType type =
-      GetCurrentWindow()->GetProperty(ash::kWindowPinTypeKey);
+chromeos::WindowPinType GetCurrentWindowPinType() {
+  chromeos::WindowPinType type =
+      GetCurrentWindow()->GetProperty(chromeos::kWindowPinTypeKey);
   return type;
 }
 
-void SetCurrentWindowPinType(ash::WindowPinType type) {
-  GetCurrentWindow()->SetProperty(ash::kWindowPinTypeKey, type);
+void SetCurrentWindowPinType(chromeos::WindowPinType type) {
+  GetCurrentWindow()->SetProperty(chromeos::kWindowPinTypeKey, type);
 }
 
 }  // namespace
@@ -396,7 +396,7 @@
 
   // Make sure the newly created window is "trusted pinned" (which means that
   // it's in locked fullscreen mode).
-  EXPECT_EQ(ash::WindowPinType::kTrustedPinned, GetCurrentWindowPinType());
+  EXPECT_EQ(chromeos::WindowPinType::kTrustedPinned, GetCurrentWindowPinType());
 }
 
 IN_PROC_BROWSER_TEST_F(WindowOpenApiTest, UpdateWindowToLockedFullscreen) {
@@ -405,13 +405,13 @@
       << message_;
 
   // Make sure the current window is put into the "trusted pinned" state.
-  EXPECT_EQ(ash::WindowPinType::kTrustedPinned, GetCurrentWindowPinType());
+  EXPECT_EQ(chromeos::WindowPinType::kTrustedPinned, GetCurrentWindowPinType());
 }
 
 IN_PROC_BROWSER_TEST_F(WindowOpenApiTest, RemoveLockedFullscreenFromWindow) {
   // After locking the window, do a LockedFullscreenStateChanged so the
   // command_controller state catches up as well.
-  SetCurrentWindowPinType(ash::WindowPinType::kTrustedPinned);
+  SetCurrentWindowPinType(chromeos::WindowPinType::kTrustedPinned);
   browser()->command_controller()->LockedFullscreenStateChanged();
 
   ASSERT_TRUE(RunExtensionTestWithArg("locked_fullscreen/with_permission",
@@ -419,7 +419,7 @@
       << message_;
 
   // Make sure the current window is removed from locked-fullscreen state.
-  EXPECT_EQ(ash::WindowPinType::kNone, GetCurrentWindowPinType());
+  EXPECT_EQ(chromeos::WindowPinType::kNone, GetCurrentWindowPinType());
 }
 
 // Make sure that commands disabling code works in locked fullscreen mode.
@@ -465,12 +465,12 @@
 
   // chrome.windows.update call fails since this extension doesn't have the
   // correct permission and hence the current window has NONE as WindowPinType.
-  EXPECT_EQ(ash::WindowPinType::kNone, GetCurrentWindowPinType());
+  EXPECT_EQ(chromeos::WindowPinType::kNone, GetCurrentWindowPinType());
 }
 
 IN_PROC_BROWSER_TEST_F(WindowOpenApiTest,
                        RemoveLockedFullscreenFromWindowWithoutPermission) {
-  SetCurrentWindowPinType(ash::WindowPinType::kTrustedPinned);
+  SetCurrentWindowPinType(chromeos::WindowPinType::kTrustedPinned);
   browser()->command_controller()->LockedFullscreenStateChanged();
 
   ASSERT_TRUE(RunExtensionTestWithArg("locked_fullscreen/without_permission",
@@ -478,7 +478,7 @@
       << message_;
 
   // The current window is still locked-fullscreen.
-  EXPECT_EQ(ash::WindowPinType::kTrustedPinned, GetCurrentWindowPinType());
+  EXPECT_EQ(chromeos::WindowPinType::kTrustedPinned, GetCurrentWindowPinType());
 }
 #endif  // defined(OS_CHROMEOS)
 
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 99dbdc7..398b865 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -237,6 +237,11 @@
     "expiry_milestone": 78
   },
   {
+    "name": "audio-player-js-modules",
+    "owners": [ "lucmult", "jboulic", "simmonsjosh" ],
+    "expiry_milestone": 91
+  },
+  {
     "name": "audio-worklet-realtime-thread",
     "owners": [ "//third_party/blink/renderer/modules/webaudio/OWNERS" ],
     "expiry_milestone": 90
@@ -2149,11 +2154,6 @@
     "expiry_milestone": 87
   },
   {
-    "name": "enable-system-webapps",
-    "owners": [ "calamity" ],
-    "expiry_milestone": 78
-  },
-  {
     "name": "enable-tab-engagement-reporting",
     "owners": [ "memex-team@google.com" ],
     "expiry_milestone": 90
@@ -2516,6 +2516,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "files-js-modules",
+    "owners": [ "lucmult", "jboulic", "simmonsjosh" ],
+    "expiry_milestone": 91
+  },
+  {
     "name": "files-ng",
     "owners": [ "adanilo", "noel" ],
     "expiry_milestone": 88
@@ -4683,6 +4688,11 @@
     "expiry_milestone": 88
   },
   {
+    "name": "video-player-js-modules",
+    "owners": [ "lucmult", "jboulic", "simmonsjosh" ],
+    "expiry_milestone": 91
+  },
+  {
     "name": "video-tutorials",
     "owners": [ "shaktisahu", "hesen" ],
     "expiry_milestone": 90
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8d44076a..dfebabd 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4101,6 +4101,18 @@
     "Enables the Files App to display Camera folder with i18n name and make it "
     "nonmodifiable";
 
+const char kFilesJsModulesName[] = "Enable JS Modules for Files app";
+const char kFilesJsModulesDescription[] =
+    "Enable running Files app using JS Modules.";
+
+const char kAudioPlayerJsModulesName[] = "Enable JS Modules for Audio Player";
+const char kAudioPlayerJsModulesDescription[] =
+    "Enable running Audio Player app using JS Modules.";
+
+const char kVideoPlayerJsModulesName[] = "Enable JS Modules for Video Player";
+const char kVideoPlayerJsModulesDescription[] =
+    "Enable running Video Player app using JS Modules.";
+
 const char kFilesNGName[] = "Enable Files App. NG.";
 const char kFilesNGDescription[] =
     "Enable the next generation UI style of the file manager.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 90fe6432..0651a4dd 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2340,6 +2340,15 @@
 extern const char kTrimOnMemoryPressureName[];
 extern const char kTrimOnMemoryPressureDescription[];
 
+extern const char kFilesJsModulesName[];
+extern const char kFilesJsModulesDescription[];
+
+extern const char kAudioPlayerJsModulesName[];
+extern const char kAudioPlayerJsModulesDescription[];
+
+extern const char kVideoPlayerJsModulesName[];
+extern const char kVideoPlayerJsModulesDescription[];
+
 extern const char kEnableSuggestedFilesName[];
 extern const char kEnableSuggestedFilesDescription[];
 
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 4d5e7c0..5bd4a7e8 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -159,19 +159,6 @@
 }
 #endif  // !defined(OS_ANDROID)
 
-#if !defined(OS_ANDROID)
-Profile* CreateGuestProfile() {
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  base::FilePath new_path = profile_manager->GetGuestProfilePath();
-  base::RunLoop run_loop;
-  profile_manager->CreateProfileAsync(
-      new_path, base::Bind(&UnblockOnProfileCreation, &run_loop),
-      base::string16(), std::string());
-  run_loop.Run();
-  return profile_manager->GetProfileByPath(new_path);
-}
-#endif  // !defined(OS_ANDROID)
-
 // A helper object for overriding metrics enabled state.
 class MetricsConsentOverride {
  public:
@@ -500,10 +487,8 @@
   EXPECT_TRUE(ukm_test_helper.IsRecordingEnabled());
   uint64_t original_client_id = ukm_test_helper.GetClientId();
 
-  // Create browser for guest profile. Only "off the record" browsers may be
-  // opened in this mode.
-  Profile* guest_profile = CreateGuestProfile();
-  Browser* guest_browser = CreateIncognitoBrowser(guest_profile);
+  // Create browser for guest profile.
+  Browser* guest_browser = InProcessBrowserTest::CreateGuestBrowser();
   EXPECT_FALSE(ukm_test_helper.IsRecordingEnabled());
 
   CloseBrowserSynchronously(guest_browser);
diff --git a/chrome/browser/platform_util_chromeos.cc b/chrome/browser/platform_util_chromeos.cc
index 61b0366b..17a1f84 100644
--- a/chrome/browser/platform_util_chromeos.cc
+++ b/chrome/browser/platform_util_chromeos.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/platform_util.h"
 
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/bind.h"
 #include "base/files/file_path.h"
 #include "chrome/browser/chromeos/file_manager/open_util.h"
@@ -16,6 +14,8 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/simple_message_box.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -113,8 +113,8 @@
   // |window| can be nullptr inside of unit tests.
   if (!window)
     return false;
-  return window->GetProperty(ash::kWindowPinTypeKey) ==
-         ash::WindowPinType::kTrustedPinned;
+  return window->GetProperty(chromeos::kWindowPinTypeKey) ==
+         chromeos::WindowPinType::kTrustedPinned;
 }
 
 }  // namespace platform_util
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 5a42593..7e0a048 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -102,8 +102,8 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/window.h"
 #endif
 
@@ -441,7 +441,7 @@
 
   // Set locked fullscreen state.
   browser()->window()->GetNativeWindow()->SetProperty(
-      ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
+      chromeos::kWindowPinTypeKey, chromeos::WindowPinType::kTrustedPinned);
 
   // All entries are disabled in locked fullscreen (testing only a subset here).
   for (auto entry : entries_to_test)
diff --git a/chrome/browser/resources/omnibox/BUILD.gn b/chrome/browser/resources/omnibox/BUILD.gn
index 1e3b932..8369776 100644
--- a/chrome/browser/resources/omnibox/BUILD.gn
+++ b/chrome/browser/resources/omnibox/BUILD.gn
@@ -2,11 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//third_party/closure_compiler/closure_args.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
 
 js_type_check("closure_compile") {
   uses_js_modules = true
+  closure_flags = mojom_js_args
   deps = [
     ":omnibox",
     ":omnibox_element",
@@ -20,7 +22,7 @@
   deps = [
     ":omnibox_input",
     ":omnibox_output",
-    "//chrome/browser/ui/webui/omnibox:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/omnibox:mojo_bindings_webui_js",
     "//ui/webui/resources/js:cr.m",
     "//ui/webui/resources/js:load_time_data.m",
     "//ui/webui/resources/js:util.m",
@@ -39,14 +41,14 @@
   deps = [
     ":omnibox_element",
     ":omnibox_input",
-    "//chrome/browser/ui/webui/omnibox:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/omnibox:mojo_bindings_webui_js",
   ]
   externs_list = [ "$externs_path/pending.js" ]
 }
 
 js_library("omnibox_popup") {
   deps = [
-    "//chrome/browser/ui/webui/omnibox:mojo_bindings_js_library_for_compile",
+    "//chrome/browser/ui/webui/omnibox:mojo_bindings_webui_js",
     "//ui/webui/resources/cr_components/omnibox:cr_autocomplete_match_list",
   ]
 }
@@ -63,5 +65,5 @@
     "-E",
     "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir),
   ]
-  deps = [ "//chrome/browser/ui/webui/omnibox:mojo_bindings_js" ]
+  deps = [ "//chrome/browser/ui/webui/omnibox:mojo_bindings_webui_js" ]
 }
diff --git a/chrome/browser/resources/omnibox/omnibox.js b/chrome/browser/resources/omnibox/omnibox.js
index 4184371..c6d1ed2 100644
--- a/chrome/browser/resources/omnibox/omnibox.js
+++ b/chrome/browser/resources/omnibox/omnibox.js
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import './chrome/browser/ui/webui/omnibox/omnibox.mojom-lite.js';
 import './strings.m.js';
 
+import {OmniboxPageCallbackRouter, OmniboxPageHandler, OmniboxPageHandlerRemote, OmniboxResponse} from '/chrome/browser/ui/webui/omnibox/omnibox.mojom-webui.js';
 import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {$} from 'chrome://resources/js/util.m.js';
@@ -31,7 +30,7 @@
 /**
  * @typedef {{
  *   inputText: string,
- *   callback: function(!mojom.OmniboxResponse):Promise,
+ *   callback: function(!OmniboxResponse):Promise,
  *   display: boolean,
  * }}
  */
@@ -49,7 +48,7 @@
  * @typedef {{
  *   queryInputs: QueryInputs,
  *   displayInputs: DisplayInputs,
- *   responsesHistory: !Array<!Array<!mojom.OmniboxResponse>>,
+ *   responsesHistory: !Array<!Array<!OmniboxResponse>>,
  * }}
  */
 let OmniboxExport;
@@ -66,8 +65,8 @@
 class BrowserProxy {
   /** @param {!OmniboxOutput} omniboxOutput */
   constructor(omniboxOutput) {
-    /** @private {!mojom.OmniboxPageCallbackRouter} */
-    this.callbackRouter_ = new mojom.OmniboxPageCallbackRouter;
+    /** @private {!OmniboxPageCallbackRouter} */
+    this.callbackRouter_ = new OmniboxPageCallbackRouter;
 
     this.callbackRouter_.handleNewAutocompleteResponse.addListener(
         this.handleNewAutocompleteResponse.bind(this));
@@ -76,8 +75,8 @@
     this.callbackRouter_.handleAnswerImageData.addListener(
         omniboxOutput.updateAnswerImage.bind(omniboxOutput));
 
-    /** @private {!mojom.OmniboxPageHandlerRemote} */
-    this.handler_ = mojom.OmniboxPageHandler.getRemote();
+    /** @private {!OmniboxPageHandlerRemote} */
+    this.handler_ = OmniboxPageHandler.getRemote();
     this.handler_.setClientPage(
         this.callbackRouter_.$.bindNewPipeAndPassRemote());
 
@@ -86,7 +85,7 @@
   }
 
   /**
-   * @param {!mojom.OmniboxResponse} response
+   * @param {!OmniboxResponse} response
    * @param {boolean} isPageController
    */
   handleNewAutocompleteResponse(response, isPageController) {
diff --git a/chrome/browser/resources/omnibox/omnibox_output.js b/chrome/browser/resources/omnibox/omnibox_output.js
index 8c1d0ea..800eb2b 100644
--- a/chrome/browser/resources/omnibox/omnibox_output.js
+++ b/chrome/browser/resources/omnibox/omnibox_output.js
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import './chrome/browser/ui/webui/omnibox/omnibox.mojom-lite.js';
+import {ACMatchClassification, AutocompleteMatch, OmniboxResponse} from '/chrome/browser/ui/webui/omnibox/omnibox.mojom-webui.js';
 
 import {OmniboxElement} from './omnibox_element.js';
-import {OmniboxInput, DisplayInputs} from './omnibox_input.js';
+import {DisplayInputs, OmniboxInput} from './omnibox_input.js';
 
 /**
  * @typedef  {{
@@ -33,7 +32,7 @@
 
     /** @private {number} */
     this.selectedResponseIndex_ = 0;
-    /** @type {!Array<!Array<!mojom.OmniboxResponse>>} */
+    /** @type {!Array<!Array<!OmniboxResponse>>} */
     this.responsesHistory = [];
     /** @private {!Array<!OutputResultsGroup>} */
     this.resultsGroups_ = [];
@@ -55,7 +54,7 @@
     this.updateFilterHighlights_();
   }
 
-  /** @param {!Array<!Array<!mojom.OmniboxResponse>>} responsesHistory */
+  /** @param {!Array<!Array<!OmniboxResponse>>} responsesHistory */
   setResponsesHistory(responsesHistory) {
     this.responsesHistory = responsesHistory;
     this.dispatchEvent(new CustomEvent(
@@ -79,7 +78,7 @@
         'responses-count-changed', {detail: this.responsesHistory.length}));
   }
 
-  /** @param {!mojom.OmniboxResponse} response */
+  /** @param {!OmniboxResponse} response */
   addAutocompleteResponse(response) {
     const lastIndex = this.responsesHistory.length - 1;
     this.responsesHistory[lastIndex].push(response);
@@ -99,7 +98,7 @@
 
   /**
    * Creates and adds a result group to the UI.
-   * @private @param {!mojom.OmniboxResponse} response
+   * @private @param {!OmniboxResponse} response
    */
   createResultsGroup_(response) {
     const resultsGroup = OutputResultsGroup.create(response);
@@ -187,7 +186,7 @@
  */
 class OutputResultsGroup extends OmniboxElement {
   /**
-   * @param {!mojom.OmniboxResponse} resultsGroup
+   * @param {!OmniboxResponse} resultsGroup
    * @return {!OutputResultsGroup}
    */
   static create(resultsGroup) {
@@ -200,7 +199,7 @@
     super('output-results-group-template');
   }
 
-  /** @param {!mojom.OmniboxResponse} resultsGroup */
+  /** @param {!OmniboxResponse} resultsGroup */
   setResultsGroup(resultsGroup) {
     /** @private {ResultsDetails} */
     this.details_ = {
@@ -357,7 +356,7 @@
  */
 class OutputResultsTable extends HTMLTableSectionElement {
   /**
-   * @param {!Array<!mojom.AutocompleteMatch>} results
+   * @param {!Array<!AutocompleteMatch>} results
    * @return {!OutputResultsTable}
    */
   static create(results) {
@@ -373,7 +372,7 @@
     this.autocompleteMatches = [];
   }
 
-  /** @param {!Array<!mojom.AutocompleteMatch>} results */
+  /** @param {!Array<!AutocompleteMatch>} results */
   set results(results) {
     this.autocompleteMatches.forEach(match => match.remove());
     this.autocompleteMatches = results.map(OutputMatch.create);
@@ -404,7 +403,7 @@
   }
 
   /**
-   * @param {!mojom.AutocompleteMatch} match
+   * @param {!AutocompleteMatch} match
    * @return {!OutputMatch}
    */
   static create(match) {
@@ -414,7 +413,7 @@
     return outputMatch;
   }
 
-  /** @param {!mojom.AutocompleteMatch} match */
+  /** @param {!AutocompleteMatch} match */
   set match(match) {
     /** @type {!Object<string, !OutputProperty>} */
     this.properties = {};
@@ -703,11 +702,11 @@
         this.values_;
     OutputAnswerProperty.renderClassifiedText_(
         this.contents_, /** @type {string} */ (contents),
-        /** @type {!Array<!mojom.ACMatchClassification>} */
+        /** @type {!Array<!ACMatchClassification>} */
         (contentsClassification));
     OutputAnswerProperty.renderClassifiedText_(
         this.description_, /** @type {string} */ (description),
-        /** @type {!Array<!mojom.ACMatchClassification>} */
+        /** @type {!Array<!ACMatchClassification>} */
         (descriptionClassification));
     this.answer_.textContent = answer;
     this.imageUrl_.textContent = image;
@@ -723,7 +722,7 @@
    * @private
    * @param {!Element} container
    * @param {string} string
-   * @param {!Array<!mojom.ACMatchClassification>} classes
+   * @param {!Array<!ACMatchClassification>} classes
    */
   static renderClassifiedText_(container, string, classes) {
     clearChildren(container);
@@ -736,7 +735,7 @@
 
   /**
    * @param {string} string
-   * @param {!Array<!mojom.ACMatchClassification>} classes
+   * @param {!Array<!ACMatchClassification>} classes
    * @return {!Array<{string: string, style: number}>}
    */
   static classify(string, classes) {
diff --git a/chrome/browser/resources/omnibox/omnibox_popup.js b/chrome/browser/resources/omnibox/omnibox_popup.js
index a6b55b23..2ce6368b 100644
--- a/chrome/browser/resources/omnibox/omnibox_popup.js
+++ b/chrome/browser/resources/omnibox/omnibox_popup.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import './chrome/browser/ui/webui/omnibox/omnibox.mojom-lite.js';
-
+import {OmniboxPageCallbackRouter, OmniboxPageHandler, OmniboxPageHandlerRemote} from '/chrome/browser/ui/webui/omnibox/omnibox.mojom-webui.js';
 import {AutocompleteMatchListElement} from 'chrome://resources/cr_components/omnibox/cr_autocomplete_match_list.js';
 
 /**
@@ -14,8 +12,8 @@
  */
 
 document.addEventListener('DOMContentLoaded', () => {
-  /** @private {!mojom.OmniboxPageCallbackRouter} */
-  const callbackRouter = new mojom.OmniboxPageCallbackRouter;
+  /** @private {!OmniboxPageCallbackRouter} */
+  const callbackRouter = new OmniboxPageCallbackRouter;
 
   // Basically a Hello World proof of concept that writes the Autocomplete
   // responses to the whole document.
@@ -31,7 +29,7 @@
         }
       });
 
-  /** @private {!mojom.OmniboxPageHandlerRemote} */
-  const handler = mojom.OmniboxPageHandler.getRemote();
+  /** @private {!OmniboxPageHandlerRemote} */
+  const handler = OmniboxPageHandler.getRemote();
   handler.setClientPage(callbackRouter.$.bindNewPipeAndPassRemote());
 });
diff --git a/chrome/browser/resources/omnibox/resources.grd b/chrome/browser/resources/omnibox/resources.grd
index 273065f1..9821c83 100644
--- a/chrome/browser/resources/omnibox/resources.grd
+++ b/chrome/browser/resources/omnibox/resources.grd
@@ -46,7 +46,7 @@
                file="omnibox.js"
                type="BINDATA" />
       <include name="IDR_OMNIBOX_MOJO_JS"
-               file="${root_gen_dir}\chrome\browser\ui\webui\omnibox\omnibox.mojom-lite.js"
+               file="${root_gen_dir}\mojom-webui\chrome\browser\ui\webui\omnibox\omnibox.mojom-webui.js"
                use_base_dir="false"
                type="BINDATA" />
     </includes>
diff --git a/chrome/browser/resources/quota_internals/event_handler.js b/chrome/browser/resources/quota_internals/event_handler.js
index d8c8b78..cde55cb1 100644
--- a/chrome/browser/resources/quota_internals/event_handler.js
+++ b/chrome/browser/resources/quota_internals/event_handler.js
@@ -245,20 +245,14 @@
   return originObject;
 }
 
-/**
- * Event Handler for |cr.quota.onAvailableSpaceUpdated|.
- * |event.detail| contains |availableSpace|.
- * |availableSpace| represents total available disk space.
- * @param {!CustomEvent<number>} event AvailableSpaceUpdated event.
- */
-function handleAvailableSpace(event) {
-  availableSpace = event.detail;
+/** @param {number} space Total available disk space. */
+function handleAvailableSpace(space) {
+  availableSpace = space;
   $('diskspace-entry').textContent = numBytesToText_(availableSpace);
 }
 
 /**
- * Event Handler for |cr.quota.onGlobalInfoUpdated|.
- * |event.detail| contains a record which has:
+ * |data| contains a record which has:
  *   |type|:
  *     Storage type, that is either 'temporary' or 'persistent'.
  *   |usage|:
@@ -270,15 +264,14 @@
  *
  *  |usage|, |unlimitedUsage| and |quota| can be missing,
  *  and some additional fields can be included.
- * @param {!CustomEvent<!{
+ * @param {!{
  *     type: string,
  *     usage: ?number,
  *     unlimitedUsage: ?number,
  *     quota: ?string
- * }>} event GlobalInfoUpdated event.
+ * }} data
  */
-function handleGlobalInfo(event) {
-  const data = event.detail;
+function handleGlobalInfo(data) {
   const storageObject = getStorageObject(data.type);
   copyAttributes_(data, storageObject.detail.payload);
   storageObject.reveal();
@@ -288,8 +281,7 @@
 }
 
 /**
- * Event Handler for |cr.quota.onPerHostInfoUpdated|.
- * |event.detail| contains records which have:
+ * |dataArray| contains records which have:
  *   |host|:
  *     Hostname of the entry. (e.g. 'example.com')
  *   |type|:
@@ -301,16 +293,14 @@
  *
  * |usage| and |quota| can be missing,
  * and some additional fields can be included.
- * @param {!CustomEvent<!Array<{
+ * @param {!Array<{
  *     host: string,
  *     type: string,
  *     usage: ?number,
  *     quota: ?number
- * }>>} event PerHostInfoUpdated event.
+ * }>} dataArray
  */
-function handlePerHostInfo(event) {
-  const dataArray = event.detail;
-
+function handlePerHostInfo(dataArray) {
   for (let i = 0; i < dataArray.length; ++i) {
     const data = dataArray[i];
     const hostObject = getHostObject(data.type, data.host);
@@ -323,8 +313,7 @@
 }
 
 /**
- * Event Handler for |cr.quota.onPerOriginInfoUpdated|.
- * |event.detail| contains records which have:
+ * |dataArray| contains records which have:
  *   |origin|:
  *     Origin URL of the entry.
  *   |type|:
@@ -344,7 +333,7 @@
  *
  * |inUse|, |usedCount|, |lastAccessTime| and |lastModifiedTime| can be missing,
  * and some additional fields can be included.
- * @param {!CustomEvent<!Array<!{
+ * @param {!Array<!{
  *     origin: string,
  *     type: string,
  *     host: string,
@@ -352,11 +341,9 @@
  *     usedCount: ?number,
  *     lastAccessTime: ?number,
  *     lastModifiedTime: ?number
- * }>>} event PerOriginInfoUpdated event.
+ * }>} dataArray
  */
-function handlePerOriginInfo(event) {
-  const dataArray = event.detail;
-
+function handlePerOriginInfo(dataArray) {
   for (let i = 0; i < dataArray.length; ++i) {
     const data = dataArray[i];
     const originObject = getOriginObject(data.type, data.host, data.origin);
@@ -369,12 +356,10 @@
 }
 
 /**
- * Event Handler for |cr.quota.onStatisticsUpdated|.
- * |event.detail| contains misc statistics data as dictionary.
- * @param {!CustomEvent<!Object>} event StatisticsUpdated event.
+ * |data| contains misc statistics data as dictionary.
+ * @param {!Object} data
  */
-function handleStatistics(event) {
-  const data = event.detail;
+function handleStatistics(data) {
   for (const key in data) {
     let entry = statistics[key];
     if (!entry) {
@@ -391,13 +376,10 @@
 }
 
 /**
- * Event Handler for |cr.quota.onStoragePressureFlagUpdated|.
- * |event.detail| contains a boolean representing whether or not to show
- * the storage pressure UI.
- * @param {!CustomEvent<!Object>} event StoragePressureFlagUpdated event.
+ * @param {!{isStoragePressureEnabled: boolean}} data Contains a boolean
+ *     representing whether or not to show the storage pressure UI.
  */
-function handleStoragePressureFlagInfo(event) {
-  const data = event.detail;
+function handleStoragePressureFlagInfo(data) {
   $('storage-pressure-loading').hidden = true;
   if (data.isStoragePressureEnabled) {
     $('storage-pressure-outer').hidden = false;
@@ -499,15 +481,14 @@
 function onLoad() {
   cr.ui.decorate('tabbox', cr.ui.TabBox);
 
-  cr.quota.onAvailableSpaceUpdated.addEventListener(
-      'update', handleAvailableSpace);
-  cr.quota.onGlobalInfoUpdated.addEventListener('update', handleGlobalInfo);
-  cr.quota.onPerHostInfoUpdated.addEventListener('update', handlePerHostInfo);
-  cr.quota.onPerOriginInfoUpdated.addEventListener(
-      'update', handlePerOriginInfo);
-  cr.quota.onStatisticsUpdated.addEventListener('update', handleStatistics);
-  cr.quota.onStoragePressureFlagUpdated.addEventListener(
-      'update', handleStoragePressureFlagInfo);
+  cr.addWebUIListener('AvailableSpaceUpdated', handleAvailableSpace);
+  cr.addWebUIListener('GlobalInfoUpdated', handleGlobalInfo);
+  cr.addWebUIListener('PerHostInfoUpdated', handlePerHostInfo);
+  cr.addWebUIListener('PerOriginInfoUpdated', handlePerOriginInfo);
+  cr.addWebUIListener('StatisticsUpdated', handleStatistics);
+  cr.addWebUIListener(
+      'StoragePressureFlagUpdated', handleStoragePressureFlagInfo);
+
   cr.quota.requestInfo();
 
   $('refresh-button').addEventListener('click', cr.quota.requestInfo, false);
diff --git a/chrome/browser/resources/quota_internals/message_dispatcher.js b/chrome/browser/resources/quota_internals/message_dispatcher.js
index 25fa7ec..3c69202 100644
--- a/chrome/browser/resources/quota_internals/message_dispatcher.js
+++ b/chrome/browser/resources/quota_internals/message_dispatcher.js
@@ -3,14 +3,10 @@
 // found in the LICENSE file.
 
 // require cr.js
-// require cr/event_target.js
-// require cr/util.js
 
 /**
  * Bridge between the browser and the page.
  * In this file:
- *   * define EventTargets to receive message from the browser,
- *   * dispatch browser messages to EventTarget,
  *   * define interface to request data to the browser.
  */
 
@@ -31,66 +27,10 @@
     chrome.send('triggerStoragePressure', [origin]);
   }
 
-  /**
-   * Callback entry point from Browser.
-   * Messages are Dispatched as Event to:
-   *   * onAvailableSpaceUpdated,
-   *   * onGlobalInfoUpdated,
-   *   * onPerHostInfoUpdated,
-   *   * onPerOriginInfoUpdated,
-   *   * onStatisticsUpdated,
-   *   * onStoragePressureFlagUpdated.
-   * @param {string} message Message label. Possible Values are:
-   *   * 'AvailableSpaceUpdated',
-   *   * 'GlobalInfoUpdated',
-   *   * 'PerHostInfoUpdated',
-   *   * 'PerOriginInfoUpdated',
-   *   * 'StatisticsUpdated',
-   *   * 'StoragePressureFlagUpdated'.
-   * @param {Object} detail Message specific additional data.
-   */
-  function messageHandler(message, detail) {
-    let target = null;
-    switch (message) {
-      case 'AvailableSpaceUpdated':
-        target = cr.quota.onAvailableSpaceUpdated;
-        break;
-      case 'GlobalInfoUpdated':
-        target = cr.quota.onGlobalInfoUpdated;
-        break;
-      case 'PerHostInfoUpdated':
-        target = cr.quota.onPerHostInfoUpdated;
-        break;
-      case 'PerOriginInfoUpdated':
-        target = cr.quota.onPerOriginInfoUpdated;
-        break;
-      case 'StatisticsUpdated':
-        target = cr.quota.onStatisticsUpdated;
-        break;
-      case 'StoragePressureFlagUpdated':
-        target = cr.quota.onStoragePressureFlagUpdated;
-        break;
-      default:
-        console.error('Unknown Message');
-        break;
-    }
-    if (target) {
-      const event = document.createEvent('CustomEvent');
-      event.initCustomEvent('update', false, false, detail);
-      target.dispatchEvent(event);
-    }
-  }
+
 
   return {
-    onAvailableSpaceUpdated: new cr.EventTarget(),
-    onGlobalInfoUpdated: new cr.EventTarget(),
-    onPerHostInfoUpdated: new cr.EventTarget(),
-    onPerOriginInfoUpdated: new cr.EventTarget(),
-    onStatisticsUpdated: new cr.EventTarget(),
-    onStoragePressureFlagUpdated: new cr.EventTarget(),
-
     requestInfo: requestInfo,
     triggerStoragePressure: triggerStoragePressure,
-    messageHandler: messageHandler
   };
 });
diff --git a/chrome/browser/sync/chrome_sync_client.h b/chrome/browser/sync/chrome_sync_client.h
index 44e4be4..141426cd 100644
--- a/chrome/browser/sync/chrome_sync_client.h
+++ b/chrome/browser/sync/chrome_sync_client.h
@@ -89,8 +89,6 @@
   Profile* const profile_;
 
   // The sync api component factory in use by this client.
-  // TODO(crbug.com/915154): Revert to SyncApiComponentFactory once common
-  // controller creation is moved elsewhere.
   std::unique_ptr<browser_sync::ProfileSyncComponentsFactoryImpl>
       component_factory_;
 
diff --git a/chrome/browser/sync/sync_startup_tracker_unittest.cc b/chrome/browser/sync/sync_startup_tracker_unittest.cc
index 836737e..a3af1d7 100644
--- a/chrome/browser/sync/sync_startup_tracker_unittest.cc
+++ b/chrome/browser/sync/sync_startup_tracker_unittest.cc
@@ -15,8 +15,8 @@
 
 class MockObserver : public SyncStartupTracker::Observer {
  public:
-  MOCK_METHOD0(SyncStartupCompleted, void());
-  MOCK_METHOD0(SyncStartupFailed, void());
+  MOCK_METHOD(void, SyncStartupCompleted, (), (override));
+  MOCK_METHOD(void, SyncStartupFailed, (), (override));
 };
 
 class SyncStartupTrackerTest : public testing::Test {
diff --git a/chrome/browser/sync/test/integration/autofill_helper.cc b/chrome/browser/sync/test/integration/autofill_helper.cc
index d872509..2b053f6 100644
--- a/chrome/browser/sync/test/integration/autofill_helper.cc
+++ b/chrome/browser/sync/test/integration/autofill_helper.cc
@@ -56,7 +56,10 @@
 class MockWebDataServiceObserver
     : public AutofillWebDataServiceObserverOnDBSequence {
  public:
-  MOCK_METHOD1(AutofillEntriesChanged, void(const AutofillChangeList& changes));
+  MOCK_METHOD(void,
+              AutofillEntriesChanged,
+              (const AutofillChangeList& changes),
+              (override));
 };
 
 scoped_refptr<AutofillWebDataService> GetWebDataService(int index) {
diff --git a/chrome/browser/sync/test/integration/autofill_helper.h b/chrome/browser/sync/test/integration/autofill_helper.h
index b6698207..62b47b3a 100644
--- a/chrome/browser/sync/test/integration/autofill_helper.h
+++ b/chrome/browser/sync/test/integration/autofill_helper.h
@@ -150,8 +150,8 @@
   PersonalDataLoadedObserverMock();
   ~PersonalDataLoadedObserverMock() override;
 
-  MOCK_METHOD0(OnPersonalDataChanged, void());
-  MOCK_METHOD0(OnPersonalDataFinishedProfileTasks, void());
+  MOCK_METHOD(void, OnPersonalDataChanged, (), (override));
+  MOCK_METHOD(void, OnPersonalDataFinishedProfileTasks, (), (override));
 };
 
 #endif  // CHROME_BROWSER_SYNC_TEST_INTEGRATION_AUTOFILL_HELPER_H_
diff --git a/chrome/browser/ui/browser_command_controller_browsertest.cc b/chrome/browser/ui/browser_command_controller_browsertest.cc
index 0eebeeb..9a0add7e 100644
--- a/chrome/browser/ui/browser_command_controller_browsertest.cc
+++ b/chrome/browser/ui/browser_command_controller_browsertest.cc
@@ -33,9 +33,9 @@
 #include "content/public/test/test_utils.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "ui/aura/window.h"
 #endif
 
@@ -126,7 +126,7 @@
   EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_EXIT));
   // Set locked fullscreen mode.
   browser()->window()->GetNativeWindow()->SetProperty(
-      ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
+      chromeos::kWindowPinTypeKey, chromeos::WindowPinType::kTrustedPinned);
   // Update the corresponding command_controller state.
   browser()->command_controller()->LockedFullscreenStateChanged();
   // Update some more states just to make sure the wrong commands don't get
@@ -156,7 +156,7 @@
 
   // Exit locked fullscreen mode.
   browser()->window()->GetNativeWindow()->SetProperty(
-      ash::kWindowPinTypeKey, ash::WindowPinType::kNone);
+      chromeos::kWindowPinTypeKey, chromeos::WindowPinType::kNone);
   // Update the corresponding command_controller state.
   browser()->command_controller()->LockedFullscreenStateChanged();
   // IDC_EXIT is enabled again.
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index 82d8e45..1cd2d02c 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -2,8 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "base/command_line.h"
 #include "chrome/browser/chromeos/login/chrome_restart_request.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
@@ -24,6 +22,8 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "components/account_id/account_id.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
@@ -134,8 +134,8 @@
                        NavigationBlockedInLockedFullscreen) {
   // Set locked fullscreen state.
   aura::Window* window = browser()->window()->GetNativeWindow();
-  window->SetProperty(ash::kWindowPinTypeKey,
-                      ash::WindowPinType::kTrustedPinned);
+  window->SetProperty(chromeos::kWindowPinTypeKey,
+                      chromeos::WindowPinType::kTrustedPinned);
 
   // Navigate to a page.
   auto url = GURL(chrome::kChromeUIVersionURL);
@@ -155,7 +155,8 @@
   // As a sanity check unset the locked fullscreen state and make sure that the
   // navigation happens (the following EXPECTs fail if the next line isn't
   // executed).
-  window->SetProperty(ash::kWindowPinTypeKey, ash::WindowPinType::kNone);
+  window->SetProperty(chromeos::kWindowPinTypeKey,
+                      chromeos::WindowPinType::kNone);
 
   Navigate(&params);
 
diff --git a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc
index dc98f64..eb2c8ed 100644
--- a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc
+++ b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.cc
@@ -89,12 +89,12 @@
   profile_pref_registrar_.Init(browser_view_->browser()->profile()->GetPrefs());
   profile_pref_registrar_.Add(
       prefs::kAccessibilityFocusHighlightEnabled,
-      base::BindRepeating(
-          &AccessibilityFocusHighlight::AddOrRemoveFocusObserver,
-          base::Unretained(this)));
+      base::BindRepeating(&AccessibilityFocusHighlight::AddOrRemoveObservers,
+                          base::Unretained(this)));
 
-  // Initialise focus observer based on current preferences.
-  AddOrRemoveFocusObserver();
+  // Initialise focus and tab strip model observers based on current
+  // preferences.
+  AddOrRemoveObservers();
 
   // One-time initialization of statics the first time an instance is created.
   if (fade_in_time_.is_zero()) {
@@ -215,8 +215,10 @@
   }
 }
 
-void AccessibilityFocusHighlight::AddOrRemoveFocusObserver() {
-  PrefService* prefs = browser_view_->browser()->profile()->GetPrefs();
+void AccessibilityFocusHighlight::AddOrRemoveObservers() {
+  Browser* browser = browser_view_->browser();
+  PrefService* prefs = browser->profile()->GetPrefs();
+  TabStripModel* tab_strip_model = browser->tab_strip_model();
 
   if (prefs->GetBoolean(prefs::kAccessibilityFocusHighlightEnabled)) {
     // Listen for focus changes. Automatically deregisters when destroyed,
@@ -224,15 +226,18 @@
     notification_registrar_.Add(this,
                                 content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
                                 content::NotificationService::AllSources());
-    return;
-  }
 
-  if (notification_registrar_.IsRegistered(
+    tab_strip_model->AddObserver(this);
+    return;
+  } else {
+    if (notification_registrar_.IsRegistered(
+            this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
+            content::NotificationService::AllSources())) {
+      notification_registrar_.Remove(
           this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
-          content::NotificationService::AllSources())) {
-    notification_registrar_.Remove(this,
-                                   content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
-                                   content::NotificationService::AllSources());
+          content::NotificationService::AllSources());
+    }
+    tab_strip_model->RemoveObserver(this);
   }
 }
 
@@ -390,3 +395,10 @@
     compositor_ = nullptr;
   }
 }
+
+void AccessibilityFocusHighlight::OnTabStripModelChanged(
+    TabStripModel*,
+    const TabStripModelChange&,
+    const TabStripSelectionChange&) {
+  RemoveLayer();
+}
diff --git a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h
index 6b622554..664d9bdb 100644
--- a/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h
+++ b/chrome/browser/ui/views/accessibility/accessibility_focus_highlight.h
@@ -10,6 +10,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/time/time.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
@@ -29,7 +30,8 @@
 // highlight the focused UI element for accessibility.
 class AccessibilityFocusHighlight : public ui::LayerDelegate,
                                     public ui::CompositorAnimationObserver,
-                                    public content::NotificationObserver {
+                                    public content::NotificationObserver,
+                                    public TabStripModelObserver {
  public:
   explicit AccessibilityFocusHighlight(BrowserView* browser_view);
   ~AccessibilityFocusHighlight() override;
@@ -56,8 +58,8 @@
   // Get rid of the layer and stop animation.
   void RemoveLayer();
 
-  // Handle preference changes by adding or removing the layer as necessary.
-  void AddOrRemoveFocusObserver();
+  // Handle preference changes by adding or removing observers as necessary.
+  void AddOrRemoveObservers();
 
   // content::NotificationObserver overrides:
   void Observe(int type,
@@ -73,6 +75,11 @@
   void OnAnimationStep(base::TimeTicks timestamp) override;
   void OnCompositingShuttingDown(ui::Compositor* compositor) override;
 
+  // TabStripModelObserver
+  void OnTabStripModelChanged(TabStripModel*,
+                              const TabStripModelChange&,
+                              const TabStripSelectionChange&) override;
+
   // Compute the highlight color based on theme colors and defaults.
   SkColor GetHighlightColor();
 
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index f10881d..e81d7ac 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -308,8 +308,8 @@
   const bool should_hide_shelf =
       !profiles::IsPublicSession() &&
       fullscreen_types != AppWindow::FULLSCREEN_TYPE_OS;
-  widget()->GetNativeWindow()->SetProperty(ash::kHideShelfWhenFullscreenKey,
-                                           should_hide_shelf);
+  widget()->GetNativeWindow()->SetProperty(
+      chromeos::kHideShelfWhenFullscreenKey, should_hide_shelf);
   widget()->non_client_view()->Layout();
 }
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
index 89b4065..d1ce34d 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_ash_browsertest.cc
@@ -10,7 +10,6 @@
 #include "ash/public/cpp/shelf_test_api.h"
 #include "ash/public/cpp/split_view_test_api.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/wm/overview/overview_controller.h"
@@ -72,6 +71,8 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/ui/base/chromeos_ui_constants.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "components/account_id/account_id.h"
@@ -736,7 +737,7 @@
 
   // Set locked fullscreen state.
   browser()->window()->GetNativeWindow()->SetProperty(
-      ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
+      chromeos::kWindowPinTypeKey, chromeos::WindowPinType::kTrustedPinned);
 
   // We're fullscreen, immersive is disabled in locked fullscreen, and while
   // we're at it, also make sure that the shelf is hidden.
@@ -762,7 +763,7 @@
 
   // Set locked fullscreen state.
   browser()->window()->GetNativeWindow()->SetProperty(
-      ash::kWindowPinTypeKey, ash::WindowPinType::kTrustedPinned);
+      chromeos::kWindowPinTypeKey, chromeos::WindowPinType::kTrustedPinned);
 
   // We're fullscreen, immersive is disabled in locked fullscreen, and while
   // we're at it, also make sure that the shelf is hidden.
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
index 5bf9730..c3c4699 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 
-#include "ash/public/cpp/window_properties.h"
 #include "base/macros.h"
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
@@ -219,14 +218,14 @@
                                ->fullscreen_controller()
                                ->IsWindowFullscreenForTabOrPending();
   browser_view_->GetNativeWindow()->SetProperty(
-      ash::kHideShelfWhenFullscreenKey, in_tab_fullscreen);
+      chromeos::kHideShelfWhenFullscreenKey, in_tab_fullscreen);
 }
 
 void ImmersiveModeControllerAsh::OnWindowPropertyChanged(aura::Window* window,
                                                          const void* key,
                                                          intptr_t old) {
   // Track locked fullscreen changes.
-  if (key == ash::kWindowPinTypeKey) {
+  if (key == chromeos::kWindowPinTypeKey) {
     browser_view_->FullscreenStateChanging();
     return;
   }
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
index 8bc2966c..d4fd4db 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_ash_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/views/frame/immersive_mode_controller_ash.h"
 
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
@@ -26,6 +25,7 @@
 #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "chromeos/ui/base/window_pin_type.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller_test_api.h"
 #include "ui/aura/window.h"
 #include "ui/base/pointer/touch_ui_controller.h"
diff --git a/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.cc
index 30ce939..2758d4f7 100644
--- a/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.cc
@@ -105,7 +105,6 @@
     std::vector<TargetInfo> targets,
     apps::mojom::IntentPtr intent,
     sharesheet::CloseCallback close_callback) {
-  targets_ = std::move(targets);
   intent_ = std::move(intent);
   close_callback_ = std::move(close_callback);
 
@@ -132,7 +131,7 @@
   main_layout->AddPaddingRow(views::GridLayout::kFixedSize, kSpacing);
 
   auto scroll_view = std::make_unique<views::ScrollView>();
-  scroll_view->SetContents(MakeScrollableTargetView());
+  scroll_view->SetContents(MakeScrollableTargetView(std::move(targets)));
   scroll_view->ClipHeightTo(kTargetViewHeight, kTargetViewExpandedHeight);
 
   // TODO(crbug.com/1097623) Update grey border lines.
@@ -142,8 +141,9 @@
 
   main_layout->StartRow(views::GridLayout::kFixedSize, COLUMN_SET_ID_TITLE,
                         kShortSpacing);
-  expand_button_ =
-      main_layout->AddView(std::make_unique<SharesheetExpandButton>(this));
+  expand_button_ = main_layout->AddView(
+      std::make_unique<SharesheetExpandButton>(base::BindRepeating(
+          &SharesheetBubbleView::ExpandButtonPressed, base::Unretained(this))));
   main_layout->AddPaddingRow(views::GridLayout::kFixedSize, kShortSpacing);
 
   main_view_->SetFocusBehavior(View::FocusBehavior::ALWAYS);
@@ -153,17 +153,18 @@
   GetWidget()->GetRootView()->Layout();
   widget->Show();
 
-  if (targets_.size() <= (kMaxRowsForDefaultView * kMaxTargetsPerRow)) {
+  if (expanded_view_->children().size() > 1) {
+    SetToDefaultBubbleSizing();
+  } else {
     width_ = kDefaultBubbleWidth;
     height_ = kNoExtensionBubbleHeight;
     expand_button_->SetVisible(false);
-  } else {
-    SetToDefaultBubbleSizing();
   }
   UpdateAnchorPosition();
 }
 
-std::unique_ptr<views::View> SharesheetBubbleView::MakeScrollableTargetView() {
+std::unique_ptr<views::View> SharesheetBubbleView::MakeScrollableTargetView(
+    std::vector<TargetInfo> targets) {
   // Set up default and expanded views.
   auto default_view = std::make_unique<views::View>();
   auto* default_layout =
@@ -197,7 +198,8 @@
   expanded_layout->AddPaddingRow(views::GridLayout::kFixedSize,
                                  kExpandViewPadding);
 
-  PopulateLayoutsWithTargets(default_layout, expanded_layout);
+  PopulateLayoutsWithTargets(std::move(targets), default_layout,
+                             expanded_layout);
   default_layout->AddPaddingRow(views::GridLayout::kFixedSize, kShortSpacing);
 
   auto scrollable_view = std::make_unique<views::View>();
@@ -215,14 +217,15 @@
 }
 
 void SharesheetBubbleView::PopulateLayoutsWithTargets(
+    std::vector<TargetInfo> targets,
     views::GridLayout* default_layout,
     views::GridLayout* expanded_layout) {
   // Add first kMaxRowsForDefaultView*kMaxTargetsPerRow targets to
   // |default_view| and subsequent targets to |expanded_view|.
-  size_t target_counter = 0;
   size_t row_count = 0;
+  size_t target_counter = 0;
   auto* layout_for_target = default_layout;
-  for (const auto& target : targets_) {
+  for (auto& target : targets) {
     if (target_counter % kMaxTargetsPerRow == 0) {
       // When we've reached kMaxRowsForDefaultView switch to populating
       // |expanded_layout|.
@@ -238,13 +241,16 @@
       layout_for_target->StartRow(views::GridLayout::kFixedSize,
                                   COLUMN_SET_ID_TARGETS);
     }
+    ++target_counter;
 
     base::string16 secondary_display_name =
         target.secondary_display_name.value_or(base::string16());
 
     auto target_view = std::make_unique<SharesheetTargetButton>(
-        this, target.display_name, secondary_display_name, &target.icon);
-    target_view->set_tag(target_counter++);
+        base::BindRepeating(&SharesheetBubbleView::TargetButtonPressed,
+                            base::Unretained(this),
+                            base::Passed(std::move(target))),
+        target.display_name, secondary_display_name, &target.icon);
 
     layout_for_target->AddView(std::move(target_view));
   }
@@ -265,7 +271,6 @@
   views::Widget* widget = View::GetWidget();
   widget->CloseWithReason(views::Widget::ClosedReason::kAcceptButtonClicked);
   // Reset all bubble values.
-  targets_.clear();
   active_target_ = base::string16();
   intent_.reset();
   keyboard_highlighted_target_ = 0;
@@ -297,63 +302,25 @@
       break;
   }
 
-  keyboard_highlighted_target_ += delta;
+  const size_t default_views = default_view_->children().size();
+  // The -1 here and +1 below account for the app list label.
+  const size_t targets =
+      default_views +
+      (show_expanded_view_ ? (expanded_view_->children().size() - 1) : 0);
+  const int new_target = int{keyboard_highlighted_target_} + delta;
+  keyboard_highlighted_target_ =
+      size_t{base::ClampToRange(new_target, 0, int{targets} - 1)};
 
-  int default_view_max = kMaxTargetsPerRow * kMaxRowsForDefaultView - 1;
-  int max_target_index = targets_.size() - 1;
-  if ((!show_expanded_view_) && (max_target_index > default_view_max)) {
-    max_target_index = default_view_max;
-  }
-
-  if (keyboard_highlighted_target_ > max_target_index) {
-    keyboard_highlighted_target_ = max_target_index;
-  } else if (keyboard_highlighted_target_ < 0) {
-    keyboard_highlighted_target_ = 0;
-  }
-
-  if (keyboard_highlighted_target_ <= default_view_max) {
+  if (keyboard_highlighted_target_ < default_views) {
     default_view_->children()[keyboard_highlighted_target_]->RequestFocus();
   } else {
-    DCHECK_LT(keyboard_highlighted_target_ - default_view_max,
-              expanded_view_->children().size());
-    DCHECK(show_expanded_view_);
-    expanded_view_->children()[keyboard_highlighted_target_ - default_view_max]
+    expanded_view_->children()[keyboard_highlighted_target_ + 1 - default_views]
         ->RequestFocus();
   }
 
   View::OnKeyEvent(event);
 }
 
-void SharesheetBubbleView::ButtonPressed(views::Button* sender,
-                                         const ui::Event& event) {
-  if (sender == expand_button_) {
-    if (show_expanded_view_) {
-      expand_button_->SetDefaultView();
-      expanded_view_->SetVisible(false);
-      ResizeBubble(kDefaultBubbleWidth, kDefaultBubbleHeight);
-    } else {
-      expand_button_->SetExpandedView();
-      expanded_view_->SetVisible(true);
-      ResizeBubble(kDefaultBubbleWidth, kExpandedBubbleHeight);
-    }
-    show_expanded_view_ = !show_expanded_view_;
-  } else {
-    auto type = targets_[sender->tag()].type;
-    if (type == sharesheet::TargetType::kAction) {
-      active_target_ = targets_[sender->tag()].launch_name;
-    } else {
-      intent_->activity_name = targets_[sender->tag()].activity_name;
-    }
-    delegate_->OnTargetSelected(targets_[sender->tag()].launch_name, type,
-                                std::move(intent_), share_action_view_);
-    intent_.reset();
-    user_cancelled_ = false;
-    if (close_callback_) {
-      std::move(close_callback_).Run(sharesheet::SharesheetResult::kSuccess);
-    }
-  }
-}
-
 std::unique_ptr<views::NonClientFrameView>
 SharesheetBubbleView::CreateNonClientFrameView(views::Widget* widget) {
   auto bubble_border =
@@ -413,6 +380,31 @@
   share_action_view_->SetVisible(false);
 }
 
+void SharesheetBubbleView::ExpandButtonPressed() {
+  show_expanded_view_ = !show_expanded_view_;
+  if (show_expanded_view_)
+    expand_button_->SetExpandedView();
+  else
+    expand_button_->SetDefaultView();
+  expanded_view_->SetVisible(show_expanded_view_);
+  ResizeBubble(kDefaultBubbleWidth, show_expanded_view_ ? kExpandedBubbleHeight
+                                                        : kDefaultBubbleHeight);
+}
+
+void SharesheetBubbleView::TargetButtonPressed(TargetInfo target) {
+  auto type = target.type;
+  if (type == sharesheet::TargetType::kAction)
+    active_target_ = target.launch_name;
+  else
+    intent_->activity_name = target.activity_name;
+  delegate_->OnTargetSelected(target.launch_name, type, std::move(intent_),
+                              share_action_view_);
+  intent_.reset();
+  user_cancelled_ = false;
+  if (close_callback_)
+    std::move(close_callback_).Run(sharesheet::SharesheetResult::kSuccess);
+}
+
 void SharesheetBubbleView::UpdateAnchorPosition() {
   // If |width_| is not set, set to default value.
   if (width_ == 0) {
diff --git a/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.h b/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.h
index 0340ec12..86f4302 100644
--- a/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.h
+++ b/chrome/browser/ui/views/sharesheet/sharesheet_bubble_view.h
@@ -10,7 +10,6 @@
 #include "chrome/browser/sharesheet/sharesheet_types.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
-#include "ui/views/controls/button/button.h"
 
 namespace views {
 class GridLayout;
@@ -26,8 +25,7 @@
 
 class SharesheetExpandButton;
 
-class SharesheetBubbleView : public views::BubbleDialogDelegateView,
-                             public views::ButtonListener {
+class SharesheetBubbleView : public views::BubbleDialogDelegateView {
  public:
   using TargetInfo = sharesheet::TargetInfo;
 
@@ -51,9 +49,6 @@
   // ui::EventHandler overrides:
   void OnKeyEvent(ui::KeyEvent* event) override;
 
-  // views::ButtonListener overrides
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // views::WidgetDelegate override
   std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
       views::Widget* widget) override;
@@ -63,16 +58,18 @@
   void OnWidgetDestroyed(views::Widget* widget) override;
 
   void CreateBubble();
-  std::unique_ptr<views::View> MakeScrollableTargetView();
-  void PopulateLayoutsWithTargets(views::GridLayout* default_layout,
+  std::unique_ptr<views::View> MakeScrollableTargetView(
+      std::vector<TargetInfo> targets);
+  void PopulateLayoutsWithTargets(std::vector<TargetInfo> targets,
+                                  views::GridLayout* default_layout,
                                   views::GridLayout* expanded_layout);
-  void OnDialogClosed();
+  void ExpandButtonPressed();
+  void TargetButtonPressed(TargetInfo target);
   void UpdateAnchorPosition();
   void SetToDefaultBubbleSizing();
 
   // Owns this class.
   sharesheet::SharesheetServiceDelegate* delegate_;
-  std::vector<TargetInfo> targets_;
   base::string16 active_target_;
   apps::mojom::IntentPtr intent_;
   sharesheet::CloseCallback close_callback_;
@@ -82,7 +79,7 @@
   bool user_cancelled_ = true;
   bool show_expanded_view_ = false;
 
-  int keyboard_highlighted_target_ = 0;
+  size_t keyboard_highlighted_target_ = 0;
 
   views::View* root_view_ = nullptr;
   views::View* main_view_ = nullptr;
diff --git a/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.cc b/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.cc
index b443a12..1fbf761 100644
--- a/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.cc
+++ b/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.cc
@@ -27,8 +27,8 @@
 
 }  // namespace
 
-SharesheetExpandButton::SharesheetExpandButton(views::ButtonListener* listener)
-    : Button(listener) {
+SharesheetExpandButton::SharesheetExpandButton(PressedCallback callback)
+    : Button(std::move(callback)) {
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kHorizontal, gfx::Insets(6, 16, 6, 16),
       kBetweenChildSpacing, true));
diff --git a/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.h b/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.h
index d899829..78fbbf55 100644
--- a/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.h
+++ b/chrome/browser/ui/views/sharesheet/sharesheet_expand_button.h
@@ -12,7 +12,7 @@
 
 class SharesheetExpandButton : public views::Button {
  public:
-  explicit SharesheetExpandButton(views::ButtonListener* listener);
+  explicit SharesheetExpandButton(PressedCallback callback);
   SharesheetExpandButton(const SharesheetExpandButton&) = delete;
   SharesheetExpandButton& operator=(const SharesheetExpandButton&) = delete;
 
diff --git a/chrome/browser/ui/views/sharesheet/sharesheet_target_button.cc b/chrome/browser/ui/views/sharesheet/sharesheet_target_button.cc
index f872f19..ae40b70 100644
--- a/chrome/browser/ui/views/sharesheet/sharesheet_target_button.cc
+++ b/chrome/browser/ui/views/sharesheet/sharesheet_target_button.cc
@@ -35,11 +35,11 @@
 
 // A button that represents a candidate share target.
 SharesheetTargetButton::SharesheetTargetButton(
-    views::ButtonListener* listener,
+    PressedCallback callback,
     const base::string16& display_name,
     const base::string16& secondary_display_name,
     const gfx::ImageSkia* icon)
-    : Button(listener) {
+    : Button(std::move(callback)) {
   // TODO(crbug.com/1097623) Margins shouldn't be within
   // SharesheetTargetButton as the margins are different in |expanded_view_|.
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
diff --git a/chrome/browser/ui/views/sharesheet/sharesheet_target_button.h b/chrome/browser/ui/views/sharesheet/sharesheet_target_button.h
index 987b70d..b808d7a 100644
--- a/chrome/browser/ui/views/sharesheet/sharesheet_target_button.h
+++ b/chrome/browser/ui/views/sharesheet/sharesheet_target_button.h
@@ -11,7 +11,7 @@
 
 class SharesheetTargetButton : public views::Button {
  public:
-  SharesheetTargetButton(views::ButtonListener* listener,
+  SharesheetTargetButton(PressedCallback callback,
                          const base::string16& display_name,
                          const base::string16& secondary_display_name,
                          const gfx::ImageSkia* icon);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_browsertest.cc b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_browsertest.cc
index 4326d37..9f25585 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_browsertest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_browsertest.cc
@@ -20,30 +20,6 @@
 #include "ui/gfx/animation/animation.h"
 #include "ui/gfx/animation/animation_test_api.h"
 
-// TODO(crbug.com/1061637): Clean this and the same code in ukm_browsertest.
-// Maybe move them to InProcessBrowserTest.
-namespace {
-
-void UnblockOnProfileCreation(base::RunLoop* run_loop,
-                              Profile* profile,
-                              Profile::CreateStatus status) {
-  if (status == Profile::CREATE_STATUS_INITIALIZED)
-    run_loop->Quit();
-}
-
-Profile* CreateGuestProfile() {
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  base::FilePath new_path = profile_manager->GetGuestProfilePath();
-  base::RunLoop run_loop;
-  profile_manager->CreateProfileAsync(
-      new_path, base::BindRepeating(&UnblockOnProfileCreation, &run_loop),
-      base::string16(), std::string());
-  run_loop.Run();
-  return profile_manager->GetProfileByPath(new_path);
-}
-
-}  // namespace
-
 // The param is whether to use the highlight in the container.
 class ToolbarAccountIconContainerViewBrowserTest : public InProcessBrowserTest {
  public:
@@ -108,9 +84,7 @@
 
 IN_PROC_BROWSER_TEST_F(ToolbarAccountIconContainerViewBrowserTest,
                        ShouldUpdateHighlightInGuestWindow) {
-  Profile* guest_profile = CreateGuestProfile();
-  Browser* guest_browser = CreateIncognitoBrowser(guest_profile);
-  ASSERT_TRUE(guest_browser->profile()->IsGuestSession());
+  Browser* guest_browser = InProcessBrowserTest::CreateGuestBrowser();
   ToolbarAccountIconContainerView* container_view =
       BrowserView::GetBrowserViewForBrowser(guest_browser)
           ->toolbar()
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
index eae3acf..21037b4 100644
--- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -10,7 +10,6 @@
 
 #include "base/check_op.h"
 #include "base/debug/dump_without_crashing.h"
-#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
@@ -35,10 +34,6 @@
 #include "chrome/browser/web_applications/system_web_app_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_launch/web_launch_files_helper.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
-#include "content/public/common/content_switches.h"
-#include "third_party/blink/public/common/features.h"
 #include "ui/base/window_open_disposition.h"
 #include "ui/display/types/display_constants.h"
 
@@ -228,39 +223,22 @@
 
   content::WebContents* web_contents = nullptr;
 
-  // TODO(crbug.com/1129340): Remove these lines and make CCA resizeable after
-  // CCA supports responsive UI.
-  bool can_resize = app_type != SystemAppType::CAMERA;
-  if (base::FeatureList::IsEnabled(features::kDesktopPWAsWithoutExtensions)) {
-    if (!browser) {
-      browser = CreateWebApplicationWindow(profile_for_launch, params->app_id,
-                                           params->disposition, can_resize);
-    }
+  if (!browser) {
+    // TODO(crbug.com/1129340): Remove these lines and make CCA resizeable after
+    // CCA supports responsive UI.
+    bool can_resize = app_type != SystemAppType::CAMERA;
+    browser = CreateWebApplicationWindow(profile_for_launch, params->app_id,
+                                         params->disposition, can_resize);
+  }
 
-    // Navigate application window to application's |url| if necessary.
-    // Help app always navigates because its url might not match the url inside
-    // the iframe, and the iframe's url is the one that matters.
-    web_contents = browser->tab_strip_model()->GetWebContentsAt(0);
-    if (!web_contents || web_contents->GetURL() != url ||
-        app_type == SystemAppType::HELP) {
-      web_contents = NavigateWebApplicationWindow(
-          browser, params->app_id, url, WindowOpenDisposition::CURRENT_TAB);
-    }
-  } else {
-    if (!browser) {
-      browser =
-          CreateApplicationWindow(profile_for_launch, *params, url, can_resize);
-    }
-
-    // Navigate application window to application's |url| if necessary.
-    // Help app always navigates because its url might not match the url inside
-    // the iframe, and the iframe's url is the one that matters.
-    web_contents = browser->tab_strip_model()->GetWebContentsAt(0);
-    if (!web_contents || web_contents->GetURL() != url ||
-        app_type == SystemAppType::HELP) {
-      web_contents = NavigateApplicationWindow(
-          browser, *params, url, WindowOpenDisposition::CURRENT_TAB);
-    }
+  // Navigate application window to application's |url| if necessary.
+  // Help app always navigates because its url might not match the url inside
+  // the iframe, and the iframe's url is the one that matters.
+  web_contents = browser->tab_strip_model()->GetWebContentsAt(0);
+  if (!web_contents || web_contents->GetURL() != url ||
+      app_type == SystemAppType::HELP) {
+    web_contents = NavigateWebApplicationWindow(
+        browser, params->app_id, url, WindowOpenDisposition::CURRENT_TAB);
   }
 
   // Send launch files.
diff --git a/chrome/browser/ui/webui/omnibox/BUILD.gn b/chrome/browser/ui/webui/omnibox/BUILD.gn
index b58f3e4..b7d5bda 100644
--- a/chrome/browser/ui/webui/omnibox/BUILD.gn
+++ b/chrome/browser/ui/webui/omnibox/BUILD.gn
@@ -6,4 +6,5 @@
 
 mojom("mojo_bindings") {
   sources = [ "omnibox.mojom" ]
+  webui_module_path = "/chrome/browser/ui/webui/omnibox"
 }
diff --git a/chrome/browser/ui/webui/omnibox/omnibox_ui.cc b/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
index ba37706f..f15c23a 100644
--- a/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
+++ b/chrome/browser/ui/webui/omnibox/omnibox_ui.cc
@@ -49,7 +49,7 @@
       {"omnibox_input.js", IDR_OMNIBOX_INPUT_JS},
       {"omnibox_output.js", IDR_OMNIBOX_OUTPUT_JS},
       {"omnibox.js", IDR_OMNIBOX_JS},
-      {"chrome/browser/ui/webui/omnibox/omnibox.mojom-lite.js",
+      {"chrome/browser/ui/webui/omnibox/omnibox.mojom-webui.js",
        IDR_OMNIBOX_MOJO_JS},
   };
   webui::AddResourcePathsBulk(source, kResources);
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc b/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
index ea3df0ae..b21849b 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_handler.cc
@@ -51,13 +51,13 @@
 }
 
 void QuotaInternalsHandler::ReportAvailableSpace(int64_t available_space) {
-  SendMessage("AvailableSpaceUpdated",
-              base::Value(static_cast<double>(available_space)));
+  FireWebUIListener("AvailableSpaceUpdated",
+                    base::Value(static_cast<double>(available_space)));
 }
 
 void QuotaInternalsHandler::ReportGlobalInfo(const GlobalStorageInfo& data) {
   std::unique_ptr<base::Value> value(data.NewValue());
-  SendMessage("GlobalInfoUpdated", *value);
+  FireWebUIListener("GlobalInfoUpdated", *value);
 }
 
 void QuotaInternalsHandler::ReportPerHostInfo(
@@ -67,7 +67,7 @@
     values.Append(itr->NewValue());
   }
 
-  SendMessage("PerHostInfoUpdated", values);
+  FireWebUIListener("PerHostInfoUpdated", values);
 }
 
 void QuotaInternalsHandler::ReportPerOriginInfo(
@@ -77,7 +77,7 @@
     origins_value.Append(itr->NewValue());
   }
 
-  SendMessage("PerOriginInfoUpdated", origins_value);
+  FireWebUIListener("PerOriginInfoUpdated", origins_value);
 }
 
 void QuotaInternalsHandler::ReportStatistics(const Statistics& stats) {
@@ -86,23 +86,18 @@
     dict.SetString(itr->first, itr->second);
   }
 
-  SendMessage("StatisticsUpdated", dict);
+  FireWebUIListener("StatisticsUpdated", dict);
 }
 
 void QuotaInternalsHandler::ReportStoragePressureFlag() {
   base::DictionaryValue flag_enabled;
   flag_enabled.SetBoolean("isStoragePressureEnabled",
                           IsStoragePressureEnabled());
-  SendMessage("StoragePressureFlagUpdated", flag_enabled);
-}
-
-void QuotaInternalsHandler::SendMessage(const std::string& message,
-                                        const base::Value& value) {
-  web_ui()->CallJavascriptFunctionUnsafe("cr.quota.messageHandler",
-                                         base::Value(message), value);
+  FireWebUIListener("StoragePressureFlagUpdated", flag_enabled);
 }
 
 void QuotaInternalsHandler::OnRequestInfo(const base::ListValue*) {
+  AllowJavascript();
   if (!proxy_.get())
     proxy_ = new QuotaInternalsProxy(this);
   ReportStoragePressureFlag();
@@ -113,6 +108,7 @@
 
 void QuotaInternalsHandler::OnTriggerStoragePressure(
     const base::ListValue* args) {
+  AllowJavascript();
   CHECK_EQ(1U, args->GetSize());
   std::string origin_string;
   CHECK(args->GetString(0, &origin_string));
diff --git a/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h b/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h
index 1469f488..2d155a6 100644
--- a/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h
+++ b/chrome/browser/ui/webui/quota_internals/quota_internals_handler.h
@@ -16,7 +16,6 @@
 #include "content/public/browser/web_ui_message_handler.h"
 
 namespace base {
-class Value;
 class ListValue;
 }
 
@@ -47,7 +46,6 @@
  private:
   void OnRequestInfo(const base::ListValue*);
   void OnTriggerStoragePressure(const base::ListValue*);
-  void SendMessage(const std::string& message, const base::Value& value);
 
   scoped_refptr<QuotaInternalsProxy> proxy_;
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index e478e89c..aba94be9 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1602955577-a20645f1db5850cab28b6e02a84227b75223b9dc.profdata
+chrome-linux-master-1603087080-212b8c940037ae1d78d2bd42956b635be6add59e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 53a489a..ef557a28 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1602955577-bb0b49bc2897e7981bccc868b6d9f8258514b16c.profdata
+chrome-mac-master-1603087080-138c0bc7a94d51e9caaa7300e866d8f7233b285e.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 8f1283ef..cf88274 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1602892676-f42fcf882f856a6f2f9f9f80f30554db75044c2b.profdata
+chrome-win32-master-1603031994-5f3b6fcba7bce636145481c18ca09ca6363425d3.profdata
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index 8b2c48a..baf3f0f8 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -154,6 +154,17 @@
 }
 #endif  // defined(OS_CHROMEOS)
 
+#if !defined(OS_ANDROID)
+// An observer that returns back to test code after a new profile is
+// initialized.
+void UnblockOnProfileCreation(base::RunLoop* run_loop,
+                              Profile* profile,
+                              Profile::CreateStatus status) {
+  if (status == Profile::CREATE_STATUS_INITIALIZED)
+    run_loop->Quit();
+}
+#endif
+
 }  // namespace
 
 // static
@@ -486,6 +497,29 @@
 }
 #endif  // !defined(OS_MAC)
 
+#if !defined(OS_ANDROID)
+Browser* InProcessBrowserTest::CreateGuestBrowser() {
+  // Get Guest profile.
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  base::FilePath guest_path = profile_manager->GetGuestProfilePath();
+
+  base::RunLoop run_loop;
+  profile_manager->CreateProfileAsync(
+      guest_path, base::Bind(&UnblockOnProfileCreation, &run_loop),
+      base::string16(), std::string());
+  run_loop.Run();
+
+  Profile* profile = profile_manager->GetProfileByPath(guest_path);
+  if (!profile->IsEphemeralGuestProfile())
+    profile = profile->GetPrimaryOTRProfile();
+
+  // Create browser and add tab.
+  Browser* browser = new Browser(Browser::CreateParams(profile, true));
+  AddBlankTabAndShow(browser);
+  return browser;
+}
+#endif
+
 void InProcessBrowserTest::AddBlankTabAndShow(Browser* browser) {
   content::WindowedNotificationObserver observer(
       content::NOTIFICATION_LOAD_STOP,
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index 6a0796f0c..867dcf2 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -227,6 +227,11 @@
   // is omitted, the currently active profile will be used.
   Browser* CreateIncognitoBrowser(Profile* profile = nullptr);
 
+#if !defined(OS_ANDROID)
+  // Similar to |CreateBrowser|, but creates a Guest browser.
+  Browser* CreateGuestBrowser();
+#endif
+
   // Creates a browser for a popup window with a single tab (about:blank), waits
   // for the tab to finish loading, and shows the browser.
   Browser* CreateBrowserForPopup(Profile* profile);
diff --git a/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js b/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js
index 07b162f..0f83ba812 100644
--- a/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js
+++ b/chrome/test/data/webui/chromeos/diagnostics/realtime_cpu_chart_test.js
@@ -81,7 +81,8 @@
     });
   });
 
-  test('InitializePlot', () => {
+  // Flaky: https://crbug.com/1139523
+  test.skip('InitializePlot', () => {
     const user = 10;
     const system = 30;
     return initializeRealtimeCpuChart(user, system).then(() => {
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 2ac4a76f..5a0c1b2a 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13538.0.0
\ No newline at end of file
+13540.0.0
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/resources/.eslintrc.js b/chromeos/components/camera_app_ui/resources/.eslintrc.js
index 204669008..271a5add 100644
--- a/chromeos/components/camera_app_ui/resources/.eslintrc.js
+++ b/chromeos/components/camera_app_ui/resources/.eslintrc.js
@@ -362,24 +362,19 @@
   'root': true,
   'env': {
     'browser': true,
-    'es6': true,
+    'es2020': true,
     'webextensions': true,
   },
   'parserOptions': {
-    'ecmaVersion': 2018,
+    'ecmaVersion': 2020,
     'sourceType': 'module',
   },
   'extends': 'eslint:recommended',
   'globals': {
     'arc': 'readable',
-    // Adds BigInt64Array here since current version of eslint does not treat
-    // BigInt64Array as a defined type.
-    'BigInt64Array': 'readable',
     'chromeosCamera': 'readable',
     'blink': 'readable',
-    'cca': 'readable',  // TODO(inker): remove this after resolving b/141518780.
     'cros': 'readable',
-    'globalThis': 'readable',
     'webkitRequestFileSystem': 'readable',
   },
   // Generally, the rules should be compatible to both bundled and the newest
diff --git a/chromeos/components/camera_app_ui/resources/js/views/camera/preview.js b/chromeos/components/camera_app_ui/resources/js/views/camera/preview.js
index f429a82..40cc51d1 100644
--- a/chromeos/components/camera_app_ui/resources/js/views/camera/preview.js
+++ b/chromeos/components/camera_app_ui/resources/js/views/camera/preview.js
@@ -226,28 +226,33 @@
       element.textContent = val;
     };
 
-    const buildInverseTable = (obj, prefix) => {
-      const tbl = {};
+    /**
+     * @param {!Object<string, number>} obj
+     * @param {string} prefix
+     * @return {!Map<number, string>}
+     */
+    const buildInverseMap = (obj, prefix) => {
+      const map = new Map();
       for (const [key, val] of Object.entries(obj)) {
         if (!key.startsWith(prefix)) {
           continue;
         }
-        if (tbl.hasOwnProperty(val)) {
+        if (map.has(val)) {
           console.error(`Duplicated value: ${val}`);
           continue;
         }
-        tbl[val] = key.slice(prefix.length);
+        map.set(val, key.slice(prefix.length));
       }
-      return tbl;
+      return map;
     };
 
-    const afStateName = buildInverseTable(
+    const afStateName = buildInverseMap(
         cros.mojom.AndroidControlAfState, 'ANDROID_CONTROL_AF_STATE_');
-    const aeStateName = buildInverseTable(
+    const aeStateName = buildInverseMap(
         cros.mojom.AndroidControlAeState, 'ANDROID_CONTROL_AE_STATE_');
-    const awbStateName = buildInverseTable(
+    const awbStateName = buildInverseMap(
         cros.mojom.AndroidControlAwbState, 'ANDROID_CONTROL_AWB_STATE_');
-    const aeAntibandingModeName = buildInverseTable(
+    const aeAntibandingModeName = buildInverseMap(
         cros.mojom.AndroidControlAeAntibandingMode,
         'ANDROID_CONTROL_AE_ANTIBANDING_MODE_');
 
@@ -262,7 +267,7 @@
         showValue('#preview-focus-distance', `${focusDistance} cm`);
       },
       [tag.ANDROID_CONTROL_AF_STATE]: ([value]) => {
-        showValue('#preview-af-state', afStateName[value]);
+        showValue('#preview-af-state', afStateName.get(value));
       },
       [tag.ANDROID_SENSOR_SENSITIVITY]: ([value]) => {
         const sensitivity = value;
@@ -277,10 +282,11 @@
         showValue('#preview-frame-duration', `${frameFrequency} Hz`);
       },
       [tag.ANDROID_CONTROL_AE_ANTIBANDING_MODE]: ([value]) => {
-        showValue('#preview-ae-antibanding-mode', aeAntibandingModeName[value]);
+        showValue(
+            '#preview-ae-antibanding-mode', aeAntibandingModeName.get(value));
       },
       [tag.ANDROID_CONTROL_AE_STATE]: ([value]) => {
-        showValue('#preview-ae-state', aeStateName[value]);
+        showValue('#preview-ae-state', aeStateName.get(value));
       },
       [tag.ANDROID_COLOR_CORRECTION_GAINS]: ([valueRed, , , valueBlue]) => {
         const wbGainRed = valueRed.toFixed(2);
@@ -289,7 +295,7 @@
         showValue('#preview-wb-gain-blue', `${wbGainBlue}x`);
       },
       [tag.ANDROID_CONTROL_AWB_STATE]: ([value]) => {
-        showValue('#preview-awb-state', awbStateName[value]);
+        showValue('#preview-awb-state', awbStateName.get(value));
       },
       [tag.ANDROID_CONTROL_AF_MODE]: ([value]) => {
         displayCategory(
diff --git a/chromeos/components/phonehub/notification_manager.cc b/chromeos/components/phonehub/notification_manager.cc
index 1613fa3b..89d11905 100644
--- a/chromeos/components/phonehub/notification_manager.cc
+++ b/chromeos/components/phonehub/notification_manager.cc
@@ -70,7 +70,9 @@
 
   for (int64_t id : notification_ids) {
     auto it = id_to_notification_map_.find(id);
-    DCHECK(it != id_to_notification_map_.end());
+    if (it == id_to_notification_map_.end())
+      continue;
+
     id_to_notification_map_.erase(it);
   }
 
diff --git a/chromeos/components/phonehub/notification_manager_impl.cc b/chromeos/components/phonehub/notification_manager_impl.cc
index f079bc3..c711d93 100644
--- a/chromeos/components/phonehub/notification_manager_impl.cc
+++ b/chromeos/components/phonehub/notification_manager_impl.cc
@@ -22,6 +22,12 @@
 void NotificationManagerImpl::DismissNotification(int64_t notification_id) {
   PA_LOG(INFO) << "Dismissing notification with ID " << notification_id << ".";
 
+  if (!GetNotification(notification_id)) {
+    PA_LOG(WARNING) << "Attempted to dismiss an invalid notification with id: "
+                    << notification_id << ".";
+    return;
+  }
+
   RemoveNotificationsInternal(base::flat_set<int64_t>{notification_id});
   message_sender_->SendDismissNotificationRequest(notification_id);
 }
diff --git a/chromeos/components/phonehub/notification_manager_impl_unittest.cc b/chromeos/components/phonehub/notification_manager_impl_unittest.cc
index 296a91db..7d800bf 100644
--- a/chromeos/components/phonehub/notification_manager_impl_unittest.cc
+++ b/chromeos/components/phonehub/notification_manager_impl_unittest.cc
@@ -176,6 +176,15 @@
   EXPECT_EQ(1u, fake_message_sender().GetDismissNotificationRequestCallCount());
   EXPECT_EQ(expected_id2,
             fake_message_sender().GetRecentDismissNotificationRequest());
+
+  // Dismiss the same notification again, verify nothing happens.
+  manager().DismissNotification(expected_id2);
+  EXPECT_EQ(1u, GetNumNotifications());
+  EXPECT_EQ(NotificationState::kAdded, GetNotificationState(expected_id1));
+  EXPECT_EQ(NotificationState::kRemoved, GetNotificationState(expected_id2));
+  EXPECT_EQ(1u, fake_message_sender().GetDismissNotificationRequestCallCount());
+  EXPECT_EQ(expected_id2,
+            fake_message_sender().GetRecentDismissNotificationRequest());
 }
 
 TEST_F(NotificationManagerImplTest, UpdatedNotification) {
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc
index 7694c6e..7fac3108 100644
--- a/chromeos/constants/chromeos_features.cc
+++ b/chromeos/constants/chromeos_features.cc
@@ -272,6 +272,18 @@
 // Enables the next generation file manager.
 const base::Feature kFilesNG{"FilesNG", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables JS modules for Files app.
+const base::Feature kFilesJsModules{"FilesJsModules",
+                                    base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables JS modules for Audio Player.
+const base::Feature kAudioPlayerJsModules{"AudioPlayerJsModules",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables JS modules for Video Player.
+const base::Feature kVideoPlayerJsModules{"VideoPlayerJsModules",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables partitioning of removable disks in file manager.
 const base::Feature kFilesSinglePartitionFormat{
     "FilesSinglePartitionFormat", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h
index a2612f43..d522df9 100644
--- a/chromeos/constants/chromeos_features.h
+++ b/chromeos/constants/chromeos_features.h
@@ -131,6 +131,11 @@
 extern const base::Feature kFamilyLinkOnSchoolDevice;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kFilesCameraFolder;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kFilesJsModules;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kAudioPlayerJsModules;
+COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
+extern const base::Feature kVideoPlayerJsModules;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kFilesNG;
 COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
 extern const base::Feature kFilesSinglePartitionFormat;
diff --git a/chromeos/ui/base/BUILD.gn b/chromeos/ui/base/BUILD.gn
index e807214e..bc21fb7 100644
--- a/chromeos/ui/base/BUILD.gn
+++ b/chromeos/ui/base/BUILD.gn
@@ -13,6 +13,8 @@
     "chromeos_ui_constants.h",
     "tablet_state.cc",
     "tablet_state.h",
+    "window_pin_type.cc",
+    "window_pin_type.h",
     "window_properties.cc",
     "window_properties.h",
     "window_state_type.cc",
diff --git a/ash/public/cpp/window_pin_type.cc b/chromeos/ui/base/window_pin_type.cc
similarity index 84%
rename from ash/public/cpp/window_pin_type.cc
rename to chromeos/ui/base/window_pin_type.cc
index 6307c2c..d6a371a7 100644
--- a/ash/public/cpp/window_pin_type.cc
+++ b/chromeos/ui/base/window_pin_type.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/public/cpp/window_pin_type.h"
+#include "chromeos/ui/base/window_pin_type.h"
 
 #include "base/notreached.h"
 
-namespace ash {
+namespace chromeos {
 
 std::ostream& operator<<(std::ostream& out, WindowPinType pin_type) {
   switch (pin_type) {
@@ -22,4 +22,4 @@
   return out;
 }
 
-}  // namespace ash
+}  // namespace chromeos
diff --git a/ash/public/cpp/window_pin_type.h b/chromeos/ui/base/window_pin_type.h
similarity index 73%
rename from ash/public/cpp/window_pin_type.h
rename to chromeos/ui/base/window_pin_type.h
index 3308bb5..5246186 100644
--- a/ash/public/cpp/window_pin_type.h
+++ b/chromeos/ui/base/window_pin_type.h
@@ -7,9 +7,9 @@
 
 #include <ostream>
 
-#include "ash/public/cpp/ash_public_export.h"
+#include "base/component_export.h"
 
-namespace ash {
+namespace chromeos {
 
 // The window's pin type enum.
 enum class WindowPinType {
@@ -23,9 +23,9 @@
   kTrustedPinned,
 };
 
-ASH_PUBLIC_EXPORT std::ostream& operator<<(std::ostream& stream,
-                                           WindowPinType pin_type);
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+std::ostream& operator<<(std::ostream& stream, WindowPinType pin_type);
 
-}  // namespace ash
+}  // namespace chromeos
 
 #endif  // ASH_PUBLIC_CPP_WINDOW_PIN_TYPE_H_
diff --git a/chromeos/ui/base/window_properties.cc b/chromeos/ui/base/window_properties.cc
index 4079eaa..d235c2c6 100644
--- a/chromeos/ui/base/window_properties.cc
+++ b/chromeos/ui/base/window_properties.cc
@@ -4,11 +4,16 @@
 
 #include "chromeos/ui/base/window_properties.h"
 
+#include "chromeos/ui/base/window_pin_type.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "ui/aura/window.h"
 
+DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(COMPONENT_EXPORT(CHROMEOS_UI_BASE),
+                                       chromeos::WindowPinType)
+
 namespace chromeos {
 
+DEFINE_UI_CLASS_PROPERTY_KEY(bool, kHideShelfWhenFullscreenKey, true)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveImpliedByFullscreen, true)
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveIsActive, false)
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect,
@@ -17,6 +22,10 @@
 
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kIsShowingInOverviewKey, false)
 
+DEFINE_UI_CLASS_PROPERTY_KEY(WindowPinType,
+                             kWindowPinTypeKey,
+                             WindowPinType::kNone)
+
 DEFINE_UI_CLASS_PROPERTY_KEY(WindowStateType,
                              kWindowStateTypeKey,
                              WindowStateType::kDefault)
diff --git a/chromeos/ui/base/window_properties.h b/chromeos/ui/base/window_properties.h
index a38a183..34b77a1e 100644
--- a/chromeos/ui/base/window_properties.h
+++ b/chromeos/ui/base/window_properties.h
@@ -20,11 +20,17 @@
 namespace chromeos {
 
 enum class WindowStateType;
+enum class WindowPinType;
 
 // Shell-specific window property keys for use by ash and lacros clients.
 
 // Alphabetical sort.
 
+// Whether the shelf should be hidden when this window is put into fullscreen.
+// Exposed because some windows want to explicitly opt-out of this.
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<bool>* const kHideShelfWhenFullscreenKey;
+
 // Whether entering fullscreen means that a window should automatically enter
 // immersive mode. This is false for some client windows, such as Chrome Apps.
 COMPONENT_EXPORT(CHROMEOS_UI_BASE)
@@ -48,6 +54,14 @@
 COMPONENT_EXPORT(CHROMEOS_UI_BASE)
 extern const aura::WindowProperty<WindowStateType>* const kWindowStateTypeKey;
 
+// A property key to store ash::WindowPinType for a window.
+// When setting this property to PINNED or TRUSTED_PINNED, the window manager
+// will try to fullscreen the window and pin it on the top of the screen. If the
+// window manager failed to do it, the property will be restored to NONE. When
+// setting this property to NONE, the window manager will restore the window.
+COMPONENT_EXPORT(CHROMEOS_UI_BASE)
+extern const aura::WindowProperty<WindowPinType>* const kWindowPinTypeKey;
+
 // Alphabetical sort.
 
 }  // namespace chromeos
diff --git a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
index 03329aa..dd81657 100644
--- a/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
+++ b/components/autofill_assistant/browser/actions/action_delegate_util_unittest.cc
@@ -108,6 +108,36 @@
                                           base::Unretained(this)));
 }
 
+TEST_F(ActionDelegateUtilTest,
+       FindElementAndExecuteMultipleActionsAbortsOnError) {
+  InSequence sequence;
+
+  Selector expected_selector({"#element"});
+  auto expected_element =
+      test_util::MockFindElement(mock_action_delegate_, expected_selector);
+
+  EXPECT_CALL(*this, MockIndexedAction(1, EqualsElement(expected_element), _))
+      .WillOnce(RunOnceCallback<2>(OkClientStatus()));
+  EXPECT_CALL(*this, MockIndexedAction(2, EqualsElement(expected_element), _))
+      .WillOnce(RunOnceCallback<2>(ClientStatus(UNEXPECTED_JS_ERROR)));
+  EXPECT_CALL(*this, MockIndexedAction(3, EqualsElement(expected_element), _))
+      .Times(0);
+  EXPECT_CALL(*this, MockDone(EqualsStatus(ClientStatus(UNEXPECTED_JS_ERROR))));
+
+  auto actions = std::make_unique<ElementActionVector>();
+  actions->emplace_back(base::BindOnce(
+      &ActionDelegateUtilTest::MockIndexedAction, base::Unretained(this), 1));
+  actions->emplace_back(base::BindOnce(
+      &ActionDelegateUtilTest::MockIndexedAction, base::Unretained(this), 2));
+  actions->emplace_back(base::BindOnce(
+      &ActionDelegateUtilTest::MockIndexedAction, base::Unretained(this), 3));
+
+  FindElementAndPerformAll(&mock_action_delegate_, expected_selector,
+                           std::move(actions),
+                           base::BindOnce(&ActionDelegateUtilTest::MockDone,
+                                          base::Unretained(this)));
+}
+
 TEST_F(ActionDelegateUtilTest, ActionDelegateDeletedDuringExecution) {
   InSequence sequence;
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index fea598f..07abf79 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -650,6 +650,9 @@
 
   // More information included for |SetFormFieldValueProto| related errors.
   optional SetFormFieldErrorInfoProto form_field_error_info = 4;
+
+  // Additional information from the |WebController|.
+  optional WebControllerErrorInfoProto web_controller_error_info = 5;
 }
 
 message NavigationInfoProto {
@@ -761,6 +764,63 @@
   optional int32 invalid_keypress_index = 1;
 }
 
+// Message to report errors related to WebController execution.
+message WebControllerErrorInfoProto {
+  enum WebAction {
+    UNSPECIFIED_WEB_ACTION = 0;
+
+    // Scroll an element into view by centering it on the page. This uses
+    // native JS functionality.
+    SCROLL_INTO_VIEW = 1;
+
+    // Waiting for the document ready state to be interactive.
+    WAIT_FOR_DOCUMENT_TO_BECOME_INTERACTIVE = 2;
+
+    // Send a click or tap event to an element.
+    CLICK_OR_TAP_ELEMENT = 3;
+
+    // Select an option from an HTML dropdown.
+    SELECT_OPTION = 4;
+
+    // Set the element's style to be highlighted by adding a BoxShadow to the
+    // element.
+    HIGHLIGHT_ELEMENT = 5;
+
+    // Scroll the element into view with padding. This does not use native JS
+    // functionality but calculates the scrolling manually.
+    SCROLL_INTO_VIEW_WITH_PADDING = 6;
+
+    // Get the |value| attribute of an element.
+    GET_FIELD_VALUE = 7;
+
+    // Get any attribute of an element.
+    GET_STRING_ATTRIBUTE = 8;
+
+    // Select an element's value. This does only work for text elements.
+    SELECT_FIELD_VALUE = 9;
+
+    // Set the |value| attribute of an element.
+    SET_VALUE_ATTRIBUTE = 10;
+
+    // Set any attribute of an element.
+    SET_ATTRIBUTE = 11;
+
+    // Send a series of keystroke inputs. This requires an element to have
+    // focus to receive them.
+    SEND_KEYBOARD_INPUT = 12;
+
+    // Get the outer HTML of an element.
+    GET_OUTER_HTML = 13;
+
+    // Get the tag of an element.
+    GET_ELEMENT_TAG = 14;
+  }
+
+  // The web-action that failed. This is usually a step in an execution chain
+  // for an action.
+  optional WebAction failed_web_action = 1;
+}
+
 // The pseudo type values come from
 // https://chromedevtools.github.io/devtools-protocol/tot/DOM#type-PseudoType.
 enum PseudoType {
diff --git a/components/autofill_assistant/browser/web/web_controller.cc b/components/autofill_assistant/browser/web/web_controller.cc
index f6e357d..c3e63ca 100644
--- a/components/autofill_assistant/browser/web/web_controller.cc
+++ b/components/autofill_assistant/browser/web/web_controller.cc
@@ -259,6 +259,32 @@
   std::move(callback).Run(status, static_cast<DocumentReadyState>(ready_state));
 }
 
+void DecorateWebControllerStatus(
+    WebControllerErrorInfoProto::WebAction web_action,
+    base::OnceCallback<void(const ClientStatus&)> callback,
+    const ClientStatus& status) {
+  ClientStatus copy = status;
+  if (!status.ok()) {
+    VLOG(1) << web_action << " failed with status: " << status;
+    FillWebControllerErrorInfo(web_action, &copy);
+  }
+  std::move(callback).Run(copy);
+}
+
+template <typename T>
+void DecorateControllerStatusWithValue(
+    WebControllerErrorInfoProto::WebAction web_action,
+    base::OnceCallback<void(const ClientStatus&, const T&)> callback,
+    const ClientStatus& status,
+    const T& result) {
+  ClientStatus copy = status;
+  if (!status.ok()) {
+    VLOG(1) << web_action << " failed with status: " << status;
+    FillWebControllerErrorInfo(web_action, &copy);
+  }
+  std::move(callback).Run(copy, result);
+}
+
 }  // namespace
 
 // static
@@ -334,8 +360,11 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &WebController::OnJavaScriptResult, weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateWebControllerStatus,
+                         WebControllerErrorInfoProto::SCROLL_INTO_VIEW,
+                         std::move(callback))));
 }
 
 void WebController::WaitForDocumentToBecomeInteractive(
@@ -348,11 +377,65 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void WebController::InternalWaitForDocumentToBecomeInteractive(
+    int remaining_rounds,
+    const std::string& object_id,
+    const std::string& node_frame_id,
+    base::OnceCallback<void(bool)> callback) {
+  devtools_client_->GetRuntime()->CallFunctionOn(
+      runtime::CallFunctionOnParams::Builder()
+          .SetObjectId(object_id)
+          .SetFunctionDeclaration(std::string(kIsDocumentReadyForInteract))
+          .SetReturnByValue(true)
+          .Build(),
+      node_frame_id,
+      base::BindOnce(
+          &WebController::OnInternalWaitForDocumentToBecomeInteractive,
+          weak_ptr_factory_.GetWeakPtr(), remaining_rounds, object_id,
+          node_frame_id, std::move(callback)));
+}
+
+void WebController::OnInternalWaitForDocumentToBecomeInteractive(
+    int remaining_rounds,
+    const std::string& object_id,
+    const std::string& node_frame_id,
+    base::OnceCallback<void(bool)> callback,
+    const DevtoolsClient::ReplyStatus& reply_status,
+    std::unique_ptr<runtime::CallFunctionOnResult> result) {
+  ClientStatus status =
+      CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
+  if (!status.ok() || remaining_rounds <= 0) {
+    VLOG(1) << __func__
+            << " Failed to wait for the document to become interactive with "
+               "remaining_rounds: "
+            << remaining_rounds;
+    std::move(callback).Run(false);
+    return;
+  }
+
+  bool ready;
+  if (SafeGetBool(result->GetResult(), &ready) && ready) {
+    std::move(callback).Run(true);
+    return;
+  }
+
+  content::GetUIThreadTaskRunner({})->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&WebController::InternalWaitForDocumentToBecomeInteractive,
+                     weak_ptr_factory_.GetWeakPtr(), --remaining_rounds,
+                     object_id, node_frame_id, std::move(callback)),
+      settings_->document_ready_check_interval);
+}
+
 void WebController::OnWaitForDocumentToBecomeInteractive(
     base::OnceCallback<void(const ClientStatus&)> callback,
     bool result) {
   if (!result) {
-    std::move(callback).Run(ClientStatus(TIMED_OUT));
+    ClientStatus error_status(TIMED_OUT);
+    FillWebControllerErrorInfo(
+        WebControllerErrorInfoProto::WAIT_FOR_DOCUMENT_TO_BECOME_INTERACTIVE,
+        &error_status);
+    std::move(callback).Run(error_status);
     return;
   }
   std::move(callback).Run(OkClientStatus());
@@ -375,9 +458,11 @@
             .SetFunctionDeclaration(kClickElement)
             .Build(),
         element.node_frame_id,
-        base::BindOnce(&WebController::OnJavaScriptResult,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       std::move(wrapped_callback)));
+        base::BindOnce(
+            &WebController::OnJavaScriptResult, weak_ptr_factory_.GetWeakPtr(),
+            base::BindOnce(&DecorateWebControllerStatus,
+                           WebControllerErrorInfoProto::CLICK_OR_TAP_ELEMENT,
+                           std::move(wrapped_callback))));
     return;
   }
 
@@ -388,9 +473,13 @@
   pending_workers_.emplace_back(std::move(getter));
   ptr->Start(
       element.container_frame_host, element.object_id,
-      base::BindOnce(&WebController::TapOrClickOnCoordinates,
-                     weak_ptr_factory_.GetWeakPtr(), ptr, element.node_frame_id,
-                     click_type, std::move(wrapped_callback)));
+      base::BindOnce(
+          &WebController::TapOrClickOnCoordinates,
+          weak_ptr_factory_.GetWeakPtr(), ptr, element.node_frame_id,
+          click_type,
+          base::BindOnce(&DecorateWebControllerStatus,
+                         WebControllerErrorInfoProto::CLICK_OR_TAP_ELEMENT,
+                         std::move(wrapped_callback))));
 }
 
 void WebController::TapOrClickOnCoordinates(
@@ -849,7 +938,10 @@
           .Build(),
       element.node_frame_id,
       base::BindOnce(&WebController::OnSelectOption,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(),
+                     base::BindOnce(&DecorateWebControllerStatus,
+                                    WebControllerErrorInfoProto::SELECT_OPTION,
+                                    std::move(callback))));
 }
 
 void WebController::OnSelectOption(
@@ -891,8 +983,11 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &WebController::OnJavaScriptResult, weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateWebControllerStatus,
+                         WebControllerErrorInfoProto::HIGHLIGHT_ELEMENT,
+                         std::move(callback))));
 }
 
 void WebController::FocusElement(
@@ -911,8 +1006,12 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &WebController::OnJavaScriptResult, weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(
+              &DecorateWebControllerStatus,
+              WebControllerErrorInfoProto::SCROLL_INTO_VIEW_WITH_PADDING,
+              std::move(callback))));
 }
 
 void WebController::GetFieldValue(
@@ -942,8 +1041,12 @@
           .SetReturnByValue(true)
           .Build(),
       element_result->node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResultForString,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &WebController::OnJavaScriptResultForString,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateControllerStatusWithValue<std::string>,
+                         WebControllerErrorInfoProto::GET_FIELD_VALUE,
+                         std::move(callback))));
 }
 
 void WebController::GetStringAttribute(
@@ -955,7 +1058,10 @@
           << "]";
 
   if (attributes.empty()) {
-    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), "");
+    ClientStatus error_status = UnexpectedErrorStatus(__FILE__, __LINE__);
+    FillWebControllerErrorInfo(
+        WebControllerErrorInfoProto::GET_STRING_ATTRIBUTE, &error_status);
+    std::move(callback).Run(error_status, "");
     return;
   }
   base::Value::ListStorage attribute_values;
@@ -973,8 +1079,12 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResultForString,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &WebController::OnJavaScriptResultForString,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateControllerStatusWithValue<std::string>,
+                         WebControllerErrorInfoProto::GET_STRING_ATTRIBUTE,
+                         std::move(callback))));
 }
 
 void WebController::SelectFieldValue(
@@ -986,8 +1096,93 @@
           .SetFunctionDeclaration(std::string(kSelectFieldValue))
           .Build(),
       element.node_frame_id,
+      base::BindOnce(
+          &WebController::OnJavaScriptResult, weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateWebControllerStatus,
+                         WebControllerErrorInfoProto::SELECT_FIELD_VALUE,
+                         std::move(callback))));
+}
+
+void WebController::SetValueAttribute(
+    const ElementFinder::Result& element,
+    const std::string& value,
+    base::OnceCallback<void(const ClientStatus&)> callback) {
+  std::vector<std::unique_ptr<runtime::CallArgument>> argument;
+  AddRuntimeCallArgument(value, &argument);
+  devtools_client_->GetRuntime()->CallFunctionOn(
+      runtime::CallFunctionOnParams::Builder()
+          .SetObjectId(element.object_id)
+          .SetArguments(std::move(argument))
+          .SetFunctionDeclaration(std::string(kSetValueAttributeScript))
+          .Build(),
+      element.node_frame_id,
+      base::BindOnce(
+          &WebController::OnJavaScriptResult, weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateWebControllerStatus,
+                         WebControllerErrorInfoProto::SET_VALUE_ATTRIBUTE,
+                         std::move(callback))));
+}
+
+void WebController::SetAttribute(
+    const ElementFinder::Result& element,
+    const std::vector<std::string>& attributes,
+    const std::string& value,
+    base::OnceCallback<void(const ClientStatus&)> callback) {
+  DVLOG(3) << __func__ << " attributes=[" << base::JoinString(attributes, ",")
+           << "], value=" << value;
+
+  if (attributes.empty()) {
+    ClientStatus error_status = UnexpectedErrorStatus(__FILE__, __LINE__);
+    FillWebControllerErrorInfo(WebControllerErrorInfoProto::SET_ATTRIBUTE,
+                               &error_status);
+    std::move(callback).Run(error_status);
+    return;
+  }
+  base::Value::ListStorage attribute_values;
+  for (const std::string& attribute : attributes) {
+    attribute_values.emplace_back(base::Value(attribute));
+  }
+
+  std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
+  AddRuntimeCallArgument(attribute_values, &arguments);
+  AddRuntimeCallArgument(value, &arguments);
+  devtools_client_->GetRuntime()->CallFunctionOn(
+      runtime::CallFunctionOnParams::Builder()
+          .SetObjectId(element.object_id)
+          .SetArguments(std::move(arguments))
+          .SetFunctionDeclaration(std::string(kSetAttributeScript))
+          .Build(),
+      element.node_frame_id,
       base::BindOnce(&WebController::OnJavaScriptResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(),
+                     base::BindOnce(&DecorateWebControllerStatus,
+                                    WebControllerErrorInfoProto::SET_ATTRIBUTE,
+                                    std::move(callback))));
+}
+
+void WebController::SendKeyboardInput(
+    const ElementFinder::Result& element,
+    const std::vector<UChar32>& codepoints,
+    const int delay_in_millisecond,
+    base::OnceCallback<void(const ClientStatus&)> callback) {
+  if (VLOG_IS_ON(3)) {
+    std::string input_str;
+    if (!UnicodeToUTF8(codepoints, &input_str)) {
+      input_str.assign("<invalid input>");
+    }
+#ifdef NDEBUG
+    VLOG(3) << __func__ << " input=(redacted)";
+#else
+    DVLOG(3) << __func__ << " input=" << input_str;
+#endif
+  }
+
+  DispatchKeyboardTextDownEvent(
+      element.node_frame_id, codepoints, 0,
+      /* delay= */ false, delay_in_millisecond,
+      base::BindOnce(&DecorateWebControllerStatus,
+                     WebControllerErrorInfoProto::SEND_KEYBOARD_INPUT,
+                     std::move(callback)));
 }
 
 void WebController::DispatchKeyboardTextDownEvent(
@@ -1073,76 +1268,6 @@
   return params;
 }
 
-void WebController::SetValueAttribute(
-    const ElementFinder::Result& element,
-    const std::string& value,
-    base::OnceCallback<void(const ClientStatus&)> callback) {
-  std::vector<std::unique_ptr<runtime::CallArgument>> argument;
-  AddRuntimeCallArgument(value, &argument);
-  devtools_client_->GetRuntime()->CallFunctionOn(
-      runtime::CallFunctionOnParams::Builder()
-          .SetObjectId(element.object_id)
-          .SetArguments(std::move(argument))
-          .SetFunctionDeclaration(std::string(kSetValueAttributeScript))
-          .Build(),
-      element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void WebController::SetAttribute(
-    const ElementFinder::Result& element,
-    const std::vector<std::string>& attributes,
-    const std::string& value,
-    base::OnceCallback<void(const ClientStatus&)> callback) {
-  DVLOG(3) << __func__ << " attributes=[" << base::JoinString(attributes, ",")
-           << "], value=" << value;
-
-  if (attributes.empty()) {
-    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
-    return;
-  }
-  base::Value::ListStorage attribute_values;
-  for (const std::string& attribute : attributes) {
-    attribute_values.emplace_back(base::Value(attribute));
-  }
-
-  std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
-  AddRuntimeCallArgument(attribute_values, &arguments);
-  AddRuntimeCallArgument(value, &arguments);
-  devtools_client_->GetRuntime()->CallFunctionOn(
-      runtime::CallFunctionOnParams::Builder()
-          .SetObjectId(element.object_id)
-          .SetArguments(std::move(arguments))
-          .SetFunctionDeclaration(std::string(kSetAttributeScript))
-          .Build(),
-      element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResult,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void WebController::SendKeyboardInput(
-    const ElementFinder::Result& element,
-    const std::vector<UChar32>& codepoints,
-    const int delay_in_millisecond,
-    base::OnceCallback<void(const ClientStatus&)> callback) {
-  if (VLOG_IS_ON(3)) {
-    std::string input_str;
-    if (!UnicodeToUTF8(codepoints, &input_str)) {
-      input_str.assign("<invalid input>");
-    }
-#ifdef NDEBUG
-    VLOG(3) << __func__ << " input=(redacted)";
-#else
-    DVLOG(3) << __func__ << " input=" << input_str;
-#endif
-  }
-
-  DispatchKeyboardTextDownEvent(element.node_frame_id, codepoints, 0,
-                                /* delay= */ false, delay_in_millisecond,
-                                std::move(callback));
-}
-
 void WebController::GetVisualViewport(
     base::OnceCallback<void(const ClientStatus&, const RectF&)> callback) {
   devtools_client_->GetRuntime()->Evaluate(
@@ -1237,8 +1362,12 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResultForString,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &WebController::OnJavaScriptResultForString,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateControllerStatusWithValue<std::string>,
+                         WebControllerErrorInfoProto::GET_OUTER_HTML,
+                         std::move(callback))));
 }
 
 void WebController::GetElementTag(
@@ -1252,58 +1381,12 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id,
-      base::BindOnce(&WebController::OnJavaScriptResultForString,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void WebController::InternalWaitForDocumentToBecomeInteractive(
-    int remaining_rounds,
-    const std::string& object_id,
-    const std::string& node_frame_id,
-    base::OnceCallback<void(bool)> callback) {
-  devtools_client_->GetRuntime()->CallFunctionOn(
-      runtime::CallFunctionOnParams::Builder()
-          .SetObjectId(object_id)
-          .SetFunctionDeclaration(std::string(kIsDocumentReadyForInteract))
-          .SetReturnByValue(true)
-          .Build(),
-      node_frame_id,
       base::BindOnce(
-          &WebController::OnInternalWaitForDocumentToBecomeInteractive,
-          weak_ptr_factory_.GetWeakPtr(), remaining_rounds, object_id,
-          node_frame_id, std::move(callback)));
-}
-
-void WebController::OnInternalWaitForDocumentToBecomeInteractive(
-    int remaining_rounds,
-    const std::string& object_id,
-    const std::string& node_frame_id,
-    base::OnceCallback<void(bool)> callback,
-    const DevtoolsClient::ReplyStatus& reply_status,
-    std::unique_ptr<runtime::CallFunctionOnResult> result) {
-  ClientStatus status =
-      CheckJavaScriptResult(reply_status, result.get(), __FILE__, __LINE__);
-  if (!status.ok() || remaining_rounds <= 0) {
-    VLOG(1) << __func__
-            << " Failed to wait for the document to become interactive with "
-               "remaining_rounds: "
-            << remaining_rounds;
-    std::move(callback).Run(false);
-    return;
-  }
-
-  bool ready;
-  if (SafeGetBool(result->GetResult(), &ready) && ready) {
-    std::move(callback).Run(true);
-    return;
-  }
-
-  content::GetUIThreadTaskRunner({})->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&WebController::InternalWaitForDocumentToBecomeInteractive,
-                     weak_ptr_factory_.GetWeakPtr(), --remaining_rounds,
-                     object_id, node_frame_id, std::move(callback)),
-      settings_->document_ready_check_interval);
+          &WebController::OnJavaScriptResultForString,
+          weak_ptr_factory_.GetWeakPtr(),
+          base::BindOnce(&DecorateControllerStatusWithValue<std::string>,
+                         WebControllerErrorInfoProto::GET_ELEMENT_TAG,
+                         std::move(callback))));
 }
 
 WebController::ScopedAssistantActionStateRunning::
diff --git a/components/autofill_assistant/browser/web/web_controller_util.cc b/components/autofill_assistant/browser/web/web_controller_util.cc
index aadf965e..d25d628 100644
--- a/components/autofill_assistant/browser/web/web_controller_util.cc
+++ b/components/autofill_assistant/browser/web/web_controller_util.cc
@@ -5,6 +5,7 @@
 #include "components/autofill_assistant/browser/web/web_controller_util.h"
 
 #include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
+#include "components/autofill_assistant/browser/service.pb.h"
 
 namespace autofill_assistant {
 
@@ -56,6 +57,14 @@
   return status;
 }
 
+void FillWebControllerErrorInfo(
+    WebControllerErrorInfoProto::WebAction failed_web_action,
+    ClientStatus* status) {
+  status->mutable_details()
+      ->mutable_web_controller_error_info()
+      ->set_failed_web_action(failed_web_action);
+}
+
 bool SafeGetObjectId(const runtime::RemoteObject* result, std::string* out) {
   if (result && result->HasObjectId()) {
     *out = result->GetObjectId();
diff --git a/components/autofill_assistant/browser/web/web_controller_util.h b/components/autofill_assistant/browser/web/web_controller_util.h
index dda9a43..c81437b 100644
--- a/components/autofill_assistant/browser/web/web_controller_util.h
+++ b/components/autofill_assistant/browser/web/web_controller_util.h
@@ -10,6 +10,7 @@
 #include "components/autofill_assistant/browser/client_status.h"
 #include "components/autofill_assistant/browser/devtools/devtools/domains/types_runtime.h"
 #include "components/autofill_assistant/browser/devtools/devtools_client.h"
+#include "components/autofill_assistant/browser/service.pb.h"
 
 namespace autofill_assistant {
 
@@ -57,6 +58,11 @@
 // Fills a ClientStatus with appropriate details for a Chrome Autofill error.
 ClientStatus FillAutofillErrorStatus(ClientStatus status);
 
+// Fills a ClientStatus with appropriate details from the
+void FillWebControllerErrorInfo(
+    WebControllerErrorInfoProto::WebAction failed_web_action,
+    ClientStatus* status);
+
 // Safely gets an object id from a RemoteObject
 bool SafeGetObjectId(const runtime::RemoteObject* result, std::string* out);
 
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index 4a22a91..3143ccb 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -307,6 +307,7 @@
       "//ash/keyboard/ui",
       "//ash/public/cpp",
       "//chromeos/constants",
+      "//chromeos/ui/base",
       "//chromeos/ui/frame",
       "//ui/base:test_support",
       "//ui/base/cursor/mojom:cursor_type",
diff --git a/components/exo/client_controlled_shell_surface.cc b/components/exo/client_controlled_shell_surface.cc
index 99bbca0..3399e59 100644
--- a/components/exo/client_controlled_shell_surface.cc
+++ b/components/exo/client_controlled_shell_surface.cc
@@ -15,7 +15,6 @@
 #include "ash/public/cpp/rounded_corner_decorator.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_backdrop.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
@@ -36,6 +35,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/traced_value.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
 #include "chromeos/ui/frame/immersive/immersive_fullscreen_controller.h"
@@ -324,7 +325,7 @@
     int container,
     bool default_scale_cancellation)
     : ShellSurfaceBase(surface, gfx::Point(), true, can_minimize, container),
-      current_pin_(ash::WindowPinType::kNone),
+      current_pin_(chromeos::WindowPinType::kNone),
       use_default_scale_cancellation_(default_scale_cancellation) {}
 
 ClientControlledShellSurface::~ClientControlledShellSurface() {
@@ -335,8 +336,8 @@
   if (client_controlled_state_)
     client_controlled_state_->ResetDelegate();
   wide_frame_.reset();
-  if (current_pin_ != ash::WindowPinType::kNone)
-    SetPinned(ash::WindowPinType::kNone);
+  if (current_pin_ != chromeos::WindowPinType::kNone)
+    SetPinned(chromeos::WindowPinType::kNone);
 }
 
 void ClientControlledShellSurface::SetBounds(int64_t display_id,
@@ -419,14 +420,14 @@
   pending_window_state_ = chromeos::WindowStateType::kPip;
 }
 
-void ClientControlledShellSurface::SetPinned(ash::WindowPinType type) {
+void ClientControlledShellSurface::SetPinned(chromeos::WindowPinType type) {
   TRACE_EVENT1("exo", "ClientControlledShellSurface::SetPinned", "type",
                static_cast<int>(type));
 
   if (!widget_)
     CreateShellSurfaceWidget(ui::SHOW_STATE_NORMAL);
 
-  widget_->GetNativeWindow()->SetProperty(ash::kWindowPinTypeKey, type);
+  widget_->GetNativeWindow()->SetProperty(chromeos::kWindowPinTypeKey, type);
   current_pin_ = type;
 }
 
diff --git a/components/exo/client_controlled_shell_surface.h b/components/exo/client_controlled_shell_surface.h
index 3dcb07e..5141d59 100644
--- a/components/exo/client_controlled_shell_surface.h
+++ b/components/exo/client_controlled_shell_surface.h
@@ -141,7 +141,7 @@
 
   // Pin/unpin the surface. Pinned surface cannot be switched to
   // other windows unless its explicitly unpinned.
-  void SetPinned(ash::WindowPinType type);
+  void SetPinned(chromeos::WindowPinType type);
 
   // Sets the surface to be on top of all other windows.
   void SetAlwaysOnTop(bool always_on_top);
@@ -350,7 +350,7 @@
 
   SurfaceFrameType pending_frame_type_ = SurfaceFrameType::NONE;
 
-  ash::WindowPinType current_pin_;
+  chromeos::WindowPinType current_pin_;
 
   bool can_maximize_ = true;
 
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc
index aafbbd3..11dde36 100644
--- a/components/exo/client_controlled_shell_surface_unittest.cc
+++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -9,8 +9,6 @@
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/frame/wide_frame_view.h"
 #include "ash/public/cpp/test/shell_test_api.h"
-#include "ash/public/cpp/window_pin_type.h"
-#include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "ash/wm/drag_window_resizer.h"
@@ -31,6 +29,8 @@
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "cc/paint/display_item_list.h"
+#include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/frame/caption_buttons/caption_button_model.h"
 #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h"
 #include "components/exo/buffer.h"
@@ -74,10 +74,10 @@
 }
 
 bool IsWidgetPinned(views::Widget* widget) {
-  ash::WindowPinType type =
-      widget->GetNativeWindow()->GetProperty(ash::kWindowPinTypeKey);
-  return type == ash::WindowPinType::kPinned ||
-         type == ash::WindowPinType::kTrustedPinned;
+  chromeos::WindowPinType type =
+      widget->GetNativeWindow()->GetProperty(chromeos::kWindowPinTypeKey);
+  return type == chromeos::WindowPinType::kPinned ||
+         type == chromeos::WindowPinType::kTrustedPinned;
 }
 
 int GetShadowElevation(aura::Window* window) {
@@ -123,16 +123,16 @@
   auto shell_surface(
       exo_test_helper()->CreateClientControlledShellSurface(surface.get()));
 
-  shell_surface->SetPinned(ash::WindowPinType::kTrustedPinned);
+  shell_surface->SetPinned(chromeos::WindowPinType::kTrustedPinned);
   EXPECT_TRUE(IsWidgetPinned(shell_surface->GetWidget()));
 
-  shell_surface->SetPinned(ash::WindowPinType::kNone);
+  shell_surface->SetPinned(chromeos::WindowPinType::kNone);
   EXPECT_FALSE(IsWidgetPinned(shell_surface->GetWidget()));
 
-  shell_surface->SetPinned(ash::WindowPinType::kPinned);
+  shell_surface->SetPinned(chromeos::WindowPinType::kPinned);
   EXPECT_TRUE(IsWidgetPinned(shell_surface->GetWidget()));
 
-  shell_surface->SetPinned(ash::WindowPinType::kNone);
+  shell_surface->SetPinned(chromeos::WindowPinType::kNone);
   EXPECT_FALSE(IsWidgetPinned(shell_surface->GetWidget()));
 }
 
diff --git a/components/exo/display_unittest.cc b/components/exo/display_unittest.cc
index b8fc458..d3696c20 100644
--- a/components/exo/display_unittest.cc
+++ b/components/exo/display_unittest.cc
@@ -4,8 +4,8 @@
 
 #include "components/exo/display.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/wm/desks/desks_util.h"
+#include "chromeos/ui/base/window_pin_type.h"
 #include "components/exo/buffer.h"
 #include "components/exo/client_controlled_shell_surface.h"
 #include "components/exo/data_device.h"
@@ -268,7 +268,7 @@
 
   // This should not crash
   shell_surface->SetAlwaysOnTop(true);
-  shell_surface->SetPinned(ash::WindowPinType::kPinned);
+  shell_surface->SetPinned(chromeos::WindowPinType::kPinned);
 }
 
 }  // namespace
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index 40b08f2..797df9baef 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -9,7 +9,6 @@
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/wm/desks/desks_util.h"
@@ -27,6 +26,7 @@
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
 #include "chromeos/ui/base//window_properties.h"
+#include "chromeos/ui/base/window_pin_type.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
diff --git a/components/exo/shell_surface_util.cc b/components/exo/shell_surface_util.cc
index 271c9e0..0541638 100644
--- a/components/exo/shell_surface_util.cc
+++ b/components/exo/shell_surface_util.cc
@@ -23,7 +23,6 @@
 #include "ui/wm/core/window_util.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/cpp/window_properties.h"
 #include "chromeos/ui/base/window_properties.h"
 #endif  // defined(OS_CHROMEOS)
 
@@ -123,7 +122,7 @@
 
   // Ensure the shelf is fully hidden in plain fullscreen, but shown
   // (auto-hides based on mouse movement) when in immersive fullscreen.
-  window->SetProperty(ash::kHideShelfWhenFullscreenKey, !value);
+  window->SetProperty(chromeos::kHideShelfWhenFullscreenKey, !value);
 #endif  // defined(OS_CHROMEOS)
 }
 
diff --git a/components/exo/wayland/zcr_remote_shell.cc b/components/exo/wayland/zcr_remote_shell.cc
index 999518f3..76f8cc6 100644
--- a/components/exo/wayland/zcr_remote_shell.cc
+++ b/components/exo/wayland/zcr_remote_shell.cc
@@ -10,7 +10,6 @@
 
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/tablet_mode_observer.h"
-#include "ash/public/cpp/window_pin_type.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_layout_manager.h"
@@ -24,6 +23,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chromeos/ui/base/window_pin_type.h"
 #include "components/exo/client_controlled_shell_surface.h"
 #include "components/exo/display.h"
 #include "components/exo/input_method_surface.h"
@@ -318,13 +318,13 @@
                         wl_resource* resource,
                         int32_t trusted) {
   GetUserDataAs<ClientControlledShellSurface>(resource)->SetPinned(
-      trusted ? ash::WindowPinType::kTrustedPinned
-              : ash::WindowPinType::kPinned);
+      trusted ? chromeos::WindowPinType::kTrustedPinned
+              : chromeos::WindowPinType::kPinned);
 }
 
 void remote_surface_unpin(wl_client* client, wl_resource* resource) {
   GetUserDataAs<ClientControlledShellSurface>(resource)->SetPinned(
-      ash::WindowPinType::kNone);
+      chromeos::WindowPinType::kNone);
 }
 
 void remote_surface_set_system_modal(wl_client* client, wl_resource* resource) {
diff --git a/components/feature_engagement/internal/BUILD.gn b/components/feature_engagement/internal/BUILD.gn
index b1320da..9894de87 100644
--- a/components/feature_engagement/internal/BUILD.gn
+++ b/components/feature_engagement/internal/BUILD.gn
@@ -58,6 +58,8 @@
     "single_invalid_configuration.h",
     "stats.cc",
     "stats.h",
+    "switches.cc",
+    "switches.h",
     "system_time_provider.cc",
     "system_time_provider.h",
     "time_provider.h",
@@ -78,9 +80,14 @@
     sources += [
       "android/tracker_impl_android.cc",
       "android/tracker_impl_android.h",
+      "android/wrapping_test_tracker.cc",
+      "android/wrapping_test_tracker.h",
     ]
 
-    deps += [ ":jni_headers" ]
+    deps += [
+      ":jni_headers",
+      "//components/feature_engagement/public:jni_headers",
+    ]
   }
 }
 
diff --git a/components/feature_engagement/internal/android/java/src/org/chromium/components/feature_engagement/internal/TrackerImpl.java b/components/feature_engagement/internal/android/java/src/org/chromium/components/feature_engagement/internal/TrackerImpl.java
index 2b2047a..934753f1 100644
--- a/components/feature_engagement/internal/android/java/src/org/chromium/components/feature_engagement/internal/TrackerImpl.java
+++ b/components/feature_engagement/internal/android/java/src/org/chromium/components/feature_engagement/internal/TrackerImpl.java
@@ -128,6 +128,12 @@
         TrackerImplJni.get().addOnInitializedCallback(mNativePtr, TrackerImpl.this, callback);
     }
 
+    @Override
+    public void injectTracker(Tracker tracker) {
+        assert mNativePtr != 0;
+        TrackerImplJni.get().injectTracker(mNativePtr, TrackerImpl.this, tracker);
+    }
+
     @CalledByNative
     private void clearNativePtr() {
         mNativePtr = 0;
@@ -155,6 +161,7 @@
         boolean isInitialized(long nativeTrackerImplAndroid, TrackerImpl caller);
         void addOnInitializedCallback(
                 long nativeTrackerImplAndroid, TrackerImpl caller, Callback<Boolean> callback);
+        void injectTracker(long nativeTrackerImplAndroid, TrackerImpl caller, Tracker tracker);
         void release(long nativeDisplayLockHandleAndroid);
     }
 }
diff --git a/components/feature_engagement/internal/android/tracker_impl_android.cc b/components/feature_engagement/internal/android/tracker_impl_android.cc
index adf3ee4..bfc159b0 100644
--- a/components/feature_engagement/internal/android/tracker_impl_android.cc
+++ b/components/feature_engagement/internal/android/tracker_impl_android.cc
@@ -13,9 +13,12 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
+#include "components/feature_engagement/internal/android/wrapping_test_tracker.h"
 #include "components/feature_engagement/internal/jni_headers/TrackerImpl_jni.h"
+#include "components/feature_engagement/internal/switches.h"
 #include "components/feature_engagement/public/feature_list.h"
 #include "components/feature_engagement/public/tracker.h"
 
@@ -175,6 +178,20 @@
       base::android::ScopedJavaGlobalRef<jobject>(j_callback_obj)));
 }
 
+void TrackerImplAndroid::InjectTracker(
+    JNIEnv* env,
+    const base::android::JavaRef<jobject>& jobj,
+    const base::android::JavaRef<jobject>& jtracker) {
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kUseJavaProxyTracker)) {
+    NOTREACHED();
+    return;
+  }
+  WrappingTestTracker* test_tracker_ =
+      static_cast<WrappingTestTracker*>(tracker_impl_);
+  test_tracker_->InjectTracker(jtracker);
+}
+
 DisplayLockHandleAndroid::DisplayLockHandleAndroid(
     std::unique_ptr<DisplayLockHandle> display_lock_handle)
     : display_lock_handle_(std::move(display_lock_handle)) {
diff --git a/components/feature_engagement/internal/android/tracker_impl_android.h b/components/feature_engagement/internal/android/tracker_impl_android.h
index 7a028314..580c13dd 100644
--- a/components/feature_engagement/internal/android/tracker_impl_android.h
+++ b/components/feature_engagement/internal/android/tracker_impl_android.h
@@ -102,6 +102,14 @@
       const base::android::JavaRef<jobject>& jobj,
       const base::android::JavaParamRef<jobject>& j_callback_obj);
 
+  /**
+   * Injects a Java tracker for encapsulation. For further details, see
+   * WrappingTestTracker::InjectTracker.
+   */
+  void InjectTracker(JNIEnv* env,
+                     const base::android::JavaRef<jobject>& jobj,
+                     const base::android::JavaRef<jobject>& jtracker);
+
  private:
   // A map from the feature name to the base::Feature, to ensure that the Java
   // version of the API can use the string name. If base::Feature becomes a Java
diff --git a/components/feature_engagement/internal/android/wrapping_test_tracker.cc b/components/feature_engagement/internal/android/wrapping_test_tracker.cc
new file mode 100644
index 0000000..27f5d0e
--- /dev/null
+++ b/components/feature_engagement/internal/android/wrapping_test_tracker.cc
@@ -0,0 +1,134 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/feature_engagement/internal/android/wrapping_test_tracker.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/android/jni_string.h"
+#include "components/feature_engagement/internal/availability_model_impl.h"
+#include "components/feature_engagement/internal/display_lock_controller_impl.h"
+#include "components/feature_engagement/internal/editable_configuration.h"
+#include "components/feature_engagement/internal/event_model_impl.h"
+#include "components/feature_engagement/internal/feature_config_condition_validator.h"
+#include "components/feature_engagement/internal/system_time_provider.h"
+#include "components/feature_engagement/public/jni_headers/CppWrappedTestTracker_jni.h"
+
+namespace feature_engagement {
+
+WrappingTestTracker::WrappingTestTracker(
+    std::unique_ptr<EventModel> event_model,
+    std::unique_ptr<AvailabilityModel> availability_model,
+    std::unique_ptr<Configuration> configuration,
+    std::unique_ptr<DisplayLockController> display_lock_controller,
+    std::unique_ptr<ConditionValidator> condition_validator,
+    std::unique_ptr<TimeProvider> time_provider)
+    : TrackerImpl(std::move(event_model),
+                  std::move(availability_model),
+                  std::move(configuration),
+                  std::make_unique<DisplayLockControllerImpl>(),
+                  std::move(condition_validator),
+                  std::move(time_provider)) {}
+
+WrappingTestTracker::~WrappingTestTracker() {}
+
+void WrappingTestTracker::InjectTracker(
+    const base::android::JavaRef<jobject>& jtracker) {
+  java_tracker_ = jtracker;
+}
+
+void WrappingTestTracker::NotifyEvent(const std::string& event) {
+  if (!java_tracker_) {
+    TrackerImpl::NotifyEvent(event);
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> jevent(
+      base::android::ConvertUTF8ToJavaString(env, event.c_str()));
+  Java_CppWrappedTestTracker_notifyEvent(base::android::AttachCurrentThread(),
+                                         java_tracker_, jevent);
+}
+
+bool WrappingTestTracker::ShouldTriggerHelpUI(const base::Feature& feature) {
+  if (!java_tracker_)
+    return TrackerImpl::ShouldTriggerHelpUI(feature);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> jfeature(
+      base::android::ConvertUTF8ToJavaString(env, feature.name));
+  return Java_CppWrappedTestTracker_shouldTriggerHelpUI(
+      base::android::AttachCurrentThread(), java_tracker_, jfeature);
+}
+
+bool WrappingTestTracker::WouldTriggerHelpUI(
+    const base::Feature& feature) const {
+  if (!java_tracker_)
+    return TrackerImpl::WouldTriggerHelpUI(feature);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> jfeature(
+      base::android::ConvertUTF8ToJavaString(env, feature.name));
+  return Java_CppWrappedTestTracker_wouldTriggerHelpUI(
+      base::android::AttachCurrentThread(), java_tracker_, jfeature);
+}
+
+bool WrappingTestTracker::HasEverTriggered(const base::Feature& feature,
+                                           bool from_window) const {
+  if (!java_tracker_)
+    return TrackerImpl::HasEverTriggered(feature, from_window);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> jfeature(
+      base::android::ConvertUTF8ToJavaString(env, feature.name));
+  return Java_CppWrappedTestTracker_hasEverTriggered(
+      base::android::AttachCurrentThread(), java_tracker_, jfeature,
+      from_window);
+}
+
+Tracker::TriggerState WrappingTestTracker::GetTriggerState(
+    const base::Feature& feature) const {
+  if (!java_tracker_)
+    return TrackerImpl::GetTriggerState(feature);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> jfeature(
+      base::android::ConvertUTF8ToJavaString(env, feature.name));
+  return static_cast<Tracker::TriggerState>(
+      Java_CppWrappedTestTracker_getTriggerState(
+          base::android::AttachCurrentThread(), java_tracker_, jfeature));
+}
+
+void WrappingTestTracker::Dismissed(const base::Feature& feature) {
+  if (!java_tracker_) {
+    TrackerImpl::Dismissed(feature);
+    return;
+  }
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> jfeature(
+      base::android::ConvertUTF8ToJavaString(env, feature.name));
+  Java_CppWrappedTestTracker_dismissed(base::android::AttachCurrentThread(),
+                                       java_tracker_, jfeature);
+}
+
+std::unique_ptr<DisplayLockHandle> WrappingTestTracker::AcquireDisplayLock() {
+  return TrackerImpl::AcquireDisplayLock();
+}
+
+bool WrappingTestTracker::IsInitialized() const {
+  if (!java_tracker_)
+    return TrackerImpl::IsInitialized();
+
+  return Java_CppWrappedTestTracker_isInitialized(
+      base::android::AttachCurrentThread(), java_tracker_);
+}
+
+void WrappingTestTracker::AddOnInitializedCallback(
+    OnInitializedCallback callback) {
+  TrackerImpl::AddOnInitializedCallback(std::move(callback));
+}
+
+}  // namespace feature_engagement
diff --git a/components/feature_engagement/internal/android/wrapping_test_tracker.h b/components/feature_engagement/internal/android/wrapping_test_tracker.h
new file mode 100644
index 0000000..f8d8c7a
--- /dev/null
+++ b/components/feature_engagement/internal/android/wrapping_test_tracker.h
@@ -0,0 +1,73 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_ANDROID_WRAPPING_TEST_TRACKER_H_
+#define COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_ANDROID_WRAPPING_TEST_TRACKER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "components/feature_engagement/internal/tracker_impl.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#endif  // defined(OS_ANDROID)
+
+namespace feature_engagement {
+class AvailabilityModel;
+class Configuration;
+class ConditionValidator;
+class DisplayLockController;
+class EventModel;
+class TimeProvider;
+
+// This class is a thin wrapper around TrackerImpl and has two modes of
+// operating. To start with, it does nothing but forward all calls to its base
+// class implementation. However, at some point a Java test can inject a
+// tracker, at
+// which point this class will start to forward the calls to Java. See
+// InjectTracker for details.
+class WrappingTestTracker : public TrackerImpl {
+ public:
+  WrappingTestTracker(
+      std::unique_ptr<EventModel> event_model,
+      std::unique_ptr<AvailabilityModel> availability_model,
+      std::unique_ptr<Configuration> configuration,
+      std::unique_ptr<DisplayLockController> display_lock_controller,
+      std::unique_ptr<ConditionValidator> condition_validator,
+      std::unique_ptr<TimeProvider> time_provider);
+  ~WrappingTestTracker() override;
+
+  // Injects a tracker from Java to proxy calls to. By default, all functions
+  // will be forwarded to the base class for processing until this function is
+  // called, at which point the proxying calls to Java starts. Note that not all
+  // functions are proxied to Java. Some are still handled by the base class.
+  // See .cc for details.
+  void InjectTracker(const base::android::JavaRef<jobject>& jtracker);
+
+  // TrackerImpl:
+
+  void NotifyEvent(const std::string& event) override;
+  bool ShouldTriggerHelpUI(const base::Feature& feature) override;
+  bool WouldTriggerHelpUI(const base::Feature& feature) const override;
+  bool HasEverTriggered(const base::Feature& feature,
+                        bool from_window) const override;
+  TriggerState GetTriggerState(const base::Feature& feature) const override;
+  void Dismissed(const base::Feature& feature) override;
+  std::unique_ptr<DisplayLockHandle> AcquireDisplayLock() override;
+  bool IsInitialized() const override;
+  void AddOnInitializedCallback(OnInitializedCallback callback) override;
+
+ private:
+  base::android::ScopedJavaGlobalRef<jobject> java_tracker_;
+
+  DISALLOW_COPY_AND_ASSIGN(WrappingTestTracker);
+};
+
+}  // namespace feature_engagement
+
+#endif  // COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_ANDROID_WRAPPING_TEST_TRACKER_H_
diff --git a/components/feature_engagement/internal/switches.cc b/components/feature_engagement/internal/switches.cc
new file mode 100644
index 0000000..5904db9
--- /dev/null
+++ b/components/feature_engagement/internal/switches.cc
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/feature_engagement/internal/switches.h"
+
+namespace feature_engagement {
+namespace switches {
+
+// This switch causes TrackerImpl to be wrapped, so that functions can be
+// proxied up into Java land, to facilitate end-to-end Java tests that exercise
+// the feature engagement.
+const char kUseJavaProxyTracker[] = "use-java-proxy-tracker";
+
+}  // namespace switches
+}  // namespace feature_engagement
\ No newline at end of file
diff --git a/components/feature_engagement/internal/switches.h b/components/feature_engagement/internal/switches.h
new file mode 100644
index 0000000..fe3509c
--- /dev/null
+++ b/components/feature_engagement/internal/switches.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_SWITCHES_H_
+#define COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_SWITCHES_H_
+
+namespace feature_engagement {
+namespace switches {
+
+extern const char kUseJavaProxyTracker[];
+
+}  // namespace switches
+}  // namespace feature_engagement
+
+#endif  // COMPONENTS_FEATURE_ENGAGEMENT_INTERNAL_SWITCHES_H_
diff --git a/components/feature_engagement/internal/tracker_impl.cc b/components/feature_engagement/internal/tracker_impl.cc
index 763bd31..59696fc0 100644
--- a/components/feature_engagement/internal/tracker_impl.cc
+++ b/components/feature_engagement/internal/tracker_impl.cc
@@ -8,12 +8,14 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/user_metrics.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 #include "components/feature_engagement/internal/availability_model_impl.h"
 #include "components/feature_engagement/internal/chrome_variations_configuration.h"
 #include "components/feature_engagement/internal/display_lock_controller_impl.h"
@@ -30,11 +32,16 @@
 #include "components/feature_engagement/internal/persistent_event_store.h"
 #include "components/feature_engagement/internal/proto/availability.pb.h"
 #include "components/feature_engagement/internal/stats.h"
+#include "components/feature_engagement/internal/switches.h"
 #include "components/feature_engagement/internal/system_time_provider.h"
 #include "components/feature_engagement/public/feature_constants.h"
 #include "components/feature_engagement/public/feature_list.h"
 #include "components/leveldb_proto/public/proto_database_provider.h"
 
+#if defined(OS_ANDROID)
+#include "components/feature_engagement/internal/android/wrapping_test_tracker.h"
+#endif
+
 namespace feature_engagement {
 
 namespace {
@@ -133,6 +140,16 @@
   auto availability_model = std::make_unique<AvailabilityModelImpl>(
       std::move(availability_store_loader));
 
+#if defined(OS_ANDROID)
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kUseJavaProxyTracker)) {
+    return new WrappingTestTracker(
+        std::move(event_model), std::move(availability_model),
+        std::move(configuration), std::make_unique<DisplayLockControllerImpl>(),
+        std::move(condition_validator), std::move(time_provider));
+  }
+#endif
+
   return new TrackerImpl(
       std::move(event_model), std::move(availability_model),
       std::move(configuration), std::make_unique<DisplayLockControllerImpl>(),
diff --git a/components/feature_engagement/public/BUILD.gn b/components/feature_engagement/public/BUILD.gn
index 090e8518..59b4cc5 100644
--- a/components/feature_engagement/public/BUILD.gn
+++ b/components/feature_engagement/public/BUILD.gn
@@ -48,15 +48,18 @@
 if (is_android) {
   android_library("public_java") {
     sources = [
+      "android/java/src/org/chromium/components/feature_engagement/CppWrappedTestTracker.java",
       "android/java/src/org/chromium/components/feature_engagement/EventConstants.java",
       "android/java/src/org/chromium/components/feature_engagement/FeatureConstants.java",
       "android/java/src/org/chromium/components/feature_engagement/Tracker.java",
     ]
 
     deps = [
+      ":jni_headers",
       "//base:base_java",
       "//third_party/android_deps:androidx_annotation_annotation_java",
     ]
+    annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
     srcjar_deps = [ ":public_java_enums_srcjar" ]
   }
@@ -66,4 +69,8 @@
 
     sources = [ "tracker.h" ]
   }
+
+  generate_jni("jni_headers") {
+    sources = [ "android/java/src/org/chromium/components/feature_engagement/CppWrappedTestTracker.java" ]
+  }
 }
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/CppWrappedTestTracker.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/CppWrappedTestTracker.java
new file mode 100644
index 0000000..5e26fbf
--- /dev/null
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/CppWrappedTestTracker.java
@@ -0,0 +1,125 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.feature_engagement;
+
+import android.text.TextUtils;
+
+import androidx.annotation.CheckResult;
+import androidx.annotation.Nullable;
+
+import org.chromium.base.Callback;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * CppWrappedTestTracker is a Java implementation of a {@link Tracker} object that is
+ * encapsulated by a Tracker object in C++, which will proxy (most) calls over from C++ to
+ * Java.
+ *
+ * NOTE: For testing, most of the time this class is overkill and it suffices to create a test
+ * object that derives from Tracker and call TrackerFactory#setTrackerForTests on it. However, this
+ * will only replace the Tracker object on the Java side, and that object will never receive the
+ * notifyEvent calls that the actual tracker in C++ receives. So, if receiving events is important
+ * for your test, you may need {@link CppWrapperTestTracker}.
+ *
+ * Example usage in tests:
+ *
+ *   mTracker = new CppWrappedTestTracker(FeatureConstants.YOU_FEATURE_HERE) {
+ *       @Override
+ *       public void notifyEvent(String event) {
+ *           super.notifyEvent(event);
+ *           // Validate that the right event was received.
+ *       }
+ *   };
+ *   TrackerFactory.getTrackerForProfile(profile).injectTracker(mTracker);
+ */
+@JNINamespace("feature_engagement")
+public class CppWrappedTestTracker implements Tracker {
+    private String mOurFeature;
+    private boolean mWasDismissed;
+    private String mLastEvent;
+
+    public CppWrappedTestTracker(String feature) {
+        mOurFeature = feature;
+    }
+
+    public boolean wasDismissed() {
+        return mWasDismissed;
+    }
+
+    public String getLastEvent() {
+        return mLastEvent;
+    }
+
+    @CalledByNative
+    @Override
+    public void notifyEvent(String event) {
+        mLastEvent = event;
+    }
+
+    @CheckResult
+    @CalledByNative
+    @Override
+    public boolean shouldTriggerHelpUI(String feature) {
+        return ourFeature(feature);
+    }
+
+    @CalledByNative
+    @Override
+    public boolean wouldTriggerHelpUI(String feature) {
+        return ourFeature(feature);
+    }
+
+    @CalledByNative
+    @Override
+    public boolean hasEverTriggered(String feature, boolean fromWindow) {
+        return true;
+    }
+
+    @TriggerState
+    @CalledByNative
+    @Override
+    public int getTriggerState(String feature) {
+        return ourFeature(feature) ? TriggerState.HAS_NOT_BEEN_DISPLAYED
+                                   : TriggerState.HAS_BEEN_DISPLAYED;
+    }
+
+    @CalledByNative
+    @Override
+    public void dismissed(String feature) {
+        if (ourFeature(feature)) {
+            mWasDismissed = true;
+        }
+    }
+
+    @CheckResult
+    @Nullable
+    @Override
+    public DisplayLockHandle acquireDisplayLock() {
+        assert false : "This should only be called on a production tracker";
+        return () -> {};
+    }
+
+    @CalledByNative
+    @Override
+    public boolean isInitialized() {
+        return true;
+    }
+
+    @Override
+    public void addOnInitializedCallback(Callback<Boolean> callback) {
+        assert false : "This should only be called on a production tracker";
+        callback.onResult(true);
+    }
+
+    @Override
+    public final void injectTracker(Tracker tracker) {
+        assert false : "This should only be called on a production tracker";
+    }
+
+    private boolean ourFeature(String feature) {
+        return TextUtils.equals(mOurFeature, feature);
+    }
+}
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
index 3fd1362..147c01c 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/EventConstants.java
@@ -202,6 +202,9 @@
     /** Reengagement events. */
     public static final String STARTED_FROM_MAIN_INTENT = "started_from_main_intent";
 
+    /** PWA install events. */
+    public static final String PWA_INSTALL_MENU_SELECTED = "pwa_install_menu_clicked";
+
     /**
      * Do not instantiate.
      */
diff --git a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/Tracker.java b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/Tracker.java
index 2af818ee..6e11417 100644
--- a/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/Tracker.java
+++ b/components/feature_engagement/public/android/java/src/org/chromium/components/feature_engagement/Tracker.java
@@ -127,4 +127,11 @@
      * ready to receive calls.
      */
     void addOnInitializedCallback(Callback<Boolean> callback);
+
+    /**
+     * Instructs the tracker in C++ to start proxying calls to Java. See {@link
+     * CppWrappedTestTracker} for usage and details.
+     * @param tracker The tracker object that should be receiving the calls.
+     */
+    void injectTracker(Tracker tracker);
 }
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
index 78ac432..7a1b94a3 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -183,42 +183,74 @@
 
 class MockSyncMetadataStore : public PasswordStoreSync::MetadataStore {
  public:
-  MOCK_METHOD0(GetAllSyncMetadata, std::unique_ptr<syncer::MetadataBatch>());
-  MOCK_METHOD0(DeleteAllSyncMetadata, void());
-  MOCK_METHOD3(UpdateSyncMetadata,
-               bool(syncer::ModelType,
-                    const std::string&,
-                    const sync_pb::EntityMetadata&));
-  MOCK_METHOD2(ClearSyncMetadata, bool(syncer::ModelType, const std::string&));
-  MOCK_METHOD2(UpdateModelTypeState,
-               bool(syncer::ModelType, const sync_pb::ModelTypeState&));
-  MOCK_METHOD1(ClearModelTypeState, bool(syncer::ModelType));
-  MOCK_METHOD1(SetDeletionsHaveSyncedCallback,
-               void(base::RepeatingCallback<void(bool)>));
-  MOCK_METHOD0(HasUnsyncedDeletions, bool());
+  MOCK_METHOD(std::unique_ptr<syncer::MetadataBatch>,
+              GetAllSyncMetadata,
+              (),
+              (override));
+  MOCK_METHOD(void, DeleteAllSyncMetadata, (), (override));
+  MOCK_METHOD(bool,
+              UpdateSyncMetadata,
+              (syncer::ModelType,
+               const std::string&,
+               const sync_pb::EntityMetadata&),
+              (override));
+  MOCK_METHOD(bool,
+              ClearSyncMetadata,
+              (syncer::ModelType, const std::string&),
+              (override));
+  MOCK_METHOD(bool,
+              UpdateModelTypeState,
+              (syncer::ModelType, const sync_pb::ModelTypeState&),
+              (override));
+  MOCK_METHOD(bool, ClearModelTypeState, (syncer::ModelType), (override));
+  MOCK_METHOD(void,
+              SetDeletionsHaveSyncedCallback,
+              (base::RepeatingCallback<void(bool)>),
+              (override));
+  MOCK_METHOD(bool, HasUnsyncedDeletions, (), (override));
 };
 
 class MockPasswordStoreSync : public PasswordStoreSync {
  public:
-  MOCK_METHOD1(ReadAllLogins, FormRetrievalResult(PrimaryKeyToFormMap*));
-  MOCK_METHOD1(RemoveLoginByPrimaryKeySync, PasswordStoreChangeList(int));
-  MOCK_METHOD0(DeleteUndecryptableLogins, DatabaseCleanupResult());
-  MOCK_METHOD2(AddLoginSync,
-               PasswordStoreChangeList(const PasswordForm&, AddLoginError*));
-  MOCK_METHOD2(UpdateLoginSync,
-               PasswordStoreChangeList(const PasswordForm&, UpdateLoginError*));
-  MOCK_METHOD1(RemoveLoginSync, PasswordStoreChangeList(const PasswordForm&));
-  MOCK_METHOD1(NotifyLoginsChanged, void(const PasswordStoreChangeList&));
-  MOCK_METHOD1(NotifyDeletionsHaveSynced, void(bool));
-
-  MOCK_METHOD1(NotifyUnsyncedCredentialsWillBeDeleted,
-               void(std::vector<PasswordForm> unsynced_credentials));
-  MOCK_METHOD0(BeginTransaction, bool());
-  MOCK_METHOD0(CommitTransaction, bool());
-  MOCK_METHOD0(RollbackTransaction, void());
-  MOCK_METHOD0(GetMetadataStore, PasswordStoreSync::MetadataStore*());
-  MOCK_CONST_METHOD0(IsAccountStore, bool());
-  MOCK_METHOD0(DeleteAndRecreateDatabaseFile, bool());
+  MOCK_METHOD(FormRetrievalResult,
+              ReadAllLogins,
+              (PrimaryKeyToFormMap*),
+              (override));
+  MOCK_METHOD(PasswordStoreChangeList,
+              RemoveLoginByPrimaryKeySync,
+              (int),
+              (override));
+  MOCK_METHOD(DatabaseCleanupResult, DeleteUndecryptableLogins, (), (override));
+  MOCK_METHOD(PasswordStoreChangeList,
+              AddLoginSync,
+              (const PasswordForm&, AddLoginError*),
+              (override));
+  MOCK_METHOD(PasswordStoreChangeList,
+              UpdateLoginSync,
+              (const PasswordForm&, UpdateLoginError*),
+              (override));
+  MOCK_METHOD(PasswordStoreChangeList,
+              RemoveLoginSync,
+              (const PasswordForm&),
+              (override));
+  MOCK_METHOD(void,
+              NotifyLoginsChanged,
+              (const PasswordStoreChangeList&),
+              (override));
+  MOCK_METHOD(void, NotifyDeletionsHaveSynced, (bool), (override));
+  MOCK_METHOD(void,
+              NotifyUnsyncedCredentialsWillBeDeleted,
+              (std::vector<PasswordForm> unsynced_credentials),
+              (override));
+  MOCK_METHOD(bool, BeginTransaction, (), (override));
+  MOCK_METHOD(bool, CommitTransaction, (), (override));
+  MOCK_METHOD(void, RollbackTransaction, (), (override));
+  MOCK_METHOD(PasswordStoreSync::MetadataStore*,
+              GetMetadataStore,
+              (),
+              (override));
+  MOCK_METHOD(bool, IsAccountStore, (), (const override));
+  MOCK_METHOD(bool, DeleteAndRecreateDatabaseFile, (), (override));
 };
 
 }  // namespace
diff --git a/components/sync/base/sync_prefs_unittest.cc b/components/sync/base/sync_prefs_unittest.cc
index 3129d1c..f4b1bebe 100644
--- a/components/sync/base/sync_prefs_unittest.cc
+++ b/components/sync/base/sync_prefs_unittest.cc
@@ -72,10 +72,10 @@
 
 class MockSyncPrefObserver : public SyncPrefObserver {
  public:
-  MOCK_METHOD1(OnSyncManagedPrefChange, void(bool));
-  MOCK_METHOD1(OnFirstSetupCompletePrefChange, void(bool));
-  MOCK_METHOD1(OnSyncRequestedPrefChange, void(bool));
-  MOCK_METHOD0(OnPreferredDataTypesPrefChange, void());
+  MOCK_METHOD(void, OnSyncManagedPrefChange, (bool), (override));
+  MOCK_METHOD(void, OnFirstSetupCompletePrefChange, (bool), (override));
+  MOCK_METHOD(void, OnSyncRequestedPrefChange, (bool), (override));
+  MOCK_METHOD(void, OnPreferredDataTypesPrefChange, (), (override));
 };
 
 TEST_F(SyncPrefsTest, ObservedPrefs) {
diff --git a/components/sync/base/weak_handle_unittest.cc b/components/sync/base/weak_handle_unittest.cc
index 5e46dc3..a02a9fc 100644
--- a/components/sync/base/weak_handle_unittest.cc
+++ b/components/sync/base/weak_handle_unittest.cc
@@ -26,14 +26,12 @@
   }
 
   void Kill() { weak_ptr_factory_.InvalidateWeakPtrs(); }
-
-  MOCK_METHOD0(Test, void());
-  MOCK_METHOD1(Test1, void(const int&));
-  MOCK_METHOD2(Test2, void(const int&, Base*));
-  MOCK_METHOD3(Test3, void(const int&, Base*, float));
-  MOCK_METHOD4(Test4, void(const int&, Base*, float, const char*));
-
-  MOCK_METHOD1(TestWithSelf, void(const WeakHandle<Base>&));
+  MOCK_METHOD(void, Test, (), ());
+  MOCK_METHOD(void, Test1, (const int&), ());
+  MOCK_METHOD(void, Test2, (const int&, Base*), ());
+  MOCK_METHOD(void, Test3, (const int&, Base*, float), ());
+  MOCK_METHOD(void, Test4, (const int&, Base*, float, const char*), ());
+  MOCK_METHOD(void, TestWithSelf, (const WeakHandle<Base>&), ());
 
  private:
   base::WeakPtrFactory<Base> weak_ptr_factory_{this};
diff --git a/components/sync/driver/backend_migrator_unittest.cc b/components/sync/driver/backend_migrator_unittest.cc
index 21c09fb..1d6c808 100644
--- a/components/sync/driver/backend_migrator_unittest.cc
+++ b/components/sync/driver/backend_migrator_unittest.cc
@@ -87,7 +87,7 @@
  public:
   ~MockMigrationObserver() override {}
 
-  MOCK_METHOD0(OnMigrationStateChange, void());
+  MOCK_METHOD(void, OnMigrationStateChange, ());
 };
 
 // Test that in the normal case a migration does transition through each state
diff --git a/components/sync/driver/data_type_manager_mock.h b/components/sync/driver/data_type_manager_mock.h
index 2a6d76a..2c119601 100644
--- a/components/sync/driver/data_type_manager_mock.h
+++ b/components/sync/driver/data_type_manager_mock.h
@@ -15,16 +15,17 @@
  public:
   DataTypeManagerMock();
   ~DataTypeManagerMock() override;
-
-  MOCK_METHOD2(Configure, void(ModelTypeSet, const ConfigureContext&));
-  MOCK_METHOD1(DataTypePreconditionChanged, void(ModelType));
-  MOCK_METHOD0(ResetDataTypeErrors, void());
-  MOCK_METHOD1(PurgeForMigration, void(ModelTypeSet));
-  MOCK_METHOD1(Stop, void(ShutdownReason));
-  MOCK_METHOD0(controllers, const DataTypeController::TypeMap&());
-  MOCK_CONST_METHOD0(GetActiveDataTypes, ModelTypeSet());
-  MOCK_CONST_METHOD0(GetPurgedDataTypes, ModelTypeSet());
-  MOCK_CONST_METHOD0(state, State());
+  MOCK_METHOD(void,
+              Configure,
+              (ModelTypeSet, const ConfigureContext&),
+              (override));
+  MOCK_METHOD(void, DataTypePreconditionChanged, (ModelType), (override));
+  MOCK_METHOD(void, ResetDataTypeErrors, (), (override));
+  MOCK_METHOD(void, PurgeForMigration, (ModelTypeSet), (override));
+  MOCK_METHOD(void, Stop, (ShutdownReason), (override));
+  MOCK_METHOD(ModelTypeSet, GetActiveDataTypes, (), (const override));
+  MOCK_METHOD(ModelTypeSet, GetPurgedDataTypes, (), (const override));
+  MOCK_METHOD(State, state, (), (const override));
 
  private:
   DataTypeManager::ConfigureResult result_;
diff --git a/components/sync/driver/glue/sync_engine_impl_unittest.cc b/components/sync/driver/glue/sync_engine_impl_unittest.cc
index 2087d5d..c7f2de3 100644
--- a/components/sync/driver/glue/sync_engine_impl_unittest.cc
+++ b/components/sync/driver/glue/sync_engine_impl_unittest.cc
@@ -143,21 +143,33 @@
  public:
   MockInvalidationService() = default;
   ~MockInvalidationService() override = default;
-
-  MOCK_METHOD1(RegisterInvalidationHandler,
-               void(syncer::InvalidationHandler* handler));
-  MOCK_METHOD2(UpdateInterestedTopics,
-               bool(syncer::InvalidationHandler* handler,
-                    const syncer::TopicSet& topics));
-  MOCK_METHOD1(UnregisterInvalidationHandler,
-               void(syncer::InvalidationHandler* handler));
-  MOCK_METHOD0(GetInvalidatorStat, syncer::InvalidatorState());
-  MOCK_CONST_METHOD0(GetInvalidatorState, syncer::InvalidatorState());
-  MOCK_CONST_METHOD0(GetInvalidatorClientId, std::string());
-  MOCK_METHOD0(GetInvalidationLogger, invalidation::InvalidationLogger*());
-  MOCK_CONST_METHOD1(RequestDetailedStatus,
-                     void(base::RepeatingCallback<
-                          void(const base::DictionaryValue&)> post_caller));
+  MOCK_METHOD(void,
+              RegisterInvalidationHandler,
+              (syncer::InvalidationHandler * handler),
+              (override));
+  MOCK_METHOD(bool,
+              UpdateInterestedTopics,
+              (syncer::InvalidationHandler * handler,
+               const syncer::TopicSet& topics),
+              (override));
+  MOCK_METHOD(void,
+              UnregisterInvalidationHandler,
+              (syncer::InvalidationHandler * handler),
+              (override));
+  MOCK_METHOD(syncer::InvalidatorState,
+              GetInvalidatorState,
+              (),
+              (const override));
+  MOCK_METHOD(std::string, GetInvalidatorClientId, (), (const override));
+  MOCK_METHOD(invalidation::InvalidationLogger*,
+              GetInvalidationLogger,
+              (),
+              (override));
+  MOCK_METHOD(
+      void,
+      RequestDetailedStatus,
+      (base::RepeatingCallback<void(const base::DictionaryValue&)> post_caller),
+      (const override));
 };
 
 std::unique_ptr<HttpPostProviderFactory> CreateHttpBridgeFactory() {
diff --git a/components/sync/driver/mock_sync_service.h b/components/sync/driver/mock_sync_service.h
index 4b7f27c..365b3a9 100644
--- a/components/sync/driver/mock_sync_service.h
+++ b/components/sync/driver/mock_sync_service.h
@@ -33,62 +33,107 @@
   // SyncService implementation.
   syncer::SyncUserSettings* GetUserSettings() override;
   const syncer::SyncUserSettings* GetUserSettings() const override;
-  MOCK_CONST_METHOD0(GetDisableReasons, DisableReasonSet());
-  MOCK_CONST_METHOD0(GetTransportState, TransportState());
-  MOCK_CONST_METHOD0(IsLocalSyncEnabled, bool());
-  MOCK_CONST_METHOD0(GetAuthenticatedAccountInfo, CoreAccountInfo());
-  MOCK_CONST_METHOD0(IsAuthenticatedAccountPrimary, bool());
-  MOCK_CONST_METHOD0(GetAuthError, GoogleServiceAuthError());
-  MOCK_CONST_METHOD0(GetAuthErrorTime, base::Time());
-  MOCK_CONST_METHOD0(RequiresClientUpgrade, bool());
-  MOCK_METHOD0(GetSetupInProgressHandle,
-               std::unique_ptr<SyncSetupInProgressHandle>());
-  MOCK_CONST_METHOD0(IsSetupInProgress, bool());
-
-  MOCK_CONST_METHOD0(GetPreferredDataTypes, ModelTypeSet());
-  MOCK_CONST_METHOD0(GetActiveDataTypes, ModelTypeSet());
-  MOCK_CONST_METHOD0(GetBackedOffDataTypes, ModelTypeSet());
-
-  MOCK_METHOD0(StopAndClear, void());
-  MOCK_METHOD1(OnDataTypeRequestsSyncStartup, void(ModelType type));
-  MOCK_METHOD1(TriggerRefresh, void(const ModelTypeSet& types));
-  MOCK_METHOD1(DataTypePreconditionChanged, void(syncer::ModelType type));
-  MOCK_METHOD1(SetInvalidationsForSessionsEnabled, void(bool enabled));
-  MOCK_METHOD3(AddTrustedVaultDecryptionKeysFromWeb,
-               void(const std::string& gaia_id,
-                    const std::vector<std::vector<uint8_t>>& keys,
-                    int last_key_version));
-  MOCK_METHOD3(AddTrustedVaultRecoveryMethodFromWeb,
-               void(const std::string& gaia_id,
-                    const std::vector<uint8_t>& public_key,
-                    base::OnceClosure callback));
-  MOCK_METHOD1(GetUserNoisedBirthYearAndGender,
-               UserDemographicsResult(base::Time now));
-
-  MOCK_METHOD1(AddObserver, void(SyncServiceObserver* observer));
-  MOCK_METHOD1(RemoveObserver, void(SyncServiceObserver* observer));
-  MOCK_CONST_METHOD1(HasObserver, bool(const SyncServiceObserver* observer));
-
-  MOCK_CONST_METHOD0(GetSyncTokenStatusForDebugging, SyncTokenStatus());
-  MOCK_CONST_METHOD1(QueryDetailedSyncStatusForDebugging,
-                     bool(SyncStatus* result));
-  MOCK_CONST_METHOD0(GetLastSyncedTimeForDebugging, base::Time());
-  MOCK_CONST_METHOD0(GetLastCycleSnapshotForDebugging, SyncCycleSnapshot());
-  MOCK_METHOD0(GetTypeStatusMapForDebugging, std::unique_ptr<base::Value>());
-  MOCK_CONST_METHOD0(GetSyncServiceUrlForDebugging, const GURL&());
-  MOCK_CONST_METHOD0(GetUnrecoverableErrorMessageForDebugging, std::string());
-  MOCK_CONST_METHOD0(GetUnrecoverableErrorLocationForDebugging,
-                     base::Location());
-  MOCK_METHOD1(AddProtocolEventObserver, void(ProtocolEventObserver* observer));
-  MOCK_METHOD1(RemoveProtocolEventObserver,
-               void(ProtocolEventObserver* observer));
-  MOCK_METHOD0(GetJsController, base::WeakPtr<JsController>());
-  MOCK_METHOD1(GetAllNodesForDebugging,
-               void(base::OnceCallback<void(std::unique_ptr<base::ListValue>)>
-                        callback));
+  MOCK_METHOD(DisableReasonSet, GetDisableReasons, (), (const override));
+  MOCK_METHOD(TransportState, GetTransportState, (), (const override));
+  MOCK_METHOD(bool, IsLocalSyncEnabled, (), (const override));
+  MOCK_METHOD(CoreAccountInfo,
+              GetAuthenticatedAccountInfo,
+              (),
+              (const override));
+  MOCK_METHOD(bool, IsAuthenticatedAccountPrimary, (), (const override));
+  MOCK_METHOD(GoogleServiceAuthError, GetAuthError, (), (const override));
+  MOCK_METHOD(base::Time, GetAuthErrorTime, (), (const override));
+  MOCK_METHOD(bool, RequiresClientUpgrade, (), (const override));
+  MOCK_METHOD(std::unique_ptr<SyncSetupInProgressHandle>,
+              GetSetupInProgressHandle,
+              (),
+              (override));
+  MOCK_METHOD(bool, IsSetupInProgress, (), (const override));
+  MOCK_METHOD(ModelTypeSet, GetPreferredDataTypes, (), (const override));
+  MOCK_METHOD(ModelTypeSet, GetActiveDataTypes, (), (const override));
+  MOCK_METHOD(ModelTypeSet, GetBackedOffDataTypes, (), (const override));
+  MOCK_METHOD(void, StopAndClear, (), (override));
+  MOCK_METHOD(void,
+              OnDataTypeRequestsSyncStartup,
+              (ModelType type),
+              (override));
+  MOCK_METHOD(void, TriggerRefresh, (const ModelTypeSet& types), (override));
+  MOCK_METHOD(void,
+              DataTypePreconditionChanged,
+              (syncer::ModelType type),
+              (override));
+  MOCK_METHOD(void,
+              SetInvalidationsForSessionsEnabled,
+              (bool enabled),
+              (override));
+  MOCK_METHOD(void,
+              AddTrustedVaultDecryptionKeysFromWeb,
+              (const std::string& gaia_id,
+               const std::vector<std::vector<uint8_t>>& keys,
+               int last_key_version),
+              (override));
+  MOCK_METHOD(void,
+              AddTrustedVaultRecoveryMethodFromWeb,
+              (const std::string& gaia_id,
+               const std::vector<uint8_t>& public_key,
+               base::OnceClosure callback),
+              (override));
+  MOCK_METHOD(UserDemographicsResult,
+              GetUserNoisedBirthYearAndGender,
+              (base::Time now),
+              (override));
+  MOCK_METHOD(void, AddObserver, (SyncServiceObserver * observer), (override));
+  MOCK_METHOD(void,
+              RemoveObserver,
+              (SyncServiceObserver * observer),
+              (override));
+  MOCK_METHOD(bool,
+              HasObserver,
+              (const SyncServiceObserver* observer),
+              (const override));
+  MOCK_METHOD(SyncTokenStatus,
+              GetSyncTokenStatusForDebugging,
+              (),
+              (const override));
+  MOCK_METHOD(bool,
+              QueryDetailedSyncStatusForDebugging,
+              (SyncStatus * result),
+              (const override));
+  MOCK_METHOD(base::Time, GetLastSyncedTimeForDebugging, (), (const override));
+  MOCK_METHOD(SyncCycleSnapshot,
+              GetLastCycleSnapshotForDebugging,
+              (),
+              (const override));
+  MOCK_METHOD(std::unique_ptr<base::Value>,
+              GetTypeStatusMapForDebugging,
+              (),
+              (override));
+  MOCK_METHOD(const GURL&, GetSyncServiceUrlForDebugging, (), (const override));
+  MOCK_METHOD(std::string,
+              GetUnrecoverableErrorMessageForDebugging,
+              (),
+              (const override));
+  MOCK_METHOD(base::Location,
+              GetUnrecoverableErrorLocationForDebugging,
+              (),
+              (const override));
+  MOCK_METHOD(void,
+              AddProtocolEventObserver,
+              (ProtocolEventObserver * observer),
+              (override));
+  MOCK_METHOD(void,
+              RemoveProtocolEventObserver,
+              (ProtocolEventObserver * observer),
+              (override));
+  MOCK_METHOD(base::WeakPtr<JsController>, GetJsController, (), (override));
+  MOCK_METHOD(
+      void,
+      GetAllNodesForDebugging,
+      (base::OnceCallback<void(std::unique_ptr<base::ListValue>)> callback),
+      (override));
 
   // KeyedService implementation.
-  MOCK_METHOD0(Shutdown, void());
+  MOCK_METHOD(void, Shutdown, (), (override));
 
  private:
   testing::NiceMock<syncer::SyncUserSettingsMock> user_settings_;
diff --git a/components/sync/driver/model_association_manager_unittest.cc b/components/sync/driver/model_association_manager_unittest.cc
index 47ac4643..d6a3b2e3 100644
--- a/components/sync/driver/model_association_manager_unittest.cc
+++ b/components/sync/driver/model_association_manager_unittest.cc
@@ -32,14 +32,21 @@
 class MockModelAssociationManagerDelegate
     : public ModelAssociationManagerDelegate {
  public:
-  MockModelAssociationManagerDelegate() {}
-  ~MockModelAssociationManagerDelegate() override {}
-  MOCK_METHOD0(OnAllDataTypesReadyForConfigure, void());
-  MOCK_METHOD1(OnSingleDataTypeAssociationDone, void(ModelType type));
-  MOCK_METHOD2(OnSingleDataTypeWillStop,
-               void(ModelType, const SyncError& error));
-  MOCK_METHOD1(OnModelAssociationDone,
-               void(const DataTypeManager::ConfigureResult& result));
+  MockModelAssociationManagerDelegate() = default;
+  ~MockModelAssociationManagerDelegate() override = default;
+  MOCK_METHOD(void, OnAllDataTypesReadyForConfigure, (), (override));
+  MOCK_METHOD(void,
+              OnSingleDataTypeAssociationDone,
+              (ModelType type),
+              (override));
+  MOCK_METHOD(void,
+              OnSingleDataTypeWillStop,
+              (ModelType, const SyncError& error),
+              (override));
+  MOCK_METHOD(void,
+              OnModelAssociationDone,
+              (const DataTypeManager::ConfigureResult& result),
+              (override));
 };
 
 MATCHER_P2(MatchesResult, status, requested_types, "") {
diff --git a/components/sync/driver/model_type_controller_unittest.cc b/components/sync/driver/model_type_controller_unittest.cc
index 6fc1c66..996aa9d1 100644
--- a/components/sync/driver/model_type_controller_unittest.cc
+++ b/components/sync/driver/model_type_controller_unittest.cc
@@ -47,12 +47,20 @@
 
 class MockDelegate : public ModelTypeControllerDelegate {
  public:
-  MOCK_METHOD2(OnSyncStarting,
-               void(const DataTypeActivationRequest& request,
-                    StartCallback callback));
-  MOCK_METHOD1(OnSyncStopping, void(SyncStopMetadataFate metadata_fate));
-  MOCK_METHOD1(GetAllNodesForDebugging, void(AllNodesCallback callback));
-  MOCK_METHOD0(RecordMemoryUsageAndCountsHistograms, void());
+  MOCK_METHOD(void,
+              OnSyncStarting,
+              (const DataTypeActivationRequest& request,
+               StartCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              OnSyncStopping,
+              (SyncStopMetadataFate metadata_fate),
+              (override));
+  MOCK_METHOD(void,
+              GetAllNodesForDebugging,
+              (AllNodesCallback callback),
+              (override));
+  MOCK_METHOD(void, RecordMemoryUsageAndCountsHistograms, (), (override));
 };
 
 // A simple processor that trackes connected state.
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc
index 3f6007e7..2b7895e5 100644
--- a/components/sync/driver/profile_sync_service.cc
+++ b/components/sync/driver/profile_sync_service.cc
@@ -146,12 +146,6 @@
       network_time_update_callback);
 }
 
-void EmitUmaMetricWithEmitTimeMinutes(const std::string& histogram_name) {
-  base::Time::Exploded now_exploded;
-  base::Time::Now().UTCExplode(&now_exploded);
-  base::UmaHistogramExactLinear(histogram_name, now_exploded.minute, 60);
-}
-
 std::string GenerateCacheGUID() {
   // Generate a GUID with 128 bits of randomness.
   const int kGuidBytes = 128 / 8;
@@ -327,9 +321,6 @@
   if (HasDisableReason(DISABLE_REASON_ENTERPRISE_POLICY) ||
       (HasDisableReason(DISABLE_REASON_NOT_SIGNED_IN) &&
        auth_manager_->IsActiveAccountInfoFullyLoaded())) {
-    // TODO(crbug/1031162): Remove once traffic investigation is closed.
-    EmitUmaMetricWithEmitTimeMinutes(
-        "Sync.PeakAnalysis.StopOnSyncPermanentlyDisabled");
     StopImpl(CLEAR_DATA);
   }
 
@@ -387,9 +378,6 @@
   if (!IsSignedIn()) {
     // The account was signed out, so shut down.
     sync_disabled_by_admin_ = false;
-    // TODO(crbug/1031162): Remove once traffic investigation is closed.
-    EmitUmaMetricWithEmitTimeMinutes(
-        "Sync.PeakAnalysis.StopAfterAccountStateChanged");
     StopImpl(CLEAR_DATA);
     DCHECK(!engine_);
   } else {
@@ -425,9 +413,6 @@
   // then shut down. This happens when the user signs out on the web, i.e. we're
   // in the "Sync paused" state.
   if (!IsEngineAllowedToRun()) {
-    // TODO(crbug/1031162): Remove once traffic investigation is closed.
-    EmitUmaMetricWithEmitTimeMinutes(
-        "Sync.PeakAnalysis.StopAfterCredentialsChanged");
     // This will notify observers if appropriate.
     StopImpl(KEEP_DATA);
     return;
@@ -1485,9 +1470,6 @@
 void ProfileSyncService::OnSyncManagedPrefChange(bool is_sync_managed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_sync_managed) {
-    // TODO(crbug/1031162): Remove once traffic investigation is closed.
-    EmitUmaMetricWithEmitTimeMinutes(
-        "Sync.PeakAnalysis.StopOnSyncManagedPrefChange");
     StopImpl(CLEAR_DATA);
   } else {
     // Sync is no longer disabled by policy. Try starting it up if appropriate.
diff --git a/components/sync/driver/sync_api_component_factory_mock.h b/components/sync/driver/sync_api_component_factory_mock.h
index 5b5b016..f19bc062 100644
--- a/components/sync/driver/sync_api_component_factory_mock.h
+++ b/components/sync/driver/sync_api_component_factory_mock.h
@@ -23,22 +23,23 @@
  public:
   SyncApiComponentFactoryMock();
   ~SyncApiComponentFactoryMock() override;
-
-  MOCK_METHOD6(CreateDataTypeManager,
-               std::unique_ptr<DataTypeManager>(
-                   ModelTypeSet,
-                   const WeakHandle<DataTypeDebugInfoListener>&,
-                   const DataTypeController::TypeMap*,
-                   const DataTypeEncryptionHandler*,
-                   ModelTypeConfigurer*,
-                   DataTypeManagerObserver*));
-  MOCK_METHOD4(CreateSyncEngine,
-               std::unique_ptr<SyncEngine>(
-                   const std::string& name,
-                   invalidation::InvalidationService* invalidator,
-                   syncer::SyncInvalidationsService* sync_invalidations_service,
-                   const base::WeakPtr<SyncPrefs>& sync_prefs));
-  MOCK_METHOD0(DeleteLegacyDirectoryFilesAndNigoriStorage, void());
+  MOCK_METHOD(std::unique_ptr<DataTypeManager>,
+              CreateDataTypeManager,
+              (ModelTypeSet,
+               const WeakHandle<DataTypeDebugInfoListener>&,
+               const DataTypeController::TypeMap*,
+               const DataTypeEncryptionHandler*,
+               ModelTypeConfigurer*,
+               DataTypeManagerObserver*),
+              (override));
+  MOCK_METHOD(std::unique_ptr<SyncEngine>,
+              CreateSyncEngine,
+              (const std::string& name,
+               invalidation::InvalidationService* invalidator,
+               syncer::SyncInvalidationsService* sync_invalidations_service,
+               const base::WeakPtr<SyncPrefs>& sync_prefs),
+              (override));
+  MOCK_METHOD(void, DeleteLegacyDirectoryFilesAndNigoriStorage, (), (override));
 };
 
 }  // namespace syncer
diff --git a/components/sync/driver/sync_client_mock.h b/components/sync/driver/sync_client_mock.h
index 3f085e26..775d855a 100644
--- a/components/sync/driver/sync_client_mock.h
+++ b/components/sync/driver/sync_client_mock.h
@@ -15,21 +15,34 @@
  public:
   SyncClientMock();
   ~SyncClientMock() override;
-
-  MOCK_METHOD0(GetPrefService, PrefService*());
-  MOCK_METHOD0(GetIdentityManager, signin::IdentityManager*());
-  MOCK_METHOD0(GetLocalSyncBackendFolder, base::FilePath());
-  MOCK_METHOD1(CreateDataTypeControllers,
-               DataTypeController::TypeVector(SyncService* sync_service));
-  MOCK_METHOD0(GetPasswordStateChangedCallback, base::RepeatingClosure());
-
-  MOCK_METHOD0(GetInvalidationService, invalidation::InvalidationService*());
-  MOCK_METHOD0(GetSyncInvalidationsService,
-               syncer::SyncInvalidationsService*());
-  MOCK_METHOD0(GetTrustedVaultClient, TrustedVaultClient*());
-  MOCK_METHOD0(GetExtensionsActivity, scoped_refptr<ExtensionsActivity>());
-  MOCK_METHOD0(GetSyncApiComponentFactory, SyncApiComponentFactory*());
-  MOCK_METHOD0(GetPreferenceProvider, SyncTypePreferenceProvider*());
+  MOCK_METHOD(PrefService*, GetPrefService, (), (override));
+  MOCK_METHOD(signin::IdentityManager*, GetIdentityManager, (), (override));
+  MOCK_METHOD(base::FilePath, GetLocalSyncBackendFolder, (), (override));
+  MOCK_METHOD(DataTypeController::TypeVector,
+              CreateDataTypeControllers,
+              (SyncService * sync_service),
+              (override));
+  MOCK_METHOD(invalidation::InvalidationService*,
+              GetInvalidationService,
+              (),
+              (override));
+  MOCK_METHOD(syncer::SyncInvalidationsService*,
+              GetSyncInvalidationsService,
+              (),
+              (override));
+  MOCK_METHOD(TrustedVaultClient*, GetTrustedVaultClient, (), (override));
+  MOCK_METHOD(scoped_refptr<ExtensionsActivity>,
+              GetExtensionsActivity,
+              (),
+              (override));
+  MOCK_METHOD(SyncApiComponentFactory*,
+              GetSyncApiComponentFactory,
+              (),
+              (override));
+  MOCK_METHOD(SyncTypePreferenceProvider*,
+              GetPreferenceProvider,
+              (),
+              (override));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SyncClientMock);
diff --git a/components/sync/driver/sync_service_crypto_unittest.cc b/components/sync/driver/sync_service_crypto_unittest.cc
index 4388b48..8d2ae25 100644
--- a/components/sync/driver/sync_service_crypto_unittest.cc
+++ b/components/sync/driver/sync_service_crypto_unittest.cc
@@ -58,11 +58,19 @@
  public:
   MockCryptoSyncPrefs() = default;
   ~MockCryptoSyncPrefs() override = default;
-
-  MOCK_CONST_METHOD0(GetEncryptionBootstrapToken, std::string());
-  MOCK_METHOD1(SetEncryptionBootstrapToken, void(const std::string&));
-  MOCK_CONST_METHOD0(GetKeystoreEncryptionBootstrapToken, std::string());
-  MOCK_METHOD1(SetKeystoreEncryptionBootstrapToken, void(const std::string&));
+  MOCK_METHOD(std::string, GetEncryptionBootstrapToken, (), (const override));
+  MOCK_METHOD(void,
+              SetEncryptionBootstrapToken,
+              (const std::string&),
+              (override));
+  MOCK_METHOD(std::string,
+              GetKeystoreEncryptionBootstrapToken,
+              (),
+              (const override));
+  MOCK_METHOD(void,
+              SetKeystoreEncryptionBootstrapToken,
+              (const std::string&),
+              (override));
 };
 
 // Object representing a server that contains the authoritative trusted vault
diff --git a/components/sync/driver/sync_user_settings_mock.h b/components/sync/driver/sync_user_settings_mock.h
index f51b26b..ef087d24 100644
--- a/components/sync/driver/sync_user_settings_mock.h
+++ b/components/sync/driver/sync_user_settings_mock.h
@@ -17,46 +17,62 @@
  public:
   SyncUserSettingsMock();
   ~SyncUserSettingsMock() override;
-
-  MOCK_CONST_METHOD0(IsSyncRequested, bool());
-  MOCK_METHOD1(SetSyncRequested, void(bool));
-
-  MOCK_CONST_METHOD0(IsSyncAllowedByPlatform, bool());
-  MOCK_METHOD1(SetSyncAllowedByPlatform, void(bool));
-
-  MOCK_CONST_METHOD0(IsFirstSetupComplete, bool());
-  MOCK_METHOD1(SetFirstSetupComplete, void(SyncFirstSetupCompleteSource));
-
-  MOCK_CONST_METHOD0(IsSyncEverythingEnabled, bool());
-  MOCK_CONST_METHOD0(GetSelectedTypes, UserSelectableTypeSet());
-  MOCK_METHOD2(SetSelectedTypes, void(bool, UserSelectableTypeSet));
-  MOCK_CONST_METHOD0(GetRegisteredSelectableTypes, UserSelectableTypeSet());
+  MOCK_METHOD(bool, IsSyncRequested, (), (const override));
+  MOCK_METHOD(void, SetSyncRequested, (bool), (override));
+  MOCK_METHOD(bool, IsSyncAllowedByPlatform, (), (const override));
+  MOCK_METHOD(void, SetSyncAllowedByPlatform, (bool), (override));
+  MOCK_METHOD(bool, IsFirstSetupComplete, (), (const override));
+  MOCK_METHOD(void,
+              SetFirstSetupComplete,
+              (SyncFirstSetupCompleteSource),
+              (override));
+  MOCK_METHOD(bool, IsSyncEverythingEnabled, (), (const override));
+  MOCK_METHOD(UserSelectableTypeSet, GetSelectedTypes, (), (const override));
+  MOCK_METHOD(void,
+              SetSelectedTypes,
+              (bool, UserSelectableTypeSet),
+              (override));
+  MOCK_METHOD(UserSelectableTypeSet,
+              GetRegisteredSelectableTypes,
+              (),
+              (const override));
 
 #if defined(OS_CHROMEOS)
-  MOCK_CONST_METHOD0(IsSyncAllOsTypesEnabled, bool());
-  MOCK_CONST_METHOD0(GetSelectedOsTypes, UserSelectableOsTypeSet());
-  MOCK_METHOD2(SetSelectedOsTypes, void(bool, UserSelectableOsTypeSet));
-  MOCK_CONST_METHOD0(GetRegisteredSelectableOsTypes, UserSelectableOsTypeSet());
-
-  MOCK_CONST_METHOD0(IsOsSyncFeatureEnabled, bool());
-  MOCK_METHOD1(SetOsSyncFeatureEnabled, void(bool));
+  MOCK_METHOD(bool, IsSyncAllOsTypesEnabled, (), (const override));
+  MOCK_METHOD(UserSelectableOsTypeSet,
+              GetSelectedOsTypes,
+              (),
+              (const override));
+  MOCK_METHOD(void,
+              SetSelectedOsTypes,
+              (bool, UserSelectableOsTypeSet),
+              (override));
+  MOCK_METHOD(UserSelectableOsTypeSet,
+              GetRegisteredSelectableOsTypes,
+              (),
+              (const override));
+  MOCK_METHOD(bool, IsOsSyncFeatureEnabled, (), (const override));
+  MOCK_METHOD(void, SetOsSyncFeatureEnabled, (bool), (override));
 #endif
-
-  MOCK_CONST_METHOD0(IsEncryptEverythingAllowed, bool());
-  MOCK_CONST_METHOD0(IsEncryptEverythingEnabled, bool());
-
-  MOCK_CONST_METHOD0(GetEncryptedDataTypes, ModelTypeSet());
-  MOCK_CONST_METHOD0(IsPassphraseRequired, bool());
-  MOCK_CONST_METHOD0(IsPassphraseRequiredForPreferredDataTypes, bool());
-  MOCK_CONST_METHOD0(IsTrustedVaultKeyRequired, bool());
-  MOCK_CONST_METHOD0(IsTrustedVaultKeyRequiredForPreferredDataTypes, bool());
-  MOCK_CONST_METHOD0(IsTrustedVaultRecoverabilityDegraded, bool());
-  MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
-  MOCK_CONST_METHOD0(GetExplicitPassphraseTime, base::Time());
-  MOCK_CONST_METHOD0(GetPassphraseType, PassphraseType());
-
-  MOCK_METHOD1(SetEncryptionPassphrase, void(const std::string&));
-  MOCK_METHOD1(SetDecryptionPassphrase, bool(const std::string&));
+  MOCK_METHOD(bool, IsEncryptEverythingAllowed, (), (const override));
+  MOCK_METHOD(bool, IsEncryptEverythingEnabled, (), (const override));
+  MOCK_METHOD(ModelTypeSet, GetEncryptedDataTypes, (), (const override));
+  MOCK_METHOD(bool, IsPassphraseRequired, (), (const override));
+  MOCK_METHOD(bool,
+              IsPassphraseRequiredForPreferredDataTypes,
+              (),
+              (const override));
+  MOCK_METHOD(bool, IsTrustedVaultKeyRequired, (), (const override));
+  MOCK_METHOD(bool,
+              IsTrustedVaultKeyRequiredForPreferredDataTypes,
+              (),
+              (const override));
+  MOCK_METHOD(bool, IsTrustedVaultRecoverabilityDegraded, (), (const override));
+  MOCK_METHOD(bool, IsUsingSecondaryPassphrase, (), (const override));
+  MOCK_METHOD(base::Time, GetExplicitPassphraseTime, (), (const override));
+  MOCK_METHOD(PassphraseType, GetPassphraseType, (), (const override));
+  MOCK_METHOD(void, SetEncryptionPassphrase, (const std::string&), (override));
+  MOCK_METHOD(bool, SetDecryptionPassphrase, (const std::string&), (override));
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine/mock_sync_engine.h b/components/sync/engine/mock_sync_engine.h
index f07de02..8c762c73 100644
--- a/components/sync/engine/mock_sync_engine.h
+++ b/components/sync/engine/mock_sync_engine.h
@@ -25,37 +25,47 @@
   ~MockSyncEngine() override;
 
   // ModelTypeConfigurer:
-  MOCK_METHOD1(ConfigureDataTypes, void(ConfigureParams));
-  MOCK_METHOD2(ActivateDataType,
-               void(ModelType, std::unique_ptr<DataTypeActivationResponse>));
-  MOCK_METHOD1(DeactivateDataType, void(ModelType));
-  MOCK_METHOD1(ActivateProxyDataType, void(ModelType));
-  MOCK_METHOD1(DeactivateProxyDataType, void(ModelType));
+  MOCK_METHOD(void, ConfigureDataTypes, (ConfigureParams), (override));
+  MOCK_METHOD(void,
+              ActivateDataType,
+              (ModelType, std::unique_ptr<DataTypeActivationResponse>),
+              (override));
+  MOCK_METHOD(void, DeactivateDataType, (ModelType), (override));
+  MOCK_METHOD(void, ActivateProxyDataType, (ModelType), (override));
+  MOCK_METHOD(void, DeactivateProxyDataType, (ModelType), (override));
 
   // SyncEngine:
-  MOCK_METHOD1(Initialize, void(InitParams));
-  MOCK_CONST_METHOD0(IsInitialized, bool());
-  MOCK_METHOD1(TriggerRefresh, void(const ModelTypeSet&));
-  MOCK_METHOD1(UpdateCredentials, void(const SyncCredentials&));
-  MOCK_METHOD0(InvalidateCredentials, void());
-  MOCK_METHOD0(StartConfiguration, void());
-  MOCK_METHOD0(StartSyncingWithServer, void());
-  MOCK_METHOD1(SetEncryptionPassphrase, void(const std::string&));
-  MOCK_METHOD1(SetDecryptionPassphrase, void(const std::string&));
-  MOCK_METHOD2(AddTrustedVaultDecryptionKeys,
-               void(const std::vector<std::vector<uint8_t>>&,
-                    base::OnceClosure));
-  MOCK_METHOD0(StopSyncingForShutdown, void());
-  MOCK_METHOD1(Shutdown, void(ShutdownReason));
-  MOCK_CONST_METHOD0(GetDetailedStatus, const SyncStatus&());
-  MOCK_CONST_METHOD1(HasUnsyncedItemsForTest,
-                     void(base::OnceCallback<void(bool)>));
-  MOCK_METHOD0(RequestBufferedProtocolEventsAndEnableForwarding, void());
-  MOCK_METHOD0(DisableProtocolEventForwarding, void());
-  MOCK_METHOD1(ClearServerData, void(base::OnceClosure));
-  MOCK_METHOD3(OnCookieJarChanged, void(bool, bool, base::OnceClosure));
-  MOCK_METHOD1(SetInvalidationsForSessionsEnabled, void(bool));
-  MOCK_METHOD1(GetNigoriNodeForDebugging, void(AllNodesCallback));
+  MOCK_METHOD(void, Initialize, (InitParams), (override));
+  MOCK_METHOD(bool, IsInitialized, (), (const override));
+  MOCK_METHOD(void, TriggerRefresh, (const ModelTypeSet&), (override));
+  MOCK_METHOD(void, UpdateCredentials, (const SyncCredentials&), (override));
+  MOCK_METHOD(void, InvalidateCredentials, (), (override));
+  MOCK_METHOD(void, StartConfiguration, (), (override));
+  MOCK_METHOD(void, StartSyncingWithServer, (), (override));
+  MOCK_METHOD(void, SetEncryptionPassphrase, (const std::string&), (override));
+  MOCK_METHOD(void, SetDecryptionPassphrase, (const std::string&), (override));
+  MOCK_METHOD(void,
+              AddTrustedVaultDecryptionKeys,
+              (const std::vector<std::vector<uint8_t>>&, base::OnceClosure),
+              (override));
+  MOCK_METHOD(void, StopSyncingForShutdown, (), (override));
+  MOCK_METHOD(void, Shutdown, (ShutdownReason), (override));
+  MOCK_METHOD(const SyncStatus&, GetDetailedStatus, (), (const override));
+  MOCK_METHOD(void,
+              HasUnsyncedItemsForTest,
+              (base::OnceCallback<void(bool)>),
+              (const override));
+  MOCK_METHOD(void,
+              RequestBufferedProtocolEventsAndEnableForwarding,
+              (),
+              (override));
+  MOCK_METHOD(void, DisableProtocolEventForwarding, (), (override));
+  MOCK_METHOD(void,
+              OnCookieJarChanged,
+              (bool, bool, base::OnceClosure),
+              (override));
+  MOCK_METHOD(void, SetInvalidationsForSessionsEnabled, (bool), (override));
+  MOCK_METHOD(void, GetNigoriNodeForDebugging, (AllNodesCallback), (override));
 };
 
 }  // namespace syncer
diff --git a/components/sync/engine_impl/commit_processor_unittest.cc b/components/sync/engine_impl/commit_processor_unittest.cc
index 9951eb8e..4c8820a 100644
--- a/components/sync/engine_impl/commit_processor_unittest.cc
+++ b/components/sync/engine_impl/commit_processor_unittest.cc
@@ -56,9 +56,10 @@
  public:
   MockCommitContributor() = default;
   ~MockCommitContributor() override = default;
-
-  MOCK_METHOD1(GetContribution,
-               std::unique_ptr<CommitContribution>(size_t max_entries));
+  MOCK_METHOD(std::unique_ptr<CommitContribution>,
+              GetContribution,
+              (size_t max_entries),
+              (override));
 };
 
 class CommitProcessorTest : public testing::Test {
diff --git a/components/sync/engine_impl/sync_manager_impl_unittest.cc b/components/sync/engine_impl/sync_manager_impl_unittest.cc
index 83a726b..20d3e631 100644
--- a/components/sync/engine_impl/sync_manager_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_manager_impl_unittest.cc
@@ -98,35 +98,59 @@
 
 class SyncManagerObserverMock : public SyncManager::Observer {
  public:
-  MOCK_METHOD1(OnSyncCycleCompleted, void(const SyncCycleSnapshot&));  // NOLINT
-  MOCK_METHOD3(OnInitializationComplete,
-               void(const WeakHandle<JsBackend>&,
-                    const WeakHandle<DataTypeDebugInfoListener>&,
-                    bool));                                         // NOLINT
-  MOCK_METHOD1(OnConnectionStatusChange, void(ConnectionStatus));   // NOLINT
-  MOCK_METHOD1(OnUpdatedToken, void(const std::string&));           // NOLINT
-  MOCK_METHOD1(OnActionableError, void(const SyncProtocolError&));  // NOLINT
-  MOCK_METHOD1(OnMigrationRequested, void(ModelTypeSet));           // NOLINT
-  MOCK_METHOD1(OnProtocolEvent, void(const ProtocolEvent&));        // NOLINT
+  MOCK_METHOD(void,
+              OnSyncCycleCompleted,
+              (const SyncCycleSnapshot&),
+              (override));
+  // NOLINT
+  MOCK_METHOD(void,
+              OnInitializationComplete,
+              (const WeakHandle<JsBackend>&,
+               const WeakHandle<DataTypeDebugInfoListener>&,
+               bool),
+              (override));
+  // NOLINT
+  MOCK_METHOD(void, OnConnectionStatusChange, (ConnectionStatus), (override));
+  // NOLINT
+  MOCK_METHOD(void, OnActionableError, (const SyncProtocolError&), (override));
+  // NOLINT
+  MOCK_METHOD(void, OnMigrationRequested, (ModelTypeSet), (override));
+  // NOLINT
+  MOCK_METHOD(void, OnProtocolEvent, (const ProtocolEvent&), (override));
+  // NOLINT
 };
 
 class SyncEncryptionHandlerObserverMock
     : public SyncEncryptionHandler::Observer {
  public:
-  MOCK_METHOD2(OnPassphraseRequired,
-               void(const KeyDerivationParams&,
-                    const sync_pb::EncryptedData&));  // NOLINT
-  MOCK_METHOD0(OnPassphraseAccepted, void());         // NOLINT
-  MOCK_METHOD0(OnTrustedVaultKeyRequired, void());    // NOLINT
-  MOCK_METHOD0(OnTrustedVaultKeyAccepted, void());    // NOLINT
-  MOCK_METHOD2(OnBootstrapTokenUpdated,
-               void(const std::string&, BootstrapTokenType type));  // NOLINT
-  MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool));  // NOLINT
-  MOCK_METHOD2(OnCryptographerStateChanged,
-               void(Cryptographer*, bool));  // NOLINT
-  MOCK_METHOD2(OnPassphraseTypeChanged,
-               void(PassphraseType,
-                    base::Time));  // NOLINT
+  MOCK_METHOD(void,
+              OnPassphraseRequired,
+              (const KeyDerivationParams&, const sync_pb::EncryptedData&),
+              (override));
+  // NOLINT
+  MOCK_METHOD(void, OnPassphraseAccepted, (), (override));
+  // NOLINT
+  MOCK_METHOD(void, OnTrustedVaultKeyRequired, (), (override));
+  // NOLINT
+  MOCK_METHOD(void, OnTrustedVaultKeyAccepted, (), (override));
+  // NOLINT
+  MOCK_METHOD(void,
+              OnBootstrapTokenUpdated,
+              (const std::string&, BootstrapTokenType type),
+              (override));
+  // NOLINT
+  MOCK_METHOD(void, OnEncryptedTypesChanged, (ModelTypeSet, bool), (override));
+  // NOLINT
+  MOCK_METHOD(void,
+              OnCryptographerStateChanged,
+              (Cryptographer*, bool),
+              (override));
+  // NOLINT
+  MOCK_METHOD(void,
+              OnPassphraseTypeChanged,
+              (PassphraseType, base::Time),
+              (override));
+  // NOLINT
 };
 
 }  // namespace
@@ -241,14 +265,13 @@
 
 class MockSyncScheduler : public FakeSyncScheduler {
  public:
-  MockSyncScheduler() : FakeSyncScheduler() {}
-  ~MockSyncScheduler() override {}
-
-  MOCK_METHOD2(Start, void(SyncScheduler::Mode, base::Time));
+  MockSyncScheduler() = default;
+  ~MockSyncScheduler() override = default;
+  MOCK_METHOD(void, Start, (SyncScheduler::Mode, base::Time), (override));
   void ScheduleConfiguration(ConfigurationParams params) override {
     ScheduleConfiguration_(params);
   }
-  MOCK_METHOD1(ScheduleConfiguration_, void(ConfigurationParams&));
+  MOCK_METHOD(void, ScheduleConfiguration_, (ConfigurationParams&), ());
 };
 
 class ComponentsFactory : public TestEngineComponentsFactory {
diff --git a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
index d135413..5100ee0 100644
--- a/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_scheduler_impl_unittest.cc
@@ -57,12 +57,17 @@
 class MockSyncer : public Syncer {
  public:
   MockSyncer();
-  MOCK_METHOD3(NormalSyncShare, bool(ModelTypeSet, NudgeTracker*, SyncCycle*));
-  MOCK_METHOD3(ConfigureSyncShare,
-               bool(const ModelTypeSet&,
-                    sync_pb::SyncEnums::GetUpdatesOrigin,
-                    SyncCycle*));
-  MOCK_METHOD2(PollSyncShare, bool(ModelTypeSet, SyncCycle*));
+  MOCK_METHOD(bool,
+              NormalSyncShare,
+              (ModelTypeSet, NudgeTracker*, SyncCycle*),
+              (override));
+  MOCK_METHOD(bool,
+              ConfigureSyncShare,
+              (const ModelTypeSet&,
+               sync_pb::SyncEnums::GetUpdatesOrigin,
+               SyncCycle*),
+              (override));
+  MOCK_METHOD(bool, PollSyncShare, (ModelTypeSet, SyncCycle*), (override));
 };
 
 std::unique_ptr<DataTypeActivationResponse> MakeFakeActivationResponse(
@@ -119,8 +124,7 @@
         : BackoffDelayProvider(
               TimeDelta::FromSeconds(kInitialBackoffRetrySeconds),
               TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)) {}
-
-    MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&));
+    MOCK_METHOD(TimeDelta, GetDelay, (const TimeDelta&), (override));
   };
 
   void SetUp() override {
diff --git a/components/sync/invalidations/fcm_handler_unittest.cc b/components/sync/invalidations/fcm_handler_unittest.cc
index 8d32db9..01f19d4c 100644
--- a/components/sync/invalidations/fcm_handler_unittest.cc
+++ b/components/sync/invalidations/fcm_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/test/task_environment.h"
 #include "components/gcm_driver/fake_gcm_driver.h"
 #include "components/gcm_driver/gcm_driver.h"
+#include "components/gcm_driver/instance_id/instance_id.h"
 #include "components/gcm_driver/instance_id/instance_id_driver.h"
 #include "components/sync/invalidations/fcm_registration_token_observer.h"
 #include "components/sync/invalidations/invalidations_listener.h"
@@ -43,48 +44,64 @@
  public:
   MockInstanceID() : InstanceID("app_id", /*gcm_driver=*/nullptr) {}
   ~MockInstanceID() override = default;
-
-  MOCK_METHOD1(GetID, void(GetIDCallback callback));
-  MOCK_METHOD1(GetCreationTime, void(GetCreationTimeCallback callback));
-  MOCK_METHOD6(GetToken,
-               void(const std::string& authorized_entity,
-                    const std::string& scope,
-                    base::TimeDelta time_to_live,
-                    const std::map<std::string, std::string>& options,
-                    std::set<Flags> flags,
-                    GetTokenCallback callback));
-  MOCK_METHOD4(ValidateToken,
-               void(const std::string& authorized_entity,
-                    const std::string& scope,
-                    const std::string& token,
-                    ValidateTokenCallback callback));
+  MOCK_METHOD(void, GetID, (GetIDCallback callback), (override));
+  MOCK_METHOD(void,
+              GetCreationTime,
+              (GetCreationTimeCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              GetToken,
+              (const std::string& authorized_entity,
+               const std::string& scope,
+               base::TimeDelta time_to_live,
+               (const std::map<std::string, std::string>& options),
+               std::set<Flags> flags,
+               GetTokenCallback callback),
+              (override));
+  MOCK_METHOD(void,
+              ValidateToken,
+              (const std::string& authorized_entity,
+               const std::string& scope,
+               const std::string& token,
+               ValidateTokenCallback callback),
+              (override));
 
  protected:
-  MOCK_METHOD3(DeleteTokenImpl,
-               void(const std::string& authorized_entity,
-                    const std::string& scope,
-                    DeleteTokenCallback callback));
-  MOCK_METHOD1(DeleteIDImpl, void(DeleteIDCallback callback));
+  MOCK_METHOD(void,
+              DeleteTokenImpl,
+              (const std::string& authorized_entity,
+               const std::string& scope,
+               DeleteTokenCallback callback),
+              (override));
+  MOCK_METHOD(void, DeleteIDImpl, (DeleteIDCallback callback), (override));
 };
 
 class MockInstanceIDDriver : public instance_id::InstanceIDDriver {
  public:
   MockInstanceIDDriver() : InstanceIDDriver(/*gcm_driver=*/nullptr) {}
   ~MockInstanceIDDriver() override = default;
-
-  MOCK_METHOD1(GetInstanceID, InstanceID*(const std::string& app_id));
-  MOCK_METHOD1(RemoveInstanceID, void(const std::string& app_id));
-  MOCK_CONST_METHOD1(ExistsInstanceID, bool(const std::string& app_id));
+  MOCK_METHOD(InstanceID*,
+              GetInstanceID,
+              (const std::string& app_id),
+              (override));
+  MOCK_METHOD(void, RemoveInstanceID, (const std::string& app_id), (override));
+  MOCK_METHOD(bool,
+              ExistsInstanceID,
+              (const std::string& app_id),
+              (const override));
 };
 
 class MockListener : public InvalidationsListener {
  public:
-  MOCK_METHOD1(OnInvalidationReceived, void(const std::string& payload));
+  MOCK_METHOD(void,
+              OnInvalidationReceived,
+              (const std::string& payload),
+              (override));
 };
 
 class MockTokenObserver : public FCMRegistrationTokenObserver {
  public:
-  MOCK_METHOD0(OnFCMRegistrationTokenChanged, void());
+  MOCK_METHOD(void, OnFCMRegistrationTokenChanged, (), (override));
 };
 
 class FCMHandlerTest : public testing::Test {
diff --git a/components/sync/invalidations/interested_data_types_manager_unittest.cc b/components/sync/invalidations/interested_data_types_manager_unittest.cc
index c70797d..c1be756 100644
--- a/components/sync/invalidations/interested_data_types_manager_unittest.cc
+++ b/components/sync/invalidations/interested_data_types_manager_unittest.cc
@@ -16,7 +16,10 @@
 
 class MockDataTypesHandler : public InterestedDataTypesHandler {
  public:
-  MOCK_METHOD1(OnInterestedDataTypesChanged, void(base::OnceClosure callback));
+  MOCK_METHOD(void,
+              OnInterestedDataTypesChanged,
+              (base::OnceClosure callback),
+              (override));
 };
 
 class InterestedDataTypesManagerTest : public testing::Test {
diff --git a/components/sync/js/js_test_util.h b/components/sync/js/js_test_util.h
index 2a76cdd..56d532be 100644
--- a/components/sync/js/js_test_util.h
+++ b/components/sync/js/js_test_util.h
@@ -45,8 +45,10 @@
   ~MockJsBackend() override;
 
   WeakHandle<JsBackend> AsWeakHandle();
-
-  MOCK_METHOD1(SetJsEventHandler, void(const WeakHandle<JsEventHandler>&));
+  MOCK_METHOD(void,
+              SetJsEventHandler,
+              (const WeakHandle<JsEventHandler>&),
+              (override));
 };
 
 class MockJsController : public JsController,
@@ -54,9 +56,8 @@
  public:
   MockJsController();
   ~MockJsController() override;
-
-  MOCK_METHOD1(AddJsEventHandler, void(JsEventHandler*));
-  MOCK_METHOD1(RemoveJsEventHandler, void(JsEventHandler*));
+  MOCK_METHOD(void, AddJsEventHandler, (JsEventHandler*), (override));
+  MOCK_METHOD(void, RemoveJsEventHandler, (JsEventHandler*), (override));
 };
 
 class MockJsEventHandler : public JsEventHandler,
@@ -66,9 +67,10 @@
   ~MockJsEventHandler() override;
 
   WeakHandle<JsEventHandler> AsWeakHandle();
-
-  MOCK_METHOD2(HandleJsEvent,
-               void(const ::std::string&, const JsEventDetails&));
+  MOCK_METHOD(void,
+              HandleJsEvent,
+              (const ::std::string&, const JsEventDetails&),
+              (override));
 };
 
 }  // namespace syncer
diff --git a/components/sync/model/sync_error_factory_mock.h b/components/sync/model/sync_error_factory_mock.h
index 8a339c6..6ffdd91 100644
--- a/components/sync/model/sync_error_factory_mock.h
+++ b/components/sync/model/sync_error_factory_mock.h
@@ -16,10 +16,10 @@
  public:
   SyncErrorFactoryMock();
   ~SyncErrorFactoryMock() override;
-
-  MOCK_METHOD2(CreateAndUploadError,
-               SyncError(const base::Location& location,
-                         const std::string& message));
+  MOCK_METHOD(SyncError,
+              CreateAndUploadError,
+              (const base::Location& location, const std::string& message),
+              (override));
 };
 
 }  // namespace syncer
diff --git a/components/sync/model_impl/in_memory_metadata_change_list_unittest.cc b/components/sync/model_impl/in_memory_metadata_change_list_unittest.cc
index 1c9edfe9..532bd8e 100644
--- a/components/sync/model_impl/in_memory_metadata_change_list_unittest.cc
+++ b/components/sync/model_impl/in_memory_metadata_change_list_unittest.cc
@@ -18,13 +18,20 @@
 
 class MockMetadataChangeList : public MetadataChangeList {
  public:
-  MOCK_METHOD1(UpdateModelTypeState,
-               void(const sync_pb::ModelTypeState& model_type_state));
-  MOCK_METHOD0(ClearModelTypeState, void());
-  MOCK_METHOD2(UpdateMetadata,
-               void(const std::string& storage_key,
-                    const sync_pb::EntityMetadata& metadata));
-  MOCK_METHOD1(ClearMetadata, void(const std::string& storage_key));
+  MOCK_METHOD(void,
+              UpdateModelTypeState,
+              (const sync_pb::ModelTypeState& model_type_state),
+              (override));
+  MOCK_METHOD(void, ClearModelTypeState, (), (override));
+  MOCK_METHOD(void,
+              UpdateMetadata,
+              (const std::string& storage_key,
+               const sync_pb::EntityMetadata& metadata),
+              (override));
+  MOCK_METHOD(void,
+              ClearMetadata,
+              (const std::string& storage_key),
+              (override));
 };
 
 TEST(InMemoryMetadataChangeListTest, ShouldTransferNothingIfEmptyChangeList) {
diff --git a/components/sync/model_impl/syncable_service_based_bridge_unittest.cc b/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
index 1419df4..aa574ce6f 100644
--- a/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
+++ b/components/sync/model_impl/syncable_service_based_bridge_unittest.cc
@@ -65,18 +65,21 @@
 
 class MockSyncableService : public SyncableService {
  public:
-  MOCK_METHOD1(WaitUntilReadyToSync, void(base::OnceClosure done));
-  MOCK_METHOD4(MergeDataAndStartSyncing,
-               base::Optional<syncer::ModelError>(
-                   ModelType type,
-                   const SyncDataList& initial_sync_data,
-                   std::unique_ptr<SyncChangeProcessor> sync_processor,
-                   std::unique_ptr<SyncErrorFactory> sync_error_factory));
-  MOCK_METHOD1(StopSyncing, void(ModelType type));
-  MOCK_METHOD2(ProcessSyncChanges,
-               base::Optional<ModelError>(const base::Location& from_here,
-                                          const SyncChangeList& change_list));
-  MOCK_CONST_METHOD1(GetAllSyncData, SyncDataList(ModelType type));
+  MOCK_METHOD(void, WaitUntilReadyToSync, (base::OnceClosure done), (override));
+  MOCK_METHOD(base::Optional<syncer::ModelError>,
+              MergeDataAndStartSyncing,
+              (ModelType type,
+               const SyncDataList& initial_sync_data,
+               std::unique_ptr<SyncChangeProcessor> sync_processor,
+               std::unique_ptr<SyncErrorFactory> sync_error_factory),
+              (override));
+  MOCK_METHOD(void, StopSyncing, (ModelType type), (override));
+  MOCK_METHOD(base::Optional<ModelError>,
+              ProcessSyncChanges,
+              (const base::Location& from_here,
+               const SyncChangeList& change_list),
+              (override));
+  MOCK_METHOD(SyncDataList, GetAllSyncData, (ModelType type), (const override));
 };
 
 class SyncableServiceBasedBridgeTest : public ::testing::Test {
diff --git a/components/sync/nigori/nigori_model_type_processor_unittest.cc b/components/sync/nigori/nigori_model_type_processor_unittest.cc
index 6d5fb2e5..2a19e0dc 100644
--- a/components/sync/nigori/nigori_model_type_processor_unittest.cc
+++ b/components/sync/nigori/nigori_model_type_processor_unittest.cc
@@ -90,25 +90,24 @@
 class MockNigoriSyncBridge : public NigoriSyncBridge {
  public:
   MockNigoriSyncBridge() = default;
-  ~MockNigoriSyncBridge() = default;
-
-  MOCK_METHOD1(MergeSyncData,
-               base::Optional<ModelError>(base::Optional<EntityData> data));
-  MOCK_METHOD1(ApplySyncChanges,
-               base::Optional<ModelError>(base::Optional<EntityData> data));
-  MOCK_METHOD0(GetData, std::unique_ptr<EntityData>());
-  MOCK_METHOD2(ResolveConflict,
-               ConflictResolution(const EntityData& local_data,
-                                  const EntityData& remote_data));
-  MOCK_METHOD0(ApplyDisableSyncChanges, void());
+  ~MockNigoriSyncBridge() override = default;
+  MOCK_METHOD(base::Optional<ModelError>,
+              MergeSyncData,
+              (base::Optional<EntityData> data),
+              (override));
+  MOCK_METHOD(base::Optional<ModelError>,
+              ApplySyncChanges,
+              (base::Optional<EntityData> data),
+              (override));
+  MOCK_METHOD(std::unique_ptr<EntityData>, GetData, (), (override));
+  MOCK_METHOD(void, ApplyDisableSyncChanges, (), (override));
 };
 
 class MockCommitQueue : public CommitQueue {
  public:
   MockCommitQueue() = default;
-  ~MockCommitQueue() = default;
-
-  MOCK_METHOD0(NudgeForCommit, void());
+  ~MockCommitQueue() override = default;
+  MOCK_METHOD(void, NudgeForCommit, (), (override));
 };
 
 class NigoriModelTypeProcessorTest : public testing::Test {
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index 2065dd2..982a7ee 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -282,43 +282,57 @@
  public:
   MockNigoriLocalChangeProcessor() = default;
   ~MockNigoriLocalChangeProcessor() override = default;
-
-  MOCK_METHOD2(ModelReadyToSync, void(NigoriSyncBridge*, NigoriMetadataBatch));
-  MOCK_METHOD1(Put, void(std::unique_ptr<EntityData>));
-  MOCK_METHOD0(IsEntityUnsynced, bool());
-  MOCK_METHOD0(GetMetadata, NigoriMetadataBatch());
-  MOCK_METHOD1(ReportError, void(const ModelError&));
-  MOCK_METHOD0(GetControllerDelegate,
-               base::WeakPtr<ModelTypeControllerDelegate>());
-  MOCK_METHOD0(IsTrackingMetadata, bool());
+  MOCK_METHOD(void,
+              ModelReadyToSync,
+              (NigoriSyncBridge*, NigoriMetadataBatch),
+              (override));
+  MOCK_METHOD(void, Put, (std::unique_ptr<EntityData>), (override));
+  MOCK_METHOD(bool, IsEntityUnsynced, (), (override));
+  MOCK_METHOD(NigoriMetadataBatch, GetMetadata, (), (override));
+  MOCK_METHOD(void, ReportError, (const ModelError&), (override));
+  MOCK_METHOD(base::WeakPtr<ModelTypeControllerDelegate>,
+              GetControllerDelegate,
+              (),
+              (override));
+  MOCK_METHOD(bool, IsTrackingMetadata, (), (override));
 };
 
 class MockObserver : public SyncEncryptionHandler::Observer {
  public:
   MockObserver() = default;
   ~MockObserver() override = default;
-
-  MOCK_METHOD2(OnPassphraseRequired,
-               void(const KeyDerivationParams&, const sync_pb::EncryptedData&));
-  MOCK_METHOD0(OnPassphraseAccepted, void());
-  MOCK_METHOD0(OnTrustedVaultKeyRequired, void());
-  MOCK_METHOD0(OnTrustedVaultKeyAccepted, void());
-  MOCK_METHOD2(OnBootstrapTokenUpdated,
-               void(const std::string&, BootstrapTokenType type));
-  MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool));
-  MOCK_METHOD2(OnCryptographerStateChanged,
-               void(Cryptographer*, bool has_pending_keys));
-  MOCK_METHOD2(OnPassphraseTypeChanged, void(PassphraseType, base::Time));
+  MOCK_METHOD(void,
+              OnPassphraseRequired,
+              (const KeyDerivationParams&, const sync_pb::EncryptedData&),
+              (override));
+  MOCK_METHOD(void, OnPassphraseAccepted, (), (override));
+  MOCK_METHOD(void, OnTrustedVaultKeyRequired, (), (override));
+  MOCK_METHOD(void, OnTrustedVaultKeyAccepted, (), (override));
+  MOCK_METHOD(void,
+              OnBootstrapTokenUpdated,
+              (const std::string&, BootstrapTokenType type),
+              (override));
+  MOCK_METHOD(void, OnEncryptedTypesChanged, (ModelTypeSet, bool), (override));
+  MOCK_METHOD(void,
+              OnCryptographerStateChanged,
+              (Cryptographer*, bool has_pending_keys),
+              (override));
+  MOCK_METHOD(void,
+              OnPassphraseTypeChanged,
+              (PassphraseType, base::Time),
+              (override));
 };
 
 class MockNigoriStorage : public NigoriStorage {
  public:
   MockNigoriStorage() = default;
   ~MockNigoriStorage() override = default;
-
-  MOCK_METHOD1(StoreData, void(const sync_pb::NigoriLocalData&));
-  MOCK_METHOD0(RestoreData, base::Optional<sync_pb::NigoriLocalData>());
-  MOCK_METHOD0(ClearData, void());
+  MOCK_METHOD(void, StoreData, (const sync_pb::NigoriLocalData&), (override));
+  MOCK_METHOD(base::Optional<sync_pb::NigoriLocalData>,
+              RestoreData,
+              (),
+              (override));
+  MOCK_METHOD(void, ClearData, (), (override));
 };
 
 class NigoriSyncBridgeImplTest : public testing::Test {
diff --git a/components/sync/test/model/mock_model_type_change_processor.h b/components/sync/test/model/mock_model_type_change_processor.h
index 66fa6d5f..809ad30 100644
--- a/components/sync/test/model/mock_model_type_change_processor.h
+++ b/components/sync/test/model/mock_model_type_change_processor.h
@@ -18,36 +18,60 @@
  public:
   MockModelTypeChangeProcessor();
   ~MockModelTypeChangeProcessor() override;
-
-  MOCK_METHOD3(Put,
-               void(const std::string& storage_key,
-                    std::unique_ptr<EntityData> entity_data,
-                    MetadataChangeList* metadata_change_list));
-  MOCK_METHOD2(Delete,
-               void(const std::string& storage_key,
-                    MetadataChangeList* metadata_change_list));
-  MOCK_METHOD3(UpdateStorageKey,
-               void(const EntityData& entity_data,
-                    const std::string& storage_key,
-                    MetadataChangeList* metadata_change_list));
-  MOCK_METHOD1(UntrackEntityForStorageKey,
-               void(const std::string& storage_key));
-  MOCK_METHOD1(UntrackEntityForClientTagHash,
-               void(const ClientTagHash& client_tag_hash));
-  MOCK_METHOD1(IsEntityUnsynced, bool(const std::string& storage_key));
-  MOCK_CONST_METHOD1(GetEntityCreationTime,
-                     base::Time(const std::string& storage_key));
-  MOCK_CONST_METHOD1(GetEntityModificationTime,
-                     base::Time(const std::string& storage_key));
-  MOCK_METHOD1(OnModelStarting, void(ModelTypeSyncBridge* bridge));
-  MOCK_METHOD1(ModelReadyToSync, void(std::unique_ptr<MetadataBatch> batch));
-  MOCK_METHOD0(IsTrackingMetadata, bool());
-  MOCK_METHOD0(TrackedAccountId, std::string());
-  MOCK_METHOD0(TrackedCacheGuid, std::string());
-  MOCK_METHOD1(ReportError, void(const ModelError& error));
-  MOCK_CONST_METHOD0(GetError, base::Optional<ModelError>());
-  MOCK_METHOD0(GetControllerDelegate,
-               base::WeakPtr<ModelTypeControllerDelegate>());
+  MOCK_METHOD(void,
+              Put,
+              (const std::string& storage_key,
+               std::unique_ptr<EntityData> entity_data,
+               MetadataChangeList* metadata_change_list),
+              (override));
+  MOCK_METHOD(void,
+              Delete,
+              (const std::string& storage_key,
+               MetadataChangeList* metadata_change_list),
+              (override));
+  MOCK_METHOD(void,
+              UpdateStorageKey,
+              (const EntityData& entity_data,
+               const std::string& storage_key,
+               MetadataChangeList* metadata_change_list),
+              (override));
+  MOCK_METHOD(void,
+              UntrackEntityForStorageKey,
+              (const std::string& storage_key),
+              (override));
+  MOCK_METHOD(void,
+              UntrackEntityForClientTagHash,
+              (const ClientTagHash& client_tag_hash),
+              (override));
+  MOCK_METHOD(bool,
+              IsEntityUnsynced,
+              (const std::string& storage_key),
+              (override));
+  MOCK_METHOD(base::Time,
+              GetEntityCreationTime,
+              (const std::string& storage_key),
+              (const override));
+  MOCK_METHOD(base::Time,
+              GetEntityModificationTime,
+              (const std::string& storage_key),
+              (const override));
+  MOCK_METHOD(void,
+              OnModelStarting,
+              (ModelTypeSyncBridge * bridge),
+              (override));
+  MOCK_METHOD(void,
+              ModelReadyToSync,
+              (std::unique_ptr<MetadataBatch> batch),
+              (override));
+  MOCK_METHOD(bool, IsTrackingMetadata, (), (override));
+  MOCK_METHOD(std::string, TrackedAccountId, (), (override));
+  MOCK_METHOD(std::string, TrackedCacheGuid, (), (override));
+  MOCK_METHOD(void, ReportError, (const ModelError& error), (override));
+  MOCK_METHOD(base::Optional<ModelError>, GetError, (), (const override));
+  MOCK_METHOD(base::WeakPtr<ModelTypeControllerDelegate>,
+              GetControllerDelegate,
+              (),
+              (override));
 
   // Returns a processor that forwards all calls to
   // |this|. |*this| must outlive the returned processor.
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index 6d472df..e9c1a71 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -49,27 +49,29 @@
  public:
   MockDelegate() = default;
   ~MockDelegate() override = default;
-
-  MOCK_METHOD0(NotifyRecoverabilityDegradedChanged, void());
+  MOCK_METHOD(void, NotifyRecoverabilityDegradedChanged, (), (override));
 };
 
 class MockTrustedVaultConnection : public TrustedVaultConnection {
  public:
   MockTrustedVaultConnection() = default;
   ~MockTrustedVaultConnection() override = default;
-
-  MOCK_METHOD5(RegisterAuthenticationFactor,
-               void(const CoreAccountInfo&,
-                    const std::vector<uint8_t>&,
-                    int,
-                    const SecureBoxPublicKey&,
-                    RegisterAuthenticationFactorCallback));
-  MOCK_METHOD5(DownloadKeys,
-               void(const CoreAccountInfo&,
-                    const std::vector<uint8_t>&,
-                    int,
-                    std::unique_ptr<SecureBoxKeyPair>,
-                    DownloadKeysCallback));
+  MOCK_METHOD(void,
+              RegisterAuthenticationFactor,
+              (const CoreAccountInfo&,
+               const std::vector<uint8_t>&,
+               int,
+               const SecureBoxPublicKey&,
+               RegisterAuthenticationFactorCallback),
+              (override));
+  MOCK_METHOD(void,
+              DownloadKeys,
+              (const CoreAccountInfo&,
+               const std::vector<uint8_t>&,
+               int,
+               std::unique_ptr<SecureBoxKeyPair>,
+               DownloadKeysCallback),
+              (override));
 };
 
 class StandaloneTrustedVaultBackendTest : public testing::Test {
diff --git a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
index 9651f2e..0a590c3f 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -153,7 +153,7 @@
 
 class MockCommitQueue : public syncer::CommitQueue {
  public:
-  MOCK_METHOD0(NudgeForCommit, void());
+  MOCK_METHOD(void, NudgeForCommit, (), (override));
 };
 
 class ProxyCommitQueue : public syncer::CommitQueue {
diff --git a/components/sync_device_info/local_device_info_provider_impl_unittest.cc b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
index dbeceac..68f52076 100644
--- a/components/sync_device_info/local_device_info_provider_impl_unittest.cc
+++ b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
@@ -41,12 +41,14 @@
   MockDeviceInfoSyncClient() = default;
   ~MockDeviceInfoSyncClient() = default;
 
-  MOCK_CONST_METHOD0(GetSigninScopedDeviceId, std::string());
-  MOCK_CONST_METHOD0(GetSendTabToSelfReceivingEnabled, bool());
-  MOCK_CONST_METHOD0(GetLocalSharingInfo,
-                     base::Optional<DeviceInfo::SharingInfo>());
-  MOCK_CONST_METHOD0(GetFCMRegistrationToken, std::string());
-  MOCK_CONST_METHOD0(GetInterestedDataTypes, ModelTypeSet());
+  MOCK_METHOD(std::string, GetSigninScopedDeviceId, (), (const override));
+  MOCK_METHOD(bool, GetSendTabToSelfReceivingEnabled, (), (const override));
+  MOCK_METHOD(base::Optional<DeviceInfo::SharingInfo>,
+              GetLocalSharingInfo,
+              (),
+              (const override));
+  MOCK_METHOD(std::string, GetFCMRegistrationToken, (), (const override));
+  MOCK_METHOD(ModelTypeSet, GetInterestedDataTypes, (), (const override));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDeviceInfoSyncClient);
diff --git a/components/sync_sessions/local_session_event_handler_impl_unittest.cc b/components/sync_sessions/local_session_event_handler_impl_unittest.cc
index 1a89835..9e73ed0 100644
--- a/components/sync_sessions/local_session_event_handler_impl_unittest.cc
+++ b/components/sync_sessions/local_session_event_handler_impl_unittest.cc
@@ -58,23 +58,28 @@
 
 class MockWriteBatch : public LocalSessionEventHandlerImpl::WriteBatch {
  public:
-  MockWriteBatch() {}
-  ~MockWriteBatch() override {}
-
-  MOCK_METHOD1(Delete, void(int tab_node_id));
-  MOCK_METHOD1(Put, void(std::unique_ptr<sync_pb::SessionSpecifics> specifics));
-  MOCK_METHOD0(Commit, void());
+  MockWriteBatch() = default;
+  ~MockWriteBatch() override = default;
+  MOCK_METHOD(void, Delete, (int tab_node_id), (override));
+  MOCK_METHOD(void,
+              Put,
+              (std::unique_ptr<sync_pb::SessionSpecifics> specifics),
+              (override));
+  MOCK_METHOD(void, Commit, (), (override));
 };
 
 class MockDelegate : public LocalSessionEventHandlerImpl::Delegate {
  public:
-  ~MockDelegate() override {}
-
-  MOCK_METHOD0(CreateLocalSessionWriteBatch,
-               std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch>());
-  MOCK_METHOD1(IsTabNodeUnsynced, bool(int tab_node_id));
-  MOCK_METHOD2(TrackLocalNavigationId,
-               void(base::Time timestamp, int unique_id));
+  ~MockDelegate() override = default;
+  MOCK_METHOD(std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch>,
+              CreateLocalSessionWriteBatch,
+              (),
+              (override));
+  MOCK_METHOD(bool, IsTabNodeUnsynced, (int tab_node_id), (override));
+  MOCK_METHOD(void,
+              TrackLocalNavigationId,
+              (base::Time timestamp, int unique_id),
+              (override));
 };
 
 class LocalSessionEventHandlerImplTest : public testing::Test {
diff --git a/components/sync_sessions/mock_sync_sessions_client.h b/components/sync_sessions/mock_sync_sessions_client.h
index fa2bd545..52e4f6b 100644
--- a/components/sync_sessions/mock_sync_sessions_client.h
+++ b/components/sync_sessions/mock_sync_sessions_client.h
@@ -16,14 +16,21 @@
   // By default, ShouldSyncURL() always returns true.
   MockSyncSessionsClient();
   ~MockSyncSessionsClient() override;
-
-  MOCK_METHOD0(GetSessionSyncPrefs, SessionSyncPrefs*());
-  MOCK_METHOD0(GetStoreFactory, syncer::RepeatingModelTypeStoreFactory());
-  MOCK_METHOD0(ClearAllOnDemandFavicons, void());
-  MOCK_CONST_METHOD1(ShouldSyncURL, bool(const GURL& url));
-  MOCK_METHOD0(GetSyncedWindowDelegatesGetter, SyncedWindowDelegatesGetter*());
-  MOCK_METHOD0(GetLocalSessionEventRouter, LocalSessionEventRouter*());
-  MOCK_METHOD0(IsProxyTabsSyncRunning, bool());
+  MOCK_METHOD(SessionSyncPrefs*, GetSessionSyncPrefs, (), (override));
+  MOCK_METHOD(syncer::RepeatingModelTypeStoreFactory,
+              GetStoreFactory,
+              (),
+              (override));
+  MOCK_METHOD(void, ClearAllOnDemandFavicons, (), (override));
+  MOCK_METHOD(bool, ShouldSyncURL, (const GURL& url), (const override));
+  MOCK_METHOD(SyncedWindowDelegatesGetter*,
+              GetSyncedWindowDelegatesGetter,
+              (),
+              (override));
+  MOCK_METHOD(LocalSessionEventRouter*,
+              GetLocalSessionEventRouter,
+              (),
+              (override));
 };
 
 }  // namespace sync_sessions
diff --git a/components/sync_sessions/session_store_unittest.cc b/components/sync_sessions/session_store_unittest.cc
index ab8b00e..00aef86 100644
--- a/components/sync_sessions/session_store_unittest.cc
+++ b/components/sync_sessions/session_store_unittest.cc
@@ -57,10 +57,12 @@
 // b) conveniently exposes the last instantiated session store.
 class MockOpenCallback {
  public:
-  MOCK_METHOD3(Run,
-               void(const base::Optional<syncer::ModelError>& error,
-                    SessionStore* store,
-                    MetadataBatch* metadata_batch));
+  MOCK_METHOD(void,
+              Run,
+              (const base::Optional<syncer::ModelError>& error,
+               SessionStore* store,
+               MetadataBatch* metadata_batch),
+              ());
 
   SessionStore::OpenCallback Get() {
     return base::BindOnce(
diff --git a/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html
index 9e77f308..74b46a4 100644
--- a/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html
+++ b/components/test/data/autofill_assistant/html/autofill_assistant_target_website.html
@@ -137,7 +137,7 @@
   <body onload="moveTouchAreaTwoAndThree()">
     <!-- Touch areas can be accessed without needing scroll. -->
     <div>
-      <p id="touch_area_one" ontouchend="removeTouchArea('touch_area_one')">
+      <p id="touch_area_one" ontouchend="removeTouchArea('touch_area_one')" onmouseup="removeTouchArea('touch_area_one')">
         Touchable Area One</p>
       <br>
     </div>
@@ -175,7 +175,7 @@
       <br>
     </div>
     <div>
-      <p id="touch_area_six" ontouchend="removeTouchArea('touch_area_six')">
+      <p id="touch_area_six" ontouchend="removeTouchArea('touch_area_six')" onclick="removeTouchArea('touch_area_six')">
         Touchable Area Six</p>
       <br>
     </div>
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 5f08cdc..5daaddd6 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -382,6 +382,7 @@
   deps = [
     "//base",
     "//gpu/config",
+    "//third_party/libyuv",
   ]
 
   if (is_win) {
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index 28c8b9f..306d174 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -33,6 +33,7 @@
   "+third_party/dawn/src/include",
   "+third_party/khronos/GLES2/gl2.h",
   "+third_party/khronos/GLES2/gl2ext.h",
+  "+third_party/libyuv",
   "+third_party/skia",
   "+ui/accelerated_widget_mac",
   "+ui/display",
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 0ac653c..7a32e1c 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -42,6 +42,7 @@
 #include "gpu/ipc/common/gpu_surface_lookup.h"
 #include "gpu/vulkan/buildflags.h"
 #include "skia/buildflags.h"
+#include "third_party/libyuv/include/libyuv/planar_functions.h"
 #include "third_party/skia/include/core/SkDeferredDisplayList.h"
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -134,21 +135,15 @@
                       int u_out_stride,
                       uint8_t* v_out,
                       int v_out_stride) const override {
-    const auto CopyPlane = [](const uint8_t* src, int src_stride, int width,
-                              int height, uint8_t* out, int out_stride) {
-      for (int i = 0; i < height; ++i, src += src_stride, out += out_stride) {
-        memcpy(out, src, width);
-      }
-    };
     auto* data0 = static_cast<const uint8_t*>(result_->data(0));
     auto* data1 = static_cast<const uint8_t*>(result_->data(1));
     auto* data2 = static_cast<const uint8_t*>(result_->data(2));
-    CopyPlane(data0, result_->rowBytes(0), width(0), height(0), y_out,
-              y_out_stride);
-    CopyPlane(data1, result_->rowBytes(1), width(1), height(1), u_out,
-              u_out_stride);
-    CopyPlane(data2, result_->rowBytes(2), width(2), height(2), v_out,
-              v_out_stride);
+    libyuv::CopyPlane(data0, result_->rowBytes(0), y_out, y_out_stride,
+                      width(0), height(0));
+    libyuv::CopyPlane(data1, result_->rowBytes(1), u_out, u_out_stride,
+                      width(1), height(1));
+    libyuv::CopyPlane(data2, result_->rowBytes(2), v_out, v_out_stride,
+                      width(2), height(2));
     return true;
   }
 
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 4497eb0..35232960 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -5048,4 +5048,29 @@
   EXPECT_EQ("POST", root_frame_host()->last_http_method());
 }
 
+// Check Chrome won't attempt automatically loading the /favicon.ico if it would
+// be blocked by CSP.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
+                       DefaultFaviconVersusCSP) {
+  auto navigate = [&](std::string csp) {
+    EXPECT_TRUE(NavigateToURL(
+        shell(), embedded_test_server()->GetURL(
+                     "/set-header?Content-Security-Policy: " + csp)));
+    // DidStopLoading() and UpdateFaviconURL() are sent together from the same
+    // task. However we have waited only for DidStopLoading(). Make a round trip
+    // with the renderer to ensure UpdateFaviconURL() to be received.
+    EXPECT_TRUE(ExecJs(root_frame_host(), ""));
+  };
+
+  // Blocked by CSP.
+  navigate("img-src 'none'");
+  EXPECT_EQ(0u, web_contents()->GetFaviconURLs().size());
+
+  // Allowed by CSP.
+  navigate("img-src *");
+  EXPECT_EQ(1u, web_contents()->GetFaviconURLs().size());
+  EXPECT_EQ("/favicon.ico",
+            web_contents()->GetFaviconURLs()[0]->icon_url.path());
+}
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_disk_cache.cc b/content/browser/service_worker/service_worker_disk_cache.cc
index 7a3020e1..a0b27259 100644
--- a/content/browser/service_worker/service_worker_disk_cache.cc
+++ b/content/browser/service_worker/service_worker_disk_cache.cc
@@ -4,14 +4,407 @@
 
 #include "content/browser/service_worker/service_worker_disk_cache.h"
 
+#include <limits>
 #include <utility>
 
-#include "net/base/io_buffer.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/base/cache_type.h"
+#include "net/base/completion_repeating_callback.h"
+#include "net/base/net_errors.h"
 
 namespace content {
 
-ServiceWorkerDiskCache::ServiceWorkerDiskCache()
-    : AppCacheDiskCache(/*use_simple_cache=*/true) {}
+// A callback shim that provides storage for the 'backend_ptr' value
+// and will delete a resulting ptr if completion occurs after the
+// callback has been canceled.
+class ServiceWorkerDiskCache::CreateBackendCallbackShim
+    : public base::RefCounted<CreateBackendCallbackShim> {
+ public:
+  explicit CreateBackendCallbackShim(ServiceWorkerDiskCache* object)
+      : service_worker_disk_cache_(object) {}
+
+  void Cancel() { service_worker_disk_cache_ = nullptr; }
+
+  void Callback(int return_value) {
+    if (service_worker_disk_cache_)
+      service_worker_disk_cache_->OnCreateBackendComplete(return_value);
+  }
+
+  std::unique_ptr<disk_cache::Backend> backend_ptr_;  // Accessed directly.
+
+ private:
+  friend class base::RefCounted<CreateBackendCallbackShim>;
+
+  ~CreateBackendCallbackShim() = default;
+
+  ServiceWorkerDiskCache* service_worker_disk_cache_;  // Unowned pointer.
+};
+
+ServiceWorkerDiskCacheEntry::ServiceWorkerDiskCacheEntry(
+    disk_cache::Entry* disk_cache_entry,
+    ServiceWorkerDiskCache* cache)
+    : disk_cache_entry_(disk_cache_entry), cache_(cache) {
+  DCHECK(disk_cache_entry);
+  DCHECK(cache);
+  cache_->AddOpenEntry(this);
+}
+
+ServiceWorkerDiskCacheEntry::~ServiceWorkerDiskCacheEntry() {
+  if (cache_)
+    cache_->RemoveOpenEntry(this);
+}
+
+int ServiceWorkerDiskCacheEntry::Read(int index,
+                                      int64_t offset,
+                                      net::IOBuffer* buf,
+                                      int buf_len,
+                                      net::CompletionOnceCallback callback) {
+  if (offset < 0 || offset > std::numeric_limits<int32_t>::max())
+    return net::ERR_INVALID_ARGUMENT;
+  if (!disk_cache_entry_)
+    return net::ERR_ABORTED;
+  return disk_cache_entry_->ReadData(index, static_cast<int>(offset), buf,
+                                     buf_len, std::move(callback));
+}
+
+int ServiceWorkerDiskCacheEntry::Write(int index,
+                                       int64_t offset,
+                                       net::IOBuffer* buf,
+                                       int buf_len,
+                                       net::CompletionOnceCallback callback) {
+  if (offset < 0 || offset > std::numeric_limits<int32_t>::max())
+    return net::ERR_INVALID_ARGUMENT;
+  if (!disk_cache_entry_)
+    return net::ERR_ABORTED;
+  const bool kTruncate = true;
+  return disk_cache_entry_->WriteData(index, static_cast<int>(offset), buf,
+                                      buf_len, std::move(callback), kTruncate);
+}
+
+int64_t ServiceWorkerDiskCacheEntry::GetSize(int index) {
+  return disk_cache_entry_ ? disk_cache_entry_->GetDataSize(index) : 0L;
+}
+
+void ServiceWorkerDiskCacheEntry::Close() {
+  if (disk_cache_entry_)
+    disk_cache_entry_->Close();
+  delete this;
+}
+
+void ServiceWorkerDiskCacheEntry::Abandon() {
+  cache_ = nullptr;
+  disk_cache_entry_->Close();
+  disk_cache_entry_ = nullptr;
+}
+
+namespace {
+
+// Separate object to hold state for each Create, Delete, or Doom call
+// while the call is in-flight and to produce an EntryImpl upon completion.
+class ActiveCall : public base::RefCounted<ActiveCall> {
+ public:
+  ActiveCall(const base::WeakPtr<ServiceWorkerDiskCache>& owner,
+             ServiceWorkerDiskCacheEntry** entry,
+             net::CompletionOnceCallback callback)
+      : owner_(owner), entry_(entry), callback_(std::move(callback)) {
+    DCHECK(owner_);
+  }
+
+  static net::Error CreateEntry(
+      const base::WeakPtr<ServiceWorkerDiskCache>& owner,
+      int64_t key,
+      ServiceWorkerDiskCacheEntry** entry,
+      net::CompletionOnceCallback callback) {
+    scoped_refptr<ActiveCall> active_call =
+        base::MakeRefCounted<ActiveCall>(owner, entry, std::move(callback));
+    disk_cache::EntryResult result = owner->disk_cache()->CreateEntry(
+        base::NumberToString(key), net::HIGHEST,
+        base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
+    return active_call->HandleImmediateReturnValue(std::move(result));
+  }
+
+  static net::Error OpenEntry(
+      const base::WeakPtr<ServiceWorkerDiskCache>& owner,
+      int64_t key,
+      ServiceWorkerDiskCacheEntry** entry,
+      net::CompletionOnceCallback callback) {
+    scoped_refptr<ActiveCall> active_call =
+        base::MakeRefCounted<ActiveCall>(owner, entry, std::move(callback));
+    disk_cache::EntryResult result = owner->disk_cache()->OpenEntry(
+        base::NumberToString(key), net::HIGHEST,
+        base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
+    return active_call->HandleImmediateReturnValue(std::move(result));
+  }
+
+  static net::Error DoomEntry(
+      const base::WeakPtr<ServiceWorkerDiskCache>& owner,
+      int64_t key,
+      net::CompletionOnceCallback callback) {
+    return owner->disk_cache()->DoomEntry(base::NumberToString(key),
+                                          net::HIGHEST, std::move(callback));
+  }
+
+ private:
+  friend class base::RefCounted<ActiveCall>;
+
+  ~ActiveCall() = default;
+
+  net::Error HandleImmediateReturnValue(disk_cache::EntryResult result) {
+    net::Error rv = result.net_error();
+    if (rv == net::ERR_IO_PENDING) {
+      // OnAsyncCompletion will be called later.
+      return rv;
+    }
+
+    if (rv == net::OK) {
+      *entry_ =
+          new ServiceWorkerDiskCacheEntry(result.ReleaseEntry(), owner_.get());
+    }
+
+    return rv;
+  }
+
+  void OnAsyncCompletion(disk_cache::EntryResult result) {
+    int rv = result.net_error();
+    if (rv == net::OK) {
+      if (owner_) {
+        *entry_ = new ServiceWorkerDiskCacheEntry(result.ReleaseEntry(),
+                                                  owner_.get());
+      } else {
+        result.ReleaseEntry()->Close();
+        rv = net::ERR_ABORTED;
+      }
+    }
+    std::move(callback_).Run(rv);
+  }
+
+  base::WeakPtr<ServiceWorkerDiskCache> owner_;
+  ServiceWorkerDiskCacheEntry** entry_;
+  net::CompletionOnceCallback callback_;
+};
+
+}  // namespace
+
+ServiceWorkerDiskCache::ServiceWorkerDiskCache() = default;
+
+ServiceWorkerDiskCache::~ServiceWorkerDiskCache() {
+  Disable();
+}
+
+net::Error ServiceWorkerDiskCache::InitWithDiskBackend(
+    const base::FilePath& disk_cache_directory,
+    bool force,
+    base::OnceClosure post_cleanup_callback,
+    net::CompletionOnceCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return Init(net::APP_CACHE, disk_cache_directory,
+              std::numeric_limits<int64_t>::max(), force,
+              std::move(post_cleanup_callback), std::move(callback));
+}
+
+net::Error ServiceWorkerDiskCache::InitWithMemBackend(
+    int64_t mem_cache_size,
+    net::CompletionOnceCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return Init(net::MEMORY_CACHE, base::FilePath(), mem_cache_size, false,
+              base::OnceClosure(), std::move(callback));
+}
+
+void ServiceWorkerDiskCache::Disable() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (is_disabled_)
+    return;
+
+  is_disabled_ = true;
+
+  if (create_backend_callback_.get()) {
+    create_backend_callback_->Cancel();
+    create_backend_callback_ = nullptr;
+    OnCreateBackendComplete(net::ERR_ABORTED);
+  }
+
+  // We need to close open file handles in order to reinitialize the
+  // service worker system on the fly. File handles held in both entries and in
+  // the main disk_cache::Backend class need to be released.
+  for (ServiceWorkerDiskCacheEntry* entry : open_entries_) {
+    entry->Abandon();
+  }
+  open_entries_.clear();
+  disk_cache_.reset();
+}
+
+net::Error ServiceWorkerDiskCache::CreateEntry(
+    int64_t key,
+    ServiceWorkerDiskCacheEntry** entry,
+    net::CompletionOnceCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(entry);
+  DCHECK(!callback.is_null());
+  if (is_disabled_)
+    return net::ERR_ABORTED;
+
+  if (is_initializing_or_waiting_to_initialize()) {
+    pending_calls_.emplace_back(PendingCallType::kCreate, key, entry,
+                                std::move(callback));
+    return net::ERR_IO_PENDING;
+  }
+
+  if (!disk_cache_)
+    return net::ERR_FAILED;
+
+  return ActiveCall::CreateEntry(weak_factory_.GetWeakPtr(), key, entry,
+                                 std::move(callback));
+}
+
+net::Error ServiceWorkerDiskCache::OpenEntry(
+    int64_t key,
+    ServiceWorkerDiskCacheEntry** entry,
+    net::CompletionOnceCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(entry);
+  DCHECK(!callback.is_null());
+  if (is_disabled_)
+    return net::ERR_ABORTED;
+
+  if (is_initializing_or_waiting_to_initialize()) {
+    pending_calls_.emplace_back(PendingCallType::kOpen, key, entry,
+                                std::move(callback));
+    return net::ERR_IO_PENDING;
+  }
+
+  if (!disk_cache_)
+    return net::ERR_FAILED;
+
+  return ActiveCall::OpenEntry(weak_factory_.GetWeakPtr(), key, entry,
+                               std::move(callback));
+}
+
+net::Error ServiceWorkerDiskCache::DoomEntry(
+    int64_t key,
+    net::CompletionOnceCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!callback.is_null());
+  if (is_disabled_)
+    return net::ERR_ABORTED;
+
+  if (is_initializing_or_waiting_to_initialize()) {
+    pending_calls_.emplace_back(PendingCallType::kDoom, key, nullptr,
+                                std::move(callback));
+    return net::ERR_IO_PENDING;
+  }
+
+  if (!disk_cache_)
+    return net::ERR_FAILED;
+
+  return ActiveCall::DoomEntry(weak_factory_.GetWeakPtr(), key,
+                               std::move(callback));
+}
+
+base::WeakPtr<ServiceWorkerDiskCache> ServiceWorkerDiskCache::GetWeakPtr() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return weak_factory_.GetWeakPtr();
+}
+
+ServiceWorkerDiskCache::PendingCall::PendingCall(
+    PendingCallType call_type,
+    int64_t key,
+    ServiceWorkerDiskCacheEntry** entry,
+    net::CompletionOnceCallback callback)
+    : call_type(call_type),
+      key(key),
+      entry(entry),
+      callback(std::move(callback)) {}
+
+ServiceWorkerDiskCache::PendingCall::PendingCall(PendingCall&& other) = default;
+
+ServiceWorkerDiskCache::PendingCall::~PendingCall() = default;
+
+net::Error ServiceWorkerDiskCache::Init(net::CacheType cache_type,
+                                        const base::FilePath& cache_directory,
+                                        int64_t cache_size,
+                                        bool force,
+                                        base::OnceClosure post_cleanup_callback,
+                                        net::CompletionOnceCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!is_initializing_or_waiting_to_initialize() && !disk_cache_.get());
+  is_disabled_ = false;
+  create_backend_callback_ =
+      base::MakeRefCounted<CreateBackendCallbackShim>(this);
+  disk_cache::ResetHandling reset_handling =
+      force ? disk_cache::ResetHandling::kResetOnError
+            : disk_cache::ResetHandling::kNeverReset;
+
+  net::Error return_value = disk_cache::CreateCacheBackend(
+      cache_type, net::CACHE_BACKEND_SIMPLE, cache_directory, cache_size,
+      reset_handling, nullptr, &(create_backend_callback_->backend_ptr_),
+      std::move(post_cleanup_callback),
+      base::BindOnce(&CreateBackendCallbackShim::Callback,
+                     create_backend_callback_));
+  if (return_value == net::ERR_IO_PENDING)
+    init_callback_ = std::move(callback);
+  else
+    OnCreateBackendComplete(return_value);
+  return return_value;
+}
+
+void ServiceWorkerDiskCache::OnCreateBackendComplete(int return_value) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (return_value == net::OK) {
+    disk_cache_ = std::move(create_backend_callback_->backend_ptr_);
+  }
+  create_backend_callback_ = nullptr;
+
+  // Invoke our clients callback function.
+  if (!init_callback_.is_null()) {
+    std::move(init_callback_).Run(return_value);
+  }
+
+  // Service pending calls that were queued up while we were initializing.
+  for (auto& call : pending_calls_) {
+    // This is safe, because the callback will only be called once.
+    net::CompletionRepeatingCallback copyable_callback =
+        base::AdaptCallbackForRepeating(std::move(call.callback));
+    return_value = net::ERR_FAILED;
+    switch (call.call_type) {
+      case PendingCallType::kCreate:
+        return_value = CreateEntry(call.key, call.entry, copyable_callback);
+        break;
+      case PendingCallType::kOpen:
+        return_value = OpenEntry(call.key, call.entry, copyable_callback);
+        break;
+      case PendingCallType::kDoom:
+        return_value = DoomEntry(call.key, copyable_callback);
+        break;
+    }
+    // disk_cache::{Create,Open,Doom}Entry() call their callbacks iff they
+    // return net::ERR_IO_PENDING. In this case, the callback was not called.
+    // However, the corresponding ServiceWorkerDiskCache wrapper returned
+    // net::ERR_IO_PENDING as it queued up the pending call. To follow the
+    // disk_cache API contract, we need to call the callback ourselves here.
+    if (return_value != net::ERR_IO_PENDING)
+      copyable_callback.Run(return_value);
+  }
+  pending_calls_.clear();
+}
+
+void ServiceWorkerDiskCache::AddOpenEntry(ServiceWorkerDiskCacheEntry* entry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  open_entries_.insert(entry);
+}
+
+void ServiceWorkerDiskCache::RemoveOpenEntry(
+    ServiceWorkerDiskCacheEntry* entry) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  open_entries_.erase(entry);
+}
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_disk_cache.h b/content/browser/service_worker/service_worker_disk_cache.h
index bed72d7..1d2ef6a 100644
--- a/content/browser/service_worker/service_worker_disk_cache.h
+++ b/content/browser/service_worker/service_worker_disk_cache.h
@@ -5,20 +5,161 @@
 #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
 #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DISK_CACHE_H_
 
-#include "content/browser/appcache/appcache_disk_cache.h"
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "base/sequence_checker.h"
 #include "content/common/content_export.h"
+#include "net/base/completion_once_callback.h"
+#include "net/disk_cache/disk_cache.h"
 
 namespace content {
 
-// Wholesale reusage of the appcache code for response reading,
-// writing, and storage. See the corresponding class in that
-// library for doc comments and other details.
-// TODO(michaeln): If this reuse sticks, refactor/move the
-// resused classes to a more common location.
+// TODO(crbug.com/586174): Use disk_cache::EntryResult for better lifetime
+// management of disk cache entries. Using EntryResult will eliminate allocating
+// raw pointers and static methods in service worker resource readers/writers.
 
-class CONTENT_EXPORT ServiceWorkerDiskCache : public AppCacheDiskCache {
+class ServiceWorkerDiskCache;
+
+// Thin wrapper around disk_cache::Entry.
+class CONTENT_EXPORT ServiceWorkerDiskCacheEntry {
+ public:
+  // The newly created entry takes ownership of `disk_cache_entry` and closes it
+  // on destruction. |cache| must outlive the newly created entry.
+  ServiceWorkerDiskCacheEntry(disk_cache::Entry* disk_cache_entry,
+                              ServiceWorkerDiskCache* cache);
+
+  // See `disk_cache::Entry::ReadData()`.
+  int Read(int index,
+           int64_t offset,
+           net::IOBuffer* buf,
+           int buf_len,
+           net::CompletionOnceCallback callback);
+
+  // See `disk_cache::Entry::WriteData()`.
+  int Write(int index,
+            int64_t offset,
+            net::IOBuffer* buf,
+            int buf_len,
+            net::CompletionOnceCallback callback);
+  int64_t GetSize(int index);
+  // Closes the disk cache and destroyes the instance.
+  void Close();
+
+  // Should only be called by ServiceWorkerDiskCache.
+  void Abandon();
+
+ private:
+  // Call Close() instead of calling this directly.
+  ~ServiceWorkerDiskCacheEntry();
+
+  // The disk_cache::Entry is owned by this entry and closed on destruction.
+  disk_cache::Entry* disk_cache_entry_;
+
+  // The cache that this entry belongs to.
+  ServiceWorkerDiskCache* cache_;
+};
+
+// net::DiskCache wrapper for the cache used by service worker resources.
+//
+// Provides ways to create/open/doom service worker disk cache entries.
+class CONTENT_EXPORT ServiceWorkerDiskCache {
  public:
   ServiceWorkerDiskCache();
+  ~ServiceWorkerDiskCache();
+
+  // Initializes the object to use disk backed storage.
+  net::Error InitWithDiskBackend(const base::FilePath& disk_cache_directory,
+                                 bool force,
+                                 base::OnceClosure post_cleanup_callback,
+                                 net::CompletionOnceCallback callback);
+
+  // Initializes the object to use memory only storage.
+  // This is used for Chrome's incognito browsing.
+  net::Error InitWithMemBackend(int64_t disk_cache_size,
+                                net::CompletionOnceCallback callback);
+
+  void Disable();
+  bool is_disabled() const { return is_disabled_; }
+
+  net::Error CreateEntry(int64_t key,
+                         ServiceWorkerDiskCacheEntry** entry,
+                         net::CompletionOnceCallback callback);
+  net::Error OpenEntry(int64_t key,
+                       ServiceWorkerDiskCacheEntry** entry,
+                       net::CompletionOnceCallback callback);
+  net::Error DoomEntry(int64_t key, net::CompletionOnceCallback callback);
+
+  base::WeakPtr<ServiceWorkerDiskCache> GetWeakPtr();
+
+  void set_is_waiting_to_initialize(bool is_waiting_to_initialize) {
+    is_waiting_to_initialize_ = is_waiting_to_initialize;
+  }
+
+  disk_cache::Backend* disk_cache() { return disk_cache_.get(); }
+
+ private:
+  class CreateBackendCallbackShim;
+  friend class ServiceWorkerDiskCacheEntry;
+
+  // PendingCalls allow CreateEntry, OpenEntry, and DoomEntry to be called
+  // immediately after construction, without waiting for the
+  // underlying disk_cache::Backend to be fully constructed. Early
+  // calls are queued up and serviced once the disk_cache::Backend is
+  // really ready to go.
+  enum class PendingCallType { kCreate, kOpen, kDoom };
+  struct PendingCall {
+    PendingCall(PendingCallType call_type,
+                int64_t key,
+                ServiceWorkerDiskCacheEntry** entry,
+                net::CompletionOnceCallback callback);
+    PendingCall(PendingCall&& other);
+    PendingCall(const PendingCall&) = delete;
+    PendingCall& operator=(const PendingCall&) = delete;
+    PendingCall& operator=(PendingCall&&) = delete;
+
+    ~PendingCall();
+
+    const PendingCallType call_type;
+    const int64_t key;
+    ServiceWorkerDiskCacheEntry** const entry;
+    net::CompletionOnceCallback callback;
+  };
+
+  bool is_initializing_or_waiting_to_initialize() const {
+    return create_backend_callback_.get() != nullptr ||
+           is_waiting_to_initialize_;
+  }
+
+  net::Error Init(net::CacheType cache_type,
+                  const base::FilePath& directory,
+                  int64_t cache_size,
+                  bool force,
+                  base::OnceClosure post_cleanup_callback,
+                  net::CompletionOnceCallback callback);
+  void OnCreateBackendComplete(int return_value);
+
+  // Called by ServiceWorkerDiskCacheEntry constructor.
+  void AddOpenEntry(ServiceWorkerDiskCacheEntry* entry);
+  // Called by ServiceWorkerDiskCacheEntry destructor.
+  void RemoveOpenEntry(ServiceWorkerDiskCacheEntry* entry);
+
+  bool is_disabled_ = false;
+  bool is_waiting_to_initialize_ = false;
+  net::CompletionOnceCallback init_callback_;
+  scoped_refptr<CreateBackendCallbackShim> create_backend_callback_;
+  std::vector<PendingCall> pending_calls_;
+  std::set<ServiceWorkerDiskCacheEntry*> open_entries_;
+  std::unique_ptr<disk_cache::Backend> disk_cache_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<ServiceWorkerDiskCache> weak_factory_{this};
 };
 
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_resource_ops.cc b/content/browser/service_worker/service_worker_resource_ops.cc
index fec55e39..cf0dd3a2 100644
--- a/content/browser/service_worker/service_worker_resource_ops.cc
+++ b/content/browser/service_worker/service_worker_resource_ops.cc
@@ -124,8 +124,9 @@
   return std::move(buffer_);
 }
 
-DiskEntryCreator::DiskEntryCreator(int64_t resource_id,
-                                   base::WeakPtr<AppCacheDiskCache> disk_cache)
+DiskEntryCreator::DiskEntryCreator(
+    int64_t resource_id,
+    base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
     : resource_id_(resource_id), disk_cache_(std::move(disk_cache)) {
   DCHECK_NE(resource_id_, blink::mojom::kInvalidServiceWorkerResourceId);
   DCHECK(disk_cache_);
@@ -154,7 +155,7 @@
     return;
   }
 
-  AppCacheDiskCacheEntry** entry_ptr = new AppCacheDiskCacheEntry*;
+  ServiceWorkerDiskCacheEntry** entry_ptr = new ServiceWorkerDiskCacheEntry*;
   creation_phase_ = CreationPhase::kInitialAttempt;
   int rv = disk_cache_->CreateEntry(
       resource_id_, entry_ptr,
@@ -168,7 +169,7 @@
 // static
 void DiskEntryCreator::DidCreateEntryForFirstAttempt(
     base::WeakPtr<DiskEntryCreator> entry_creator,
-    AppCacheDiskCacheEntry** entry,
+    ServiceWorkerDiskCacheEntry** entry,
     int rv) {
   if (!entry_creator) {
     delete entry;
@@ -223,7 +224,7 @@
   }
 
   entry_creator->creation_phase_ = CreationPhase::kSecondAttempt;
-  auto** entry_ptr = new AppCacheDiskCacheEntry*;
+  auto** entry_ptr = new ServiceWorkerDiskCacheEntry*;
   rv = entry_creator->disk_cache_->CreateEntry(
       entry_creator->resource_id_, entry_ptr,
       base::BindOnce(&DiskEntryCreator::DidCreateEntryForSecondAttempt,
@@ -236,7 +237,7 @@
 // static
 void DiskEntryCreator::DidCreateEntryForSecondAttempt(
     base::WeakPtr<DiskEntryCreator> entry_creator,
-    AppCacheDiskCacheEntry** entry,
+    ServiceWorkerDiskCacheEntry** entry,
     int rv) {
   if (!entry_creator) {
     delete entry;
@@ -272,8 +273,9 @@
   std::move(ensure_entry_is_created_callback_).Run();
 }
 
-DiskEntryOpener::DiskEntryOpener(int64_t resource_id,
-                                 base::WeakPtr<AppCacheDiskCache> disk_cache)
+DiskEntryOpener::DiskEntryOpener(
+    int64_t resource_id,
+    base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
     : resource_id_(resource_id), disk_cache_(std::move(disk_cache)) {
   DCHECK_NE(resource_id_, blink::mojom::kInvalidServiceWorkerResourceId);
   DCHECK(disk_cache_);
@@ -290,13 +292,13 @@
   ensure_entry_is_opened_callback_ = std::move(callback);
 
   int rv;
-  AppCacheDiskCacheEntry** entry_ptr = nullptr;
+  ServiceWorkerDiskCacheEntry** entry_ptr = nullptr;
   if (entry_) {
     rv = net::OK;
   } else if (!disk_cache_) {
     rv = net::ERR_FAILED;
   } else {
-    entry_ptr = new AppCacheDiskCacheEntry*;
+    entry_ptr = new ServiceWorkerDiskCacheEntry*;
     rv = disk_cache_->OpenEntry(
         resource_id_, entry_ptr,
         base::BindOnce(&DiskEntryOpener::DidOpenEntry,
@@ -310,7 +312,7 @@
 
 // static
 void DiskEntryOpener::DidOpenEntry(base::WeakPtr<DiskEntryOpener> entry_opener,
-                                   AppCacheDiskCacheEntry** entry,
+                                   ServiceWorkerDiskCacheEntry** entry,
                                    int rv) {
   if (!entry_opener) {
     delete entry;
@@ -500,7 +502,7 @@
 
 ServiceWorkerResourceReaderImpl::ServiceWorkerResourceReaderImpl(
     int64_t resource_id,
-    base::WeakPtr<AppCacheDiskCache> disk_cache)
+    base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
     : entry_opener_(resource_id, std::move(disk_cache)) {}
 
 ServiceWorkerResourceReaderImpl::~ServiceWorkerResourceReaderImpl() = default;
@@ -691,7 +693,7 @@
 
 ServiceWorkerResourceWriterImpl::ServiceWorkerResourceWriterImpl(
     int64_t resource_id,
-    base::WeakPtr<AppCacheDiskCache> disk_cache)
+    base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
     : entry_creator_(resource_id, std::move(disk_cache)) {}
 
 ServiceWorkerResourceWriterImpl::~ServiceWorkerResourceWriterImpl() = default;
@@ -807,7 +809,7 @@
 ServiceWorkerResourceMetadataWriterImpl::
     ServiceWorkerResourceMetadataWriterImpl(
         int64_t resource_id,
-        base::WeakPtr<AppCacheDiskCache> disk_cache)
+        base::WeakPtr<ServiceWorkerDiskCache> disk_cache)
     : entry_opener_(resource_id, std::move(disk_cache)) {}
 
 ServiceWorkerResourceMetadataWriterImpl::
diff --git a/content/browser/service_worker/service_worker_resource_ops.h b/content/browser/service_worker/service_worker_resource_ops.h
index 8edad23..87a6f993 100644
--- a/content/browser/service_worker/service_worker_resource_ops.h
+++ b/content/browser/service_worker/service_worker_resource_ops.h
@@ -17,14 +17,14 @@
 class DiskEntryCreator {
  public:
   DiskEntryCreator(int64_t resource_id,
-                   base::WeakPtr<AppCacheDiskCache> disk_cache);
+                   base::WeakPtr<ServiceWorkerDiskCache> disk_cache);
   ~DiskEntryCreator();
 
   DiskEntryCreator(const DiskEntryCreator&) = delete;
   DiskEntryCreator& operator=(const DiskEntryCreator&) = delete;
 
   // Can be nullptr when a disk cache error occurs.
-  AppCacheDiskCacheEntry* entry() {
+  ServiceWorkerDiskCacheEntry* entry() {
     DCHECK_EQ(creation_phase_, CreationPhase::kDone);
     return entry_;
   }
@@ -53,26 +53,26 @@
   };
 
   // Callbacks of EnsureEntryIsCreated(). These are static to manage the
-  // ownership of AppCacheDiskCacheEntry correctly.
+  // ownership of ServiceWorkerDiskCacheEntry correctly.
   // TODO(crbug.com/586174): Refactor service worker's disk cache to use
   // disk_cache::EntryResult to make these callbacks non-static.
   static void DidCreateEntryForFirstAttempt(
       base::WeakPtr<DiskEntryCreator> entry_creator,
-      AppCacheDiskCacheEntry** entry,
+      ServiceWorkerDiskCacheEntry** entry,
       int rv);
   static void DidDoomExistingEntry(
       base::WeakPtr<DiskEntryCreator> entry_creator,
       int rv);
   static void DidCreateEntryForSecondAttempt(
       base::WeakPtr<DiskEntryCreator> entry_creator,
-      AppCacheDiskCacheEntry** entry,
+      ServiceWorkerDiskCacheEntry** entry,
       int rv);
 
   void RunEnsureEntryIsCreatedCallback();
 
   const int64_t resource_id_;
-  base::WeakPtr<AppCacheDiskCache> disk_cache_;
-  AppCacheDiskCacheEntry* entry_ = nullptr;
+  base::WeakPtr<ServiceWorkerDiskCache> disk_cache_;
+  ServiceWorkerDiskCacheEntry* entry_ = nullptr;
 
   CreationPhase creation_phase_ = CreationPhase::kNoAttempt;
 
@@ -86,14 +86,14 @@
 class DiskEntryOpener {
  public:
   DiskEntryOpener(int64_t resource_id,
-                  base::WeakPtr<AppCacheDiskCache> disk_cache);
+                  base::WeakPtr<ServiceWorkerDiskCache> disk_cache);
   ~DiskEntryOpener();
 
   DiskEntryOpener(const DiskEntryOpener&) = delete;
   DiskEntryOpener& operator=(const DiskEntryOpener&) = delete;
 
   // Can be nullptr when a disk cache error occurs.
-  AppCacheDiskCacheEntry* entry() { return entry_; }
+  ServiceWorkerDiskCacheEntry* entry() { return entry_; }
 
   // Calls the callback when entry() is opened and can be used.
   //
@@ -106,12 +106,12 @@
   // TODO(crbug.com/586174): Refactor service worker's disk cache to use
   // disk_cache::EntryResult to make this callback non-static.
   static void DidOpenEntry(base::WeakPtr<DiskEntryOpener> entry_creator,
-                           AppCacheDiskCacheEntry** entry,
+                           ServiceWorkerDiskCacheEntry** entry,
                            int rv);
 
   const int64_t resource_id_;
-  base::WeakPtr<AppCacheDiskCache> disk_cache_;
-  AppCacheDiskCacheEntry* entry_ = nullptr;
+  base::WeakPtr<ServiceWorkerDiskCache> disk_cache_;
+  ServiceWorkerDiskCacheEntry* entry_ = nullptr;
 
   // Stored as a data member to handle //net-style maybe-async methods.
   base::OnceClosure ensure_entry_is_opened_callback_;
@@ -123,8 +123,9 @@
 class ServiceWorkerResourceReaderImpl
     : public storage::mojom::ServiceWorkerResourceReader {
  public:
-  ServiceWorkerResourceReaderImpl(int64_t resource_id,
-                                  base::WeakPtr<AppCacheDiskCache> disk_cache);
+  ServiceWorkerResourceReaderImpl(
+      int64_t resource_id,
+      base::WeakPtr<ServiceWorkerDiskCache> disk_cache);
 
   ServiceWorkerResourceReaderImpl(const ServiceWorkerResourceReaderImpl&) =
       delete;
@@ -189,8 +190,9 @@
 class ServiceWorkerResourceWriterImpl
     : public storage::mojom::ServiceWorkerResourceWriter {
  public:
-  ServiceWorkerResourceWriterImpl(int64_t resource_id,
-                                  base::WeakPtr<AppCacheDiskCache> disk_cache);
+  ServiceWorkerResourceWriterImpl(
+      int64_t resource_id,
+      base::WeakPtr<ServiceWorkerDiskCache> disk_cache);
 
   ServiceWorkerResourceWriterImpl(const ServiceWorkerResourceWriterImpl&) =
       delete;
@@ -249,7 +251,7 @@
  public:
   ServiceWorkerResourceMetadataWriterImpl(
       int64_t resource_id,
-      base::WeakPtr<AppCacheDiskCache> disk_cache);
+      base::WeakPtr<ServiceWorkerDiskCache> disk_cache);
 
   ServiceWorkerResourceMetadataWriterImpl(
       const ServiceWorkerResourceMetadataWriterImpl&) = delete;
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index d7ade410..f8f5b9b0 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -9,7 +9,7 @@
 // declarations instead of including more headers. If that is infeasible, adjust
 // the limit. For more info, see
 // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
-#pragma clang max_tokens_here 890000
+#pragma clang max_tokens_here 910000
 
 #include <utility>
 
diff --git a/docs/clang_tidy.md b/docs/clang_tidy.md
index 834051989..2cf7aab 100644
--- a/docs/clang_tidy.md
+++ b/docs/clang_tidy.md
@@ -75,7 +75,7 @@
 
 ```
 $ cd ${chromium}/src
-$ ${chromium_build}/scripts/slave/recipe_modules/tricium_clang_tidy/resources/tricium_clang_tidy.py \
+$ ${chromium_build}/recipes/recipe_modules/tricium_clang_tidy/resources/tricium_clang_tidy.py \
     --base_path $PWD \
     --out_dir out/Linux \
     --findings_file all_findings.json \
@@ -212,7 +212,7 @@
 ```
 4.  Run clang-tidy.
 ```
-<PATH_TO_LLVM_SRC>/clang-tidy/tool/run-clang-tidy.py \
+<PATH_TO_LLVM_SRC>/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py \
     -p . \# Set the root project directory, where compile_commands.json is.
     # Set the clang-tidy binary path, if it's not in your $PATH.
     -clang-tidy-binary <PATH_TO_LLVM_BUILD>/bin/clang-tidy \
@@ -220,11 +220,11 @@
     # and you are using the `fix` behavior of clang-tidy.
     -clang-apply-replacements-binary \
         <PATH_TO_LLVM_BUILD>/bin/clang-apply-replacements \
-    # The checks to employ in the build. Use `-*` to omit default checks.
+    # The checks to employ in the build. Use `-*,...` to omit default checks.
     -checks=<CHECKS> \
     -header-filter=<FILTER> \# Optional, limit results to only certain files.
     -fix \# Optional, used if you want to have clang-tidy auto-fix errors.
-    chrome/browser # The path to the files you want to check.
+    'chrome/browser/.*' # A regex of the files you want to check.
 
 Copy-Paste Friendly (though you'll still need to stub in the variables):
 <PATH_TO_LLVM_SRC>/clang-tools-extra/clang-tidy/tool/run-clang-tidy.py \
@@ -235,12 +235,12 @@
     -checks=<CHECKS> \
     -header-filter=<FILTER> \
     -fix \
-    chrome/browser
+    'chrome/browser/.*'
 ```
 
-\*It's not clear which, if any, `gn` flags may cause issues for
-`clang-tidy`. I've had no problems building a component release build,
-both with and without goma. if you run into issues, let us know!
+Note that the source file regex must match how the build specified the file.
+This means that on Windows, you must use (escaped) backslashes even from a bash
+shell.
 
 ### Questions
 
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index bf9d51b..9156e93 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -214,6 +214,7 @@
     "//ios/chrome/browser/history",
     "//ios/chrome/browser/main",
     "//ios/chrome/browser/memory",
+    "//ios/chrome/browser/metrics:metrics",
     "//ios/chrome/browser/metrics:metrics_internal",
     "//ios/chrome/browser/net",
     "//ios/chrome/browser/ntp:features",
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 5287606..e93ca9b 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -69,6 +69,7 @@
 #import "ios/chrome/browser/main/browser_list_factory.h"
 #import "ios/chrome/browser/memory/memory_debugger_manager.h"
 #include "ios/chrome/browser/metrics/first_user_action_recorder.h"
+#import "ios/chrome/browser/metrics/window_configuration_recorder.h"
 #import "ios/chrome/browser/net/cookie_util.h"
 #import "ios/chrome/browser/omaha/omaha_service.h"
 #include "ios/chrome/browser/pref_names.h"
@@ -257,6 +258,8 @@
   // Variable backing metricsMediator property.
   __weak MetricsMediator* _metricsMediator;
 
+  WindowConfigurationRecorder* _windowConfigurationRecorder;
+
   // Hander for the startup tasks, deferred or not.
   StartupTasks* _startupTasks;
 }
@@ -527,6 +530,8 @@
         self.appState.mainBrowserState);
   }
 
+  _windowConfigurationRecorder = [[WindowConfigurationRecorder alloc] init];
+
   return needRestoration;
 }
 
diff --git a/ios/chrome/browser/metrics/BUILD.gn b/ios/chrome/browser/metrics/BUILD.gn
index 56658c3..5f10b7d 100644
--- a/ios/chrome/browser/metrics/BUILD.gn
+++ b/ios/chrome/browser/metrics/BUILD.gn
@@ -41,6 +41,8 @@
     "mobile_session_shutdown_metrics_provider.mm",
     "pageload_foreground_duration_tab_helper.h",
     "pageload_foreground_duration_tab_helper.mm",
+    "window_configuration_recorder.h",
+    "window_configuration_recorder.mm",
   ]
   public_deps = [ "//components/ukm/ios:ukm_url_recorder" ]
   deps = [
@@ -79,6 +81,7 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/translate",
     "//ios/chrome/browser/ui/overscroll_actions",
+    "//ios/chrome/browser/ui/util:multiwindow_util",
     "//ios/chrome/browser/ui/whats_new:utils",
     "//ios/chrome/browser/variations",
     "//ios/chrome/browser/variations:ios_chrome_ui_string_overrider_factory",
diff --git a/ios/chrome/browser/metrics/window_configuration_recorder.h b/ios/chrome/browser/metrics/window_configuration_recorder.h
new file mode 100644
index 0000000..e541856
--- /dev/null
+++ b/ios/chrome/browser/metrics/window_configuration_recorder.h
@@ -0,0 +1,64 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_METRICS_WINDOW_CONFIGURATION_RECORDER_H_
+#define IOS_CHROME_BROWSER_METRICS_WINDOW_CONFIGURATION_RECORDER_H_
+
+#import <UIKit/UIKit.h>
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class WindowConfiguration {
+  kUnspecified = 0,
+  // Chrome is showing one window, fullscreen.
+  kFullscreen,
+  // Chrome is showing two windows, one fullscreen and one slide-over.
+  kFullscreenWithSlideover,
+  // Chrome is showing one standard-width window, and another app has
+  // a window alongside it.
+  kSharedStandard,
+  // As above, and Chrome also has a slide-over window.
+  kSharedStandardWithSlideover,
+  // Chrome is showing one compact-width window, and another app has
+  // a window alongside it.
+  kSharedCompact,
+  // As above, and Chrome also has a slide-over window.
+  kSharedCompactWithSlideover,
+  // Chrome has only one foreground window, and it's in slide-over mode.
+  kSlideoverOnly,
+  // Chrome has two windows, both standard-width
+  kStandardBesideStandard,
+  // As above, and Chrome also has a third window in  slide-over mode.
+  kStandardBesideStandardWithSlideover,
+  // Chrome has two windows, one standard-width and one compact-width.
+  kStandardBesideCompact,
+  // As above, and Chrome also has a third window in  slide-over mode.
+  kStandardBesideCompactWithSlideover,
+  // Chrome has two windows, both compact-width
+  kCompactBesideCompact,
+  // As above, and Chrome also has a third window in  slide-over mode.
+  kCompactBesideCompactWithSlideover,
+  // NOTE: add new configurations above this line.
+  kMaxValue = kCompactBesideCompactWithSlideover
+};
+
+// Reports time spent for each MultiWindow configuration.
+// It looks at the window configuration every minute, and increments the
+// histogram bucket for that configuration. Suspends when app is
+// backgrounded and restarts when foregrounded.
+@interface WindowConfigurationRecorder : NSObject
+
+// State of recording.
+@property(nonatomic) BOOL recording;
+
+@end
+
+@interface WindowConfigurationRecorder (VisibleForTesting)
+
+// Computes configuration from given screen and windows.
+- (WindowConfiguration)configurationForScreen:(UIScreen*)screen
+                                      windows:(NSArray<UIWindow*>*)windows;
+@end
+
+#endif  // IOS_CHROME_BROWSER_METRICS_WINDOW_CONFIGURATION_RECORDER_H_
diff --git a/ios/chrome/browser/metrics/window_configuration_recorder.mm b/ios/chrome/browser/metrics/window_configuration_recorder.mm
new file mode 100644
index 0000000..efc62b2
--- /dev/null
+++ b/ios/chrome/browser/metrics/window_configuration_recorder.mm
@@ -0,0 +1,221 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/metrics/window_configuration_recorder.h"
+
+#include "base/check.h"
+#include "base/mac/foundation_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/timer/timer.h"
+#import "ios/chrome/browser/ui/util/multi_window_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface WindowConfigurationRecorder ()
+
+// Called by the recording_timer_ to record current config.
+- (void)recordConfiguration;
+
+@end
+
+namespace {
+
+// Delay between a recording of a new configuration.
+static constexpr base::TimeDelta kRecordDelay =
+    base::TimeDelta::FromSeconds(20);
+
+// Timer callback for recording configuration after a delay.
+void RecordWindowGeometryMetrics(WindowConfigurationRecorder* recorder) {
+  [recorder recordConfiguration];
+}
+
+// Returns all Foreground active windows that are Chrome windows.
+NSArray<UIWindow*>* ForegroundWindowsForApplication(
+    UIApplication* application) {
+  NSMutableArray<UIWindow*>* windows = [NSMutableArray arrayWithCapacity:3];
+
+  if (IsSceneStartupSupported()) {
+    if (@available(iOS 13, *)) {
+      for (UIScene* scene in application.connectedScenes) {
+        if (scene.activationState != UISceneActivationStateForegroundActive)
+          continue;
+
+        UIWindowScene* windowScene = base::mac::ObjCCast<UIWindowScene>(scene);
+        for (UIWindow* window in windowScene.windows) {
+          // Skip other windows (like keyboard) that keep showing up.
+          if (![window isKindOfClass:NSClassFromString(@"ChromeOverlayWindow")])
+            continue;
+
+          [windows addObject:window];
+          break;  // Stop after one window per scene. This may be wrong.
+        }
+      }
+    }
+  }
+  return [windows copy];
+}
+}  // namespace
+
+@implementation WindowConfigurationRecorder {
+  // Repeating delay timer.
+  base::RepeatingTimer recording_timer_;
+}
+
+- (instancetype)init {
+  if (self == [super init]) {
+    // When the app becomes active, set recording on.
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(applicationDidBecomeActive)
+               name:UIApplicationDidBecomeActiveNotification
+             object:nil];
+
+    // When the app resigns active, turn recording off.
+    [[NSNotificationCenter defaultCenter]
+        addObserver:self
+           selector:@selector(applicationDidEnterBackground)
+               name:UIApplicationDidEnterBackgroundNotification
+             object:nil];
+
+    [self scheduleRecordConfiguration];
+  }
+  return self;
+}
+
+// Called when notified of UIApplicationDidBecomeActiveNotification.
+- (void)applicationDidBecomeActive {
+  self.recording = YES;
+  [self scheduleRecordConfiguration];
+}
+
+// Called when notified of UIApplicationDidEnterBackgroundNotification
+- (void)applicationDidEnterBackground {
+  self.recording = NO;
+  recording_timer_.Stop();
+}
+
+// Called to set or reset the timer.
+- (void)scheduleRecordConfiguration {
+  if (recording_timer_.IsRunning()) {
+    recording_timer_.Reset();
+  } else {
+    recording_timer_.Start(
+        FROM_HERE, kRecordDelay,
+        base::BindRepeating(&RecordWindowGeometryMetrics, self));
+  }
+}
+
+// Called when the timer actually fires.
+- (void)recordConfiguration {
+  [self recordGeometryForScreen:[UIScreen mainScreen]
+                        windows:ForegroundWindowsForApplication(
+                                    UIApplication.sharedApplication)];
+}
+
+// Computes configuration for given screen and windows and records it.
+- (void)recordGeometryForScreen:(UIScreen*)screen
+                        windows:(NSArray<UIWindow*>*)windows {
+  WindowConfiguration configuration = [self configurationForScreen:screen
+                                                           windows:windows];
+  base::UmaHistogramEnumeration("IOS.MultiWindow.Configuration", configuration);
+}
+
+#pragma mark - Visible For Testing
+
+- (WindowConfiguration)configurationForScreen:(UIScreen*)screen
+                                      windows:(NSArray<UIWindow*>*)windows {
+  NSMutableArray<UIWindow*>* fullscreenWindows = [[NSMutableArray alloc] init];
+  NSMutableArray<UIWindow*>* slideoverWindows = [[NSMutableArray alloc] init];
+  NSMutableArray<UIWindow*>* sharedWindows = [[NSMutableArray alloc] init];
+
+  CGRect screenRect = screen.bounds;
+  for (UIWindow* window in windows) {
+    CGRect windowRect = window.frame;
+
+    // Is the window full screen?
+    if (CGRectEqualToRect(screenRect, windowRect)) {
+      [fullscreenWindows addObject:window];
+      continue;
+    }
+    // Is the window in slideover? Slideover windows are always both shorter
+    // and narrower than the screen.
+    if (screenRect.size.width > windowRect.size.width &&
+        screenRect.size.height > windowRect.size.height) {
+      [slideoverWindows addObject:window];
+      continue;
+    }
+
+    // Otherwise, the window is shared. This shouldn't happen if there's
+    // a fullscreen window.
+    [sharedWindows addObject:window];
+  }
+
+  WindowConfiguration configuration = WindowConfiguration::kUnspecified;
+
+  if (sharedWindows.count == 0) {
+    if (fullscreenWindows.count > 0) {
+      configuration = slideoverWindows.count > 0
+                          ? WindowConfiguration::kFullscreenWithSlideover
+                          : WindowConfiguration::kFullscreen;
+    } else if (slideoverWindows.count > 0) {
+      configuration = WindowConfiguration::kSlideoverOnly;
+    } else {
+      // Configuration remains unspecificed -- were there no windows?
+    }
+  } else if (sharedWindows.count == 1) {
+    // Single Shared window cases.
+    UIUserInterfaceSizeClass sharedWindowSize =
+        sharedWindows[0].traitCollection.horizontalSizeClass;
+    if (sharedWindowSize == UIUserInterfaceSizeClassRegular) {
+      configuration = slideoverWindows.count > 0
+                          ? WindowConfiguration::kSharedStandardWithSlideover
+                          : WindowConfiguration::kSharedStandard;
+    } else if (sharedWindowSize == UIUserInterfaceSizeClassCompact) {
+      configuration = slideoverWindows.count > 0
+                          ? WindowConfiguration::kSharedCompactWithSlideover
+                          : WindowConfiguration::kSharedCompact;
+    } else {
+      // Configuration remains unspecified -- shared window has an unspecified
+      // size class.
+    }
+  } else if (sharedWindows.count == 2) {
+    UIUserInterfaceSizeClass firstWindowSize =
+        sharedWindows[0].traitCollection.horizontalSizeClass;
+    UIUserInterfaceSizeClass secondWindowSize =
+        sharedWindows[1].traitCollection.horizontalSizeClass;
+
+    if (firstWindowSize == UIUserInterfaceSizeClassRegular &&
+        secondWindowSize == UIUserInterfaceSizeClassRegular) {
+      configuration =
+          slideoverWindows.count > 0
+              ? WindowConfiguration::kStandardBesideStandardWithSlideover
+              : WindowConfiguration::kStandardBesideStandard;
+    } else if (firstWindowSize == UIUserInterfaceSizeClassCompact &&
+               secondWindowSize == UIUserInterfaceSizeClassCompact) {
+      configuration =
+          slideoverWindows.count > 0
+              ? WindowConfiguration::kCompactBesideCompactWithSlideover
+              : WindowConfiguration::kCompactBesideCompact;
+    } else if (firstWindowSize != UIUserInterfaceSizeClassUnspecified &&
+               secondWindowSize != UIUserInterfaceSizeClassUnspecified) {
+      // Since the sizes are neither both standard, nor both compact, nor is
+      // either of them unspecified, one must be standard and the other compact.
+      configuration =
+          slideoverWindows.count > 0
+              ? WindowConfiguration::kStandardBesideCompactWithSlideover
+              : WindowConfiguration::kStandardBesideCompact;
+    } else {
+      // Configuration remains unspecified -- one of the two shared windows has
+      // an unspecified size class.
+    }
+  } else {
+    // Configuration remains unspecified -- more than two shared windows.
+  }
+
+  return configuration;
+}
+
+@end
diff --git a/ios/chrome/browser/sync/ios_chrome_sync_client.h b/ios/chrome/browser/sync/ios_chrome_sync_client.h
index 3cbac92..6364596 100644
--- a/ios/chrome/browser/sync/ios_chrome_sync_client.h
+++ b/ios/chrome/browser/sync/ios_chrome_sync_client.h
@@ -62,8 +62,6 @@
   ChromeBrowserState* const browser_state_;
 
   // The sync api component factory in use by this client.
-  // TODO(crbug.com/915154): Revert to SyncApiComponentFactory once common
-  // controller creation is moved elsewhere.
   std::unique_ptr<browser_sync::ProfileSyncComponentsFactoryImpl>
       component_factory_;
 
diff --git a/ios/chrome/browser/sync/sync_setup_service_mock.h b/ios/chrome/browser/sync/sync_setup_service_mock.h
index a703e5f..928497a 100644
--- a/ios/chrome/browser/sync/sync_setup_service_mock.h
+++ b/ios/chrome/browser/sync/sync_setup_service_mock.h
@@ -14,23 +14,17 @@
  public:
   SyncSetupServiceMock(syncer::SyncService* sync_service);
   ~SyncSetupServiceMock();
-
-  MOCK_CONST_METHOD0(IsSyncEnabled, bool());
-
-  MOCK_CONST_METHOD0(IsSyncingAllDataTypes, bool());
-
-  MOCK_METHOD0(GetSyncServiceState, SyncServiceState());
-
-  MOCK_CONST_METHOD1(IsDataTypePreferred, bool(syncer::ModelType));
-
-  MOCK_CONST_METHOD1(IsDataTypeActive, bool(syncer::ModelType));
-
-  MOCK_METHOD0(HasFinishedInitialSetup, bool());
-
-  MOCK_METHOD0(PrepareForFirstSyncSetup, void());
-
-  MOCK_METHOD1(SetFirstSetupComplete,
-               void(syncer::SyncFirstSetupCompleteSource));
+  MOCK_METHOD(bool, IsSyncEnabled, (), (const override));
+  MOCK_METHOD(bool, IsSyncingAllDataTypes, (), (const override));
+  MOCK_METHOD(SyncServiceState, GetSyncServiceState, (), (override));
+  MOCK_METHOD(bool, IsDataTypePreferred, (syncer::ModelType), (const override));
+  MOCK_METHOD(bool, IsDataTypeActive, (syncer::ModelType), (const override));
+  MOCK_METHOD(bool, HasFinishedInitialSetup, (), (override));
+  MOCK_METHOD(void, PrepareForFirstSyncSetup, (), (override));
+  MOCK_METHOD(void,
+              SetFirstSetupComplete,
+              (syncer::SyncFirstSetupCompleteSource),
+              (override));
 
   // Allow the real SyncSetupService::HasFinishedInitialSetup() to be used when
   // mocking HasFinishedInitialSetup().
diff --git a/ios/web_view/internal/sync/web_view_sync_client.h b/ios/web_view/internal/sync/web_view_sync_client.h
index 81515f2..7aeac6a 100644
--- a/ios/web_view/internal/sync/web_view_sync_client.h
+++ b/ios/web_view/internal/sync/web_view_sync_client.h
@@ -74,8 +74,6 @@
   invalidation::InvalidationService* invalidation_service_;
   syncer::SyncInvalidationsService* sync_invalidations_service_;
 
-  // TODO(crbug.com/915154): Revert to SyncApiComponentFactory once common
-  // controller creation is moved elsewhere.
   std::unique_ptr<browser_sync::ProfileSyncComponentsFactoryImpl>
       component_factory_;
 
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 79c09d6..67d0ecbd 100644
--- a/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
+++ b/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -58,10 +58,8 @@
     : public media::VideoCaptureDevice::Client::Buffer::ScopedAccessPermission {
  public:
   CMSampleBufferScopedAccessPermission(CMSampleBufferRef buffer)
-      : buffer_(buffer, base::scoped_policy::RETAIN) {
-    buffer_.reset();
-  }
-  ~CMSampleBufferScopedAccessPermission() override {}
+      : buffer_(buffer, base::scoped_policy::RETAIN) {}
+  ~CMSampleBufferScopedAccessPermission() override { buffer_.reset(); }
 
  private:
   base::ScopedCFTypeRef<CMSampleBufferRef> buffer_;
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6d7a9b6..42a454f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -558,7 +558,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_20200917",
+                    "name": "Enabled_20201015",
                     "enable_features": [
                         "AssistPersonalInfo",
                         "AssistPersonalInfoAddress",
diff --git a/third_party/blink/public/common/privacy_budget/identifiable_surface.h b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
index c9b52d6..1d0ba2ca 100644
--- a/third_party/blink/public/common/privacy_budget/identifiable_surface.h
+++ b/third_party/blink/public/common/privacy_budget/identifiable_surface.h
@@ -229,8 +229,8 @@
   };
 
   enum class ScrollbarSurface : uint64_t {
-    kBodyOffsetWidth = 0,
-    kBodyOffsetHeight = 1,
+    kScrollingElementWidth = 0,
+    kScrollingElementHeight = 1,
     kElemScrollbarWidth = 2,
     kElemScrollbarHeight = 3,
   };
diff --git a/third_party/blink/public/public_features.gni b/third_party/blink/public/public_features.gni
index 45096cb2..d7a4699 100644
--- a/third_party/blink/public/public_features.gni
+++ b/third_party/blink/public/public_features.gni
@@ -27,4 +27,4 @@
 enable_unhandled_tap = is_android
 
 # Use Minikin hyphenation engine.
-use_minikin_hyphenation = is_android
+use_minikin_hyphenation = !is_mac
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 34b5c49..cda3024 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -2943,6 +2943,13 @@
 // CSSStyleDeclaration is abusing named properties.
 // Do not intercept.  Fallback to OrdinaryDefineOwnProperty.
 """))
+    elif cg_context.interface.identifier in ("HTMLEmbedElement",
+                                             "HTMLObjectElement"):
+        body.append(
+            TextNode("""\
+// HTMLEmbedElement and HTMLObjectElement are abusing named properties.
+// Do not intercept.  Fallback to OrdinaryDefineOwnProperty.
+"""))
     elif not cg_context.interface.indexed_and_named_properties.named_setter:
         body.append(
             TextNode("""\
@@ -2986,17 +2993,7 @@
   }
   return;
 }
-"""))
-        if cg_context.interface.identifier in ("HTMLEmbedElement",
-                                               "HTMLObjectElement"):
-            body.append(
-                TextNode("""\
-// HTMLEmbedElement and HTMLObjectElement's named properties implementation
-// depend on the default fallback behavior.  So, just fallback.
-"""))
-        else:
-            body.append(
-                TextNode("""\
+
 // step 2.2.2.2. Invoke the named property setter with P and Desc.[[Value]].
 ${class_name}::NamedPropertySetterCallback(
     ${v8_property_name}, ${v8_property_desc}.value(), ${info});
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 6ed50f59..3039c40 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -33,7 +33,7 @@
 // instead of including more headers. If that is infeasible, adjust the limit.
 // For more info, see
 // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/wmax_tokens.md
-#pragma clang max_tokens_here 950000
+#pragma clang max_tokens_here 960000
 
 #include <memory>
 #include <utility>
@@ -391,6 +391,26 @@
     second->NotifyPriorityScrollAnchorStatusChanged();
 }
 
+// Before fetching the default URL, make sure it won't be blocked by CSP. The
+// webpage didn't requested "/favicon.ico", it is automatic. Developers
+// shouldn't suffer from any errors provoked by Chrome.
+// See https://crbug.com/820846
+bool DefaultFaviconAllowedByCSP(const Document* document, const IconURL& icon) {
+  ExecutionContext* context = document->GetExecutionContext();
+  if (!context) {
+    // LocalFrame::UpdateFaviconURL() is sometimes called after a LocalFrame
+    // swap. When this happens, the document has lost its ExecutionContext and
+    // the favicon won't be loaded anyway. The output of this function doesn't
+    // matter anymore.
+    return false;
+  }
+
+  return context->GetContentSecurityPolicy()->AllowImageFromSource(
+      icon.icon_url_, icon.icon_url_, RedirectStatus::kNoRedirect,
+      ReportingDisposition::kSuppressReporting,
+      ContentSecurityPolicy::CheckHeaderType::kCheckAll);
+}
+
 }  // namespace
 
 class DocumentOutliveTimeReporter : public BlinkGCObserver {
@@ -7081,7 +7101,9 @@
   } else if (url_.ProtocolIsInHTTPFamily() &&
              icon_types_mask & 1 << static_cast<int>(
                                    mojom::blink::FaviconIconType::kFavicon)) {
-    icon_urls.push_back(IconURL::DefaultFavicon(url_));
+    IconURL default_favicon = IconURL::DefaultFavicon(url_);
+    if (DefaultFaviconAllowedByCSP(this, default_favicon))
+      icon_urls.push_back(std::move(default_favicon));
   }
 
   if (first_touch_icon.icon_type_ != mojom::blink::FaviconIconType::kInvalid)
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 06c24ca..ee8341a 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -33,6 +33,8 @@
 #include <utility>
 
 #include "cc/input/snap_selection_strategy.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
+#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -1273,6 +1275,70 @@
   return 0;
 }
 
+void Element::RecordScrollbarSizeForStudy(int measurement,
+                                          bool is_width,
+                                          bool is_offset) {
+  if (!IdentifiabilityStudySettings::Get()->IsTypeAllowed(
+          IdentifiableSurface::Type::kScrollbarSize))
+    return;
+
+  // Check for presence of a scrollbar.
+  PaintLayerScrollableArea* area;
+  if (this == GetDocument().ScrollingElementNoLayout()) {
+    auto* view = GetDocument().View();
+    if (!view)
+      return;
+    area = view->LayoutViewport();
+  } else {
+    auto* layout = GetLayoutBox();
+    if (!layout)
+      return;
+    area = layout->GetScrollableArea();
+  }
+  if (!area || area->HasOverlayOverflowControls())
+    return;
+
+  Scrollbar* scrollbar =
+      is_width ? area->VerticalScrollbar() : area->HorizontalScrollbar();
+  // We intentionally exclude platform overlay scrollbars since their size
+  // cannot be detected in JavaScript using the methods below.
+  if (!scrollbar)
+    return;
+
+  IdentifiableSurface::ScrollbarSurface surface;
+  int scrollbar_size;
+
+  // There are two common ways to detect the size of a scrollbar in a DOM
+  // window. They are:
+  // 1. Compute the difference of the window.inner[Width|Height] and the
+  //    corresponding document.scrollingElement.offset[Width|Height].
+  // 2. Any HTML element that insets the layout to fit a scrollbar, so it is
+  //    measurable by a JavaScript program on a site.
+  if (this == GetDocument().scrollingElement()) {
+    LocalDOMWindow* dom_window = GetDocument().domWindow();
+    scrollbar_size =
+        (is_width ? dom_window->innerWidth() : dom_window->innerHeight()) -
+        measurement;
+    surface =
+        is_width
+            ? IdentifiableSurface::ScrollbarSurface::kScrollingElementWidth
+            : IdentifiableSurface::ScrollbarSurface::kScrollingElementHeight;
+  } else if (is_offset) {
+    scrollbar_size = measurement - (is_width ? clientWidth() : clientHeight());
+    surface = is_width
+                  ? IdentifiableSurface::ScrollbarSurface::kElemScrollbarWidth
+                  : IdentifiableSurface::ScrollbarSurface::kElemScrollbarHeight;
+  } else {
+    return;
+  }
+
+  blink::IdentifiabilityMetricBuilder(GetDocument().UkmSourceID())
+      .Set(blink::IdentifiableSurface::FromTypeAndToken(
+               blink::IdentifiableSurface::Type::kScrollbarSize, surface),
+           scrollbar_size)
+      .Record(GetDocument().UkmRecorder());
+}
+
 int Element::clientWidth() {
   // When in strict mode, clientWidth for the document element should return the
   // width of the containing frame.
@@ -1293,29 +1359,40 @@
         // OverflowClipRect() may return infinite along a particular axis if
         // |layout_view| is not a scroll-container.
         DCHECK(layout_view->IsScrollContainer());
-        return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-                   layout_view->OverflowClipRect(PhysicalOffset()).Width(),
-                   layout_view->StyleRef())
-            .Round();
+        int result =
+            AdjustForAbsoluteZoom::AdjustLayoutUnit(
+                layout_view->OverflowClipRect(PhysicalOffset()).Width(),
+                layout_view->StyleRef())
+                .Round();
+        RecordScrollbarSizeForStudy(result, /* is_width= */ true,
+                                    /* is_offset= */ false);
+        return result;
       }
-      return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-                 LayoutUnit(layout_view->GetLayoutSize().Width()),
-                 layout_view->StyleRef())
-          .Round();
+      int result = AdjustForAbsoluteZoom::AdjustLayoutUnit(
+                       LayoutUnit(layout_view->GetLayoutSize().Width()),
+                       layout_view->StyleRef())
+                       .Round();
+      RecordScrollbarSizeForStudy(result, /* is_width= */ true,
+                                  /* is_offset= */ false);
+      return result;
     }
   }
 
   GetDocument().UpdateStyleAndLayoutForNode(this,
                                             DocumentUpdateReason::kJavaScript);
 
-  if (LayoutBox* layout_object = GetLayoutBox())
-    return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-               LayoutUnit(
-                   layout_object
-                       ->PixelSnappedClientWidthWithTableSpecialBehavior()),
-               layout_object->StyleRef())
-        .Round();
-  return 0;
+  int result = 0;
+  if (LayoutBox* layout_object = GetLayoutBox()) {
+    result =
+        AdjustForAbsoluteZoom::AdjustLayoutUnit(
+            LayoutUnit(layout_object
+                           ->PixelSnappedClientWidthWithTableSpecialBehavior()),
+            layout_object->StyleRef())
+            .Round();
+    RecordScrollbarSizeForStudy(result, /* is_width= */ true,
+                                /* is_offset= */ false);
+  }
+  return result;
 }
 
 int Element::clientHeight() {
@@ -1339,29 +1416,40 @@
         // OverflowClipRect() may return infinite along a particular axis if
         // |layout_view| is not a scroll-container.
         DCHECK(layout_view->IsScrollContainer());
-        return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-                   layout_view->OverflowClipRect(PhysicalOffset()).Height(),
-                   layout_view->StyleRef())
-            .Round();
+        int result =
+            AdjustForAbsoluteZoom::AdjustLayoutUnit(
+                layout_view->OverflowClipRect(PhysicalOffset()).Height(),
+                layout_view->StyleRef())
+                .Round();
+        RecordScrollbarSizeForStudy(result, /* is_width= */ false,
+                                    /* is_offset= */ false);
+        return result;
       }
-      return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-                 LayoutUnit(layout_view->GetLayoutSize().Height()),
-                 layout_view->StyleRef())
-          .Round();
+      int result = AdjustForAbsoluteZoom::AdjustLayoutUnit(
+                       LayoutUnit(layout_view->GetLayoutSize().Height()),
+                       layout_view->StyleRef())
+                       .Round();
+      RecordScrollbarSizeForStudy(result, /* is_width= */ false,
+                                  /* is_offset= */ false);
+      return result;
     }
   }
 
   GetDocument().UpdateStyleAndLayoutForNode(this,
                                             DocumentUpdateReason::kJavaScript);
 
-  if (LayoutBox* layout_object = GetLayoutBox())
-    return AdjustForAbsoluteZoom::AdjustLayoutUnit(
-               LayoutUnit(
-                   layout_object
-                       ->PixelSnappedClientHeightWithTableSpecialBehavior()),
-               layout_object->StyleRef())
-        .Round();
-  return 0;
+  int result = 0;
+  if (LayoutBox* layout_object = GetLayoutBox()) {
+    result = AdjustForAbsoluteZoom::AdjustLayoutUnit(
+                 LayoutUnit(
+                     layout_object
+                         ->PixelSnappedClientHeightWithTableSpecialBehavior()),
+                 layout_object->StyleRef())
+                 .Round();
+    RecordScrollbarSizeForStudy(result, /* is_width= */ false,
+                                /* is_offset= */ false);
+  }
+  return result;
 }
 
 LayoutBox* Element::GetLayoutBoxForScrolling() const {
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 3229ad32..2d141739 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -934,6 +934,10 @@
   const ElementData* GetElementData() const { return element_data_.Get(); }
   UniqueElementData& EnsureUniqueElementData();
 
+  void RecordScrollbarSizeForStudy(int measurement,
+                                   bool is_width,
+                                   bool is_offset);
+
   void AddPropertyToPresentationAttributeStyle(MutableCSSPropertyValueSet*,
                                                CSSPropertyID,
                                                CSSValueID identifier);
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 4c5c62b..c1ee60f 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -26,8 +26,6 @@
 #include "third_party/blink/renderer/core/html/html_element.h"
 
 #include "base/stl_util.h"
-#include "third_party/blink/public/common/privacy_budget/identifiability_metric_builder.h"
-#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_or_trusted_script.h"
 #include "third_party/blink/renderer/bindings/core/v8/string_treat_null_as_empty_string_or_trusted_script.h"
@@ -1497,67 +1495,6 @@
   return 0;
 }
 
-void HTMLElement::RecordScrollbarSizeForStudy(int offset_measurement,
-                                              bool is_width) {
-  if (!IdentifiabilityStudySettings::Get()->IsTypeAllowed(
-          IdentifiableSurface::Type::kScrollbarSize))
-    return;
-
-  // Check for presence of a scrollbar.
-  PaintLayerScrollableArea* area;
-  if (this == GetDocument().ScrollingElementNoLayout()) {
-    auto* view = GetDocument().View();
-    if (!view)
-      return;
-    area = view->LayoutViewport();
-  } else {
-    auto* layout = GetLayoutBox();
-    if (!layout)
-      return;
-    area = layout->GetScrollableArea();
-  }
-  if (!area || area->HasOverlayOverflowControls())
-    return;
-
-  Scrollbar* scrollbar =
-      is_width ? area->VerticalScrollbar() : area->HorizontalScrollbar();
-  // We intentionally exclude platform overlay scrollbars since their size
-  // cannot be detected in JavaScript using the methods below.
-  if (!scrollbar)
-    return;
-
-  IdentifiableSurface::ScrollbarSurface surface;
-  int scrollbar_size;
-
-  // There are two common ways to detect the size of a scrollbar in a DOM
-  // window. They are:
-  // 1. Compute the difference of the window.inner[Width|Height] and the
-  //    corresponding document.scrollingElement.offset[Width|Height].
-  // 2. Any HTML element that insets the layout to fit a scrollbar, so it is
-  //    measurable by a JavaScript program on a site.
-  if (this == GetDocument().scrollingElement()) {
-    LocalDOMWindow* dom_window = GetDocument().domWindow();
-    scrollbar_size =
-        (is_width ? dom_window->innerWidth() : dom_window->innerHeight()) -
-        offset_measurement;
-    surface = is_width
-                  ? IdentifiableSurface::ScrollbarSurface::kBodyOffsetWidth
-                  : IdentifiableSurface::ScrollbarSurface::kBodyOffsetHeight;
-  } else {
-    scrollbar_size =
-        offset_measurement - (is_width ? clientWidth() : clientHeight());
-    surface = is_width
-                  ? IdentifiableSurface::ScrollbarSurface::kElemScrollbarWidth
-                  : IdentifiableSurface::ScrollbarSurface::kElemScrollbarHeight;
-  }
-
-  blink::IdentifiabilityMetricBuilder(GetDocument().UkmSourceID())
-      .Set(blink::IdentifiableSurface::FromTypeAndToken(
-               blink::IdentifiableSurface::Type::kScrollbarSize, surface),
-           scrollbar_size)
-      .Record(GetDocument().UkmRecorder());
-}
-
 int HTMLElement::offsetWidthForBinding() {
   GetDocument().EnsurePaintLocationDataValidForNode(
       this, DocumentUpdateReason::kJavaScript);
@@ -1569,7 +1506,8 @@
             LayoutUnit(layout_object->PixelSnappedOffsetWidth(offset_parent)),
             layout_object->StyleRef())
             .Round();
-    RecordScrollbarSizeForStudy(result, /* isWidth= */ true);
+    RecordScrollbarSizeForStudy(result, /* isWidth= */ true,
+                                /* is_offset= */ true);
   }
   return result;
 }
@@ -1586,7 +1524,8 @@
             LayoutUnit(layout_object->PixelSnappedOffsetHeight(offset_parent)),
             layout_object->StyleRef())
             .Round();
-    RecordScrollbarSizeForStudy(result, /* isWidth= */ false);
+    RecordScrollbarSizeForStudy(result, /* is_width= */ false,
+                                /* is_offset= */ true);
   }
   return result;
 }
diff --git a/third_party/blink/renderer/core/html/html_element.h b/third_party/blink/renderer/core/html/html_element.h
index 94cba571..0f54c2fa 100644
--- a/third_party/blink/renderer/core/html/html_element.h
+++ b/third_party/blink/renderer/core/html/html_element.h
@@ -212,8 +212,6 @@
 
   void HandleKeypressEvent(KeyboardEvent&);
 
-  void RecordScrollbarSizeForStudy(int, bool);
-
   static AttributeTriggers* TriggersForAttributeName(
       const QualifiedName& attr_name);
 
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.cc b/third_party/blink/renderer/core/layout/layout_text_control.cc
index b0847fa..274b3a3 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control.cc
@@ -54,7 +54,13 @@
                                        const ComputedStyle* old_style) {
   NOT_DESTROYED();
   LayoutBlockFlow::StyleDidChange(diff, old_style);
-  TextControlInnerEditorElement* inner_editor = InnerEditorElement();
+  StyleDidChange(InnerEditorElement(), old_style, StyleRef());
+}
+
+// static
+void LayoutTextControl::StyleDidChange(HTMLElement* inner_editor,
+                                       const ComputedStyle* old_style,
+                                       const ComputedStyle& new_style) {
   if (!inner_editor)
     return;
   LayoutBlock* inner_editor_layout_object =
@@ -65,7 +71,7 @@
     // (See TextControlInnerEditorElement::CreateInnerEditorStyle()).
     {
       StyleEngine::AllowMarkStyleDirtyFromRecalcScope scope(
-          GetDocument().GetStyleEngine());
+          inner_editor->GetDocument().GetStyleEngine());
       inner_editor->SetNeedsStyleRecalc(
           kLocalStyleChange,
           StyleChangeReasonForTracing::Create(style_change_reason::kControl));
@@ -75,7 +81,7 @@
     // (see: GetUncachedSelectionStyle in SelectionPaintingUtils.cpp) so ensure
     // the inner editor selection is invalidated anytime style changes and a
     // ::selection style is or was present on LayoutTextControl.
-    if (StyleRef().HasPseudoElementStyle(kPseudoIdSelection) ||
+    if (new_style.HasPseudoElementStyle(kPseudoIdSelection) ||
         (old_style && old_style->HasPseudoElementStyle(kPseudoIdSelection))) {
       inner_editor_layout_object->InvalidateSelectedChildrenOnStyleChange();
     }
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.h b/third_party/blink/renderer/core/layout/layout_text_control.h
index a26a2d46..602bae9 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.h
+++ b/third_party/blink/renderer/core/layout/layout_text_control.h
@@ -49,6 +49,9 @@
     return true;
   }
 
+  static void StyleDidChange(HTMLElement* inner_editor,
+                             const ComputedStyle* old_style,
+                             const ComputedStyle& new_style);
   static int ScrollbarThickness(const LayoutBox& box);
   static float GetAvgCharWidth(const ComputedStyle& style);
   static bool HasValidAvgCharWidth(const Font& font);
diff --git a/third_party/blink/renderer/core/layout/layout_text_control_test.cc b/third_party/blink/renderer/core/layout/layout_text_control_test.cc
index b844616..f1efd17d 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control_test.cc
@@ -5,28 +5,60 @@
 #include "third_party/blink/renderer/core/layout/layout_text_control.h"
 
 #include "build/build_config.h"
-#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 namespace blink {
 
 namespace {
 
-class LayoutTextControlTest : public RenderingTest {
+class LayoutTextControlTest : public testing::WithParamInterface<bool>,
+                              public RenderingTest {
+ public:
+  LayoutTextControlTest()
+      : scoped_text_area_flag_(GetParam()),
+        scoped_text_field_flag_(GetParam()) {}
+
  protected:
-  HTMLInputElement* GetHTMLInputElementById(const char* id) {
-    return To<HTMLInputElement>(GetDocument().getElementById(id));
+  TextControlElement* GetTextControlElementById(const char* id) {
+    return To<TextControlElement>(GetDocument().getElementById(id));
   }
-  // Return the LayoutText from inside an HTMLInputElement's user agent shadow
-  // tree.
-  LayoutText* GetInnerLayoutText(HTMLInputElement* input) {
+  // Return the LayoutText from inside a text control's user agent shadow tree.
+  LayoutText* GetInnerLayoutText(TextControlElement* control) {
     return ToLayoutText(
-        input->InnerEditorElement()->GetLayoutObject()->SlowFirstChild());
+        control->InnerEditorElement()->GetLayoutObject()->SlowFirstChild());
   }
+
+  // Focus on |control|, select 1-3 characters, get the first LayoutText, and
+  // check if selection invalidation state is clean.
+  LayoutText* SetupLayoutTextWithCleanSelection(TextControlElement* control) {
+    control->focus();
+    control->SetSelectionRange(1, 3);
+    UpdateAllLifecyclePhasesForTest();
+    auto* selected_text = GetInnerLayoutText(control);
+    EXPECT_FALSE(selected_text->ShouldInvalidateSelection());
+    return selected_text;
+  }
+
+  void CheckSelectionInvalidationChanges(const LayoutText& selected_text) {
+    GetDocument().View()->UpdateLifecycleToLayoutClean(
+        DocumentUpdateReason::kTest);
+    EXPECT_TRUE(selected_text.ShouldInvalidateSelection());
+
+    UpdateAllLifecyclePhasesForTest();
+    EXPECT_FALSE(selected_text.ShouldInvalidateSelection());
+  }
+
+ private:
+  ScopedLayoutNGTextAreaForTest scoped_text_area_flag_;
+  ScopedLayoutNGTextFieldForTest scoped_text_field_flag_;
 };
 
-TEST_F(LayoutTextControlTest,
-       ChangingPseudoSelectionStyleShouldInvalidateSelection) {
+INSTANTIATE_TEST_SUITE_P(All, LayoutTextControlTest, testing::Bool());
+
+TEST_P(LayoutTextControlTest,
+       ChangingPseudoSelectionStyleShouldInvalidateSelectionSingle) {
   SetBodyInnerHTML(R"HTML(
     <style>
       input::selection { background-color: blue; }
@@ -35,25 +67,32 @@
     <input id="input" type="text" value="AAAAAAAAAAAA">
   )HTML");
 
-  auto* inputElement = GetHTMLInputElementById("input");
-  inputElement->focus();
-  inputElement->SetSelectionRange(1, 3);
-  UpdateAllLifecyclePhasesForTest();
+  auto* text_control = GetTextControlElementById("input");
+  auto* selected_text = SetupLayoutTextWithCleanSelection(text_control);
 
-  auto* selectedText = GetInnerLayoutText(inputElement);
-  EXPECT_FALSE(selectedText->ShouldInvalidateSelection());
-
-  inputElement->setAttribute(html_names::kClassAttr, "pseudoSelection");
-  GetDocument().View()->UpdateLifecycleToLayoutClean(
-      DocumentUpdateReason::kTest);
-  EXPECT_TRUE(selectedText->ShouldInvalidateSelection());
-
-  UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(selectedText->ShouldInvalidateSelection());
+  text_control->setAttribute(html_names::kClassAttr, "pseudoSelection");
+  CheckSelectionInvalidationChanges(*selected_text);
 }
 
-TEST_F(LayoutTextControlTest,
-       AddingPseudoSelectionStyleShouldInvalidateSelection) {
+TEST_P(LayoutTextControlTest,
+       ChangingPseudoSelectionStyleShouldInvalidateSelectionMulti) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      textarea::selection { background-color: blue; }
+      .pseudoSelection::selection { background-color: green; }
+    </style>
+    <textarea id="textarea">AAAAAAAAAAAA</textarea>
+  )HTML");
+
+  auto* text_control = GetTextControlElementById("textarea");
+  auto* selected_text = SetupLayoutTextWithCleanSelection(text_control);
+
+  text_control->setAttribute(html_names::kClassAttr, "pseudoSelection");
+  CheckSelectionInvalidationChanges(*selected_text);
+}
+
+TEST_P(LayoutTextControlTest,
+       AddingPseudoSelectionStyleShouldInvalidateSelectionSingle) {
   SetBodyInnerHTML(R"HTML(
     <style>
       .pseudoSelection::selection { background-color: green; }
@@ -61,25 +100,31 @@
     <input id="input" type="text" value="AAAAAAAAAAAA">
   )HTML");
 
-  auto* inputElement = GetHTMLInputElementById("input");
-  inputElement->focus();
-  inputElement->SetSelectionRange(1, 3);
-  UpdateAllLifecyclePhasesForTest();
+  auto* text_control = GetTextControlElementById("input");
+  auto* selected_text = SetupLayoutTextWithCleanSelection(text_control);
 
-  auto* selectedText = GetInnerLayoutText(inputElement);
-  EXPECT_FALSE(selectedText->ShouldInvalidateSelection());
-
-  inputElement->setAttribute(html_names::kClassAttr, "pseudoSelection");
-  GetDocument().View()->UpdateLifecycleToLayoutClean(
-      DocumentUpdateReason::kTest);
-  EXPECT_TRUE(selectedText->ShouldInvalidateSelection());
-
-  UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(selectedText->ShouldInvalidateSelection());
+  text_control->setAttribute(html_names::kClassAttr, "pseudoSelection");
+  CheckSelectionInvalidationChanges(*selected_text);
 }
 
-TEST_F(LayoutTextControlTest,
-       RemovingPseudoSelectionStyleShouldInvalidateSelection) {
+TEST_P(LayoutTextControlTest,
+       AddingPseudoSelectionStyleShouldInvalidateSelectionMulti) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .pseudoSelection::selection { background-color: green; }
+    </style>
+    <textarea id="textarea" >AAAAAAAAAAAA</textarea>
+  )HTML");
+
+  auto* text_control = GetTextControlElementById("textarea");
+  auto* selected_text = SetupLayoutTextWithCleanSelection(text_control);
+
+  text_control->setAttribute(html_names::kClassAttr, "pseudoSelection");
+  CheckSelectionInvalidationChanges(*selected_text);
+}
+
+TEST_P(LayoutTextControlTest,
+       RemovingPseudoSelectionStyleShouldInvalidateSelectionSingle) {
   SetBodyInnerHTML(R"HTML(
     <style>
       .pseudoSelection::selection { background-color: green; }
@@ -87,30 +132,36 @@
     <input id="input" type="text" class="pseudoSelection" value="AAAAAAAAAAAA">
   )HTML");
 
-  auto* inputElement = GetHTMLInputElementById("input");
-  inputElement->focus();
-  inputElement->SetSelectionRange(1, 3);
-  UpdateAllLifecyclePhasesForTest();
+  auto* text_control = GetTextControlElementById("input");
+  auto* selected_text = SetupLayoutTextWithCleanSelection(text_control);
 
-  auto* selectedText = GetInnerLayoutText(inputElement);
-  EXPECT_FALSE(selectedText->ShouldInvalidateSelection());
-
-  inputElement->removeAttribute(html_names::kClassAttr);
-  GetDocument().View()->UpdateLifecycleToLayoutClean(
-      DocumentUpdateReason::kTest);
-  EXPECT_TRUE(selectedText->ShouldInvalidateSelection());
-
-  UpdateAllLifecyclePhasesForTest();
-  EXPECT_FALSE(selectedText->ShouldInvalidateSelection());
+  text_control->removeAttribute(html_names::kClassAttr);
+  CheckSelectionInvalidationChanges(*selected_text);
 }
 
-TEST_F(LayoutTextControlTest, HitTestSearchInput) {
+TEST_P(LayoutTextControlTest,
+       RemovingPseudoSelectionStyleShouldInvalidateSelectionMulti) {
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      .pseudoSelection::selection { background-color: green; }
+    </style>
+    <textarea id="textarea" class="pseudoSelection">AAAAAAAAAAAA</textarea>
+  )HTML");
+
+  auto* text_control = GetTextControlElementById("textarea");
+  auto* selected_text = SetupLayoutTextWithCleanSelection(text_control);
+
+  text_control->removeAttribute(html_names::kClassAttr);
+  CheckSelectionInvalidationChanges(*selected_text);
+}
+
+TEST_P(LayoutTextControlTest, HitTestSearchInput) {
   SetBodyInnerHTML(R"HTML(
     <input id="input" type="search"
            style="border-width: 20px; font-size: 30px; padding: 0">
   )HTML");
 
-  auto* input = GetHTMLInputElementById("input");
+  auto* input = GetTextControlElementById("input");
   HitTestResult result;
   HitTestLocation location(PhysicalOffset(40, 30));
   EXPECT_TRUE(input->GetLayoutObject()->HitTestAllPhases(result, location,
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
index 7e73c11..0903a2cb 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.cc
@@ -256,4 +256,14 @@
 }
 #endif
 
+std::ostream& operator<<(std::ostream& ostream, const NGLineInfo& line_info) {
+  // Feel free to add more NGLneInfo members.
+  ostream << "NGLineInfo available_width_=" << line_info.AvailableWidth()
+          << " width_=" << line_info.Width() << " Results=[\n";
+  for (const auto& result : line_info.Results()) {
+    ostream << "\t" << result.item->ToString() << "\n";
+  }
+  return ostream << "]";
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
index 9ab17dd2..df14bd22 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_item_result.h
@@ -298,6 +298,8 @@
   bool is_ruby_text_ = false;
 };
 
+std::ostream& operator<<(std::ostream& ostream, const NGLineInfo& line_info);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_INLINE_ITEM_RESULT_H_
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc
index 0af95963..093c5a6 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_ruby_utils.cc
@@ -194,7 +194,9 @@
   if (items->size() < 2U)
     return LayoutUnit();
   const NGInlineItemResult& text_item = items->back();
-  DCHECK_EQ(text_item.item->Type(), NGInlineItem::kText);
+  if (text_item.item->Type() == NGInlineItem::kControl)
+    return LayoutUnit();
+  DCHECK(text_item.item->Type() == NGInlineItem::kText);
   wtf_size_t i = items->size() - 2;
   while ((*items)[i].item->Type() != NGInlineItem::kAtomicInline) {
     const auto type = (*items)[i].item->Type();
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.cc
index e743bb0..4469ef6 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.cc
@@ -12,11 +12,23 @@
 LayoutNGTextControlMultiLine::LayoutNGTextControlMultiLine(Element* element)
     : LayoutNGBlockFlow(element) {}
 
+HTMLElement* LayoutNGTextControlMultiLine::InnerEditorElement() const {
+  return To<TextControlElement>(GetNode())->InnerEditorElement();
+}
+
 bool LayoutNGTextControlMultiLine::IsOfType(LayoutObjectType type) const {
   return type == kLayoutObjectNGTextControlMultiLine ||
          LayoutNGBlockFlow::IsOfType(type);
 }
 
+void LayoutNGTextControlMultiLine::StyleDidChange(
+    StyleDifference style_diff,
+    const ComputedStyle* old_style) {
+  LayoutNGBlockFlow::StyleDidChange(style_diff, old_style);
+  LayoutTextControl::StyleDidChange(InnerEditorElement(), old_style,
+                                    StyleRef());
+}
+
 bool LayoutNGTextControlMultiLine::NodeAtPoint(
     HitTestResult& result,
     const HitTestLocation& hit_test_location,
@@ -30,8 +42,7 @@
   if (stop_node && stop_node->NodeForHitTest() == result.InnerNode())
     return true;
 
-  HTMLElement* inner_editor =
-      To<TextControlElement>(GetNode())->InnerEditorElement();
+  HTMLElement* inner_editor = InnerEditorElement();
   if (result.InnerNode() == GetNode() || result.InnerNode() == inner_editor) {
     LayoutTextControl::HitInnerEditorElement(
         *this, *inner_editor, result, hit_test_location, accumulated_offset);
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.h b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.h
index 94cfad8..054541a 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_multi_line.h
@@ -15,6 +15,8 @@
   explicit LayoutNGTextControlMultiLine(Element* element);
 
  private:
+  HTMLElement* InnerEditorElement() const;
+
   bool IsOfType(LayoutObjectType) const override;
 
   const char* GetName() const override {
@@ -27,6 +29,8 @@
     return true;
   }
 
+  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
+
   bool NodeAtPoint(HitTestResult& result,
                    const HitTestLocation& hit_test_location,
                    const PhysicalOffset& accumulated_offset,
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.cc
index 72a4b3f..98bff21 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.cc
@@ -4,14 +4,29 @@
 
 #include "third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.h"
 
+#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
+#include "third_party/blink/renderer/core/layout/layout_text_control.h"
+
 namespace blink {
 
 LayoutNGTextControlSingleLine::LayoutNGTextControlSingleLine(Element* element)
     : LayoutNGBlockFlow(element) {}
 
+HTMLElement* LayoutNGTextControlSingleLine::InnerEditorElement() const {
+  return To<TextControlElement>(GetNode())->InnerEditorElement();
+}
+
 bool LayoutNGTextControlSingleLine::IsOfType(LayoutObjectType type) const {
   return type == kLayoutObjectNGTextControlSingleLine ||
          LayoutNGBlockFlow::IsOfType(type);
 }
 
+void LayoutNGTextControlSingleLine::StyleDidChange(
+    StyleDifference style_diff,
+    const ComputedStyle* old_style) {
+  LayoutNGBlockFlow::StyleDidChange(style_diff, old_style);
+  LayoutTextControl::StyleDidChange(InnerEditorElement(), old_style,
+                                    StyleRef());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.h b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.h
index 0573297..1a532ba8 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_text_control_single_line.h
@@ -15,6 +15,8 @@
   explicit LayoutNGTextControlSingleLine(Element* element);
 
  private:
+  HTMLElement* InnerEditorElement() const;
+
   bool IsOfType(LayoutObjectType) const override;
 
   const char* GetName() const override {
@@ -26,6 +28,8 @@
     NOT_DESTROYED();
     return true;
   }
+
+  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
index 80ed156..557b849 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h
@@ -192,6 +192,10 @@
     block_end_annotation_space_ = space;
   }
 
+  void SetHasDescendantThatDependsOnPercentageBlockSize() {
+    has_descendant_that_depends_on_percentage_block_size_ = true;
+  }
+
   const NGConstraintSpace* ConstraintSpace() const { return space_; }
 
 #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
index e99e2831..496b3ca 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -131,6 +131,16 @@
 
   NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
 
+  const auto& style = Style();
+  if (style.LogicalHeight().IsPercentOrCalc() ||
+      style.LogicalMinHeight().IsPercentOrCalc() ||
+      style.LogicalMaxHeight().IsPercentOrCalc()) {
+    // The height of the fieldset content box depends on the percent-height of
+    // the fieldset. So we should assume the fieldset has a percent-height
+    // descendant.
+    container_builder_.SetHasDescendantThatDependsOnPercentageBlockSize();
+  }
+
   return container_builder_.ToBoxFragment();
 }
 
diff --git a/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc b/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc
index 63fbf013..50ac292 100644
--- a/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc
+++ b/third_party/blink/renderer/platform/instrumentation/partition_alloc_memory_dump_provider.cc
@@ -135,15 +135,15 @@
   allocator_dump->AddScalar("discardable_size", "bytes",
                             memory_stats->discardable_bytes);
   // TODO(bartekn): Rename the scalar names.
-  allocator_dump->AddScalar("total_pages_size", "bytes",
+  allocator_dump->AddScalar("total_slot_span_size", "bytes",
                             memory_stats->allocated_slot_span_size);
-  allocator_dump->AddScalar("active_pages", "objects",
+  allocator_dump->AddScalar("active_slot_spans", "objects",
                             memory_stats->num_active_slot_spans);
-  allocator_dump->AddScalar("full_pages", "objects",
+  allocator_dump->AddScalar("full_slot_spans", "objects",
                             memory_stats->num_full_slot_spans);
-  allocator_dump->AddScalar("empty_pages", "objects",
+  allocator_dump->AddScalar("empty_slot_spans", "objects",
                             memory_stats->num_empty_slot_spans);
-  allocator_dump->AddScalar("decommitted_pages", "objects",
+  allocator_dump->AddScalar("decommitted_slot_spans", "objects",
                             memory_stats->num_decommitted_slot_spans);
 }
 
diff --git a/third_party/blink/renderer/platform/text/hyphenation_test.cc b/third_party/blink/renderer/platform/text/hyphenation_test.cc
index 1cd3d876..2354c16 100644
--- a/third_party/blink/renderer/platform/text/hyphenation_test.cc
+++ b/third_party/blink/renderer/platform/text/hyphenation_test.cc
@@ -70,14 +70,13 @@
 }
 
 #if defined(USE_MINIKIN_HYPHENATION) || defined(OS_MAC)
-// TODO(crbug.com/851413): Reenable this test.
-#if defined(OS_ANDROID)
-#define MAYBE_HyphenLocations DISABLED_HyphenLocations
-#else
-#define MAYBE_HyphenLocations HyphenLocations
-#endif
-TEST_F(HyphenationTest, MAYBE_HyphenLocations) {
+TEST_F(HyphenationTest, HyphenLocations) {
   scoped_refptr<Hyphenation> hyphenation = GetHyphenation("en-us");
+#if defined(OS_ANDROID)
+  // Hyphenation is available only for Android M MR1 or later.
+  if (!hyphenation)
+    return;
+#endif
   ASSERT_TRUE(hyphenation) << "Cannot find the hyphenation engine";
 
   // Get all hyphenation points by |HyphenLocations|.
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7af9af2..3d38dfd 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -145,7 +145,7 @@
 virtual/composite-after-paint/scrollingcoordinator/* [ Pass ]
 # --- End CompositeAfterPaint Tests --
 
-# --- BEGIN OOP-R Canvas tests ---
+# --- BEGIN OOP-R Canvas tests --- 
 crbug.com/1081534 [ Win7 ] virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow.html [ Pass Failure ]
 crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/canvas-clearRect-first.html [ Failure ]
 crbug.com/1081534 [ Win ] virtual/oopr-canvas2d/fast/canvas/canvas-clearRect-with-clip.html [ Failure ]
@@ -875,7 +875,6 @@
 
 crbug.com/591099 css3/filters/composited-layer-child-bounds-after-composited-to-sw-shadow-change.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/boundary-shaping/boundary-shaping-009.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/hyphens/shy-styling-001.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-009.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-010.html [ Failure ]
 crbug.com/591099 external/wpt/css/css-text/shaping/shaping-011.html [ Failure ]
@@ -928,7 +927,6 @@
 
 # LayoutNG failures that needs to be triaged
 crbug.com/591099 virtual/text-antialias/selection/selection-rect-line-height-too-small.html [ Failure ]
-crbug.com/591099 external/wpt/css/css-text/hyphens/hyphens-out-of-flow-002.html [ Failure ]
 crbug.com/591099 fast/css-intrinsic-dimensions/width-avoid-floats.html [ Failure ]
 crbug.com/591099 [ Linux ] fast/selectors/shadow-host-div-with-span.html [ Failure ]
 crbug.com/591099 [ Win ] fast/selectors/shadow-host-div-with-span.html [ Failure ]
@@ -969,9 +967,6 @@
 crbug.com/591099 [ Mac ] fast/multicol/vertical-rl/float-big-line.html [ Failure ]
 crbug.com/591099 [ Mac ] fast/multicol/vertical-rl/float-content-break.html [ Failure ]
 crbug.com/591099 [ Mac ] fast/multicol/vertical-rl/float-edge.html [ Failure ]
-crbug.com/591099 [ Mac ] virtual/text-antialias/hyphens/hyphen-min-preferred-width-mock.html [ Failure ]
-crbug.com/591099 [ Mac ] virtual/text-antialias/hyphens/hyphens-auto-mock.html [ Failure ]
-crbug.com/591099 [ Mac ] virtual/text-antialias/hyphens/midword-break-priority.html [ Failure ]
 crbug.com/591099 [ Mac ] paint/invalidation/box/hover-pseudo-borders.html [ Failure ]
 crbug.com/591099 [ Mac ] fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-auto.html [ Failure ]
 crbug.com/591099 [ Mac ] paint/invalidation/flexbox/remove-inline-block-descendant-of-flex.html [ Failure ]
@@ -1563,30 +1558,6 @@
 crbug.com/1056027 [ Fuchsia ] virtual/text-antialias/small-caps-aat.html [ Skip ]
 crbug.com/1057339 [ Fuchsia ] virtual/text-antialias/international/rtl-mark.html [ Skip ]
 
-# `hyphens: auto` not supported on Win/Linux/ChromeOS
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphen-min-preferred-width-mock.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphen-min-preferred-width-mock.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphens-align.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphens-align.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphens-auto.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphens-auto.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphens-auto-mock.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphens-auto-mock.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphens-auto-nowrap.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphens-auto-nowrap.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphens-locale.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphens-locale.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/hyphens-orphaned-word.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/hyphens-orphaned-word.html [ Skip ]
-crbug.com/652964 [ Linux ] virtual/text-antialias/hyphens/midword-break-priority.html [ Skip ]
-crbug.com/652964 [ Win ] virtual/text-antialias/hyphens/midword-break-priority.html [ Skip ]
-crbug.com/652964 [ Linux ] external/wpt/css/css-text/hyphens/hyphens-auto-010.html [ Skip ]
-crbug.com/652964 [ Win ] external/wpt/css/css-text/hyphens/hyphens-auto-010.html [ Skip ]
-crbug.com/652964 [ Linux ] external/wpt/css/css-text/hyphens/hyphens-auto-inline-010.html [ Skip ]
-crbug.com/652964 [ Win ] external/wpt/css/css-text/hyphens/hyphens-auto-inline-010.html [ Skip ]
-crbug.com/652964 [ Linux ] external/wpt/css/css-text/hyphens/hyphens-none-013.html [ Skip ]
-crbug.com/652964 [ Win ] external/wpt/css/css-text/hyphens/hyphens-none-013.html [ Skip ]
-
 crbug.com/305376 external/wpt/css/css-overflow/webkit-line-clamp-018.html [ Failure ]
 crbug.com/305376 external/wpt/css/css-overflow/webkit-line-clamp-024.html [ Failure ]
 crbug.com/1007065 external/wpt/css/css-overflow/overflow-codependent-scrollbars.html [ Failure ]
@@ -1660,11 +1631,20 @@
 crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html [ Pass ]
 
 crbug.com/676270 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-auto-001.html [ Failure ]
-crbug.com/1022415 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-auto-010.html [ Failure ]
+crbug.com/1022415 external/wpt/css/css-text/hyphens/hyphens-auto-010.html [ Failure ]
+crbug.com/1022415 [ Linux ] external/wpt/css/css-text/hyphens/hyphens-auto-inline-010.html [ Failure ]
+crbug.com/1022415 [ Win ] external/wpt/css/css-text/hyphens/hyphens-auto-inline-010.html [ Failure ]
 crbug.com/963369 external/wpt/css/css-text/hyphens/hyphens-out-of-flow-001.html [ Failure ]
-crbug.com/958672 external/wpt/css/css-text/hyphens/hyphens-shaping-002.html [ Failure ]
-crbug.com/958672 external/wpt/css/css-text/hyphens/hyphens-span-001.html [ Failure ]
-crbug.com/958672 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-span-002.html [ Failure ]
+crbug.com/963369 external/wpt/css/css-text/hyphens/hyphens-out-of-flow-002.html [ Failure ]
+crbug.com/639223 external/wpt/css/css-text/hyphens/hyphens-shaping-002.html [ Failure ]
+crbug.com/639223 external/wpt/css/css-text/hyphens/hyphens-span-001.html [ Failure ]
+crbug.com/639223 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-span-002.html [ Failure ]
+crbug.com/639223 external/wpt/css/css-text/hyphens/shy-styling-001.html [ Failure ]
+crbug.com/870219 virtual/text-antialias/hyphens/hyphen-min-preferred-width-mock.html [ Failure ]
+crbug.com/870219 [ Win ] virtual/text-antialias/hyphens/hyphens-auto.html [ Failure ]
+crbug.com/870219 virtual/text-antialias/hyphens/hyphens-auto-mock.html [ Failure ]
+crbug.com/1139693 virtual/text-antialias/hyphens/midword-break-priority.html [ Failure ]
+
 crbug.com/626703 external/wpt/css/css-text/letter-spacing/letter-spacing-control-chars-001.html [ Failure ]
 crbug.com/921318 external/wpt/css/css-text/tab-size/tab-size-spacing-001.html [ Failure ]
 crbug.com/970200 external/wpt/css/css-text/text-indent/text-indent-tab-positions-001.html [ Failure ]
@@ -6139,6 +6119,9 @@
 crbug.com/1011811 http/tests/devtools/network/download.js [ Pass Failure ]
 crbug.com/1011811 http/tests/devtools/sxg/sxg-disable-cache.js [ Pass Failure ]
 
+crbug.com/1080609 virtual/threaded/external/wpt/scroll-animations/element-based-offset.html [ Pass Failure ]
+crbug.com/1080609 virtual/threaded/external/wpt/scroll-animations/element-based-offset-clamp.html [ Pass Failure ]
+
 # Fails when test moved to use full compositor pipe.
 crbug.com/1085832 [ Fuchsia ] images/size-failure.html [ Timeout ]
 
@@ -6475,3 +6458,5 @@
 crbug.com/1138589 [ Mac10.15 ] fast/events/mouse-cursor-image-set-svg.html [ Failure ]
 crbug.com/1138591 [ Mac10.15 ] http/tests/dom/raf-throttling-out-of-view-cross-origin-page.html [ Failure ]
 
+# WebRTC: Payload demuxing times out in Plan B. This is expected.
+crbug.com/1139052 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/protocol/unbundled-pt-demuxing.https.html [ Timeout ]
diff --git a/third_party/blink/web_tests/android/ChromiumWPTExpectations b/third_party/blink/web_tests/android/ChromiumWPTExpectations
index ae18897..69e57b7 100644
--- a/third_party/blink/web_tests/android/ChromiumWPTExpectations
+++ b/third_party/blink/web_tests/android/ChromiumWPTExpectations
@@ -2753,8 +2753,9 @@
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/dynamic-import/success.tentative.html [ Timeout ]
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/script-tag/success.tentative.html [ Timeout ]
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/worker-request/success.tentative.html [ Timeout ]
-crbug.com/1050754 external/wpt/import-maps/common/parsing.tentative.html [ Failure ]
-crbug.com/1050754 external/wpt/import-maps/common/resolving.tentative.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/parsing.tentative.https.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/resolving.tentative.https.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/resolving-internal.tentative.https.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/bare.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/data.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/http.sub.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WeblayerWPTExpectations b/third_party/blink/web_tests/android/WeblayerWPTExpectations
index 1541aca..28c9e1c 100644
--- a/third_party/blink/web_tests/android/WeblayerWPTExpectations
+++ b/third_party/blink/web_tests/android/WeblayerWPTExpectations
@@ -2657,8 +2657,9 @@
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/dynamic-import/success.tentative.html [ Timeout ]
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/script-tag/success.tentative.html [ Timeout ]
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/worker-request/success.tentative.html [ Timeout ]
-crbug.com/1050754 external/wpt/import-maps/common/parsing.tentative.html [ Failure ]
-crbug.com/1050754 external/wpt/import-maps/common/resolving.tentative.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/parsing.tentative.https.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/resolving.tentative.https.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/resolving-internal.tentative.https.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/bare.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/data.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/http.sub.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index 667e397..405b546 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -2870,8 +2870,9 @@
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/dynamic-import/success.tentative.html [ Timeout ]
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/script-tag/success.tentative.html [ Timeout ]
 crbug.com/1050754 external/wpt/import-maps/acquire-import-maps-flag/worker-request/success.tentative.html [ Timeout ]
-crbug.com/1050754 external/wpt/import-maps/common/parsing.tentative.html [ Failure ]
-crbug.com/1050754 external/wpt/import-maps/common/resolving.tentative.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/parsing.tentative.https.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/resolving.tentative.https.html [ Failure ]
+crbug.com/1050754 external/wpt/import-maps/common/resolving-internal.tentative.https.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/bare.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/data.sub.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/import-maps/core/http.sub.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 7336e02e7..6c9c8b2 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -59793,7 +59793,7 @@
       ]
      ],
      "align-items-007.html": [
-      "01e8f001a8a39c8726cf9a9453dfad07d0e1b83a",
+      "704c82fc6ad92410ff50ba4e5bbe02913ab33fe9",
       [
        null,
        [
@@ -61706,7 +61706,7 @@
       ]
      ],
      "flex-minimum-height-flex-items-007.xht": [
-      "e0e612627482d576acb6dd0857cc9f78f7cfa9e4",
+      "790ecb9141e40ecd583d0541cabb52939d5cfa37",
       [
        null,
        [
@@ -196009,12 +196009,6 @@
       []
      ],
      "aspect-ratio": {
-      "parsing": {
-       "contain-intrinsic-size-valid-expected.txt": [
-        "034b6db192f1fbc3b4c4b544602a6672b325f695",
-        []
-       ]
-      },
       "reference": {
        "ref-filled-green-100x20-rect.html": [
         "5d8ebce4ec5a42533cb82829b778e1f65029d7e4",
@@ -210124,7 +210118,7 @@
      []
     ],
     "density-corrected-size-bg-ref.html": [
-     "758ca8edc60adccf491f7db3aa40dbba9d75798b",
+     "154c1344444c45180628d858ed053434fbd9cab2",
      []
     ],
     "density-corrected-size-img-ref.html": [
@@ -219332,7 +219326,7 @@
          []
         ],
         "drawing-text-to-the-canvas.yaml": [
-         "550746bbc230f13ae55367751965370d1f42be21",
+         "a00caef1764fcced8f51673e08e4738d95df6a4f",
          []
         ],
         "fill-and-stroke-styles.yaml": [
@@ -219426,7 +219420,7 @@
          []
         ],
         "text.yaml": [
-         "d204a562c2fdc74bc37b2aaff8dd0799ff849521",
+         "280f4335ae2bae7bfa09ff1d87951f2bcfe3af53",
          []
         ],
         "the-canvas-state.yaml": [
@@ -232807,7 +232801,7 @@
       []
      ],
      "mediasource-worker-util.js": [
-      "e0c4bdc0e10bb5707f7af75bf6cafa293a6f7b9b",
+      "4cee4862fa4e6c39c43fee7af1a332e8a7cd7ce2",
       []
      ]
     },
@@ -284759,7 +284753,14 @@
       ]
      ],
      "flex-aspect-ratio-img-column-011.html": [
-      "66cb0e015f6618eb7cc3daee99f29ddbf7ed4729",
+      "6a0adb70dfd4886d25036104c1d72be90b564718",
+      [
+       null,
+       {}
+      ]
+     ],
+     "flex-aspect-ratio-img-column-017.html": [
+      "b2b97f0a31cfc7e67a67946b1cbad6ef0fbdbda8",
       [
        null,
        {}
@@ -295153,21 +295154,21 @@
      "aspect-ratio": {
       "parsing": {
        "contain-intrinsic-size-computed.html": [
-        "76fb5842ae691de4025795bc80777a61ceebbb6e",
+        "6a1f08a8a205222100a06d2fc3ff918564a3140a",
         [
          null,
          {}
         ]
        ],
        "contain-intrinsic-size-invalid.html": [
-        "e13c3ad07e1c25777cc9533407c0e57f6c6e5ddb",
+        "d85df5a844850d354621e835134a5c81d8b77ea7",
         [
          null,
          {}
         ]
        ],
        "contain-intrinsic-size-valid.html": [
-        "12f035d26f1e0d19e12fde8f981c05f36afb2c0e",
+        "7860e7556eca33c3f043bb553cefb9f71ae1b506",
         [
          null,
          {}
@@ -334402,6 +334403,20 @@
          {}
         ]
        ],
+       "2d.text.drawing.style.fontKerning.html": [
+        "b422b311276e854f5b9f0633bfba52fc7bc967c2",
+        [
+         null,
+         {}
+        ]
+       ],
+       "2d.text.drawing.style.fontKerning.with.uppercase.html": [
+        "db4adf73f09bf895e95b3e81b20962772ee7799e",
+        [
+         null,
+         {}
+        ]
+       ],
        "2d.text.drawing.style.spacing.html": [
         "a4ec083311ba0cdb32babf9b4e3351111c29ade5",
         [
@@ -348808,6 +348823,34 @@
          {}
         ]
        ],
+       "2d.text.drawing.style.fontKerning.html": [
+        "88b56c7562d388762d837a00e0f8389b8709c0d8",
+        [
+         null,
+         {}
+        ]
+       ],
+       "2d.text.drawing.style.fontKerning.with.uppercase.html": [
+        "5e394dba50a74da6c4ef07c40af1e4cbb7163df2",
+        [
+         null,
+         {}
+        ]
+       ],
+       "2d.text.drawing.style.fontKerning.with.uppercase.worker.js": [
+        "df3cd661884c38058326dd2e61084a9312fb952c",
+        [
+         "html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.with.uppercase.worker.html",
+         {}
+        ]
+       ],
+       "2d.text.drawing.style.fontKerning.worker.js": [
+        "d26bdec6ccf09e2153e393fadb594cf99e972fa7",
+        [
+         "html/canvas/offscreen/text/2d.text.drawing.style.fontKerning.worker.html",
+         {}
+        ]
+       ],
        "2d.text.drawing.style.spacing.html": [
         "d3cf89ad7be91fda67b7f5e005533293b1581e0e",
         [
@@ -372580,15 +372623,22 @@
      ]
     ],
     "dedicated-worker": {
-     "mediasource-worker-attach.html": [
-      "b09f05c248531029b9b787597af1ae84e0b65ccd",
+     "mediasource-worker-objecturl.html": [
+      "382a9a4f3632a53cd2a94dab121fe34a07d9238d",
       [
        null,
        {}
       ]
      ],
-     "mediasource-worker-objecturl.html": [
-      "382a9a4f3632a53cd2a94dab121fe34a07d9238d",
+     "mediasource-worker-play-terminate-worker.html": [
+      "d1c1c3d3fe8b0feba9eebb8c4796e904c0bcbf2f",
+      [
+       null,
+       {}
+      ]
+     ],
+     "mediasource-worker-play.html": [
+      "200f8a851bfc396861ad93b501bcebd7209dffd6",
       [
        null,
        {}
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/is-where-error-recovery.tentative.html b/third_party/blink/web_tests/external/wpt/css/selectors/is-where-error-recovery.html
similarity index 95%
rename from third_party/blink/web_tests/external/wpt/css/selectors/is-where-error-recovery.tentative.html
rename to third_party/blink/web_tests/external/wpt/css/selectors/is-where-error-recovery.html
index 31f5b8cc..1d6e870e 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/is-where-error-recovery.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/is-where-error-recovery.html
@@ -4,6 +4,7 @@
 <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/3676">
 <link rel="help" href="https://drafts.csswg.org/selectors-4/#matches">
 <link rel="help" href="https://drafts.csswg.org/selectors-4/#zero-matches">
+<link rel="help" href="https://drafts.csswg.org/selectors-4/#typedef-forgiving-selector-list">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style id="test-sheet">
diff --git a/third_party/blink/web_tests/external/wpt/density-size-correction/density-corrected-size-bg-ref.html b/third_party/blink/web_tests/external/wpt/density-size-correction/density-corrected-size-bg-ref.html
index 758ca8ed..154c1344 100644
--- a/third_party/blink/web_tests/external/wpt/density-size-correction/density-corrected-size-bg-ref.html
+++ b/third_party/blink/web_tests/external/wpt/density-size-correction/density-corrected-size-bg-ref.html
@@ -6,26 +6,24 @@
 <body>
     <style>
         body {
-            --lores: url(resources/exif-resolution-valid-lores.jpg);
-            --hires: url(resources/exif-resolution-valid-hires.jpg);
             --default: url(resources/exif-resolution-none.jpg);
-            --non-uniform: url(resources/exif-resolution-valid-non-uniform.jpg);
         }
         .default-bg {background-image: var(--default); }
-        .lores-bg {background-image: var(--lores); }
-        .hires-bg {background-image: var(--hires); }
+        .lores-bg {background-image: var(--default); background-size: 200px 100px; }
+        .hires-bg {background-image: var(--default); background-size: 50px 25px; }
         .invalid-bg {background-image: var(--invalid); }
-        .non-uniform-bg {background-image: var(--non-uniform); }
+        .non-uniform-bg {background-image: var(--default); background-size: 50px 100px; }
         .box { width: 200px; height: 200px; display: inline-block; }
         .tiled {background-repeat: repeat; }
         .stretch {background-repeat: no-repeat; background-size: contain; }
+        .non-uniform-bg.stretch { background-size: 100px 200px; }
     </style>
     <div class="default-bg tiled box"></div>
     <div class="lores-bg tiled box"></div>
     <div class="hires-bg tiled box"></div>
     <div class="non-uniform-bg tiled box"></div>
     <br/>
-    <div class="lores-bg stretch box"></div>
+    <div class="default-bg stretch box"></div>
     <div class="default-bg stretch box"></div>
     <div class="non-uniform-bg stretch box"></div>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percent-height.html b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percent-height.html
new file mode 100644
index 0000000..bbe40785
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-percent-height.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<table cellspacing="0" cellpadding="0" style="width:100px; height:60px;">
+  <tr>
+    <td>
+      <fieldset style="border:none; padding:0; height:100%; margin:0; margin-top:13px;">
+        <div><div id="elm"></div></div>
+      </fieldset>
+    </td>
+  </tr>
+</table>
+<script>
+// crbug.com/1138204. Though the specification doesn't mention this behavior,
+// there must be no doubt about the expected behavior.
+test(() => {
+  const fieldset = document.querySelector('fieldset');
+  const initialHeight = fieldset.offsetHeight;
+  document.querySelector('#elm').style.display = 'none';
+  assert_equals(fieldset.offsetHeight, initialHeight);
+}, 'Fieldset with a percent height should not increase the height on every reflow.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/common-test-service-worker.js b/third_party/blink/web_tests/external/wpt/import-maps/common/common-test-service-worker.js
new file mode 100644
index 0000000..6985c90
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/common-test-service-worker.js
@@ -0,0 +1,28 @@
+let serveImporterScript = false;
+
+self.addEventListener('message', event => {
+  serveImporterScript = true;
+  event.source.postMessage('Done');
+});
+
+self.addEventListener('fetch', event => {
+    if (event.request.url.indexOf('common-test-helper-iframe.js') >= 0) {
+      return;
+    }
+    if (serveImporterScript) {
+      serveImporterScript = false;
+      event.respondWith(
+        new Response(
+          'window.importHelper = (specifier) => import(specifier);',
+          {headers: {'Content-Type': 'text/javascript'}}
+        ));
+    } else {
+      event.respondWith(
+        new Response(
+          'export const response = ' +
+              JSON.stringify({url: event.request.url}) + ';',
+          {headers: {'Access-Control-Allow-Origin': '*',
+                     'Content-Type': 'text/javascript'}}
+        ));
+    }
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/parsing.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/common/parsing.tentative.https.html
similarity index 61%
rename from third_party/blink/web_tests/external/wpt/import-maps/common/parsing.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/common/parsing.tentative.https.html
index d3334d7..0dc9e24 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/common/parsing.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/parsing.tentative.https.html
@@ -2,9 +2,16 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+// All parsing tests requires Chromium's internal methods.
+globalThis.useInternalMethods = true;
+</script>
 <body>
 <script type="module">
-import { runTestsFromJSON } from "./resources/common-test-helper.js";
+import { runTestsFromJSON, setupGlobalCleanup } from "./resources/common-test-helper.js";
+
+const promises = [];
 
 for (const json of [
   'resources/parsing-addresses-absolute.json',
@@ -19,8 +26,13 @@
   'resources/parsing-specifier-keys.json',
   'resources/parsing-trailing-slashes.json',
 ]) {
-  promise_test(() =>
-    runTestsFromJSON(json),
+  promise_test(() => {
+      const promise = runTestsFromJSON(json);
+      promises.push(promise);
+      return promise;
+    },
     "Test helper: fetching and sanity checking test JSON: " + json);
 }
+
+Promise.all(promises).then(setupGlobalCleanup);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/resolving-internal.tentative.https.html b/third_party/blink/web_tests/external/wpt/import-maps/common/resolving-internal.tentative.https.html
new file mode 100644
index 0000000..a9f7c82
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/resolving-internal.tentative.https.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+// This test file is for resolution tests that require Chromium's internal
+// methods.
+// For tests that don't use Chromium's internal methods, see
+// resolving.tentative.https.html.
+globalThis.useInternalMethods = true;
+</script>
+<body>
+<script type="module">
+import { runTestsFromJSON, setupGlobalCleanup } from "./resources/common-test-helper.js";
+
+const promises = [];
+
+for (const json of [
+  'resources/empty-import-map-internal.json',
+]) {
+  promise_test(() => {
+      const promise = runTestsFromJSON(json);
+      promises.push(promise);
+      return promise;
+    },
+    "Test helper: fetching and sanity checking test JSON: " + json);
+}
+
+Promise.all(promises).then(setupGlobalCleanup);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/resolving.tentative.html b/third_party/blink/web_tests/external/wpt/import-maps/common/resolving.tentative.https.html
similarity index 62%
rename from third_party/blink/web_tests/external/wpt/import-maps/common/resolving.tentative.html
rename to third_party/blink/web_tests/external/wpt/import-maps/common/resolving.tentative.https.html
index 8dd3e3a2..abb30e0 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/common/resolving.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/resolving.tentative.https.html
@@ -2,9 +2,12 @@
 <meta name="timeout" content="long">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
 <body>
 <script type="module">
-import { runTestsFromJSON } from "./resources/common-test-helper.js";
+import { runTestsFromJSON, setupGlobalCleanup } from "./resources/common-test-helper.js";
+
+const promises = [];
 
 for (const json of [
   'resources/scopes.json',
@@ -17,8 +20,13 @@
   'resources/overlapping-entries.json',
   'resources/resolving-null.json',
 ]) {
-  promise_test(() =>
-    runTestsFromJSON(json),
+  promise_test(() => {
+      const promise = runTestsFromJSON(json);
+      promises.push(promise);
+      return promise;
+    },
     "Test helper: fetching and sanity checking test JSON: " + json);
 }
+
+Promise.all(promises).then(setupGlobalCleanup);
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper-iframe.js b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper-iframe.js
new file mode 100644
index 0000000..597955f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper-iframe.js
@@ -0,0 +1,103 @@
+// Handle errors around fetching, parsing and registering import maps.
+const onScriptError = event => {
+  window.registrationResult = {type: 'FetchError', error: event.error};
+  return false;
+};
+window.windowErrorHandler = event => {
+  window.registrationResult = {type: 'ParseError', error: event.error};
+  return false;
+};
+window.addEventListener('error', window.windowErrorHandler);
+
+// Handle specifier resolution requests from the parent frame.
+// For failures, we post error names and messages instead of error
+// objects themselves and re-create error objects later, to avoid
+// issues around serializing error objects which is a quite new feature.
+window.addEventListener('message', event => {
+  if (event.data.action === 'prepareResolve') {
+    // To get the result of #resolve-a-module-specifier given a script
+    // (with base URL = |baseURL|) and |specifier|, the service worker
+    // first serves an importer script with response URL = |baseURL|:
+    //     window.importHelper = (specifier) => import(specifier);
+    // This is to use |baseURL| as the referringScript's base URL.
+
+    // Step 1. Signal the service worker to serve
+    // the importer script for the next fetch request.
+    parent.worker.postMessage('serveImporterScript');
+  } else if (event.data.action === 'resolve') {
+    if (event.data.expectedURL === null ||
+        new URL(event.data.expectedURL).protocol === 'https:') {
+      // Testing without internal methods:
+      // If the resolution is expected to fail (null case here),
+      // we can test the failure just by catching the exception.
+      // If the expected URL is HTTPS, we can test the result by
+      // intercepting requests by service workers.
+
+      // Step 3. Evaluate the importer script as a classic script,
+      // in order to prevent |baseURL| from being mapped by import maps.
+      const script = document.createElement('script');
+      script.onload = () => {
+        // Step 4. Trigger dynamic import from |baseURL|.
+        importHelper(event.data.specifier)
+          .then(module => {
+              // Step 5. Service worker responds with a JSON containing
+              // the request URL for the dynamic import
+              // (= the result of #resolve-a-module-specifier).
+              parent.postMessage({type: 'ResolutionSuccess',
+                                  result: module.response.url},
+                                 '*');
+            })
+          .catch(e => {
+              parent.postMessage(
+                  {type: 'Failure', result: e.name, message: e.message},
+                  '*');
+            });
+      };
+      script.src = event.data.baseURL;
+      document.body.appendChild(script);
+    } else {
+      // Testing with internal methods.
+      // For example, the resolution results are data: URLs.
+      if (!event.data.useInternalMethods) {
+        parent.postMessage(
+            {type: 'Failure',
+             result: 'Error',
+             message: 'internals.resolveModuleSpecifier is not available'},
+            '*');
+        return;
+      }
+      try {
+        const result = internals.resolveModuleSpecifier(
+          event.data.specifier,
+          event.data.baseURL,
+          document);
+        parent.postMessage(
+            {type: 'ResolutionSuccess', result: result}, '*');
+      } catch (e) {
+        parent.postMessage(
+            {type: 'Failure', result: e.name, message: e.message}, '*');
+      }
+    }
+  } else if (event.data.action === 'getParsedImportMap') {
+    if (!event.data.useInternalMethods) {
+      parent.postMessage(
+          {type: 'Failure',
+           result: 'Error',
+           message: 'internals.getParsedImportMap is not available'},
+          '*');
+    }
+    try {
+      parent.postMessage({
+          type: 'GetParsedImportMapSuccess',
+          result: internals.getParsedImportMap(document)}, '*');
+    } catch (e) {
+      parent.postMessage(
+          {type: 'Failure', result: e.name, message: e.message}, '*');
+    }
+  } else {
+    parent.postMessage({
+        type: 'Failure',
+        result: 'Error',
+        message: 'Invalid Action: ' + event.data.action}, '*');
+  }
+});
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper.js b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper.js
index f7269db..2bf9918 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper.js
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/common-test-helper.js
@@ -1,5 +1,27 @@
 setup({allow_uncaught_exception : true});
 
+// Set window.useInternalMethods = true when needed && available.
+
+let registration;
+const scope = './scope/';
+
+// Global setup: this must be the first promise_test.
+promise_test(async (t) => {
+  const script = 'common-test-service-worker.js';
+
+  registration =
+      await service_worker_unregister_and_register(t, script, scope);
+  window.worker = registration.installing;
+  await wait_for_state(t, window.worker, 'activated');
+}, 'global setup');
+
+export function setupGlobalCleanup() {
+  // Global cleanup: the final promise_test.
+  promise_test(() => {
+     return registration.unregister();
+  }, 'global cleanup');
+}
+
 // Creates a new Document (via <iframe>) and add an inline import map.
 function parse(importMap, importMapBaseURL) {
   return new Promise(resolve => {
@@ -15,79 +37,51 @@
       {once: true});
 
     const testHTML = `
-      <script>
-      // Handle errors around fetching, parsing and registering import maps.
-      let registrationResult;
-      const onScriptError = event => {
-        registrationResult = {type: 'FetchError', error: event.error};
-        return false;
-      };
-      const windowErrorHandler = event => {
-        registrationResult = {type: 'ParseError', error: event.error};
-        return false;
-      };
-      window.addEventListener('error', windowErrorHandler);
-      window.addEventListener('load', event => {
-        if (!registrationResult) {
-          registrationResult = {type: 'Success'};
-        }
-        window.removeEventListener('error', windowErrorHandler);
-        parent.postMessage(registrationResult, '*');
-      });
-
-      // Handle specifier resolution requests from the parent frame.
-      window.addEventListener('message', event => {
-        try {
-          if (event.data.action === 'resolve') {
-            // URL resolution is tested using Chromium's internals.
-            // TODO(hiroshige): Remove the Chromium-specific dependency.
-            const result = internals.resolveModuleSpecifier(
-                event.data.specifier,
-                event.data.baseURL,
-                document);
-            parent.postMessage({type: 'ResolutionSuccess', result: result}, '*');
-         } else if (event.data.action === 'getParsedImportMap') {
-           parent.postMessage({
-               type: 'GetParsedImportMapSuccess',
-               result: internals.getParsedImportMap(document)}, '*');
-         } else {
-           parent.postMessage({
-               type: 'Failure',
-               result: "Invalid Action: " + event.data.action}, '*');
-         }
-        } catch (e) {
-          // We post error names instead of error objects themselves and
-          // re-create error objects later, to avoid issues around serializing
-          // error objects which is a quite new feature.
-          parent.postMessage({type: 'Failure', result: e.name}, '*');
-        }
-      });
-      </script>
+      <body>
+      <script src="${location.origin}/import-maps/common/resources/common-test-helper-iframe.js"></script>
       <script type="importmap" onerror="onScriptError(event)">
       ${importMapString}
       </script>
+      <script type="module">
+        if (!window.registrationResult) {
+          window.registrationResult = {type: 'Success'};
+        }
+        window.removeEventListener('error', window.windowErrorHandler);
+        parent.postMessage(window.registrationResult, '*');
+      </script>
+      </body>
     `;
 
     if (new URL(importMapBaseURL).protocol === 'data:') {
+      if (!window.useInternalMethods) {
+        throw new Error(
+            'Import maps with base URL = data: URL requires internal methods');
+      }
       iframe.src = 'data:text/html;base64,' + btoa(testHTML);
     } else {
-      iframe.srcdoc = `<base href="${importMapBaseURL}">` + testHTML;
+      // Set the src to `scope` in order to make requests from `iframe`
+      // intercepted by the service worker.
+      iframe.src = scope;
+      iframe.onload = () => {
+        iframe.contentDocument.write(
+            `<base href="${importMapBaseURL}">` + testHTML);
+        iframe.contentDocument.close();
+      };
     }
-
     document.body.appendChild(iframe);
-
   });
 }
 
 // Returns a promise that is resolved with the resulting URL.
-function resolve(specifier, parsedImportMap, baseURL) {
+// `expectedURL` is a string, or null if to be thrown.
+function resolve(specifier, parsedImportMap, baseURL, expectedURL) {
   return new Promise((resolve, reject) => {
     window.addEventListener('message', event => {
         if (event.data.type === 'ResolutionSuccess') {
           resolve(event.data.result);
         } else if (event.data.type === 'Failure') {
           if (event.data.result === 'TypeError') {
-            reject(new TypeError());
+            reject(new TypeError(event.data.message));
           } else {
             reject(new Error(event.data.result));
           }
@@ -97,8 +91,20 @@
       },
       {once: true});
 
-    parsedImportMap.contentWindow.postMessage(
-        {action: "resolve", specifier: specifier, baseURL: baseURL}, '*');
+    parsedImportMap.contentWindow.postMessage({action: 'prepareResolve'}, '*');
+
+    navigator.serviceWorker.addEventListener('message', event => {
+      // Step 2. After postMessage() at Step 1 is processed, the service worker
+      // sends back a message and the parent Window receives the message here
+      // and sends a 'resolve' message to the iframe.
+      parsedImportMap.contentWindow.postMessage(
+          {action: 'resolve',
+           specifier: specifier,
+           baseURL: baseURL,
+           expectedURL: expectedURL,
+           useInternalMethods: window.useInternalMethods},
+          '*');
+    }, {once: true});
   });
 }
 
@@ -112,14 +118,15 @@
       {once: true});
 
     parsedImportMap.contentWindow.postMessage(
-        {action: "getParsedImportMap"}, '*');
+        {action: 'getParsedImportMap',
+         useInternalMethods: window.useInternalMethods}, '*');
   });
 }
 
 function assert_no_extra_properties(object, expectedProperties, description) {
   for (const actualProperty in object) {
     assert_true(expectedProperties.indexOf(actualProperty) !== -1,
-        description + ": unexpected property " + actualProperty);
+        description + ': unexpected property ' + actualProperty);
   }
 }
 
@@ -184,18 +191,18 @@
       assert_own_property(j, 'baseURL');
       assert_equals(
           j.parsedImportMap.parseImportMapResult,
-          "Success",
-          "Import map registration should be successful for resolution tests");
+          'Success',
+          'Import map registration should be successful for resolution tests');
       for (const specifier in j.expectedResults) {
         const expected = j.expectedResults[specifier];
         promise_test(async t => {
             if (expected === null) {
               return promise_rejects_js(t, TypeError,
-                  resolve(specifier, j.parsedImportMap, j.baseURL));
+                  resolve(specifier, j.parsedImportMap, j.baseURL, null));
             } else {
               // Should be resolved to `expected`.
               const actual = await resolve(
-                  specifier, j.parsedImportMap, j.baseURL);
+                  specifier, j.parsedImportMap, j.baseURL, expected);
               assert_equals(actual, expected);
             }
           },
@@ -207,7 +214,7 @@
     if (j.hasOwnProperty('expectedParsedImportMap')) {
       promise_test(async t => {
         if (j.expectedParsedImportMap === null) {
-          assert_equals(j.parsedImportMap.parseImportMapResult, "ParseError");
+          assert_equals(j.parsedImportMap.parseImportMapResult, 'ParseError');
         } else {
           const actualParsedImportMap =
               await getParsedImportMap(j.parsedImportMap);
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map-internal.json b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map-internal.json
new file mode 100644
index 0000000..59390c8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map-internal.json
@@ -0,0 +1,20 @@
+{
+  "importMap": {},
+  "importMapBaseURL": "https://example.com/app/index.html",
+  "baseURL": "https://example.com/js/app.mjs",
+  "tests": {
+    "non-HTTPS fetch scheme absolute URLs": {
+      "expectedResults": {
+        "about:fetch-scheme": "about:fetch-scheme"
+      }
+    },
+    "non-fetch scheme absolute URLs": {
+      "expectedResults": {
+        "mailto:non-fetch-scheme": "mailto:non-fetch-scheme",
+        "import:non-fetch-scheme": "import:non-fetch-scheme",
+        "javascript:non-fetch-scheme": "javascript:non-fetch-scheme",
+        "wss:non-fetch-scheme": "wss://non-fetch-scheme/"
+      }
+    }
+  }
+}
diff --git a/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map.json b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map.json
index ce6c185..ff85a6d 100644
--- a/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map.json
+++ b/third_party/blink/web_tests/external/wpt/import-maps/common/resources/empty-import-map.json
@@ -18,23 +18,14 @@
         "/../foo/../bar": "https://example.com/bar"
       }
     },
-    "fetch scheme absolute URLs": {
+    "HTTPS scheme absolute URLs": {
       "expectedResults": {
-        "about:fetch-scheme": "about:fetch-scheme",
         "https://fetch-scheme.net": "https://fetch-scheme.net/",
         "https:fetch-scheme.org": "https://fetch-scheme.org/",
         "https://fetch%2Dscheme.com/": "https://fetch-scheme.com/",
         "https://///fetch-scheme.com///": "https://fetch-scheme.com///"
       }
     },
-    "non-fetch scheme absolute URLs": {
-      "expectedResults": {
-        "mailto:non-fetch-scheme": "mailto:non-fetch-scheme",
-        "import:non-fetch-scheme": "import:non-fetch-scheme",
-        "javascript:non-fetch-scheme": "javascript:non-fetch-scheme",
-        "wss:non-fetch-scheme": "wss://non-fetch-scheme/"
-      }
-    },
     "valid relative URLs that are invalid as specifiers should fail": {
       "expectedResults": {
         "invalid-specifier": null,
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index fe2933b..b44faa4 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -690,7 +690,7 @@
 MISSING-LINK: css/filter-effects/*.any.js
 
 # Tests that use WebKit/Blink testing APIs
-LAYOUTTESTS APIS: import-maps/common/resources/common-test-helper.js
+LAYOUTTESTS APIS: import-maps/common/resources/common-test-helper-iframe.js
 LAYOUTTESTS APIS: resources/chromium/enable-hyperlink-auditing.js
 LAYOUTTESTS APIS: resources/chromium/generic_sensor_mocks.js
 LAYOUTTESTS APIS: resources/chromium/webxr-test.js
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/element-based-offset-clamp.html b/third_party/blink/web_tests/external/wpt/scroll-animations/element-based-offset-clamp.html
index 6467af19..b1405074 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/element-based-offset-clamp.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/element-based-offset-clamp.html
@@ -7,19 +7,25 @@
 <script src="testcommon.js"></script>
 
 <style>
-/*
- * Overflow hidden prevents user scroll including mouse wheel; however, the
- * element is still a scrollable container and can be scrolled programmatically.
- * Removing the visible scrollbars in this manner simplifies the position
- * calculations in the text.
- */
 .scroller {
-  overflow: hidden;
+  overflow: scroll;
   height: 500px;
   width: 500px;
   will-change: transform;
 }
 
+/* Disable scrollbars to simplify the calculations in the test. */
+.scroller {
+  scrollbar-width: 0;
+}
+/*
+Chrome does not support scrollbar-width so we use this non-standard property
+until it does.
+*/
+.scroller::-webkit-scrollbar {
+  display: none;
+}
+
 .contents {
   height: 1200px;
   width: 1200px;
@@ -206,4 +212,4 @@
       createScrollAnimationTest(description, config);
     }
   }
-</script>
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/protocol/unbundled-pt-demuxing.https.html b/third_party/blink/web_tests/external/wpt/webrtc/protocol/unbundled-pt-demuxing.https.html
new file mode 100644
index 0000000..e9f07e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/protocol/unbundled-pt-demuxing.https.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCPeerConnection payload type demuxing for unbundled connections</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../RTCPeerConnection-helper.js"></script>
+<script>
+'use strict';
+promise_test(async t => {
+  const caller = new RTCPeerConnection({bundlePolicy: 'max-compat'});
+  t.add_cleanup(() => caller.close());
+  const callee = new RTCPeerConnection();
+  t.add_cleanup(() => callee.close());
+
+  const stream = await getNoiseStream({video: true});
+  t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+  stream.getTracks().forEach(track => caller.addTrack(track, stream));
+  stream.getTracks().forEach(track => caller.addTrack(track.clone(), stream.clone()));
+
+  let callCount = 0;
+  let metadataToBeLoaded = new Promise(resolve => {
+    callee.ontrack = (e) => {
+      const stream = e.streams[0];
+      const v = document.createElement('video');
+      v.autoplay = true;
+      v.srcObject = stream;
+      v.id = stream.id
+      v.addEventListener('loadedmetadata', () => {
+        if (++callCount === 2) {
+          resolve();
+        }
+      });
+    };
+  });
+
+  exchangeIceCandidates(caller, callee);
+
+  const offer = await caller.createOffer();
+  // Replace BUNDLE, the mid header extension and all ssrc lines
+  // with bogus. The receiver will be forced to do payload type demuxing
+  // which is still possible because the different m-lines arrive on
+  // different ports/sockets.
+  const sdp = offer.sdp.replace('BUNDLE', 'SOMETHING')
+    .replace(/rtp-hdrext:sdes/g, 'rtp-hdrext:something')
+    .replace(/a=ssrc:/g, 'a=notssrc');
+
+  await callee.setRemoteDescription({type: 'offer', sdp});
+  await caller.setLocalDescription(offer);
+
+  const answer = await callee.createAnswer();
+  await caller.setRemoteDescription(answer);
+  await callee.setLocalDescription(answer);
+
+  await metadataToBeLoaded;
+}, 'Can demux two video tracks on an unbundled connection by payload type');
+</script>
diff --git a/third_party/blink/web_tests/fast/ruby/following-tab-crash.html b/third_party/blink/web_tests/fast/ruby/following-tab-crash.html
new file mode 100644
index 0000000..5388ec9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/ruby/following-tab-crash.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<style>
+#container {
+  width: 10em;
+  white-space: pre-wrap;
+}
+</style>
+<body>
+<div id=container></div>
+<script>
+test(() => {
+  const div = document.querySelector('#container');
+  div.innerHTML = '<ruby>xx<rt>foo bar foo bar foo bar foo bar foo bar</rt></ruby><span>&#9;</span>';
+  // Force layout.
+  div.clientHeight;
+  // Success if CommitPendingEndOverhang() didn't crash in |div.clientHeight|.
+}, 'Overhanging ruby followed by a TAB should not crash');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/websocket/resources/connect-error-by-no-websocket-server.js b/third_party/blink/web_tests/http/tests/websocket/resources/connect-error-by-no-websocket-server.js
index 4a10595..1c8c2a1c 100644
--- a/third_party/blink/web_tests/http/tests/websocket/resources/connect-error-by-no-websocket-server.js
+++ b/third_party/blink/web_tests/http/tests/websocket/resources/connect-error-by-no-websocket-server.js
@@ -53,9 +53,9 @@
         }
     };
 
-    // Each failure to connect to 127.0.0.1 takes 1 second on Windows.
-    // Allow 2 seconds for padding.
-    timeoutID = setTimeout(timeOutCallback, 2000);
+    // Each failure to connect to 127.0.0.1 takes 3 seconds on Windows.
+    // Allow 4 seconds for padding.
+    timeoutID = setTimeout(timeOutCallback, 4000);
 }
 
 function timeOutCallback()
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt
deleted file mode 100644
index 10fa6879..0000000
--- a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS Property hyphens value 'none'
-PASS Property hyphens value 'manual'
-PASS Property hyphens value 'auto'
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt
deleted file mode 100644
index de12ec39..0000000
--- a/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/parsing/hyphens-computed-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS Property hyphens value 'none'
-PASS Property hyphens value 'manual'
-FAIL Property hyphens value 'auto' assert_true: 'auto' is a supported value for hyphens. expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/parsing/hyphens-valid-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/parsing/hyphens-valid-expected.txt
deleted file mode 100644
index bc3ee835..0000000
--- a/third_party/blink/web_tests/platform/win/external/wpt/css/css-text/parsing/hyphens-valid-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS e.style['hyphens'] = "none" should set the property value
-PASS e.style['hyphens'] = "manual" should set the property value
-FAIL e.style['hyphens'] = "auto" should set the property value assert_not_equals: property should be set got disallowed value ""
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/win/virtual/text-antialias/hyphens/hyphens-parsing-001-expected.txt b/third_party/blink/web_tests/platform/win/virtual/text-antialias/hyphens/hyphens-parsing-001-expected.txt
deleted file mode 100644
index f6344a7..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/text-antialias/hyphens/hyphens-parsing-001-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a testharness.js-based test.
-PASS Initial value
-FAIL hyphens: auto assert_equals: expected "auto" but got "manual"
-PASS hyphens: manual
-PASS hyphens: none
-FAIL hyphens should inherit assert_equals: expected "auto" but got "manual"
-Harness: the test ran to completion.
-
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp b/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp
index 068cbc6..826b94c1 100644
--- a/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp
+++ b/tools/clang/blink_gc_plugin/BlinkGCPluginConsumer.cpp
@@ -88,8 +88,10 @@
   options_.checked_namespaces.insert("blink");
 
   // Ignore GC implementation files.
-  options_.ignored_directories.push_back("/heap/");
-  options_.allowed_directories.push_back("/test/");
+  options_.ignored_directories.push_back(
+      "third_party/blink/renderer/platform/heap/");
+  options_.allowed_directories.push_back(
+      "third_party/blink/renderer/platform/heap/test/");
 }
 
 void BlinkGCPluginConsumer::HandleTranslationUnit(ASTContext& context) {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index eb8e2ad..643c55d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -15999,6 +15999,7 @@
   <int value="36" label="Web Store"/>
   <int value="37" label="YouTube"/>
   <int value="38" label="YouTube Music"/>
+  <int value="40" label="Stadia"/>
 </enum>
 
 <enum name="DefaultBrowserAsyncAttemptResult">
@@ -38731,6 +38732,23 @@
   <int value="6" label="Most Visited Entry"/>
 </enum>
 
+<enum name="IOSMultiWindowConfiguration">
+  <int value="0" label="Unspecified"/>
+  <int value="1" label="Fullscreen"/>
+  <int value="2" label="Fullscreen with Slideover"/>
+  <int value="3" label="Shared Standard"/>
+  <int value="4" label="Shared Standard with Slideover"/>
+  <int value="5" label="Shared Compact"/>
+  <int value="6" label="Shared Compact with Slideover"/>
+  <int value="7" label="Slideover"/>
+  <int value="8" label="Standard beside Standard"/>
+  <int value="9" label="Standard beside Standard with Slideover"/>
+  <int value="10" label="Standard beside Compact"/>
+  <int value="11" label="Standard beside Compact with Slideover"/>
+  <int value="12" label="Compact beside Compact"/>
+  <int value="13" label="Compact beside Compact with Slideover"/>
+</enum>
+
 <enum name="IOSNTPImpression">
   <int value="0" label="Local suggestions"/>
   <int value="1" label="Remote suggestions"/>
@@ -41913,6 +41931,7 @@
   <int value="-1722208902" label="CCTModuleCustomRequestHeader:disabled"/>
   <int value="-1720653947" label="WebRtcHybridAgc:disabled"/>
   <int value="-1719833926" label="disable-answers-in-suggest"/>
+  <int value="-1719699712" label="AudioPlayerJsModules:enabled"/>
   <int value="-1718074215" label="HomepageSettingsUIConversion:enabled"/>
   <int value="-1716654100" label="tab-capture-downscale-quality"/>
   <int value="-1716140224" label="EnableEmbeddedAssistantUI:disabled"/>
@@ -43111,6 +43130,7 @@
   <int value="-547301855" label="SyncPseudoUSSSupervisedUsers:enabled"/>
   <int value="-544685871" label="NewOsSettingsSearch:enabled"/>
   <int value="-544629557" label="CSSBackdropFilter:enabled"/>
+  <int value="-541764089" label="AudioPlayerJsModules:disabled"/>
   <int value="-541611402" label="OfflinePagesPrefetching:enabled"/>
   <int value="-540150399" label="TapVisualizerApp:enabled"/>
   <int value="-538141684" label="SafetyTip:enabled"/>
@@ -43215,6 +43235,7 @@
   <int value="-457292000" label="HappinessTrackingSurveysForDesktop:enabled"/>
   <int value="-457174225" label="Av1Decoder:enabled"/>
   <int value="-456321929" label="ForceEnableSystemAec:disabled"/>
+  <int value="-455413094" label="FilesJsModules:enabled"/>
   <int value="-455203267" label="use_new_features_summary"/>
   <int value="-454362199" label="HelpAppV2:disabled"/>
   <int value="-450976085"
@@ -43268,6 +43289,7 @@
   <int value="-396046249" label="PhotoPickerVideoSupport:enabled"/>
   <int value="-395606844" label="enable-site-settings"/>
   <int value="-395454065" label="DisablePostScriptPrinting:disabled"/>
+  <int value="-395259448" label="VideoPlayerJsModules:disabled"/>
   <int value="-394734604" label="FillingPasswordsFromAnyOrigin:disabled"/>
   <int value="-389664522"
       label="OmniboxUIExperimentHideSteadyStateUrlPathQueryAndRefOnInteraction:enabled"/>
@@ -43792,6 +43814,7 @@
   <int value="128086566" label="D3D11VideoDecoder:enabled"/>
   <int value="128323385" label="WebUIOmniboxPopup:enabled"/>
   <int value="129458360" label="LiteVideo:disabled"/>
+  <int value="130939792" label="FilesJsModules:disabled"/>
   <int value="131881947" label="D3DVsync:enabled"/>
   <int value="132560299"
       label="OmniboxUIExperimentHideSteadyStateUrlScheme:disabled"/>
@@ -43877,6 +43900,7 @@
   <int value="245896533" label="SearchSuggestionsOnLocalNtp:enabled"/>
   <int value="247200195" label="EnhancedProtectionPromoCard:enabled"/>
   <int value="250855010" label="WebAssemblyBaseline:disabled"/>
+  <int value="254497185" label="VideoPlayerJsModules:enabled"/>
   <int value="255375615" label="stop-non-timers-in-background:enabled"/>
   <int value="258621334"
       label="HappinessTrackingSurveysForDesktopDemo:disabled"/>
@@ -73122,6 +73146,10 @@
   <int value="57" label="kErrorCodeInternalLibCurlError"/>
   <int value="58" label="kErrorCodeUnresolvedHostError"/>
   <int value="59" label="kErrorCodeUnresolvedHostRecovered"/>
+  <int value="60" label="kErrorUnresolvedHostRecovered"/>
+  <int value="61" label="kErrorNotEnoughSpace"/>
+  <int value="62" label="kErrorDeviceCorrupted"/>
+  <int value="63" label="kErrorPackageExcludedFromUpdate"/>
 </enum>
 
 <enum name="UpdateEngineInstallDateProvisioningSource">
diff --git a/tools/metrics/histograms/histograms_xml/ios/histograms.xml b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
index 6f83a1c..bfccb0ac 100644
--- a/tools/metrics/histograms/histograms_xml/ios/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/ios/histograms.xml
@@ -495,6 +495,13 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.MultiWindow.Configuration"
+    enum="IOSMultiWindowConfiguration" expires_after="2021-06-30">
+  <owner>marq@chromium.org</owner>
+  <owner>djean@chromium.org</owner>
+  <summary>MultiWindow configration sampled once per minute.</summary>
+</histogram>
+
 <histogram name="IOS.MultiWindow.OpenInNewWindow" enum="WindowActivityOrigin"
     expires_after="2021-06-30">
   <owner>marq@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/sync/histograms.xml b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
index 85ec85f..8a6b0c4 100644
--- a/tools/metrics/histograms/histograms_xml/sync/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/sync/histograms.xml
@@ -616,7 +616,10 @@
 </histogram>
 
 <histogram name="Sync.PeakAnalysis.StopAfterAccountStateChanged" units="hits"
-    expires_after="M88">
+    expires_after="2020-10-15">
+  <obsolete>
+    Removed 2020-10.
+  </obsolete>
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -630,7 +633,10 @@
 </histogram>
 
 <histogram name="Sync.PeakAnalysis.StopAfterCredentialsChanged" units="hits"
-    expires_after="M88">
+    expires_after="2020-10-15">
+  <obsolete>
+    Removed 2020-10.
+  </obsolete>
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -643,7 +649,10 @@
 </histogram>
 
 <histogram name="Sync.PeakAnalysis.StopOnSyncManagedPrefChange" units="hits"
-    expires_after="M88">
+    expires_after="2020-10-15">
+  <obsolete>
+    Removed 2020-10.
+  </obsolete>
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
@@ -655,7 +664,10 @@
 </histogram>
 
 <histogram name="Sync.PeakAnalysis.StopOnSyncPermanentlyDisabled" units="hits"
-    expires_after="M88">
+    expires_after="2020-10-15">
+  <obsolete>
+    Removed 2020-10.
+  </obsolete>
   <owner>tschumann@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <summary>
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 2c00a02..da87fb9 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -435,6 +435,8 @@
     ":progress_center",
     "//ui/webui/resources/js/cr:event_target",
   ]
+  visibility +=
+      [ "//ui/file_manager/file_manager/foreground/js:file_tasks_unittest" ]
 }
 
 js_library("progress_center") {
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index d36abca6..5645abd 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -2647,7 +2647,7 @@
   background-position: center center;
   background-repeat: no-repeat;
   color: #888;
-  content: '\00a0';
+  content: ' ';
   padding-inline-start: 13px;
   position: relative;
   top: 1px;
@@ -2660,7 +2660,7 @@
   background-position: center center;
   background-repeat: no-repeat;
   color: #888;
-  content: '\00a0';
+  content: ' ';
   padding-inline-start: 13px;
   position: relative;
   top: -1px;
diff --git a/ui/file_manager/file_manager/foreground/css/menu.css b/ui/file_manager/file_manager/foreground/css/menu.css
index 3b98fd05..7b89199 100644
--- a/ui/file_manager/file_manager/foreground/css/menu.css
+++ b/ui/file_manager/file_manager/foreground/css/menu.css
@@ -43,13 +43,13 @@
 
 cr-menu > [sub-menu]::after {
   color: rgb(100, 100, 100);
-  content: '\25b6';
+  content: '▶';
   float: right;
   font-size: 0.7em;
 }
 
 html[dir='rtl'] cr-menu > [sub-menu]::after {
-  content: '\25c0';
+  content: '◀';
   float: left;
 }
 
diff --git a/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html b/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html
index b7d0a67..a9837ec 100644
--- a/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html
+++ b/ui/file_manager/file_manager/foreground/elements/files_metadata_entry.html
@@ -48,7 +48,7 @@
 
       #value[loading]::after {
         animation: ellipsis 1s steps(4,end) 100ms infinite;
-        content: '\2026'; /* Unicode horizontal ellipsis */
+        content: '…';
         display: inline-block;
         overflow: hidden;
         transform: scale(2.5) translate(0, 1px);
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 98e88d58..5029f19 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -462,6 +462,7 @@
     "//ui/file_manager/base/js:mock_chrome",
     "//ui/file_manager/base/js:test_error_reporting",
     "//ui/file_manager/file_manager/background/js:mock_crostini",
+    "//ui/file_manager/file_manager/background/js:mock_progress_center",
     "//ui/file_manager/file_manager/common/js:mock_entry",
   ]
 }
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
index c997a6a..615549f 100644
--- a/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/file_tasks_unittest.js
@@ -64,6 +64,34 @@
  */
 const mockFileTransferController = /** @type {!FileTransferController} */ ({});
 
+/**
+ * Mock directory change tracker.
+ * @type {!Object}
+ */
+const fakeTracker = {
+  hasChange: false,
+};
+
+/**
+ * Fake url for mounted ZIP file.
+ * @type {string}
+ */
+const fakeMountedZipUrl =
+    'filesystem:chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/' +
+    'external/fakePath/test.zip';
+
+/**
+ * Panel IDs for ZIP mount operations.
+ * @type {string}
+ */
+const zipMountPanelId = 'Mounting: ' + fakeMountedZipUrl;
+
+/**
+ * Panel IDs for ZIP mount errors.
+ * @type {string}
+ */
+const errorZipMountPanelId = 'Cannot mount: ' + fakeMountedZipUrl;
+
 // Set up test components.
 function setUp() {
   // Mock LoadTimeData strings.
@@ -156,6 +184,7 @@
       alertDialog: {
         showHtml: function(title, text, onOk, onCancel, onShow) {},
       },
+      passwordDialog: /** @type {!FilesPasswordDialog} */ ({}),
       speakA11yMessage: (text) => {},
     }),
     metadataModel: /** @type {!MetadataModel} */ ({}),
@@ -164,9 +193,10 @@
       getCurrentRootType: function() {
         return null;
       },
+      changeDirectoryEntry: function(displayRoot) {}
     }),
     crostini: crostini,
-    progressCenter: /** @type {!ProgressCenter} */ ({}),
+    progressCenter: /** @type {!ProgressCenter} */ (new MockProgressCenter()),
   };
 
   fileManager.crostini.initVolumeManager(fileManager.volumeManager);
@@ -759,3 +789,161 @@
 
   done();
 }
+
+/**
+ * Checks that the progress center is properly updated when mounting archives
+ * successfully.
+ * @suppress {visibility}
+ */
+async function testMountArchiveAndChangeDirectoryNotificationSuccess(done) {
+  const fileManager = getMockFileManager();
+
+  // Define FileTasks instance.
+  const tasks = await FileTasks.create(
+      fileManager.volumeManager, fileManager.metadataModel,
+      fileManager.directoryModel, fileManager.ui, mockFileTransferController,
+      [], [null], mockTaskHistory, fileManager.namingController,
+      fileManager.crostini, fileManager.progressCenter);
+
+  fileManager.volumeManager.mountArchive = function(url, password) {
+    // Check: progressing state.
+    assertEquals(
+        ProgressItemState.PROGRESSING,
+        fileManager.progressCenter.getItemById(zipMountPanelId).state);
+
+    const volumeInfo = {resolveDisplayRoot: () => null};
+    return volumeInfo;
+  };
+
+  // Mount archive.
+  await tasks.mountArchiveAndChangeDirectory_(fakeTracker, fakeMountedZipUrl);
+
+  // Check: mount completed, no error.
+  assertEquals(
+      ProgressItemState.COMPLETED,
+      fileManager.progressCenter.getItemById(zipMountPanelId).state);
+  assertEquals(
+      undefined, fileManager.progressCenter.getItemById(errorZipMountPanelId));
+
+  done();
+}
+
+/**
+ * Checks that the progress center is properly updated when mounting an archive
+ * resolves with an error.
+ * @suppress {visibility}
+ */
+async function testMountArchiveAndChangeDirectoryNotificationInvalidArchive(
+    done) {
+  const fileManager = getMockFileManager();
+
+  // Define FileTasks instance.
+  const tasks = await FileTasks.create(
+      fileManager.volumeManager, fileManager.metadataModel,
+      fileManager.directoryModel, fileManager.ui, mockFileTransferController,
+      [], [null], mockTaskHistory, fileManager.namingController,
+      fileManager.crostini, fileManager.progressCenter);
+
+  fileManager.volumeManager.mountArchive = function(url, password) {
+    return Promise.reject(VolumeManagerCommon.VolumeError.INTERNAL);
+  };
+
+  // Mount archive.
+  await tasks.mountArchiveAndChangeDirectory_(fakeTracker, fakeMountedZipUrl);
+
+  // Check: mount is completed with an error.
+  assertEquals(
+      ProgressItemState.COMPLETED,
+      fileManager.progressCenter.getItemById(zipMountPanelId).state);
+  assertEquals(
+      ProgressItemState.ERROR,
+      fileManager.progressCenter.getItemById(errorZipMountPanelId).state);
+
+  done();
+}
+
+/**
+ * Checks that the progress center is properly updated when the password prompt
+ * for an encrypted archive is canceled.
+ * @suppress {visibility}
+ */
+async function testMountArchiveAndChangeDirectoryNotificationCancelPassword(
+    done) {
+  const fileManager = getMockFileManager();
+
+  // Define FileTasks instance.
+  const tasks = await FileTasks.create(
+      fileManager.volumeManager, fileManager.metadataModel,
+      fileManager.directoryModel, fileManager.ui, mockFileTransferController,
+      [], [null], mockTaskHistory, fileManager.namingController,
+      fileManager.crostini, fileManager.progressCenter);
+
+  fileManager.volumeManager.mountArchive = function(url, password) {
+    return Promise.reject(VolumeManagerCommon.VolumeError.NEED_PASSWORD);
+  };
+
+  fileManager.ui.passwordDialog.askForPassword =
+      async function(filename, password = null) {
+    return Promise.reject(FilesPasswordDialog.USER_CANCELLED);
+  };
+
+  // Mount archive.
+  await tasks.mountArchiveAndChangeDirectory_(fakeTracker, fakeMountedZipUrl);
+
+  // Check: mount is completed, no error since the user canceled the password
+  // prompt.
+  assertEquals(
+      ProgressItemState.COMPLETED,
+      fileManager.progressCenter.getItemById(zipMountPanelId).state);
+  assertEquals(
+      undefined, fileManager.progressCenter.getItemById(errorZipMountPanelId));
+
+  done();
+}
+
+/**
+ * Checks that the progress center is properly updated when mounting an
+ * encrypted archive.
+ * @suppress {visibility}
+ */
+async function testMountArchiveAndChangeDirectoryNotificationEncryptedArchive(
+    done) {
+  const fileManager = getMockFileManager();
+
+  // Define FileTasks instance.
+  const tasks = await FileTasks.create(
+      fileManager.volumeManager, fileManager.metadataModel,
+      fileManager.directoryModel, fileManager.ui, mockFileTransferController,
+      [], [null], mockTaskHistory, fileManager.namingController,
+      fileManager.crostini, fileManager.progressCenter);
+
+  fileManager.volumeManager.mountArchive = function(url, password) {
+    return new Promise((resolve, reject) => {
+      if (password) {
+        assertEquals('testpassword', password);
+        const volumeInfo = {resolveDisplayRoot: () => null};
+        resolve(volumeInfo);
+      } else {
+        reject(VolumeManagerCommon.VolumeError.NEED_PASSWORD);
+      }
+    });
+  };
+
+  fileManager.ui.passwordDialog.askForPassword =
+      async function(filename, password = null) {
+    return Promise.resolve('testpassword');
+  };
+
+  // Mount archive.
+  await tasks.mountArchiveAndChangeDirectory_(fakeTracker, fakeMountedZipUrl);
+
+  // Check: mount is completed, no error since the user entered a valid
+  // password.
+  assertEquals(
+      ProgressItemState.COMPLETED,
+      fileManager.progressCenter.getItemById(zipMountPanelId).state);
+  assertEquals(
+      undefined, fileManager.progressCenter.getItemById(errorZipMountPanelId));
+
+  done();
+}
diff --git a/ui/file_manager/file_manager/foreground/js/naming_controller.js b/ui/file_manager/file_manager/foreground/js/naming_controller.js
index fe930e5..79d6c68b 100644
--- a/ui/file_manager/file_manager/foreground/js/naming_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/naming_controller.js
@@ -262,14 +262,13 @@
     const entry = input.currentEntry;
     const newName = input.value;
 
-    if (!newName || newName == entry.name) {
-      this.cancelRename_();
-      return;
-    }
-
     const renamedItemElement = this.listContainer_.findListItemForNode(
         this.listContainer_.renameInput);
     const nameNode = renamedItemElement.querySelector('.filename-label');
+    if (!newName || newName == nameNode.textContent) {
+      this.cancelRename_();
+      return;
+    }
 
     input.validation_ = true;
     const validationDone = valid => {
diff --git a/ui/file_manager/file_manager_resources.grd b/ui/file_manager/file_manager_resources.grd
index 09c49296..749b388 100644
--- a/ui/file_manager/file_manager_resources.grd
+++ b/ui/file_manager/file_manager_resources.grd
@@ -115,6 +115,7 @@
       <include name="IDR_VIDEO_PLAYER" file="video_player/video_player.html" allowexternalscript="true" flattenhtml="true" type="BINDATA" />
       <include name="IDR_VIDEO_PLAYER_JS" file="video_player/js/video_player_scripts.js" flattenhtml="true" type="BINDATA" />
       <include name="IDR_VIDEO_PLAYER_BACKGROUND_JS" file="video_player/js/background_scripts.js" flattenhtml="true" type="BINDATA" />
+      <include name="IDR_VIDEO_PLAYER_BACKGROUND_HTML" file="video_player/background.html" type="BINDATA" />
       <include name="IDR_VIDEO_PLAYER_ICON_FAVICON_16" file="video_player/images/icon/video-player-favicon-16.png" type="BINDATA" />
       <include name="IDR_VIDEO_PLAYER_ICON_16" file="video_player/images/icon/video-player-16.png" type="BINDATA" />
       <include name="IDR_VIDEO_PLAYER_ICON_32" file="video_player/images/icon/video-player-32.png" type="BINDATA" />
diff --git a/ui/file_manager/video_player/background.html b/ui/file_manager/video_player/background.html
new file mode 100644
index 0000000..61ebf8ff
--- /dev/null
+++ b/ui/file_manager/video_player/background.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<script src="chrome://resources/js/cr.js"></script>
+<script src="chrome://resources/js/cr/event_target.js"></script>
+<script src="chrome://resources/js/cr/ui/array_data_model.js"></script>
+<script src="chrome://resources/js/load_time_data.js"></script>
+<script src="chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/background_common_scripts.js"></script>
+<script src="js/background_scripts.js"></script>
diff --git a/ui/file_manager/video_player/manifest.json b/ui/file_manager/video_player/manifest.json
index 945296a..c5c1781 100644
--- a/ui/file_manager/video_player/manifest.json
+++ b/ui/file_manager/video_player/manifest.json
@@ -58,14 +58,7 @@
   },
   "app": {
     "background": {
-      "scripts": [
-        "chrome://resources/js/cr.js",
-        "chrome://resources/js/cr/event_target.js",
-        "chrome://resources/js/cr/ui/array_data_model.js",
-        "chrome://resources/js/load_time_data.js",
-        "chrome-extension://hhaomjibdihmijegdhdafkllkbggdgoj/background/js/background_common_scripts.js",
-        "js/background_scripts.js"
-      ]
+      "page": "background.html"
     },
     // enhhojjnijigcajfphajepfemndkmdlo is the Dev Media Router component extension ID.
     // pkedcjkdefgpdelpbcmbmeomcjbeemfm is the Stable Media Router component extension ID.
diff --git a/ui/views/.clang-tidy b/ui/views/.clang-tidy
index 1c0e0dc..b6c0d7f 100644
--- a/ui/views/.clang-tidy
+++ b/ui/views/.clang-tidy
@@ -1,29 +1,7 @@
 ---
-Checks: '-*,
-         google-build-namespaces,
-         google-explicit-constructor,
-         google-readability-casting,
-         google-readability-namespace-comments,
-         modernize-avoid-bind,
-         modernize-loop-convert,
-         modernize-make-shared,
-         modernize-make-unique,
-         modernize-redundant-void-arg,
-         modernize-replace-auto-ptr,
-         modernize-replace-random-shuffle,
-         modernize-shrink-to-fit,
-         modernize-use-bool-literals,
-         modernize-use-default-member-init,
-         modernize-use-emplace,
-         modernize-use-equals-default,
-         modernize-use-noexcept,
-         modernize-use-nullptr,
-         modernize-use-override,
-         modernize-use-transparent-functors,
-         modernize-use-using,
-         readability-redundant-member-init'
-HeaderFilterRegex: 'ui/views/*'
-CheckOptions:
-  - key: modernize-use-default-member-init.UseAssignment
-    value: 1
+  Checks:          'google-build-namespaces,
+                    google-readability-namespace-comments,
+                    modernize-replace-auto-ptr,
+                    modernize-use-using'
+  HeaderFilterRegex: 'ui/views/*'
 ...
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index f22c34d..04bf0870 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -1830,6 +1830,7 @@
     const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {
   // TODO(https://crbug.com/952355): Support custom text spans.
   DCHECK(!model_->HasCompositionText());
+  OnBeforeUserAction();
   model_->SetCompositionFromExistingText(range);
   SchedulePaint();
   OnAfterUserAction();
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index eee49b41..42d1f0f7 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -2996,6 +2996,21 @@
   EXPECT_EQ(composed_text_length, static_cast<uint32_t>(0));
 }
 
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
+// SetCompositionFromExistingText is only available on Windows and Chrome OS.
+TEST_F(TextfieldTest, SetCompositionFromExistingTextTest) {
+  InitTextfield();
+  textfield_->SetText(ASCIIToUTF16("abc"));
+
+  textfield_->SetCompositionFromExistingText(gfx::Range(1, 3), {});
+
+  gfx::Range actual_range;
+  EXPECT_TRUE(textfield_->HasCompositionText());
+  EXPECT_TRUE(textfield_->GetCompositionTextRange(&actual_range));
+  EXPECT_EQ(actual_range, gfx::Range(1, 3));
+}
+#endif
+
 TEST_F(TextfieldTest, GetCompositionCharacterBoundsTest) {
   InitTextfield();
   ui::CompositionText composition;