diff --git a/AUTHORS b/AUTHORS
index d3a0e81..4f13b601 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -328,6 +328,7 @@
 Frédéric Jacob <frederic.jacob.78@gmail.com>
 Frédéric Wang <fred.wang@free.fr>
 Fu Junwei <junwei.fu@intel.com>
+Gabriel Campana <gabriel.campana@ledger.fr>
 Gabor Rapcsanyi <g.rapcsanyi@samsung.com>
 Gaetano Mendola <mendola@gmail.com>
 Gajendra N <gajendra.n@samsung.com>
diff --git a/DEPS b/DEPS
index 608fc20..d01f468a 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,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': '2199cde1f52b39182a21f5ff41ab8aafde1f3731',
+  'skia_revision': '97eede48be1e9c2846f5a7aa037d7bc8d365b026',
   # 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': '5d60a181de0863d8d00ae42ec0ee3521f51b501a',
+  'v8_revision': 'cd58c1903e60fe64d72fa46c6a109f9c33d1ee6a',
   # 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.
@@ -211,7 +211,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': '0b90eef09f607336330f318bc8e65fa981634160',
+  'angle_revision': 'ac05066a00a9c7fecbe3c0fe4bf71c3994c8c200',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -219,7 +219,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': 'f3b585e8edf5b271445c1ef7ce07f461e87a038f',
+  'pdfium_revision': '84c7d11419c50573f3025beb6898391588a13321',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'e174329bab1589f8df20965fcb9dde4428673a64',
+  'catapult_revision': '12607ec3c98d43259369d7ce29f6ff490ee816f0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -274,7 +274,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': '56e9ceb621af13f4d35ff0a65e1206f7b50772a1',
+  'devtools_frontend_revision': 'a1da2af9a674eb2bd7ad1e285a78996698d3a55c',
   # 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.
@@ -314,7 +314,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '3a0405597374c9ae12fb93e33aac35826455a958',
+  'dawn_revision': '1670c5184b9eca52aac58636723e13e4d20fda75',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1249,7 +1249,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + '31a6a84b0393546e7076a90d0d5b5c5c606825a0',
+    Var('chromium_git') + '/openscreen' + '@' + 'f9e9052e7e6f1f75212737926b267a0e41621892',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '97cfe495bb7a3853266b646d1c79e169387f9c7a',
@@ -1266,7 +1266,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '35f78f4a4562f61cde3b096492628e1d47acb04e',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'eddc7d0df5d1b1fc6a35162da39375cbaa809fe7',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1518,7 +1518,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3c2fe3888658d82b47ca831d59a2e07579619c2d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '42082f9045e74e65b9e3a66b51093e0dc2f9375a',
+    Var('webrtc_git') + '/src.git' + '@' + '8cf47fb7ac36ccfa009bde04f18139affb0cbca9',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1590,7 +1590,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ae141f7298b13c959b64a3cff9ffcc191c8f5f2c',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6d48354d5a1c13a7a6237cf71e8b55358a7b253c',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index de183423..a47f5c55 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2839,6 +2839,7 @@
                   'yhanada+watch@chromium.org',
                   'yusukes+watch@chromium.org'],
     'textinput_chromeos': ['keithlee+watch@chromium.org',
+                           'kentonlam+watch@google.com',
                            'nona+watch@chromium.org',
                            'shuchen+watch@chromium.org',
                            'tranbaoduy+watch@chromium.org',
@@ -2869,6 +2870,7 @@
     'video_capture': ['chfremer+watch@chromium.org',
                       'rijubrata.bhaumik@intel.com'],
     'virtual_keyboard': ['dfaden+virtualkb@google.com',
+                         'kentonlam+watch@google.com',
                          'shend+watch@chromium.org',
                          'tranbaoduy+watch@chromium.org',
                          'yhanada+watchvk@chromium.org'],
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 3c2bd23..bcee359 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -185,7 +185,7 @@
 
   drag_data_ = std::move(data);
   drag_operation_ = operation;
-  current_drag_actions_ = 0;
+  current_drag_info_ = aura::client::DragUpdateInfo();
 
   start_location_ = screen_location;
   current_location_ = screen_location;
@@ -432,7 +432,7 @@
 
 void DragDropController::DragUpdate(aura::Window* target,
                                     const ui::LocatedEvent& event) {
-  int op = ui::DragDropTypes::DRAG_NONE;
+  aura::client::DragUpdateInfo drag_info;
   if (target != drag_window_) {
     if (drag_window_) {
       aura::client::DragDropDelegate* delegate =
@@ -463,25 +463,24 @@
                             event.root_location_f(), drag_operation_);
       e.set_flags(event.flags());
       ui::Event::DispatcherApi(&e).set_target(target);
-      op = delegate->OnDragUpdated(e);
+      drag_info = delegate->OnDragUpdated(e);
       gfx::NativeCursor cursor = ui::mojom::CursorType::kNoDrop;
-      if (op & ui::DragDropTypes::DRAG_COPY)
+      if (drag_info.drag_operation & ui::DragDropTypes::DRAG_COPY)
         cursor = ui::mojom::CursorType::kCopy;
-      else if (op & ui::DragDropTypes::DRAG_LINK)
+      else if (drag_info.drag_operation & ui::DragDropTypes::DRAG_LINK)
         cursor = ui::mojom::CursorType::kAlias;
-      else if (op & ui::DragDropTypes::DRAG_MOVE)
+      else if (drag_info.drag_operation & ui::DragDropTypes::DRAG_MOVE)
         cursor = ui::mojom::CursorType::kGrabbing;
 
       Shell::Get()->cursor_manager()->SetCursor(cursor);
     }
   }
 
-  if (op != current_drag_actions_) {
-    current_drag_actions_ = op;
-
+  if (drag_info.drag_operation != current_drag_info_.drag_operation) {
     for (aura::client::DragDropClientObserver& observer : observers_)
-      observer.OnDragActionsChanged(op);
+      observer.OnDragActionsChanged(drag_info.drag_operation);
   }
+  current_drag_info_ = drag_info;
 
   gfx::Point root_location_in_screen = event.root_location();
   ::wm::ConvertPointToScreen(target->GetRootWindow(), &root_location_in_screen);
@@ -492,7 +491,7 @@
   if (drag_image->GetVisible()) {
     current_location_ = root_location_in_screen;
     drag_image->SetScreenPosition(root_location_in_screen - drag_image_offset_);
-    drag_image->SetTouchDragOperation(op);
+    drag_image->SetTouchDragOperation(drag_info.drag_operation);
   }
 
   if (tab_drag_drop_delegate_) {
diff --git a/ash/drag_drop/drag_drop_controller.h b/ash/drag_drop/drag_drop_controller.h
index f860f38..a57d64f 100644
--- a/ash/drag_drop/drag_drop_controller.h
+++ b/ash/drag_drop/drag_drop_controller.h
@@ -17,6 +17,7 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/window_observer.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
@@ -124,7 +125,7 @@
   gfx::Vector2d drag_image_offset_;
   std::unique_ptr<ui::OSExchangeData> drag_data_;
   int drag_operation_ = 0;
-  int current_drag_actions_ = 0;
+  aura::client::DragUpdateInfo current_drag_info_;
 
   // Used when processing a Chrome tab drag from a WebUI tab strip.
   base::Optional<TabDragDropDelegate> tab_drag_drop_delegate_;
diff --git a/ash/drag_drop/drag_drop_controller_unittest.cc b/ash/drag_drop/drag_drop_controller_unittest.cc
index a4995ee..7310734 100644
--- a/ash/drag_drop/drag_drop_controller_unittest.cc
+++ b/ash/drag_drop/drag_drop_controller_unittest.cc
@@ -30,6 +30,7 @@
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
@@ -256,12 +257,15 @@
     EXPECT_EQ(window_, event.target());
     state_ = State::kDragEnteredInvoked;
   }
-  int OnDragUpdated(const ui::DropTargetEvent& event) override {
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override {
     EXPECT_TRUE(State::kDragEnteredInvoked == state_ ||
                 State::kDragUpdateInvoked == state_);
     EXPECT_EQ(window_, event.target());
     state_ = State::kDragUpdateInvoked;
-    return ui::DragDropTypes::DRAG_MOVE;
+    return aura::client::DragUpdateInfo(
+        ui::DragDropTypes::DRAG_MOVE,
+        ui::DataTransferEndpoint(ui::EndpointType::kDefault));
   }
   void OnDragExited() override { ADD_FAILURE(); }
   int OnPerformDrop(const ui::DropTargetEvent& event,
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
index d93b4ced..ec9d333 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -8,6 +8,7 @@
 #include "base/allocator/buildflags.h"
 #include "base/allocator/partition_allocator/memory_reclaimer.h"
 #include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/allocator/partition_allocator/partition_alloc_features.h"
 #include "base/allocator/partition_allocator/partition_stats.h"
@@ -153,6 +154,34 @@
 #endif  // defined(OS_WIN) && defined(ARCH_CPU_X86)
 }
 
+void* AllocateAlignedMemory(size_t alignment, size_t size) {
+  // Memory returned by the regular allocator *always* respects |kAlignment|,
+  // which is a power of two, and any valid alignment is also a power of two. So
+  // we can directly fulfill these requests with the main allocator.
+  //
+  // This has several advantages:
+  // - The thread cache is supported on the main partition
+  // - Reduced fragmentation
+  // - Better coverage for MiraclePtr variants requiring extras
+  //
+  // There are several call sites in Chromium where base::AlignedAlloc is called
+  // with a small alignment. Some may be due to overly-careful code, some are
+  // because the client code doesn't know the required alignment at compile
+  // time.
+  //
+  // Note that all "AlignedFree()" variants (_aligned_free() on Windows for
+  // instance) directly call PartitionFree(), so there is no risk of
+  // mismatch. (see below the default_dispatch definition).
+  if (alignment <= base::kAlignment) {
+    // This is mandated by |posix_memalign()| and friends, so should never fire.
+    PA_CHECK(base::bits::IsPowerOfTwo(alignment));
+    return Allocator()->AllocFlagsNoHooks(0, size);
+  }
+
+  return AlignedAllocator()->AlignedAllocFlags(base::PartitionAllocNoHooks,
+                                               alignment, size);
+}
+
 }  // namespace
 
 namespace base {
@@ -181,16 +210,14 @@
                         size_t alignment,
                         size_t size,
                         void* context) {
-  return AlignedAllocator()->AlignedAllocFlags(
-      base::PartitionAllocNoHooks, alignment, MaybeAdjustSize(size));
+  return AllocateAlignedMemory(alignment, size);
 }
 
 void* PartitionAlignedAlloc(const AllocatorDispatch* dispatch,
                             size_t size,
                             size_t alignment,
                             void* context) {
-  return AlignedAllocator()->AlignedAllocFlags(
-      base::PartitionAllocNoHooks, alignment, MaybeAdjustSize(size));
+  return AllocateAlignedMemory(alignment, size);
 }
 
 // aligned_realloc documentation is
diff --git a/base/memory/ref_counted_unittest.nc b/base/memory/ref_counted_unittest.nc
index f0a29fe90..0830ce2b 100644
--- a/base/memory/ref_counted_unittest.nc
+++ b/base/memory/ref_counted_unittest.nc
@@ -14,10 +14,7 @@
   ~InitialRefCountIsZero() {}
 };
 
-// TODO(hans): Remove .* and update the static_assert expectations once we roll
-// past Clang r313315. https://crbug.com/765692.
-
-#if defined(NCTEST_ADOPT_REF_TO_ZERO_START)  // [r"fatal error: static_assert failed .*\"Use AdoptRef only if the reference count starts from one\.\""]
+#if defined(NCTEST_ADOPT_REF_TO_ZERO_START)  // [r"fatal error: static_assert failed due to requirement 'std::is_same<base::subtle::StartRefCountFromOneTag, base::subtle::StartRefCountFromZeroTag>::value' \"Use AdoptRef only if the reference count starts from one\.\""]
 
 void WontCompile() {
   AdoptRef(new InitialRefCountIsZero());
diff --git a/base/memory/weak_ptr_unittest.nc b/base/memory/weak_ptr_unittest.nc
index 2c53f36..46e42af7 100644
--- a/base/memory/weak_ptr_unittest.nc
+++ b/base/memory/weak_ptr_unittest.nc
@@ -115,17 +115,14 @@
   WeakPtr<Unrelated> ptr = AsWeakPtr<Unrelated>(&f);
 }
 
-// TODO(hans): Remove .* and update the static_assert expectations once we roll
-// past Clang r313315. https://crbug.com/765692.
-
-#elif defined(NCTEST_COMPLETELY_UNRELATED_HELPER)  // [r"fatal error: static_assert failed .*\"AsWeakPtr argument must inherit from SupportsWeakPtr\""]
+#elif defined(NCTEST_COMPLETELY_UNRELATED_HELPER)  // [r"fatal error: static_assert failed due to requirement 'std::is_base_of<base::internal::SupportsWeakPtrBase, base::Unrelated>::value' \"AsWeakPtr argument must inherit from SupportsWeakPtr\""]
 
 void WontCompile() {
   Unrelated f;
   WeakPtr<Unrelated> ptr = AsWeakPtr(&f);
 }
 
-#elif defined(NCTEST_DERIVED_COMPLETELY_UNRELATED_HELPER)  // [r"fatal error: static_assert failed .*\"AsWeakPtr argument must inherit from SupportsWeakPtr\""]
+#elif defined(NCTEST_DERIVED_COMPLETELY_UNRELATED_HELPER)  // [r"fatal error: static_assert failed due to requirement 'std::is_base_of<base::internal::SupportsWeakPtrBase, base::DerivedUnrelated>::value' \"AsWeakPtr argument must inherit from SupportsWeakPtr\""]
 
 void WontCompile() {
   DerivedUnrelated f;
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
index a867123f..9bf8376 100644
--- a/base/trace_event/malloc_dump_provider.cc
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -81,6 +81,28 @@
     ReportPartitionAllocThreadCacheStats(main_thread_cache_dump,
                                          main_thread_stats);
   }
+
+  // Not reported in UMA, detailed dumps only.
+  if (detailed) {
+    SimplePartitionStatsDumper aligned_allocator_dumper;
+    internal::PartitionAllocMalloc::AlignedAllocator()->DumpStats(
+        "malloc/aligned", !detailed /* is_light_dump */,
+        &aligned_allocator_dumper);
+    // These should be included in the overall figure, so using a child dump.
+    auto* aligned_allocator_dump = pmd->CreateAllocatorDump("malloc/aligned");
+    // See
+    // //base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
+    // for the sum of the aligned and regular partitions.
+    aligned_allocator_dump->AddScalar(
+        "virtual_size", MemoryAllocatorDump::kUnitsBytes,
+        aligned_allocator_dumper.stats().total_mmapped_bytes);
+    aligned_allocator_dump->AddScalar(
+        MemoryAllocatorDump::kNameSize, MemoryAllocatorDump::kUnitsBytes,
+        aligned_allocator_dumper.stats().total_resident_bytes);
+    aligned_allocator_dump->AddScalar(
+        "allocated_size", MemoryAllocatorDump::kUnitsBytes,
+        aligned_allocator_dumper.stats().total_active_bytes);
+  }
 }
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
diff --git a/build/android/gyp/compile_resources.py b/build/android/gyp/compile_resources.py
index 7c71f2bb..ef9e8ad5 100755
--- a/build/android/gyp/compile_resources.py
+++ b/build/android/gyp/compile_resources.py
@@ -460,9 +460,15 @@
       options.android_manifest)
 
   if extra_manifest:
-    _, _, extra_app_nodes = manifest_utils.ParseManifest(extra_manifest)
-    for node in extra_app_nodes:
+    _, extra_manifest_node, extra_app_node = manifest_utils.ParseManifest(
+        extra_manifest)
+    for node in extra_app_node:
       app_node.append(node)
+    for node in extra_manifest_node:
+      # DFM manifests have a bunch of tags we don't care about inside
+      # <manifest>, so only take <queries>.
+      if node.tag == 'queries':
+        manifest_node.append(node)
 
   manifest_utils.AssertUsesSdk(manifest_node, options.min_sdk_version,
                                options.target_sdk_version)
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index ae34d0f..324da28 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20210110.2.1
+0.20210110.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index ae34d0f..324da28 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20210110.2.1
+0.20210110.3.1
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 9e7ab3d..317e970 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -6,6 +6,7 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/compiler/compiler.gni")
+import("//build/config/compiler/pgo/pgo.gni")
 import("//build/config/features.gni")
 import("//build/config/locales.gni")
 import("//build/config/python.gni")
@@ -1069,7 +1070,9 @@
   assert(_framework_binary_path != "",
          "Ignore configuration-dependent unused variable warning")
 
-  if (!is_component_build) {
+  # TOOD(crbug/1163903#c8) - thakis@ look into why profile and coverage
+  # instrumentation adds these symbols in different orders
+  if (!is_component_build && chrome_pgo_phase != 1) {
     action("verify_chrome_framework_order") {
       script = "//chrome/tools/build/mac/verify_order.py"
       stamp_file = "$target_out_dir/run_$target_name.stamp"
diff --git a/chrome/VERSION b/chrome/VERSION
index 391f4ad..1fdb8ae 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=89
 MINOR=0
-BUILD=4385
+BUILD=4386
 PATCH=0
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 42ce5df0..1a1ec57 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -767,7 +767,6 @@
   "java/res/layout/bookmark_widget.xml",
   "java/res/layout/bookmark_widget_icons_only.xml",
   "java/res/layout/bookmark_widget_item.xml",
-  "java/res/layout/bottom_control_container.xml",
   "java/res/layout/chip_view_menu_item.xml",
   "java/res/layout/clear_browsing_data_button.xml",
   "java/res/layout/clear_browsing_data_tabs.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 1337737..8265217 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1389,10 +1389,6 @@
   "java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java",
   "java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBar.java",
   "java/src/org/chromium/chrome/browser/toolbar/ToolbarTabControllerImpl.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java",
-  "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java",
   "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressCoordinator.java",
   "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediator.java",
   "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressProperties.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 3e2c05e..a5d7501 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -207,10 +207,10 @@
   "junit/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapterTest.java",
   "junit/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivityTest.java",
   "junit/src/org/chromium/chrome/browser/sharing/click_to_call/ClickToCallMessageHandlerTest.java",
+  "junit/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImplTest.java",
   "junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java",
   "junit/src/org/chromium/chrome/browser/signin/SigninPromoUtilTest.java",
   "junit/src/org/chromium/chrome/browser/signin/SigninUtilsAccountPickerTest.java",
-  "junit/src/org/chromium/chrome/browser/signin/SigninUtilsStartActivityTest.java",
   "junit/src/org/chromium/chrome/browser/signin/account_picker/AccountPickerDelegateTest.java",
   "junit/src/org/chromium/chrome/browser/status_indicator/StatusIndicatorMediatorTest.java",
   "junit/src/org/chromium/chrome/browser/suggestions/SuggestionsImageFetcherTest.java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 2b42a1a..c282fa9 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -205,6 +205,7 @@
     "//chrome/browser/ui/android/favicon:java",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/browser/ui/android/theme:java",
+    "//chrome/browser/ui/android/toolbar:java",
     "//chrome/browser/ui/messages/android:java",
     "//chrome/browser/util:java",
     "//components/browser_ui/android/bottomsheet:java",
diff --git a/chrome/android/features/tab_ui/DEPS b/chrome/android/features/tab_ui/DEPS
index f93af16..f1ac519 100644
--- a/chrome/android/features/tab_ui/DEPS
+++ b/chrome/android/features/tab_ui/DEPS
@@ -8,6 +8,7 @@
  "+chrome/browser/tabpersistence/android/java",
  "+chrome/browser/ui/android/favicon/java",
  "+chrome/browser/ui/android/theme/java",
+ "+chrome/browser/ui/android/toolbar/java",
  "+chrome/browser/ui/messages/android/java",
  "+components/browser_ui/styles/android",
  "+components/browser_ui/widget/android",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUi.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUi.java
index f9407d4..6b3cfe13 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUi.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUi.java
@@ -4,29 +4,15 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
-import android.app.Activity;
-
 import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
+import org.chromium.chrome.browser.toolbar.bottom.BottomControlsContentDelegate;
 
 /**
- * Interface for the Tab Groups related UI. This UI manages its own visibility through {@link
- * BottomControlsCoordinator.BottomControlsVisibilityController}.
+ * Interface for the Tab Groups related UI.
  */
-public interface TabGroupUi {
-    /**
-     * Called by the ToolbarManager when the system back button is pressed.
-     * @return Whether or not the TabGroupUi consumed the event.
-     */
-    boolean onBackPressed();
-
-    void initializeWithNative(Activity activity,
-            BottomControlsCoordinator.BottomControlsVisibilityController visibilityController);
-
+public interface TabGroupUi extends BottomControlsContentDelegate {
     /**
      * @return {@link Supplier} that provides dialog visibility.
      */
     Supplier<Boolean> getTabGridDialogVisibilitySupplier();
-
-    void destroy();
 }
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index a8586a8..ed8b3941 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -236,9 +236,6 @@
     <dimen name="tablet_toolbar_start_padding">4dp</dimen>
     <dimen name="toolbar_optional_button_animation_translation">10dp</dimen>
 
-    <!-- Bottom controls dimensions -->
-    <dimen name="bottom_controls_height">@dimen/min_touch_target_size</dimen>
-
     <!-- Start surface toolbar dimensions -->
     <dimen name="start_surface_toolbar_button_padding_to_button">8dp</dimen>
     <dimen name="start_surface_toolbar_button_padding_to_edge">16dp</dimen>
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 0330c79..0064f94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1529,12 +1529,10 @@
         if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()) {
             dialogVisibilitySupplier = () -> {
                 assert mStartSurfaceSupplier.get() != null;
-                assert getToolbarManager().getBottomControlsCoordinator() != null;
+                assert getToolbarManager().getTabGroupUi() != null;
                 // Return true if dialog from either tab switcher or tab strip is visible.
                 Supplier<Boolean> tabGroupUiDialogVisibilitySupplier =
-                        getToolbarManager()
-                                .getBottomControlsCoordinator()
-                                .getTabGridDialogVisibilitySupplier();
+                        getToolbarManager().getTabGroupUi().getTabGridDialogVisibilitySupplier();
                 Supplier<Boolean> tabSwitcherDialogVisibilitySupplier =
                         mStartSurfaceSupplier.get().getTabGridDialogVisibilitySupplier();
                 boolean isDialogVisible = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
index 058f185..20f7360 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/base/SplitChromeApplication.java
@@ -95,6 +95,8 @@
         // N), the onComplete function will run immediately so it must handle the case where the
         // base context of the application has not been set yet.
         sSplitPreloader.preload(CHROME_SPLIT_NAME, (chromeContext) -> {
+            // When installed, the vr module is always loaded on startup, so preload here.
+            sSplitPreloader.preload("vr", null);
             // If the chrome module is not enabled or isolated splits are not supported,
             // chromeContext will have the same ClassLoader as the base context, so no need to
             // replace the ClassLoaders here.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
index 3e0c60f..9454f70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
@@ -6,8 +6,6 @@
 
 import android.app.Activity;
 
-import androidx.annotation.Nullable;
-
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.SyncFirstSetupCompleteSource;
 import org.chromium.chrome.browser.childaccounts.ChildAccountService;
@@ -47,12 +45,12 @@
      * This is triggered once per Chrome Application lifetime and every time the Account state
      * changes with early exit if an account has already been signed in.
      */
-    public static void start(@Nullable final Runnable onComplete) {
+    public static void start() {
         ChildAccountService.checkChildAccountStatus(status -> {
             boolean hasChildAccount = ChildAccountStatus.isChild(status);
             AccountManagementFragment.setSignOutAllowedPreferenceValue(!hasChildAccount);
             if (hasChildAccount) {
-                processForcedSignIn(onComplete);
+                processForcedSignIn();
             }
         });
     }
@@ -61,17 +59,16 @@
      * Processes the fully automatic non-FRE-related forced sign-in.
      * This is used to enforce the environment for Android EDU and child accounts.
      */
-    private static void processForcedSignIn(@Nullable final Runnable onComplete) {
+    private static void processForcedSignIn() {
+        final Profile profile = Profile.getLastUsedRegularProfile();
         if (FirstRunUtils.canAllowSync()
-                && IdentityServicesProvider.get()
-                           .getIdentityManager(Profile.getLastUsedRegularProfile())
-                           .hasPrimaryAccount()) {
+                && IdentityServicesProvider.get().getIdentityManager(profile).hasPrimaryAccount()) {
             // TODO(https://crbug.com/1044206): Remove this.
             ProfileSyncService.get().setFirstSetupComplete(SyncFirstSetupCompleteSource.BASIC_FLOW);
         }
 
-        final SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
-                Profile.getLastUsedRegularProfile());
+        final SigninManager signinManager =
+                IdentityServicesProvider.get().getSigninManager(profile);
         // By definition we have finished all the checks for first run.
         signinManager.onFirstRunCheckDone();
         if (!FirstRunUtils.canAllowSync() || !signinManager.isSignInAllowed()) {
@@ -90,17 +87,10 @@
                             // TODO(https://crbug.com/1044206): Remove this.
                             ProfileSyncService.get().setFirstSetupComplete(
                                     SyncFirstSetupCompleteSource.BASIC_FLOW);
-                            if (onComplete != null) {
-                                onComplete.run();
-                            }
                         }
 
                         @Override
-                        public void onSignInAborted() {
-                            if (onComplete != null) {
-                                onComplete.run();
-                            }
-                        }
+                        public void onSignInAborted() {}
                     });
         });
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
index bf1b170..29c9626 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SigninFirstRunFragment.java
@@ -16,7 +16,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ntp.cards.SignInPromo;
 import org.chromium.chrome.browser.signin.SigninFragmentBase;
-import org.chromium.chrome.browser.signin.services.SigninManager;
+import org.chromium.chrome.browser.signin.services.SigninMetricsUtils;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.ChildAccountStatus;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
@@ -52,7 +52,7 @@
                 "Signin.AndroidDeviceAccountsNumberWhenEnteringFRE", Math.min(numAccounts, 2));
         RecordUserAction.record("MobileFre.SignInShown");
         RecordUserAction.record("Signin_Signin_FromStartPage");
-        SigninManager.logSigninStartAccessPoint(SigninAccessPoint.START_PAGE);
+        SigninMetricsUtils.logSigninStartAccessPoint(SigninAccessPoint.START_PAGE);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
index 72b53a0..ca2f6cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ProcessInitializationHandler.java
@@ -371,13 +371,13 @@
         deferredStartupHandler.addDeferredTask(new Runnable() {
             @Override
             public void run() {
-                ForcedSigninProcessor.start(null);
+                ForcedSigninProcessor.start();
                 AccountManagerFacadeProvider.getInstance().addObserver(
                         new AccountsChangeObserver() {
                             @Override
                             public void onAccountsChanged() {
                                 PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT,
-                                        () -> { ForcedSigninProcessor.start(null); });
+                                        () -> { ForcedSigninProcessor.start(); });
                             }
                         });
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImpl.java
index 7def9b9..9414a62 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImpl.java
@@ -10,7 +10,11 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
+import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.signin.ui.SigninActivityLauncher;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 
 /**
@@ -82,6 +86,26 @@
         launchInternal(context, SigninFragment.createArguments(accessPoint));
     }
 
+    /**
+     * Launches the {@link SigninActivity} if signin is allowed.
+     * @param context A {@link Context} object.
+     * @param accessPoint {@link SigninAccessPoint} for starting sign-in flow.
+     * @return a boolean indicating if the {@link SigninActivity} is launched.
+     */
+    @Override
+    public boolean launchActivityIfAllowed(Context context, @SigninAccessPoint int accessPoint) {
+        SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
+                Profile.getLastUsedRegularProfile());
+        if (signinManager.isSignInAllowed()) {
+            launchActivity(context, accessPoint);
+            return true;
+        }
+        if (signinManager.isSigninDisabledByPolicy()) {
+            ManagedPreferencesUtils.showManagedByAdministratorToast(context);
+        }
+        return false;
+    }
+
     private void launchInternal(Context context, Bundle fragmentArgs) {
         Intent intent = SigninActivity.createIntent(context, fragmentArgs);
         context.startActivity(intent);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
index f1961ce..5a7ce31 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninFragment.java
@@ -21,6 +21,7 @@
 import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninManager;
+import org.chromium.chrome.browser.signin.services.SigninMetricsUtils;
 import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.settings.ManageSyncSettings;
@@ -118,7 +119,7 @@
         mPromoAction =
                 getSigninArguments().getInt(ARGUMENT_PERSONALIZED_PROMO_ACTION, PromoAction.NONE);
 
-        SigninManager.logSigninStartAccessPoint(mSigninAccessPoint);
+        SigninMetricsUtils.logSigninStartAccessPoint(mSigninAccessPoint);
         recordSigninStartedHistogramAccountInfo();
         recordSigninStartedUserAction();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java
index 8d77ae01..3e6559af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninPromoUtil.java
@@ -67,7 +67,8 @@
             return false;
         }
 
-        SigninUtils.startSigninActivityIfAllowed(activity, SigninAccessPoint.SIGNIN_PROMO);
+        SigninActivityLauncherImpl.get().launchActivityIfAllowed(
+                activity, SigninAccessPoint.SIGNIN_PROMO);
         preferencesManager.setSigninPromoLastShownVersion(currentMajorVersion);
         preferencesManager.setSigninPromoLastAccountNames(accountNames);
         return true;
@@ -163,7 +164,7 @@
     private static void openSigninActivityForPromo(WindowAndroid window, int accessPoint) {
         Activity activity = window.getActivity().get();
         if (activity != null) {
-            SigninUtils.startSigninActivityIfAllowed(activity, accessPoint);
+            SigninActivityLauncherImpl.get().launchActivityIfAllowed(activity, accessPoint);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
index bed507eac..c1d5e3ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
@@ -31,11 +31,9 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetControllerProvider;
-import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.GAIAServiceType;
 import org.chromium.components.signin.metrics.AccountConsistencyPromoAction;
-import org.chromium.components.signin.metrics.SigninAccessPoint;
 import org.chromium.ui.base.WindowAndroid;
 
 /**
@@ -122,23 +120,4 @@
                         new WebSigninBridge.Factory(), continueUrl),
                 regularTabModel, incognitoTabCreator, HelpAndFeedbackLauncherImpl.getInstance());
     }
-
-    /**
-     * Launches the {@link SigninActivity} if signin is allowed.
-     * @param accessPoint {@link SigninAccessPoint} for starting sign-in flow.
-     * @return a boolean indicating if the SigninActivity is launched.
-     */
-    public static boolean startSigninActivityIfAllowed(
-            Context context, @SigninAccessPoint int accessPoint) {
-        SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
-                Profile.getLastUsedRegularProfile());
-        if (signinManager.isSignInAllowed()) {
-            SigninActivityLauncherImpl.get().launchActivity(context, accessPoint);
-            return true;
-        }
-        if (signinManager.isSigninDisabledByPolicy()) {
-            ManagedPreferencesUtils.showManagedByAdministratorToast(context);
-        }
-        return false;
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java
index 2a28bf3bb..d4d75e96 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java
@@ -18,7 +18,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.signin.SigninUtils;
+import org.chromium.chrome.browser.signin.SigninActivityLauncherImpl;
 import org.chromium.chrome.browser.signin.services.DisplayableProfileData;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.ProfileDataCache;
@@ -193,7 +193,7 @@
         setWidgetLayoutResource(0);
         setViewEnabled(true);
         setOnPreferenceClickListener(pref
-                -> SigninUtils.startSigninActivityIfAllowed(
+                -> SigninActivityLauncherImpl.get().launchActivityIfAllowed(
                         getContext(), SigninAccessPoint.SETTINGS));
 
         if (!mWasGenericSigninPromoDisplayed) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
index 0a90fb5..49c8d967d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
@@ -10,8 +10,10 @@
 
 specific_include_rules = {
   'ToolbarManager.java': [
+    "+chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management",
     "+chrome/android/java/src/org/chromium/chrome/browser",
     "-chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
+    "+chrome/browser/ui/android/toolbar",
   ],
   'ToolbarButtonInProductHelpController.java': [
     "+chrome/android/java/src/org/chromium/chrome/browser",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 012e3a7..f3cc47e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -18,6 +18,7 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
+import android.view.ViewStub;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -90,11 +91,14 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
+import org.chromium.chrome.browser.tasks.tab_management.TabGroupUi;
+import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.theme.ThemeColorProvider.ThemeColorObserver;
 import org.chromium.chrome.browser.theme.ThemeColorProvider.TintObserver;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsCoordinator;
+import org.chromium.chrome.browser.toolbar.bottom.ScrollingBottomViewResourceFrameLayout;
 import org.chromium.chrome.browser.toolbar.load_progress.LoadProgressCoordinator;
 import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
 import org.chromium.chrome.browser.toolbar.top.ActionModeController;
@@ -183,7 +187,6 @@
     private FindToolbarManager mFindToolbarManager;
 
     private LayoutManagerImpl mLayoutManager;
-    private final ObservableSupplier<ShareDelegate> mShareDelegateSupplier;
 
     private TabObserver mTabObserver;
     private BookmarkBridge.BookmarkModelObserver mBookmarksObserver;
@@ -253,6 +256,8 @@
     private ObservableSupplierImpl<Boolean> mOverlayPanelVisibilitySupplier =
             new ObservableSupplierImpl<>();
 
+    private TabGroupUi mTabGroupUi;
+
     private static class TabObscuringCallback implements Callback<Boolean> {
         private final TabObscuringHandler mTabObscuringHandler;
         /** A token held while the toolbar/omnibox is obscuring all visible tabs. */
@@ -350,7 +355,6 @@
         mFullscreenManager = fullscreenManager;
         mActionBarDelegate = new ViewShiftingActionBarDelegate(activity.getSupportActionBar(),
                 controlContainer, activity.findViewById(R.id.action_bar_black_background));
-        mShareDelegateSupplier = shareDelegateSupplier;
         mCanAnimateNativeBrowserControls = canAnimateNativeBrowserControls;
         mScrimCoordinator = scrimCoordinator;
         mTabModelSelectorSupplier = tabModelSelectorSupplier;
@@ -996,19 +1000,24 @@
      * Enable the bottom controls.
      */
     public void enableBottomControls() {
+        View root = ((ViewStub) mActivity.findViewById(R.id.bottom_controls_stub)).inflate();
+        mTabGroupUi = TabManagementModuleProvider.getDelegate().createTabGroupUi(
+                root.findViewById(R.id.bottom_container_slot), mAppThemeColorProvider,
+                mScrimCoordinator, mOmniboxFocusStateSupplier);
         mBottomControlsCoordinatorSupplier.set(
                 new BottomControlsCoordinator(mActivity, mWindowAndroid, mLayoutManager,
                         mCompositorViewHolder.getResourceManager(), mBrowserControlsSizer,
-                        mFullscreenManager, mActivity.findViewById(R.id.bottom_controls_stub),
-                        mAppThemeColorProvider, mShareDelegateSupplier, mScrimCoordinator,
-                        mOmniboxFocusStateSupplier, mOverlayPanelVisibilitySupplier));
+                        mFullscreenManager, (ScrollingBottomViewResourceFrameLayout) root,
+                        mAppThemeColorProvider, mTabGroupUi, mOverlayPanelVisibilitySupplier));
     }
 
     /**
-     * @return The coordinator for the bottom controls if it exists.
+     * TODO(https://crbug.com/1164216): Remove this getter in favor of extracting tab group
+     * feature details from ChromeTabbedActivity directly.
+     * @return The coordinator for the tab group UI if it exists.
      */
-    public BottomControlsCoordinator getBottomControlsCoordinator() {
-        return mBottomControlsCoordinatorSupplier.get();
+    public TabGroupUi getTabGroupUi() {
+        return mTabGroupUi;
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
index 0a6279c..a7c49173 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
@@ -55,6 +55,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.incognito.IncognitoDataTestUtils;
+import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.styles.ChromeColors;
@@ -211,6 +212,36 @@
 
     @Test
     @MediumTest
+    @Features.EnableFeatures({ChromeFeatureList.CCT_INCOGNITO})
+    public void toolbarHasNonPrimaryIncognitoProfile_ForIncognitoCCT() throws Exception {
+        Intent intent = createMinimalIncognitoCustomTabIntent();
+        launchIncognitoCustomTab(intent);
+
+        CustomTabToolbar customTabToolbar =
+                mCustomTabActivityTestRule.getActivity().findViewById(R.id.toolbar);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Profile profile = customTabToolbar.getToolbarDataProvider().getProfile();
+            assertTrue(profile.isOffTheRecord());
+            assertFalse(profile.isPrimaryOTRProfile());
+        });
+    }
+
+    @Test
+    @MediumTest
+    public void toolbarHasRegularProfile_ForRegularCCT() {
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
+                InstrumentationRegistry.getContext(), "about:blank");
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CustomTabToolbar customTabToolbar =
+                mCustomTabActivityTestRule.getActivity().findViewById(R.id.toolbar);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            Profile profile = customTabToolbar.getToolbarDataProvider().getProfile();
+            assertFalse(profile.isOffTheRecord());
+        });
+    }
+
+    @Test
+    @MediumTest
     @Features.DisableFeatures({ChromeFeatureList.CCT_INCOGNITO})
     public void canLaunchFirstPartyIncognitoWithExtraWhenDisabled() throws Exception {
         Intent intent = createMinimalIncognitoCustomTabIntent();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarDataProviderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarDataProviderTest.java
index e70191b..6b3cb71 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarDataProviderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/ToolbarDataProviderTest.java
@@ -9,9 +9,6 @@
 
 import static org.chromium.base.test.util.Batch.PER_CLASS;
 
-import android.content.Intent;
-import android.support.test.InstrumentationRegistry;
-
 import androidx.test.filters.MediumTest;
 
 import org.junit.Rule;
@@ -21,15 +18,9 @@
 
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
-import org.chromium.chrome.browser.customtabs.IncognitoCustomTabActivityTestRule;
-import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.incognito.IncognitoDataTestUtils;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.toolbar.top.ToolbarPhone;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -39,15 +30,12 @@
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
-import java.util.concurrent.TimeoutException;
-
 /**
  * Instrumentation tests for {@link ToolbarDataProvider}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-@Features.EnableFeatures({ChromeFeatureList.CCT_INCOGNITO})
 @Batch(PER_CLASS)
 public class ToolbarDataProviderTest {
     @Rule
@@ -57,10 +45,6 @@
     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
 
     @Rule
-    public IncognitoCustomTabActivityTestRule mCustomTabActivityTestRule =
-            new IncognitoCustomTabActivityTestRule();
-
-    @Rule
     public BlankCTATabInitialStateRule mBlankCTATabInitialStateRule =
             new BlankCTATabInitialStateRule(mActivityTestRule, false);
 
@@ -85,35 +69,4 @@
             assertFalse(profile.isOffTheRecord());
         });
     }
-
-    @Test
-    @MediumTest
-    @FlakyTest(message = "https://crbug.com/1154445")
-    public void testNonPrimaryOTRProfileUsedForIncognitoCCT() throws TimeoutException {
-        Intent intent = CustomTabsTestUtils.createMinimalIncognitoCustomTabIntent(
-                InstrumentationRegistry.getContext(), "about:blank");
-        IncognitoDataTestUtils.fireAndWaitForCctWarmup();
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CustomTabToolbar customTabToolbar =
-                mCustomTabActivityTestRule.getActivity().findViewById(R.id.toolbar);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            Profile profile = customTabToolbar.getToolbarDataProvider().getProfile();
-            assertTrue(profile.isOffTheRecord());
-            assertFalse(profile.isPrimaryOTRProfile());
-        });
-    }
-
-    @Test
-    @MediumTest
-    public void testRegularProfileUsedForRegularCCT() {
-        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(
-                InstrumentationRegistry.getContext(), "about:blank");
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CustomTabToolbar customTabToolbar =
-                mCustomTabActivityTestRule.getActivity().findViewById(R.id.toolbar);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            Profile profile = customTabToolbar.getToolbarDataProvider().getProfile();
-            assertFalse(profile.isOffTheRecord());
-        });
-    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninUtilsStartActivityTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImplTest.java
similarity index 64%
rename from chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninUtilsStartActivityTest.java
rename to chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImplTest.java
index d75c260..de1d98d 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninUtilsStartActivityTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninActivityLauncherImplTest.java
@@ -1,13 +1,12 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2021 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 package org.chromium.chrome.browser.signin;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.MockitoAnnotations.initMocks;
@@ -29,14 +28,16 @@
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 
-/** Tests for the method startSigninActivityIfAllowed {@link SigninUtils}. */
+/**
+ * Tests {@link SigninActivityLauncherImpl}.
+ */
 @RunWith(BaseRobolectricTestRunner.class)
-public class SigninUtilsStartActivityTest {
+public class SigninActivityLauncherImplTest {
     @Mock
     private SigninManager mSigninManagerMock;
 
     @Mock
-    private SigninActivityLauncherImpl mLauncherMock;
+    private Context mContextMock;
 
     @Mock
     private Profile mProfile;
@@ -49,40 +50,34 @@
         IdentityServicesProvider.setInstanceForTests(mock(IdentityServicesProvider.class));
         Profile.setLastUsedProfileForTesting(mProfile);
         when(IdentityServicesProvider.get().getSigninManager(any())).thenReturn(mSigninManagerMock);
-        SigninActivityLauncherImpl.setLauncherForTest(mLauncherMock);
     }
 
     @Test
-    public void testSigninActivityLaunchedWhenSigninIsAllowed() {
+    public void testLaunchActivityIfAllowedWhenSigninIsAllowed() {
         when(mSigninManagerMock.isSignInAllowed()).thenReturn(true);
-        Assert.assertTrue(
-                SigninUtils.startSigninActivityIfAllowed(mContext, SigninAccessPoint.SETTINGS));
-        verify(SigninActivityLauncherImpl.get())
-                .launchActivity(mContext, SigninAccessPoint.SETTINGS);
+        Assert.assertTrue(SigninActivityLauncherImpl.get().launchActivityIfAllowed(
+                mContextMock, SigninAccessPoint.SETTINGS));
+        verify(mContextMock).startActivity(notNull());
     }
 
     @Test
-    public void testSigninActivityNotLaunchedWhenSigninIsNotAllowed() {
+    public void testLaunchActivityIfAllowedWhenSigninIsNotAllowed() {
         when(mSigninManagerMock.isSignInAllowed()).thenReturn(false);
         when(mSigninManagerMock.isSigninDisabledByPolicy()).thenReturn(false);
         Object toastBeforeCall = ShadowToast.getLatestToast();
-        Assert.assertFalse(
-                SigninUtils.startSigninActivityIfAllowed(mContext, SigninAccessPoint.SETTINGS));
+        Assert.assertFalse(SigninActivityLauncherImpl.get().launchActivityIfAllowed(
+                mContext, SigninAccessPoint.SETTINGS));
         Object toastAfterCall = ShadowToast.getLatestToast();
-        verify(SigninActivityLauncherImpl.get(), never())
-                .launchActivity(any(Context.class), anyInt());
         Assert.assertEquals(
                 "No new toast should be made during the call!", toastBeforeCall, toastAfterCall);
     }
 
     @Test
-    public void testToastShownWhenSigninIsDisabledByPolicy() {
+    public void testLaunchActivityIfAllowedWhenSigninIsDisabledByPolicy() {
         when(mSigninManagerMock.isSignInAllowed()).thenReturn(false);
         when(mSigninManagerMock.isSigninDisabledByPolicy()).thenReturn(true);
-        Assert.assertFalse(
-                SigninUtils.startSigninActivityIfAllowed(mContext, SigninAccessPoint.SETTINGS));
-        verify(SigninActivityLauncherImpl.get(), never())
-                .launchActivity(any(Context.class), anyInt());
+        Assert.assertFalse(SigninActivityLauncherImpl.get().launchActivityIfAllowed(
+                mContext, SigninAccessPoint.SETTINGS));
         Assert.assertTrue(ShadowToast.showedCustomToast(
                 mContext.getResources().getString(R.string.managed_by_your_organization),
                 R.id.toast_text));
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index da0660e..424233c1 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-89.0.4383.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-89.0.4384.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index 944e278..354f4a3 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -578,10 +578,6 @@
 #if defined(OS_ANDROID)
   UmaSessionStats::OnStartup();
 #endif
-
-#if defined(OS_MAC)
-  chrome::CacheChannelInfo();
-#endif
 }
 
 bool ChromeMainDelegate::ShouldCreateFeatureList() {
diff --git a/chrome/browser/chromeos/camera_mic/vm_camera_mic_manager.cc b/chrome/browser/chromeos/camera_mic/vm_camera_mic_manager.cc
index 6775ade..b5e8a5d 100644
--- a/chrome/browser/chromeos/camera_mic/vm_camera_mic_manager.cc
+++ b/chrome/browser/chromeos/camera_mic/vm_camera_mic_manager.cc
@@ -302,6 +302,8 @@
   rich_notification_data.pinned = true;
   rich_notification_data.buttons.emplace_back(
       l10n_util::GetStringUTF16(IDS_INTERNAL_APP_SETTINGS));
+  rich_notification_data.fullscreen_visibility =
+      message_center::FullscreenVisibility::OVER_USER;
 
   message_center::Notification notification(
       message_center::NOTIFICATION_TYPE_SIMPLE, GetNotificationId(vm, type),
diff --git a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
index d0acf14..de39c29 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_jstest.cc
@@ -50,7 +50,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DirectoryTreeTest) {
-  RunTestURL("foreground/js/ui/directory_tree_unittest_gen.html");
+  RunTestURL("foreground/js/ui/directory_tree_unittest.m_gen.html");
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerJsTest, DriveSyncHandlerTest) {
diff --git a/chrome/browser/chromeos/login/app_mode/kiosk_browsertest.cc b/chrome/browser/chromeos/login/app_mode/kiosk_browsertest.cc
index 04bd564..acc0f101 100644
--- a/chrome/browser/chromeos/login/app_mode/kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/app_mode/kiosk_browsertest.cc
@@ -83,6 +83,7 @@
 #include "chrome/browser/ui/webui/chromeos/login/kiosk_enable_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/user_creation_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/welcome_screen_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
@@ -1347,6 +1348,23 @@
   ASSERT_TRUE(catcher.GetNextResult()) << catcher.message();
 }
 
+IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest, OpenA11ySettings) {
+  StartAppLaunchFromLoginScreen(
+      NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE);
+  WaitForAppLaunchWithOptions(true /* check_launch_data */,
+                              false /* terminate_app */,
+                              true /* keep_app_open */);
+
+  auto* settings_manager = chrome::SettingsWindowManager::GetInstance();
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+
+  settings_manager->ShowOSSettings(
+      profile, chromeos::settings::mojom::kManageAccessibilitySubpagePath);
+
+  Browser* settings_browser = settings_manager->FindBrowserForProfile(profile);
+  ASSERT_TRUE(settings_browser);
+}
+
 IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest, SettingsWindow) {
   StartAppLaunchFromLoginScreen(
       NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE);
diff --git a/chrome/browser/chromeos/login/app_mode/web_kiosk_browsertest.cc b/chrome/browser/chromeos/login/app_mode/web_kiosk_browsertest.cc
index 1a7611d..a7b8819 100644
--- a/chrome/browser/chromeos/login/app_mode/web_kiosk_browsertest.cc
+++ b/chrome/browser/chromeos/login/app_mode/web_kiosk_browsertest.cc
@@ -18,10 +18,13 @@
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/ownership/fake_owner_settings_service.h"
 #include "chrome/browser/chromeos/policy/device_local_account.h"
+#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client_test_helper.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
+#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom-forward.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "components/account_id/account_id.h"
 #include "content/public/test/browser_task_environment.h"
@@ -279,4 +282,20 @@
   ExpectKeyboardConfig();
 }
 
+IN_PROC_BROWSER_TEST_F(WebKioskTest, OpenA11ySettings) {
+  SetOnline(true);
+  PrepareAppLaunch();
+  LaunchApp();
+  KioskSessionInitializedWaiter().Wait();
+
+  auto* settings_manager = chrome::SettingsWindowManager::GetInstance();
+  Profile* profile = ProfileManager::GetPrimaryUserProfile();
+
+  settings_manager->ShowOSSettings(
+      profile, chromeos::settings::mojom::kManageAccessibilitySubpagePath);
+
+  Browser* settings_browser = settings_manager->FindBrowserForProfile(profile);
+  ASSERT_TRUE(settings_browser);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/security_token_login_browsertest.cc b/chrome/browser/chromeos/login/security_token_login_browsertest.cc
index a5e8f71..2cda41b 100644
--- a/chrome/browser/chromeos/login/security_token_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/security_token_login_browsertest.cc
@@ -67,9 +67,9 @@
 constexpr char kPinDialogInvalidPinTitle[] = "Invalid PIN.";
 constexpr char kPinDialogInvalidPin2AttemptsTitle[] =
     "Invalid PIN. 2 attempts left";
-// TODO(crbug.com/1060695): Fix the incorrect plural in the message.
+
 constexpr char kPinDialogInvalidPin1AttemptTitle[] =
-    "Invalid PIN. 1 attempts left";
+    "Invalid PIN. 1 attempt left";
 constexpr char kPinDialogNoAttemptsLeftTitle[] =
     "Maximum allowed attempts exceeded.";
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notification_helper.cc b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notification_helper.cc
index f068302..fa414972 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notification_helper.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_notification_helper.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "ash/public/cpp/ash_features.h"
+#include "ash/public/cpp/style/color_provider.h"
 #include "ash/public/cpp/toast_data.h"
 #include "ash/public/cpp/toast_manager.h"
 #include "ash/public/cpp/window_tree_host_lookup.h"
@@ -28,6 +29,7 @@
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/text_utils.h"
@@ -52,9 +54,6 @@
 // The blur radius for the bubble background.
 constexpr int kBubbleBlurRadius = 80;
 
-// The alpha component of the bubble background.
-constexpr float kBubbleBackgroundAlpha = 0.8f;
-
 // The size of the managed icon.
 constexpr int kManagedIconSize = 20;
 
@@ -85,8 +84,11 @@
 // The duration of the clipboard toast.
 constexpr int kToastDurationMs = 2500;
 
-// The font of the text used in the bubble.
-constexpr char kTextFont[] = "Roboto, 13px";
+// The font name of the text used in the bubble.
+constexpr char kTextFontName[] = "Roboto";
+
+// The font size of the text used in the bubble.
+constexpr int kTextFontSize = 13;
 
 // The height of the dismiss button.
 constexpr int kButtonHeight = 32;
@@ -108,16 +110,26 @@
     const base::string16 button_label(l10n_util::GetStringUTF16(
         IDS_POLICY_DLP_CLIPBOARD_BLOCK_DISMISS_BUTTON));
     SetText(button_label);
-    label()->SetFontList(gfx::FontList(kTextFont));
-    SetTextColor(ButtonState::STATE_NORMAL, gfx::kGoogleBlue800);
+
+    const gfx::FontList font_list = GetFontList();
+    label()->SetFontList(font_list);
+
+    SetTextColor(
+        ButtonState::STATE_NORMAL,
+        ash::ColorProvider::Get()->GetContentLayerColor(
+            ash::ColorProvider::ContentLayerType::kButtonLabelColorBlue));
     SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER);
-    SetSize({gfx::GetStringWidth(button_label, gfx::FontList(kTextFont)) +
-                 2 * kButtonPadding,
+    SetSize({gfx::GetStringWidth(button_label, font_list) + 2 * kButtonPadding,
              kButtonHeight});
   }
 
   int GetLabelWidth() { return label()->bounds().width(); }
 
+  gfx::FontList GetFontList() {
+    return gfx::FontList({kTextFontName}, gfx::Font::NORMAL, kTextFontSize,
+                         gfx::Font::Weight::MEDIUM);
+  }
+
   DismissButton(const DismissButton&) = delete;
   DismissButton& operator=(const DismissButton&) = delete;
 
@@ -128,17 +140,17 @@
 class ClipboardBubbleView : public views::View {
  public:
   explicit ClipboardBubbleView(const base::string16& text) {
-    // TODO(crbug.com/1150740): Change colors in case of dark mode.
-
     SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-    layer()->SetColor(
-        SkColorSetA(SK_ColorWHITE, SK_AlphaOPAQUE * kBubbleBackgroundAlpha));
+    ash::ColorProvider* color_provider = ash::ColorProvider::Get();
+    layer()->SetColor(color_provider->GetBaseLayerColor(
+        ash::ColorProvider::BaseLayerType::kTransparent80));
     if (ash::features::IsBackgroundBlurEnabled())
       layer()->SetBackgroundBlur(kBubbleBlurRadius);
     layer()->SetRoundedCornerRadius(kCornerRadii);
 
     // Add the managed icon.
-    SkColor icon_color = SK_ColorGRAY;
+    SkColor icon_color = color_provider->GetContentLayerColor(
+        ash::ColorProvider::ContentLayerType::kIconColorPrimary);
     clipboard_icon_ = AddChildView(std::make_unique<views::ImageView>());
     clipboard_icon_->SetPaintToLayer();
     clipboard_icon_->layer()->SetFillsBoundsOpaquely(false);
@@ -157,8 +169,11 @@
     // Set the styling of the text.
     // TODO(crbug.com/1150741): Handle RTL.
     label_->SetText(text);
-    label_->SetFontList(gfx::FontList("Roboto, 13px"));
-    label_->SetEnabledColor(SK_ColorBLACK);
+    label_->SetFontList(gfx::FontList({kTextFontName}, gfx::Font::NORMAL,
+                                      kTextFontSize,
+                                      gfx::Font::Weight::NORMAL));
+    label_->SetEnabledColor(color_provider->GetContentLayerColor(
+        ash::ColorProvider::ContentLayerType::kTextColorPrimary));
     label_->SetLineHeight(kLineHeight);
     label_->SetMultiLine(true);
     label_->SizeToFit(kBubbleWidth - 2 * kBubblePadding - kManagedIconSize -
diff --git a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc
index 3f3aa09..aad16cd2 100644
--- a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc
+++ b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "ash/public/cpp/tablet_mode.h"
 #include "base/files/file_path.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -19,9 +18,7 @@
 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
-#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
 #include "chrome/browser/web_applications/components/web_app_id_constants.h"
 #include "chrome/browser/web_applications/web_app_tab_helper.h"
 #include "chrome/browser/web_launch/web_launch_files_helper.h"
@@ -34,6 +31,15 @@
 #include "ui/gfx/native_widget_types.h"
 #include "url/gurl.h"
 
+// static
+void ChromeCameraAppUIDelegate::CameraAppDialog::ShowIntent(
+    const std::string& queries,
+    gfx::NativeWindow parent) {
+  std::string url = chromeos::kChromeUICameraAppMainURL + queries;
+  CameraAppDialog* dialog = new CameraAppDialog(url);
+  dialog->ShowSystemDialog(parent);
+}
+
 ChromeCameraAppUIDelegate::CameraAppDialog::CameraAppDialog(
     const std::string& url)
     : chromeos::SystemWebDialogDelegate(GURL(url),
@@ -74,23 +80,6 @@
 ChromeCameraAppUIDelegate::ChromeCameraAppUIDelegate(content::WebUI* web_ui)
     : web_ui_(web_ui) {}
 
-// static
-void ChromeCameraAppUIDelegate::ShowIntent(const std::string& queries,
-                                           gfx::NativeWindow parent) {
-  std::string url = chromeos::kChromeUICameraAppMainURL + queries;
-
-  // For tablet mode, apps should better display in fullscreen. Therefore, it is
-  // preferable to launch CCA into a browser, which will handle the tablet mode
-  // case itself, rather than a system dialog.
-  if (ash::TabletMode::Get()->InTabletMode()) {
-    web_app::LaunchSystemWebApp(ProfileManager::GetActiveUserProfile(),
-                                web_app::SystemAppType::CAMERA, GURL(url));
-  } else {
-    CameraAppDialog* dialog = new CameraAppDialog(url);
-    dialog->ShowSystemDialog(parent);
-  }
-}
-
 void ChromeCameraAppUIDelegate::SetLaunchDirectory() {
   Profile* profile = Profile::FromWebUI(web_ui_);
   content::WebContents* web_contents = web_ui_->GetWebContents();
diff --git a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h
index b106f2cf..403b1d78c1 100644
--- a/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h
+++ b/chrome/browser/chromeos/web_applications/chrome_camera_app_ui_delegate.h
@@ -36,7 +36,8 @@
  public:
   class CameraAppDialog : public chromeos::SystemWebDialogDelegate {
    public:
-    explicit CameraAppDialog(const std::string& url);
+    static void ShowIntent(const std::string& queries,
+                           gfx::NativeWindow parent);
 
     // SystemWebDialogDelegate
     ui::ModalType GetDialogModalType() const override;
@@ -54,6 +55,7 @@
         blink::mojom::MediaStreamType type) override;
 
    private:
+    explicit CameraAppDialog(const std::string& url);
     ~CameraAppDialog() override;
 
     DISALLOW_COPY_AND_ASSIGN(CameraAppDialog);
@@ -65,8 +67,6 @@
   ChromeCameraAppUIDelegate& operator=(const ChromeCameraAppUIDelegate&) =
       delete;
 
-  static void ShowIntent(const std::string& queries, gfx::NativeWindow parent);
-
   // CameraAppUIDelegate
   void SetLaunchDirectory() override;
   void PopulateLoadTimeData(content::WebUIDataSource* source) override;
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 6598c55..05222bef 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -1611,8 +1611,8 @@
     wp_prefs->SetKey(kDevToolsApp, std::move(dev_tools_defaults));
   }
 
-  if (Browser::GetBrowserCreationStatusForProfile(profile_) !=
-      Browser::BrowserCreationStatus::kOk) {
+  if (Browser::GetCreationStatusForProfile(profile_) !=
+      Browser::CreationStatus::kOk) {
     return;
   }
   browser_ =
diff --git a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
index 6603123..28ee35f 100644
--- a/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
+++ b/chrome/browser/native_file_system/chrome_native_file_system_permission_context.cc
@@ -383,7 +383,6 @@
 void ChromeNativeFileSystemPermissionContext::PerformAfterWriteChecks(
     std::unique_ptr<content::NativeFileSystemWriteItem> item,
     content::GlobalFrameRoutingId frame_id,
-
     base::OnceCallback<void(AfterWriteCheckResult)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   content::GetUIThreadTaskRunner({})->PostTask(
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index b9216e04..0b3437e 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -55,7 +55,6 @@
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/views/controls/button/image_button.h"
 #include "ui/views/view_observer.h"
-#include "ui/views/widget/widget_observer.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/public/cpp/accelerators.h"
@@ -226,31 +225,6 @@
       ASSERT_EQ(IsOverlayWindowControlVisible(control), expected_visible);
   }
 
-  class WidgetBoundsChangeWaiter final : public views::WidgetObserver {
-   public:
-    explicit WidgetBoundsChangeWaiter(views::Widget* widget)
-        : widget_(widget), initial_bounds_(widget->GetWindowBoundsInScreen()) {
-      widget_->AddObserver(this);
-    }
-
-    ~WidgetBoundsChangeWaiter() final { widget_->RemoveObserver(this); }
-
-    void OnWidgetBoundsChanged(views::Widget* widget, const gfx::Rect&) final {
-      run_loop_.Quit();
-    }
-
-    void Wait() {
-      if (widget_->GetWindowBoundsInScreen() != initial_bounds_)
-        return;
-      run_loop_.Run();
-    }
-
-   private:
-    views::Widget* widget_;
-    gfx::Rect initial_bounds_;
-    base::RunLoop run_loop_;
-  };
-
   void MoveMouseOverOverlayWindow() {
     auto* const window = GetOverlayWindow();
     gfx::Point p(window->GetBounds().x(), window->GetBounds().y());
@@ -1328,66 +1302,6 @@
   EXPECT_FALSE(window_controller()->GetWindowForTesting()->IsVisible());
 }
 
-// Tests that when a new surface id is sent to the Picture-in-Picture window, it
-// doesn't move back to its default position.
-// TODO(crbug.com/1146047): Test is flaky on Linux.
-// TODO(crbug.com/1052397): Revisit once build flag switch of lacros-chrome is
-// complete.
-#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
-#define MAYBE_SurfaceIdChangeDoesNotMoveWindow DISABLED_SurfaceIdChangeDoesNotMoveWindow
-#else
-#define MAYBE_SurfaceIdChangeDoesNotMoveWindow SurfaceIdChangeDoesNotMoveWindow
-#endif
-IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
-                       MAYBE_SurfaceIdChangeDoesNotMoveWindow) {
-  LoadTabAndEnterPictureInPicture(
-      browser(), base::FilePath(kPictureInPictureWindowSizePage));
-
-  content::WebContents* active_web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  ASSERT_NE(GetOverlayWindow(), nullptr);
-  ASSERT_TRUE(GetOverlayWindow()->IsVisible());
-
-  const auto max_size = GetOverlayWindow()->GetMaximumSize();
-  const int side_length = std::min(max_size.width(), max_size.height());
-
-  // Move and resize the window to the top left corner and wait for ack.
-  {
-    WidgetBoundsChangeWaiter waiter(GetOverlayWindow());
-
-    GetOverlayWindow()->SetBounds(gfx::Rect(0, 0, side_length, side_length));
-
-    waiter.Wait();
-  }
-
-  // Wait for signal that the window was resized.
-  base::string16 expected_title = base::ASCIIToUTF16("resized");
-  EXPECT_EQ(expected_title,
-            content::TitleWatcher(active_web_contents, expected_title)
-                .WaitAndGetTitle());
-
-  // Simulate a new surface layer and a change in aspect ratio then wait for
-  // ack.
-  {
-    WidgetBoundsChangeWaiter waiter(GetOverlayWindow());
-
-    GetOverlayWindow()->SetSurfaceId(viz::SurfaceId(
-        viz::FrameSinkId(1, 1),
-        viz::LocalSurfaceId(9, base::UnguessableToken::Create())));
-    GetOverlayWindow()->UpdateVideoSize(
-        gfx::Size(side_length / 2, side_length / 4));
-
-    waiter.Wait();
-  }
-
-  // The position should be closer to the (0, 0) than the default one (bottom
-  // right corner). This will be reflected by checking that the position is
-  // below (100, 100).
-  EXPECT_LT(GetOverlayWindow()->GetBounds().x(), 100);
-  EXPECT_LT(GetOverlayWindow()->GetBounds().y(), 100);
-}
-
 // Tests that the Picture-in-Picture state is properly updated when the window
 // is closed at a system level.
 IN_PROC_BROWSER_TEST_F(PictureInPictureWindowControllerBrowserTest,
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 11c48ce..1e9b66d3 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -941,8 +941,8 @@
   Profile* otr_profile =
       browser()->profile()->GetOffTheRecordProfile(otr_profile_id);
 
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-            Browser::GetBrowserCreationStatusForProfile(otr_profile));
+  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+            Browser::GetCreationStatusForProfile(otr_profile));
 }
 
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog_old.html b/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog_old.html
index 257ca14..33ebd4c 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog_old.html
+++ b/chrome/browser/resources/chromeos/login/components/oobe_adaptive_dialog/oobe_adaptive_dialog_old.html
@@ -21,27 +21,13 @@
         no-lazy$="[[noLazy]]" no-header$="[[noHeader]]"
         no-footer-padding$="[[noFooterPadding]]"
         footer-shrinkable$="[[footerShrinkable]]">
-      <div slot="oobe-icon">
-        <slot name="icon"></slot>
-      </div>
-      <div slot="title">
-        <slot name="title"></slot>
-      </div>
-      <div slot="progress">
-        <slot name="progress"></slot>
-      </div>
-      <div slot="subtitle">
-        <slot name="subtitle"></slot>
-      </div>
-      <div slot="footer">
-        <slot name="content"></slot>
-      </div>
-      <div slot="back-navigation">
-        <slot name="back-navigation"></slot>
-      </div>
-      <div slot="bottom-buttons">
-        <slot name="bottom-buttons"></slot>
-      </div>
+      <slot slot="oobe-icon" name="icon"></slot>
+      <slot slot="title" name="title"></slot>
+      <slot slot="subtitle" name="subtitle"></slot>
+      <slot slot="progress" name="progress"></slot>
+      <slot slot="footer" name="content"></slot>
+      <slot slot="back-navigation" name="back-navigation"></slot>
+      <slot slot="bottom-buttons" name="bottom-buttons"></slot>
     </oobe-dialog>
   </template>
   <script src="oobe_adaptive_dialog.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/components/oobe_types.js b/chrome/browser/resources/chromeos/login/components/oobe_types.js
index 0380a16c..430dabd 100644
--- a/chrome/browser/resources/chromeos/login/components/oobe_types.js
+++ b/chrome/browser/resources/chromeos/login/components/oobe_types.js
@@ -129,8 +129,10 @@
  * @typedef {{
  *   codeType: OobeTypes.SecurityTokenPinDialogType,
  *   enableUserInput: boolean,
- *   errorLabel: OobeTypes.SecurityTokenPinDialogErrorType,
  *   attemptsLeft: number,
+ *   hasError: boolean,
+ *   formattedError: string,
+ *   formattedAttemptsLeft: string,
  * }}
  */
 OobeTypes.SecurityTokenPinDialogParameters;
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.html b/chrome/browser/resources/chromeos/login/security_token_pin.html
index f88d0ef..fbebaac2 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.html
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.html
@@ -33,16 +33,14 @@
       <div slot="footer">
         <div id="pinKeyboardContainer" hidden="[[processingCompletion_]]">
           <pin-keyboard id="pinKeyboard" enable-letters allow-non-digit
-              has-error="[[isErrorLabelVisible_(errorLabelId_, userEdited_)]]"
-              aria-label="[[getLabel_(locale, parameters, errorLabelId_,
-                                      userEdited_)]]"
+              has-error="[[isErrorLabelVisible_(parameters, userEdited_)]]"
+              aria-label="[[getLabel_(parameters, userEdited_)]]"
               on-pin-change="onPinChange_" on-submit="onSubmit_"
               disabled="[[!canEdit_]]">
             <div id="errorContainer" role="alert" problem
                 invisible$="[[!isLabelVisible_(parameters, userEdited_)]]">
               <iron-icon id="errorIcon" icon="cr:error-outline"></iron-icon>
-              <span id="error">[[getLabel_(locale, parameters, errorLabelId_,
-                                           userEdited_)]]</span>
+              <span id="error">[[getLabel_(parameters, userEdited_)]]</span>
             </div>
           </pin-keyboard>
         </div>
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.js b/chrome/browser/resources/chromeos/login/security_token_pin.js
index cc7981a..f3c7542 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.js
+++ b/chrome/browser/resources/chromeos/login/security_token_pin.js
@@ -35,16 +35,6 @@
     },
 
     /**
-     * The i18n string ID containing the error label to be shown to the user.
-     * Is null when there's no error label.
-     * @private
-     */
-    errorLabelId_: {
-      type: String,
-      computed: 'computeErrorLabelId_(parameters)',
-    },
-
-    /**
      * Whether the current state is the wait for the processing completion
      * (i.e., the backend is verifying the entered PIN).
      * @private
@@ -80,7 +70,7 @@
     canEdit_: {
       type: Boolean,
       computed:
-          'computeCanEdit_(parameters.attemptsLeft, processingCompletion_)',
+          'computeCanEdit_(parameters.enableUserInput, processingCompletion_)',
     },
 
     /**
@@ -89,7 +79,7 @@
      */
     canSubmit_: {
       type: Boolean,
-      computed: 'computeCanSubmit_(parameters.attemptsLeft, ' +
+      computed: 'computeCanSubmit_(parameters.enableUserInput, ' +
           'hasValue_, processingCompletion_)',
     },
   },
@@ -102,51 +92,26 @@
   },
 
   /**
-   * Returns the i18n string ID for the current error label.
-   * @param {OobeTypes.SecurityTokenPinDialogParameters} parameters
-   * @return {string|null}
-   * @private
-   */
-  computeErrorLabelId_(parameters) {
-    if (!parameters)
-      return null;
-    switch (parameters.errorLabel) {
-      case OobeTypes.SecurityTokenPinDialogErrorType.NONE:
-        return null;
-      case OobeTypes.SecurityTokenPinDialogErrorType.UNKNOWN:
-        return 'securityTokenPinDialogUnknownError';
-      case OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN:
-        return 'securityTokenPinDialogUnknownInvalidPin';
-      case OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PUK:
-        return 'securityTokenPinDialogUnknownInvalidPuk';
-      case OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED:
-        return 'securityTokenPinDialogUnknownMaxAttemptsExceeded';
-      default:
-        assertNotReached(`Unexpected enum value: ${parameters.errorLabel}`);
-    }
-  },
-
-  /**
    * Computes the value of the canEdit_ property.
-   * @param {number} attemptsLeft
+   * @param {boolean} enableUserInput
    * @param {boolean} processingCompletion
    * @return {boolean}
    * @private
    */
-  computeCanEdit_(attemptsLeft, processingCompletion) {
-    return attemptsLeft != 0 && !processingCompletion;
+  computeCanEdit_(enableUserInput, processingCompletion) {
+    return enableUserInput && !processingCompletion;
   },
 
   /**
    * Computes the value of the canSubmit_ property.
-   * @param {number} attemptsLeft
+   * @param {boolean} enableUserInput
    * @param {boolean} hasValue
    * @param {boolean} processingCompletion
    * @return {boolean}
    * @private
    */
-  computeCanSubmit_(attemptsLeft, hasValue, processingCompletion) {
-    return attemptsLeft != 0 && hasValue && !processingCompletion;
+  computeCanSubmit_(enableUserInput, hasValue, processingCompletion) {
+    return enableUserInput && hasValue && !processingCompletion;
   },
 
   /**
@@ -203,10 +168,7 @@
    * @private
    */
   isErrorLabelVisible_(parameters, userEdited) {
-    return parameters &&
-        parameters.errorLabel !==
-        OobeTypes.SecurityTokenPinDialogErrorType.NONE &&
-        !userEdited;
+    return parameters && parameters.hasError && !userEdited;
   },
 
   /**
@@ -234,14 +196,12 @@
 
   /**
    * Returns the label to be used for the PIN input field.
-   * @param {string} locale
    * @param {OobeTypes.SecurityTokenPinDialogParameters} parameters
-   * @param {string|null} errorLabelId
    * @param {boolean} userEdited
    * @return {string}
    * @private
    */
-  getLabel_(locale, parameters, errorLabelId, userEdited) {
+  getLabel_(parameters, userEdited) {
     if (!this.isLabelVisible_(parameters, userEdited)) {
       // Neither error nor the number of left attempts are to be displayed.
       return '';
@@ -249,20 +209,9 @@
     if (!this.isErrorLabelVisible_(parameters, userEdited) &&
         this.isAttemptsLeftVisible_(parameters)) {
       // There's no error, but the number of left attempts has to be displayed.
-      return this.i18n(
-          'securityTokenPinDialogAttemptsLeft', parameters.attemptsLeft);
+      return parameters.formattedAttemptsLeft;
     }
-    // If we get here, |parameters| must be defined, and |parameters.errorLabel|
-    // != NONE, so |errorLabelId| will be defined.
-    assert(errorLabelId);
-    // Format the error and, if present, the number of left attempts.
-    if ((parameters && !parameters.enableUserInput) ||
-        !this.isAttemptsLeftVisible_(parameters)) {
-      return this.i18n(errorLabelId);
-    }
-    return this.i18n(
-        'securityTokenPinDialogErrorAttempts', this.i18n(errorLabelId),
-        parameters.attemptsLeft);
+    return parameters.formattedError;
   },
 });
 })();
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js b/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js
index 6420c00..5175ca3 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js
+++ b/chrome/browser/resources/chromeos/login/security_token_pin_browsertest.js
@@ -36,8 +36,10 @@
   const DEFAULT_PARAMETERS = {
     codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
     enableUserInput: true,
-    errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.NONE,
-    attemptsLeft: -1
+    attemptsLeft: -1,
+    hasError: false,
+    formattedError: '',
+    formattedAttemptsLeft: ''
   };
 
   let securityTokenPin;
@@ -109,8 +111,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
 
     // The user enters some value. No new 'completed' event is triggered so far.
@@ -175,8 +179,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     expectFalse(inputField.disabled);
 
@@ -187,9 +193,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: false,
-      errorLabel:
-          OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED,
-      attemptsLeft: 0
+      attemptsLeft: 0,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     expectTrue(inputField.disabled);
   });
@@ -206,15 +213,17 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     expectEquals(pinInput.value, '');
     expectEquals(inputField.value, '');
   });
 
-  // Test that the input field gets cleared when the request fails with the
-  // final error.
+  // // Test that the input field gets cleared when the request fails with the
+  // // final error.
   test('input cleared on final error', () => {
     // The user enters and submits a PIN. The response arrives, requesting the
     // PIN again. The input is cleared.
@@ -228,9 +237,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: false,
-      errorLabel:
-          OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED,
-      attemptsLeft: 0
+      attemptsLeft: 0,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     expectEquals(pinInput.value, '');
     expectEquals(inputField.value, '');
@@ -275,8 +285,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     expectEquals(getErrorContainerVisibility(), 'visible');
     expectTrue(pinInput.hasAttribute('invalid'));
@@ -292,8 +304,11 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError:
+          loadTimeData.getString('securityTokenPinDialogUnknownInvalidPin'),
+      formattedAttemptsLeft: ''
     };
     expectEquals(
         errorElement.textContent,
@@ -305,8 +320,11 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PUK,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PUK,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError:
+          loadTimeData.getString('securityTokenPinDialogUnknownInvalidPuk'),
+      formattedAttemptsLeft: ''
     };
     expectEquals(
         errorElement.textContent,
@@ -318,9 +336,11 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: false,
-      errorLabel:
-          OobeTypes.SecurityTokenPinDialogErrorType.MAX_ATTEMPTS_EXCEEDED,
-      attemptsLeft: 0
+      attemptsLeft: 0,
+      hasError: true,
+      formattedError: loadTimeData.getString(
+          'securityTokenPinDialogUnknownMaxAttemptsExceeded'),
+      formattedAttemptsLeft: ''
     };
     expectEquals(
         errorElement.textContent,
@@ -333,8 +353,11 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.UNKNOWN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError:
+          loadTimeData.getString('securityTokenPinDialogUnknownError'),
+      formattedAttemptsLeft: ''
     };
     expectEquals(
         errorElement.textContent,
@@ -347,13 +370,23 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.NONE,
-      attemptsLeft: ATTEMPTS_LEFT
+      attemptsLeft: ATTEMPTS_LEFT,
+      hasError: false,
+      formattedError: '',
+      formattedAttemptsLeft: '3 attempts left'
     };
-    expectEquals(
-        errorElement.textContent,
-        loadTimeData.getStringF(
-            'securityTokenPinDialogAttemptsLeft', ATTEMPTS_LEFT));
+    expectEquals(errorElement.textContent, '3 attempts left');
+
+    const ONE_ATTEMPT_LEFT = 1;
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      attemptsLeft: ONE_ATTEMPT_LEFT,
+      hasError: false,
+      formattedError: '',
+      formattedAttemptsLeft: '1 attempt left'
+    };
+    expectEquals(errorElement.textContent, '1 attempt left');
   });
 
   // Test that the label is empty when the number of attempts is, heuristically,
@@ -363,8 +396,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.NONE,
-      attemptsLeft: BIG_ATTEMPTS_LEFT
+      attemptsLeft: BIG_ATTEMPTS_LEFT,
+      hasError: false,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     expectEquals(errorElement.textContent, '');
   });
@@ -376,15 +411,23 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: ATTEMPTS_LEFT
+      attemptsLeft: ATTEMPTS_LEFT,
+      hasError: true,
+      formattedError: 'Invalid PIN. 3 attempts left',
+      formattedAttemptsLeft: ''
     };
-    expectEquals(
-        errorElement.textContent,
-        loadTimeData.getStringF(
-            'securityTokenPinDialogErrorAttempts',
-            loadTimeData.getString('securityTokenPinDialogUnknownInvalidPin'),
-            ATTEMPTS_LEFT));
+    expectEquals(errorElement.textContent, 'Invalid PIN. 3 attempts left');
+
+    const ONE_ATTEMPT_LEFT = 1;
+    securityTokenPin.parameters = {
+      codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
+      enableUserInput: true,
+      attemptsLeft: ONE_ATTEMPT_LEFT,
+      hasError: true,
+      formattedError: 'Invalid PIN. 1 attempt left',
+      formattedAttemptsLeft: ''
+    };
+    expectEquals(errorElement.textContent, 'Invalid PIN. 1 attempt left');
   });
 
   // Test the text of the label for the |INVALID_PIN| error when the number of
@@ -394,8 +437,11 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: BIG_ATTEMPTS_LEFT
+      attemptsLeft: BIG_ATTEMPTS_LEFT,
+      hasError: true,
+      formattedError:
+          loadTimeData.getString('securityTokenPinDialogUnknownInvalidPin'),
+      formattedAttemptsLeft: ''
     };
     expectEquals(
         errorElement.textContent,
@@ -444,8 +490,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     // The PIN keyboard is shown again, replacing the animation UI.
     expectFalse(pinKeyboardContainer.hidden);
@@ -473,8 +521,10 @@
     securityTokenPin.parameters = {
       codeType: OobeTypes.SecurityTokenPinDialogType.PIN,
       enableUserInput: true,
-      errorLabel: OobeTypes.SecurityTokenPinDialogErrorType.INVALID_PIN,
-      attemptsLeft: -1
+      attemptsLeft: -1,
+      hasError: true,
+      formattedError: '',
+      formattedAttemptsLeft: ''
     };
     // The PIN keyboard is shown again, replacing the animation UI.
     expectFalse(pinKeyboardContainer.hidden);
diff --git a/chrome/browser/resources/domain_reliability_internals/BUILD.gn b/chrome/browser/resources/domain_reliability_internals/BUILD.gn
index fede7099..bbe7b4e9 100644
--- a/chrome/browser/resources/domain_reliability_internals/BUILD.gn
+++ b/chrome/browser/resources/domain_reliability_internals/BUILD.gn
@@ -5,13 +5,14 @@
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_type_check("closure_compile") {
+  uses_js_modules = true
   deps = [ ":domain_reliability_internals" ]
 }
 
 js_library("domain_reliability_internals") {
   deps = [
     "//third_party/jstemplate:jstemplate",
-    "//ui/webui/resources/js:cr",
-    "//ui/webui/resources/js:util",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:util.m",
   ]
 }
diff --git a/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html
index bacb13f2..23c80761 100644
--- a/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html
+++ b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.html
@@ -4,12 +4,7 @@
   <meta charset="utf-8">
   <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
   <link rel="stylesheet" href="domain_reliability_internals.css">
-  <script src="chrome://resources/js/assert.js"></script>
-  <script src="chrome://resources/js/promise_resolver.js"></script>
-  <script src="chrome://resources/js/cr.js"></script>
-  <script src="chrome://resources/js/util.js"></script>
-  <script src="chrome://resources/js/jstemplate_compiled.js"></script>
-  <script src="domain_reliability_internals.js"></script>
+  <script type="module" src="domain_reliability_internals.js"></script>
 </head>
 <body>
   <div id="template">
diff --git a/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.js b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.js
index b9fa8e2..10855b1 100644
--- a/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.js
+++ b/chrome/browser/resources/domain_reliability_internals/domain_reliability_internals.js
@@ -2,20 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('DomainReliabilityInternals', function() {
-  'use strict';
+import 'chrome://resources/js/jstemplate_compiled.js';
 
-  function initialize() {
-    cr.sendWithPromise('updateData').then(data => {
-      jstProcess(new JsEvalContext(data), $('template'));
-    });
-  }
+import {sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {$} from 'chrome://resources/js/util.m.js';
 
-  // Return an object with all of the exports.
-  return {
-    initialize: initialize,
-  };
+document.addEventListener('DOMContentLoaded', function() {
+  sendWithPromise('updateData').then(data => {
+    jstProcess(new JsEvalContext(data), $('template'));
+  });
 });
-
-document.addEventListener(
-    'DOMContentLoaded', DomainReliabilityInternals.initialize);
diff --git a/chrome/browser/resources/inline_login/inline_login_app.html b/chrome/browser/resources/inline_login/inline_login_app.html
index 12f6399e..3490bcf 100644
--- a/chrome/browser/resources/inline_login/inline_login_app.html
+++ b/chrome/browser/resources/inline_login/inline_login_app.html
@@ -93,7 +93,8 @@
     </cr-button>
 
     <div class="action-buttons"
-        hidden$="[[!shouldShowGaiaButtons_(currentView_)]]">
+        hidden$="[[!shouldShowGaiaButtons_(enableGaiaActionButtons_,
+                                           currentView_)]]">
       <gaia-action-buttons authenticator="[[authExtHost_]]">
       </gaia-action-buttons>
     </div>
diff --git a/chrome/browser/resources/inline_login/inline_login_app.js b/chrome/browser/resources/inline_login/inline_login_app.js
index 80c7316..3f3ff13 100644
--- a/chrome/browser/resources/inline_login/inline_login_app.js
+++ b/chrome/browser/resources/inline_login/inline_login_app.js
@@ -111,7 +111,10 @@
    */
   isLoginPrimaryAccount_: false,
 
-  /** @private {boolean} */
+  /**
+   * TODO(crbug.com/1164862): cleanup this flag, since it's enabled by default.
+   * @private {boolean}
+   */
   enableGaiaActionButtons_: false,
 
   /** @override */
diff --git a/chrome/browser/signin/dice_signed_in_profile_creator.cc b/chrome/browser/signin/dice_signed_in_profile_creator.cc
index e19658f..98a6247 100644
--- a/chrome/browser/signin/dice_signed_in_profile_creator.cc
+++ b/chrome/browser/signin/dice_signed_in_profile_creator.cc
@@ -141,12 +141,16 @@
       callback_(std::move(callback)) {
   if (use_guest_profile) {
     DCHECK(Profile::IsEphemeralGuestProfileEnabled());
-    DCHECK(!ProfileManager::GuestProfileExists());
-    g_browser_process->profile_manager()->CreateProfileAsync(
-        ProfileManager::GetGuestProfilePath(),
-        base::BindRepeating(&DiceSignedInProfileCreator::OnNewProfileCreated,
-                            weak_pointer_factory_.GetWeakPtr()),
-        base::string16(), std::string());
+    // Make sure the callback is not called synchronously.
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ProfileManager::CreateProfileAsync,
+                       base::Unretained(g_browser_process->profile_manager()),
+                       ProfileManager::GetGuestProfilePath(),
+                       base::BindRepeating(
+                           &DiceSignedInProfileCreator::OnNewProfileCreated,
+                           weak_pointer_factory_.GetWeakPtr()),
+                       base::string16(), std::string()));
   } else {
     ProfileAttributesStorage& storage =
         g_browser_process->profile_manager()->GetProfileAttributesStorage();
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 7db0265..cfadff02 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -90,7 +90,9 @@
 
 bool GuestOptionAvailable() {
   return Profile::IsEphemeralGuestProfileEnabled() &&
-         !ProfileManager::GuestProfileExists();
+         !ProfileManager::GuestProfileExists() &&
+         g_browser_process->local_state()->GetBoolean(
+             prefs::kBrowserGuestModeEnabled);
 }
 
 }  // namespace
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
index fc962d5b..0f6afb2 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
@@ -10,7 +10,6 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.base.Callback;
-import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
@@ -83,17 +82,6 @@
     }
 
     /**
-     * Logs the access point when the user see the view of choosing account to sign in. Sign-in
-     * completion histogram is recorded by {@link #signinAndEnableSync}.
-     *
-     * @param accessPoint {@link SigninAccessPoint} that initiated the sign-in flow.
-     */
-    static void logSigninStartAccessPoint(@SigninAccessPoint int accessPoint) {
-        RecordHistogram.recordEnumeratedHistogram(
-                "Signin.SigninStartedAccessPoint", accessPoint, SigninAccessPoint.MAX);
-    }
-
-    /**
      * Extracts the domain name of a given account's email.
      */
     String extractDomainName(String accountEmail);
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java
index f470b24..cbd4a7f 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninMetricsUtils.java
@@ -11,6 +11,7 @@
 import org.chromium.chrome.browser.profiles.ProfileAccountManagementMetrics;
 import org.chromium.components.signin.GAIAServiceType;
 import org.chromium.components.signin.metrics.AccountConsistencyPromoAction;
+import org.chromium.components.signin.metrics.SigninAccessPoint;
 
 /**
  * Util methods for signin metrics logging.
@@ -42,6 +43,17 @@
                 100);
     }
 
+    /**
+     * Logs the access point when the user see the view of choosing account to sign in. Sign-in
+     * completion histogram is recorded by {@link SigninManager#signinAndEnableSync}.
+     *
+     * @param accessPoint {@link SigninAccessPoint} that initiated the sign-in flow.
+     */
+    public static void logSigninStartAccessPoint(@SigninAccessPoint int accessPoint) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "Signin.SigninStartedAccessPoint", accessPoint, SigninAccessPoint.MAX);
+    }
+
     @VisibleForTesting
     @NativeMethods
     public interface Natives {
diff --git a/chrome/browser/signin/signin_manager.cc b/chrome/browser/signin/signin_manager.cc
index 8affdbd..63e6b1aa 100644
--- a/chrome/browser/signin/signin_manager.cc
+++ b/chrome/browser/signin/signin_manager.cc
@@ -104,8 +104,7 @@
 }
 
 // signin::IdentityManager::Observer implementation.
-void SigninManager::BeforePrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
+void SigninManager::AfterSyncPrimaryAccountCleared() {
   // This is needed for the case where the user chooses to start syncing
   // with an account that is different from the unconsented primary account
   // (not the first in cookies) but then cancels. In that case, the tokens stay
diff --git a/chrome/browser/signin/signin_manager.h b/chrome/browser/signin/signin_manager.h
index adf8dae..4ecab06 100644
--- a/chrome/browser/signin/signin_manager.h
+++ b/chrome/browser/signin/signin_manager.h
@@ -36,8 +36,7 @@
   base::Optional<CoreAccountInfo> ComputeUnconsentedPrimaryAccountInfo() const;
 
   // signin::IdentityManager::Observer implementation.
-  void BeforePrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
+  void AfterSyncPrimaryAccountCleared() override;
   void OnRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info) override;
   void OnRefreshTokenRemovedForAccount(
diff --git a/chrome/browser/signin/signin_manager_unittest.cc b/chrome/browser/signin/signin_manager_unittest.cc
index c99afbe..6c44d0dd 100644
--- a/chrome/browser/signin/signin_manager_unittest.cc
+++ b/chrome/browser/signin/signin_manager_unittest.cc
@@ -19,12 +19,30 @@
 const char kTestEmail[] = "me@gmail.com";
 const char kTestEmail2[] = "me2@gmail.com";
 
-class IdentityManagerObserver : public IdentityManager::Observer {
+class FakeIdentityManagerObserver : public IdentityManager::Observer {
  public:
-  MOCK_METHOD1(OnUnconsentedPrimaryAccountChanged,
-               void(const CoreAccountInfo& unconsented_primary_account_info));
-  MOCK_METHOD1(BeforePrimaryAccountCleared,
-               void(const CoreAccountInfo& previous_primary_account_info));
+  explicit FakeIdentityManagerObserver(IdentityManager* identity_manager)
+      : identity_manager_(identity_manager) {}
+  ~FakeIdentityManagerObserver() override = default;
+
+  void OnPrimaryAccountChanged(
+      const PrimaryAccountChangeEvent& event) override {
+    auto current_state = event.GetCurrentState();
+    EXPECT_EQ(
+        current_state.primary_account,
+        identity_manager_->GetPrimaryAccountInfo(current_state.consent_level));
+    events_.push_back(event);
+  }
+
+  const std::vector<PrimaryAccountChangeEvent>& events() const {
+    return events_;
+  }
+
+  void Reset() { events_.clear(); }
+
+ private:
+  IdentityManager* identity_manager_;
+  std::vector<PrimaryAccountChangeEvent> events_;
 };
 }  // namespace
 
@@ -34,12 +52,12 @@
       : identity_test_env_(/*test_url_loader_factory=*/nullptr,
                            /*pref_service=*/nullptr,
                            signin::AccountConsistencyMethod::kDice,
-                           /*test_signin_client=*/nullptr) {}
+                           /*test_signin_client=*/nullptr),
+        observer_(identity_test_env_.identity_manager()) {}
 
   void SetUp() override {
     testing::Test::SetUp();
     RecreateSigninManager();
-    VerifyAndResetCallExpectations();
     identity_manager()->AddObserver(&observer_);
   }
 
@@ -58,29 +76,80 @@
     return account_info;
   }
 
+  void ExpectUnconsentedPrimaryAccountSetEvent(
+      const CoreAccountInfo& expected_primary_account) {
+    EXPECT_EQ(1U, observer().events().size());
+    auto event = observer().events()[0];
+    EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet,
+              event.GetEventTypeFor(ConsentLevel::kNotRequired));
+    EXPECT_TRUE(event.GetPreviousState().primary_account.IsEmpty());
+    EXPECT_EQ(expected_primary_account,
+              event.GetCurrentState().primary_account);
+    observer().Reset();
+  }
+
+  void ExpectUnconsentedPrimaryAccountClearedEvent(
+      const CoreAccountInfo& expected_cleared_account) {
+    EXPECT_EQ(1U, observer().events().size());
+    auto event = observer().events()[0];
+    EXPECT_EQ(PrimaryAccountChangeEvent::Type::kCleared,
+              event.GetEventTypeFor(ConsentLevel::kNotRequired));
+    EXPECT_EQ(expected_cleared_account,
+              event.GetPreviousState().primary_account);
+    EXPECT_TRUE(event.GetCurrentState().primary_account.IsEmpty());
+    observer().Reset();
+  }
+
+  void ExpectSyncPrimaryAccountSetEvent(
+      const CoreAccountInfo& expected_primary_account) {
+    EXPECT_EQ(1U, observer().events().size());
+    auto event = observer().events()[0];
+    EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet,
+              event.GetEventTypeFor(ConsentLevel::kNotRequired));
+    EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet,
+              event.GetEventTypeFor(ConsentLevel::kSync));
+    EXPECT_TRUE(event.GetPreviousState().primary_account.IsEmpty());
+    EXPECT_EQ(expected_primary_account,
+              event.GetCurrentState().primary_account);
+    observer().Reset();
+  }
+
   IdentityManager* identity_manager() {
     return identity_test_env_.identity_manager();
   }
 
   IdentityTestEnvironment* identity_test_env() { return &identity_test_env_; }
 
-  void MakeAccountAvailableWithCookies(const AccountInfo& account_info) {
-    EXPECT_EQ(account_info, identity_test_env_.MakeAccountAvailableWithCookies(
-                                account_info.email, account_info.gaia));
+  AccountInfo MakeAccountAvailableWithCookies(const std::string& email) {
+    AccountInfo account = GetAccountInfo(kTestEmail);
+    identity_test_env_.MakeAccountAvailableWithCookies(account.email,
+                                                       account.gaia);
+    EXPECT_FALSE(account.IsEmpty());
+    EXPECT_TRUE(
+        identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
+    EXPECT_EQ(account, identity_manager()->GetPrimaryAccountInfo(
+                           ConsentLevel::kNotRequired));
+    EXPECT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSync));
+    return account;
   }
 
-  IdentityManagerObserver& observer() { return observer_; }
-
-  void VerifyAndResetCallExpectations() {
-    Mock::VerifyAndClear(&observer_);
-    EXPECT_CALL(observer_, OnUnconsentedPrimaryAccountChanged(_)).Times(0);
-    EXPECT_CALL(observer_, BeforePrimaryAccountCleared(_)).Times(0);
+  AccountInfo MakeSyncAccountAvailableWithCookies(const std::string& email) {
+    AccountInfo account = identity_test_env_.MakePrimaryAccountAvailable(email);
+    identity_test_env_.SetCookieAccounts({{account.email, account.gaia}});
+    EXPECT_EQ(account, identity_manager()->GetPrimaryAccountInfo(
+                           ConsentLevel::kNotRequired));
+    EXPECT_EQ(account,
+              identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSync));
+    EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken());
+    return account;
   }
 
+  FakeIdentityManagerObserver& observer() { return observer_; }
+
   content::BrowserTaskEnvironment task_environment_;
   IdentityTestEnvironment identity_test_env_;
   std::unique_ptr<SigninManager> signin_manger_;
-  IdentityManagerObserver observer_;
+  FakeIdentityManagerObserver observer_;
 
   DISALLOW_COPY_AND_ASSIGN(SigninManagerTest);
 };
@@ -89,278 +158,237 @@
     SigninManagerTest,
     UnconsentedPrimaryAccountUpdatedOnItsAccountRefreshTokenUpdateWithValidTokenWhenNoSyncConsent) {
   // Add an unconsented primary account, incl. proper cookies.
-  AccountInfo account_info = GetAccountInfo(kTestEmail);
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
-  MakeAccountAvailableWithCookies(account_info);
-  VerifyAndResetCallExpectations();
-
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      account_info);
+  AccountInfo account = MakeAccountAvailableWithCookies(kTestEmail);
+  ExpectUnconsentedPrimaryAccountSetEvent(account);
+  EXPECT_EQ(account, identity_manager()->GetPrimaryAccountInfo(
+                         ConsentLevel::kNotRequired));
 }
 
 TEST_F(
     SigninManagerTest,
     UnconsentedPrimaryAccountUpdatedOnItsAccountRefreshTokenUpdateWithInvalidTokenWhenNoSyncConsent) {
-  // Add an unconsented primary account, incl. proper cookies.
-  AccountInfo account_info = GetAccountInfo(kTestEmail);
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
-  MakeAccountAvailableWithCookies(account_info);
-  VerifyAndResetCallExpectations();
+  // Prerequisite: add an unconsented primary account, incl. proper cookies.
+  AccountInfo account = MakeAccountAvailableWithCookies(kTestEmail);
+  ExpectUnconsentedPrimaryAccountSetEvent(account);
 
   // Invalid token.
-  CoreAccountInfo empty_info;
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(empty_info))
-      .Times(1);
-  SetInvalidRefreshTokenForAccount(identity_manager(), account_info.account_id);
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      empty_info);
-  VerifyAndResetCallExpectations();
+  SetInvalidRefreshTokenForAccount(identity_manager(), account.account_id);
+  ExpectUnconsentedPrimaryAccountClearedEvent(account);
+  EXPECT_FALSE(
+      identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
 
   // Update with a valid token.
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
   UpdatePersistentErrorOfRefreshTokenForAccount(
-      identity_manager(), account_info.account_id,
+      identity_manager(), account.account_id,
       GoogleServiceAuthError::AuthErrorNone());
+  ExpectUnconsentedPrimaryAccountSetEvent(account);
   EXPECT_EQ(
       identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      account_info);
-  // Unconsented primary account should not be called.
-  VerifyAndResetCallExpectations();
+      account);
 }
 
 TEST_F(
     SigninManagerTest,
     UnconsentedPrimaryAccountRemovedOnItsAccountRefreshTokenRemovalWhenNoSyncConsent) {
-  // Add an unconsented primary account, incl. proper cookies.
-  AccountInfo account_info = GetAccountInfo(kTestEmail);
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
-  MakeAccountAvailableWithCookies(account_info);
-  VerifyAndResetCallExpectations();
+  // Prerequisite: Add an unconsented primary account, incl. proper cookies.
+  AccountInfo account = MakeAccountAvailableWithCookies(kTestEmail);
+  ExpectUnconsentedPrimaryAccountSetEvent(account);
 
   // With no refresh token, there is no unconsented primary account any more.
-  CoreAccountInfo empty_info;
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(empty_info))
-      .Times(1);
-  identity_test_env()->RemoveRefreshTokenForAccount(account_info.account_id);
-  VerifyAndResetCallExpectations();
+  identity_test_env()->RemoveRefreshTokenForAccount(account.account_id);
+  ExpectUnconsentedPrimaryAccountClearedEvent(account);
   EXPECT_FALSE(
       identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
-
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      empty_info);
-  VerifyAndResetCallExpectations();
 }
 
 TEST_F(SigninManagerTest, UnconsentedPrimaryAccountNotChangedOnSignout) {
-  // Setup cookies and token for the main account.
-  AccountInfo account_info = GetAccountInfo(kTestEmail);
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
-  identity_test_env()->MakePrimaryAccountAvailable(account_info.email);
-  identity_test_env()->SetCookieAccounts(
-      {{account_info.email, account_info.gaia}});
-
-  EXPECT_EQ(account_info, identity_manager()->GetPrimaryAccountInfo(
-                              ConsentLevel::kNotRequired));
-  EXPECT_EQ(account_info,
+  // Set a primary account at sync consent level.
+  AccountInfo account = MakeSyncAccountAvailableWithCookies(kTestEmail);
+  EXPECT_EQ(account, identity_manager()->GetPrimaryAccountInfo(
+                         ConsentLevel::kNotRequired));
+  EXPECT_EQ(account,
             identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSync));
   EXPECT_TRUE(identity_manager()->HasPrimaryAccountWithRefreshToken());
-  VerifyAndResetCallExpectations();
-  // Tests that OnUnconsentedPrimaryAccountChanged is never called.
-  EXPECT_CALL(observer(), BeforePrimaryAccountCleared(account_info)).Times(1);
+
+  // Verify the primary account changed event.
+  ExpectSyncPrimaryAccountSetEvent(account);
+
+  // Tests that sync primary account is cleared, but unconsented account is not.
   identity_test_env()->RevokeSyncConsent();
-  // Primary account is cleared, but unconsented account is not.
-  EXPECT_FALSE(identity_manager()->HasPrimaryAccount());
-  EXPECT_EQ(account_info, identity_manager()->GetPrimaryAccountInfo(
-                              ConsentLevel::kNotRequired));
-  // OnUnconsentedPrimaryAccountChanged was not fired.
-  VerifyAndResetCallExpectations();
+  EXPECT_EQ(account, identity_manager()->GetPrimaryAccountInfo(
+                         ConsentLevel::kNotRequired));
+  EXPECT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSync));
+
+  EXPECT_EQ(1U, observer().events().size());
+  auto event = observer().events()[0];
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kNone,
+            event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kCleared,
+            event.GetEventTypeFor(ConsentLevel::kSync));
+  EXPECT_EQ(account, event.GetPreviousState().primary_account);
+  EXPECT_EQ(account, event.GetCurrentState().primary_account);
 }
 
 TEST_F(SigninManagerTest,
        UnconsentedPrimaryAccountTokenRevokedWithStaleCookies) {
-  AccountInfo account_info = GetAccountInfo(kTestEmail);
-  // Add an unconsented primary account, incl. proper cookies.
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
-  MakeAccountAvailableWithCookies(account_info);
-  VerifyAndResetCallExpectations();
-
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      account_info);
+  // Prerequisite: add an unconsented primary account, incl. proper cookies.
+  AccountInfo account = MakeAccountAvailableWithCookies(kTestEmail);
+  ExpectUnconsentedPrimaryAccountSetEvent(account);
 
   // Make the cookies stale and remove the account.
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(CoreAccountInfo()))
-      .Times(1);
-  identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
   // Removing the refresh token for the unconsented primary account is
   // sufficient to clear it.
-  identity_test_env()->RemoveRefreshTokenForAccount(account_info.account_id);
-  AccountsInCookieJarInfo cookie_info =
-      identity_manager()->GetAccountsInCookieJar();
-  ASSERT_FALSE(cookie_info.accounts_are_fresh);
+  identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
+  identity_test_env()->RemoveRefreshTokenForAccount(account.account_id);
+  ASSERT_FALSE(identity_manager()->GetAccountsInCookieJar().accounts_are_fresh);
+
   // Unconsented account was removed.
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      CoreAccountInfo());
+  EXPECT_FALSE(
+      identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
+  ExpectUnconsentedPrimaryAccountClearedEvent(account);
 }
 
 TEST_F(SigninManagerTest,
        UnconsentedPrimaryAccountTokenRevokedWithStaleCookiesMultipleAccounts) {
   // Add two accounts with cookies.
-  AccountInfo main_account_info =
+  AccountInfo main_account =
       identity_test_env()->MakeAccountAvailable(kTestEmail);
-  AccountInfo secondary_account_info =
+  AccountInfo secondary_account =
       identity_test_env()->MakeAccountAvailable(kTestEmail2);
-
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(main_account_info))
-      .Times(1);
-
   identity_test_env()->SetCookieAccounts(
-      {{main_account_info.email, main_account_info.gaia},
-       {secondary_account_info.email, secondary_account_info.gaia}});
+      {{main_account.email, main_account.gaia},
+       {secondary_account.email, secondary_account.gaia}});
 
-  VerifyAndResetCallExpectations();
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      main_account_info);
+  EXPECT_TRUE(
+      identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
+  EXPECT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSync));
+  EXPECT_EQ(main_account, identity_manager()->GetPrimaryAccountInfo(
+                              ConsentLevel::kNotRequired));
+  ExpectUnconsentedPrimaryAccountSetEvent(main_account);
 
   // Make the cookies stale and remove the main account.
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(CoreAccountInfo()))
-      .Times(1);
   identity_test_env()->SetFreshnessOfAccountsInGaiaCookie(false);
-  identity_test_env()->RemoveRefreshTokenForAccount(
-      main_account_info.account_id);
-  AccountsInCookieJarInfo cookie_info =
-      identity_manager()->GetAccountsInCookieJar();
-  ASSERT_FALSE(cookie_info.accounts_are_fresh);
+  identity_test_env()->RemoveRefreshTokenForAccount(main_account.account_id);
+  ASSERT_FALSE(identity_manager()->GetAccountsInCookieJar().accounts_are_fresh);
+
   // Unconsented account was removed.
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      CoreAccountInfo());
+  EXPECT_FALSE(
+      identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
+  ExpectUnconsentedPrimaryAccountClearedEvent(main_account);
 }
 
 TEST_F(SigninManagerTest, UnconsentedPrimaryAccountDuringLoad) {
-  // Add two accounts with cookies.
-  AccountInfo main_account_info =
+  // Pre-requisite: Add two accounts with cookies.
+  AccountInfo main_account =
       identity_test_env()->MakeAccountAvailable(kTestEmail);
-  AccountInfo secondary_account_info =
+  AccountInfo secondary_account =
       identity_test_env()->MakeAccountAvailable(kTestEmail2);
-
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(main_account_info))
-      .Times(1);
-
   identity_test_env()->SetCookieAccounts(
-      {{main_account_info.email, main_account_info.gaia},
-       {secondary_account_info.email, secondary_account_info.gaia}});
-
-  VerifyAndResetCallExpectations();
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      main_account_info);
+      {{main_account.email, main_account.gaia},
+       {secondary_account.email, secondary_account.gaia}});
+  ASSERT_EQ(main_account, identity_manager()->GetPrimaryAccountInfo(
+                              ConsentLevel::kNotRequired));
+  ASSERT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSync));
+  ExpectUnconsentedPrimaryAccountSetEvent(main_account);
 
   // Set the token service in "loading" mode.
   identity_test_env()->ResetToAccountsNotYetLoadedFromDiskState();
   RecreateSigninManager();
 
   // Unconsented primary account is available while tokens are not loaded.
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      main_account_info);
-  VerifyAndResetCallExpectations();
+  EXPECT_EQ(main_account, identity_manager()->GetPrimaryAccountInfo(
+                              ConsentLevel::kNotRequired));
+  EXPECT_TRUE(observer().events().empty());
 
   // Revoking an unrelated token doesn't change the unconsented primary account.
   identity_test_env()->RemoveRefreshTokenForAccount(
-      secondary_account_info.account_id);
-  EXPECT_EQ(
-      identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kNotRequired),
-      main_account_info);
+      secondary_account.account_id);
+  EXPECT_EQ(main_account, identity_manager()->GetPrimaryAccountInfo(
+                              ConsentLevel::kNotRequired));
+  EXPECT_TRUE(observer().events().empty());
 
   // Revoke the unconsented primary account while tokens are not loaded.
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(CoreAccountInfo()))
-      .Times(1);
-
-  identity_test_env()->RemoveRefreshTokenForAccount(
-      main_account_info.account_id);
+  identity_test_env()->RemoveRefreshTokenForAccount(main_account.account_id);
   EXPECT_FALSE(
       identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
-  VerifyAndResetCallExpectations();
+  ExpectUnconsentedPrimaryAccountClearedEvent(main_account);
 
   // Finish the token load.
   identity_test_env()->ReloadAccountsFromDisk();
   EXPECT_FALSE(
       identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
+  EXPECT_TRUE(observer().events().empty());
 }
 
 TEST_F(SigninManagerTest,
        UnconsentedPrimaryAccountUpdatedOnSyncConsentRevoked) {
-  AccountInfo first_account_info =
+  AccountInfo first_account =
       identity_test_env()->MakeAccountAvailable(kTestEmail);
-  AccountInfo second_account_info =
+  AccountInfo second_account =
       identity_test_env()->MakeAccountAvailable(kTestEmail2);
-
-  EXPECT_CALL(observer(),
-              OnUnconsentedPrimaryAccountChanged(first_account_info))
-      .Times(1);
-
   identity_test_env()->SetCookieAccounts(
-      {{first_account_info.email, first_account_info.gaia},
-       {second_account_info.email, second_account_info.gaia}});
+      {{first_account.email, first_account.gaia},
+       {second_account.email, second_account.gaia}});
+  ASSERT_EQ(first_account, identity_manager()->GetPrimaryAccountInfo(
+                               ConsentLevel::kNotRequired));
+  ExpectUnconsentedPrimaryAccountSetEvent(first_account);
 
-  VerifyAndResetCallExpectations();
-
-  // Set the primary account to the second account in cookies.
+  // Set the sync primary account to the second account in cookies.
   // The unconsented primary account should be updated.
-  EXPECT_CALL(observer(),
-              OnUnconsentedPrimaryAccountChanged(second_account_info))
-      .Times(1);
-  identity_test_env()->SetPrimaryAccount(second_account_info.email);
-  EXPECT_EQ(identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSync),
-            second_account_info);
-  VerifyAndResetCallExpectations();
+  identity_test_env()->SetPrimaryAccount(second_account.email);
+  EXPECT_EQ(second_account,
+            identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSync));
+  EXPECT_EQ(1U, observer().events().size());
+  auto event = observer().events()[0];
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet,
+            event.GetEventTypeFor(ConsentLevel::kSync));
+  EXPECT_EQ(first_account, event.GetPreviousState().primary_account);
+  EXPECT_EQ(second_account, event.GetCurrentState().primary_account);
+  observer().Reset();
 
   // Clear primary account but do not delete the account. The unconsented
   // primary account should be updated to be the first account in cookies.
-  EXPECT_CALL(observer(), BeforePrimaryAccountCleared(second_account_info))
-      .Times(1);
-  EXPECT_CALL(observer(),
-              OnUnconsentedPrimaryAccountChanged(first_account_info))
-      .Times(1);
   identity_test_env()->RevokeSyncConsent();
   // Primary account is cleared, but unconsented account is not.
   EXPECT_FALSE(identity_manager()->HasPrimaryAccount());
   EXPECT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSync));
-  EXPECT_EQ(first_account_info, identity_manager()->GetPrimaryAccountInfo(
-                                    ConsentLevel::kNotRequired));
-  // OnUnconsentedPrimaryAccountChanged was fired.
-  VerifyAndResetCallExpectations();
+  EXPECT_TRUE(
+      identity_manager()->HasPrimaryAccount(ConsentLevel::kNotRequired));
+  EXPECT_EQ(first_account, identity_manager()->GetPrimaryAccountInfo(
+                               ConsentLevel::kNotRequired));
+
+  EXPECT_EQ(2U, observer().events().size());
+  event = observer().events()[0];
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kCleared,
+            event.GetEventTypeFor(ConsentLevel::kSync));
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kNone,
+            event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(second_account, event.GetPreviousState().primary_account);
+  EXPECT_EQ(second_account, event.GetCurrentState().primary_account);
+
+  event = observer().events()[1];
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kNone,
+            event.GetEventTypeFor(ConsentLevel::kSync));
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet,
+            event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(second_account, event.GetPreviousState().primary_account);
+  EXPECT_EQ(first_account, event.GetCurrentState().primary_account);
 }
 
 TEST_F(SigninManagerTest, ClearPrimaryAccountAndSignOut) {
-  AccountInfo account_info = GetAccountInfo(kTestEmail);
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(account_info))
-      .Times(1);
-  identity_test_env()->MakePrimaryAccountAvailable(kTestEmail);
-  EXPECT_EQ(identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSync),
-            account_info);
-  VerifyAndResetCallExpectations();
+  AccountInfo account = MakeSyncAccountAvailableWithCookies(kTestEmail);
+  ExpectSyncPrimaryAccountSetEvent(account);
 
-  identity_test_env()->SetCookieAccounts(
-      {{account_info.email, account_info.gaia}});
-
-  EXPECT_CALL(observer(), BeforePrimaryAccountCleared(account_info)).Times(1);
-  EXPECT_CALL(observer(), OnUnconsentedPrimaryAccountChanged(CoreAccountInfo()))
-      .Times(1);
   identity_test_env()->ClearPrimaryAccount();
-  VerifyAndResetCallExpectations();
+
+  EXPECT_EQ(1U, observer().events().size());
+  auto event = observer().events()[0];
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kCleared,
+            event.GetEventTypeFor(ConsentLevel::kNotRequired));
+  EXPECT_EQ(PrimaryAccountChangeEvent::Type::kCleared,
+            event.GetEventTypeFor(ConsentLevel::kSync));
+  EXPECT_EQ(account, event.GetPreviousState().primary_account);
+  EXPECT_TRUE(event.GetCurrentState().primary_account.IsEmpty());
 }
 
 }  // namespace signin
diff --git a/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninActivityLauncher.java b/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninActivityLauncher.java
index 0c7d30cd..0c24fcf 100644
--- a/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninActivityLauncher.java
+++ b/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninActivityLauncher.java
@@ -51,4 +51,12 @@
      * @param accessPoint {@link SigninAccessPoint} enum value representing.
      */
     void launchActivity(Context context, @SigninAccessPoint int accessPoint);
+
+    /**
+     * Launches the {@link SigninActivity} if signin is allowed.
+     * @param context A {@link Context} object.
+     * @param accessPoint {@link SigninAccessPoint} for starting sign-in flow.
+     * @return a boolean indicating if the {@link SigninActivity} is launched.
+     */
+    boolean launchActivityIfAllowed(Context context, @SigninAccessPoint int accessPoint);
 }
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index 882b9b6..abd6f29 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -18,6 +18,11 @@
     "java/src/org/chromium/chrome/browser/toolbar/ToolbarProgressBarAnimatingView.java",
     "java/src/org/chromium/chrome/browser/toolbar/ToolbarTabController.java",
     "java/src/org/chromium/chrome/browser/toolbar/VoiceToolbarButtonController.java",
+    "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java",
+    "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java",
+    "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java",
+    "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java",
+    "java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java",
     "java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewResourceFrameLayout.java",
     "java/src/org/chromium/chrome/browser/toolbar/bottom/ScrollingBottomViewSceneLayer.java",
     "java/src/org/chromium/chrome/browser/toolbar/top/ActionModeController.java",
@@ -47,6 +52,7 @@
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//ui/android:ui_full_java",
+    "//ui/android:ui_utils_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   resources_package = "org.chromium.chrome.browser.toolbar"
@@ -63,6 +69,7 @@
     "java/res/drawable-xhdpi/btn_toolbar_home.png",
     "java/res/drawable-xxhdpi/btn_toolbar_home.png",
     "java/res/drawable-xxxhdpi/btn_toolbar_home.png",
+    "java/res/layout/bottom_control_container.xml",
     "java/res/layout/control_container.xml",
     "java/res/values-sw600dp/dimens.xml",
     "java/res/values/dimens.xml",
diff --git a/chrome/android/java/res/layout/bottom_control_container.xml b/chrome/browser/ui/android/toolbar/java/res/layout/bottom_control_container.xml
similarity index 100%
rename from chrome/android/java/res/layout/bottom_control_container.xml
rename to chrome/browser/ui/android/toolbar/java/res/layout/bottom_control_container.xml
diff --git a/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml b/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml
index 3d97ed8..1c1ae45 100644
--- a/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml
+++ b/chrome/browser/ui/android/toolbar/java/res/values/dimens.xml
@@ -6,4 +6,7 @@
 <resources>
     <!-- Tab Strip Dimensions -->
     <dimen name="tab_strip_height">0dp</dimen>
+
+    <!-- Bottom controls dimensions -->
+    <dimen name="bottom_controls_height">@dimen/min_touch_target_size</dimen>
 </resources>
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java
new file mode 100644
index 0000000..9b2f236
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsContentDelegate.java
@@ -0,0 +1,31 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.toolbar.bottom;
+
+import android.app.Activity;
+
+/**
+ * Interface for the bottom controls content UI. This UI delegates various operations to
+ * the implementation. This UI manages its own visibility through
+ * {@link BottomControlsCoordinator.BottomControlsVisibilityController}.
+ */
+public interface BottomControlsContentDelegate {
+    /**
+     * Called by the ToolbarManager when the system back button is pressed.
+     * @return Whether or not the TabGroupUi consumed the event.
+     */
+    boolean onBackPressed();
+
+    /**
+     * Initialize the delegate on native initialization.
+     * @param activity Activity for the delegate.
+     * @param visibilityController Bottom controls visibility controller.
+     */
+    void initializeWithNative(Activity activity,
+            BottomControlsCoordinator.BottomControlsVisibilityController visibilityController);
+
+    /** Destroy the delegate. */
+    void destroy();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
similarity index 71%
rename from chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
rename to chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
index b39adab2..1b95c9e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsCoordinator.java
@@ -8,24 +8,17 @@
 import android.app.Activity;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewStub;
 
 import androidx.annotation.Nullable;
 
 import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.Supplier;
-import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.layouts.LayoutManager;
 import org.chromium.chrome.browser.layouts.LayoutStateProvider;
-import org.chromium.chrome.browser.share.ShareDelegate;
-import org.chromium.chrome.browser.tasks.tab_management.TabGroupUi;
-import org.chromium.chrome.browser.tasks.tab_management.TabManagementModuleProvider;
-import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
+import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.bottom.BottomControlsViewBinder.ViewHolder;
-import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -50,8 +43,8 @@
     /** The mediator that handles events from outside the bottom controls. */
     private final BottomControlsMediator mMediator;
 
-    /** The coordinator for the split toolbar's bottom toolbar component. */
-    private @Nullable TabGroupUi mTabGroupUi;
+    /** The Delegate for the split toolbar's bottom toolbar component UI operation. */
+    private @Nullable BottomControlsContentDelegate mContentDelegate;
 
     /**
      * Build the coordinator that manages the bottom controls.
@@ -62,10 +55,7 @@
      * @param fullscreenManager A {@link FullscreenManager} to listen for fullscreen changes.
      * @param stub The bottom controls {@link ViewStub} to inflate.
      * @param themeColorProvider The {@link ThemeColorProvider} for the bottom toolbar.
-     * @param shareDelegateSupplier The supplier for the {@link ShareDelegate} the bottom controls
-     *         should use to share content.
-     * @param scrimCoordinator The {@link ScrimCoordinator} to control scrim view.
-     * @param omniboxFocusStateSupplier Supplier to access the focus state of the omnibox.
+     * @param contentDelegate Delegate for bottom controls UI operations.
      * @param overlayPanelVisibilitySupplier Notifies overlay panel visibility event.
      * @param resourceManager A {@link ResourceManager} for loading textures into the compositor.
      * @param layoutManager A {@link LayoutManagerImpl} to attach overlays to.
@@ -73,15 +63,10 @@
     @SuppressLint("CutPasteId") // Not actually cut and paste since it's View vs ViewGroup.
     public BottomControlsCoordinator(Activity activity, WindowAndroid windowAndroid,
             LayoutManager layoutManager, ResourceManager resourceManager,
-            BrowserControlsSizer controlsSizer, FullscreenManager fullscreenManager, ViewStub stub,
-            ThemeColorProvider themeColorProvider,
-            ObservableSupplier<ShareDelegate> shareDelegateSupplier,
-            ScrimCoordinator scrimCoordinator,
-            ObservableSupplier<Boolean> omniboxFocusStateSupplier,
+            BrowserControlsSizer controlsSizer, FullscreenManager fullscreenManager,
+            ScrollingBottomViewResourceFrameLayout root, ThemeColorProvider themeColorProvider,
+            BottomControlsContentDelegate contentDelegate,
             ObservableSupplier<Boolean> overlayPanelVisibilitySupplier) {
-        final ScrollingBottomViewResourceFrameLayout root =
-                (ScrollingBottomViewResourceFrameLayout) stub.inflate();
-
         PropertyModel model = new PropertyModel(BottomControlsProperties.ALL_KEYS);
 
         ScrollingBottomViewSceneLayer sceneLayer =
@@ -103,12 +88,7 @@
         resourceManager.getDynamicResourceLoader().registerResource(
                 root.getId(), root.getResourceAdapter());
 
-        if (TabUiFeatureUtilities.isTabGroupsAndroidEnabled()
-                || TabUiFeatureUtilities.isConditionalTabStripEnabled()) {
-            mTabGroupUi = TabManagementModuleProvider.getDelegate().createTabGroupUi(
-                    root.findViewById(R.id.bottom_container_slot), themeColorProvider,
-                    scrimCoordinator, omniboxFocusStateSupplier);
-        }
+        mContentDelegate = contentDelegate;
         Toast.setGlobalExtraYOffset(
                 root.getResources().getDimensionPixelSize(bottomControlsHeightId));
 
@@ -119,8 +99,8 @@
         sceneLayer.setIsVisible(mMediator.isCompositedViewVisible());
         layoutManager.addSceneOverlay(sceneLayer);
 
-        if (mTabGroupUi != null) {
-            mTabGroupUi.initializeWithNative(activity, mMediator::setBottomControlsVisible);
+        if (mContentDelegate != null) {
+            mContentDelegate.initializeWithNative(activity, mMediator::setBottomControlsVisible);
         }
     }
 
@@ -143,24 +123,14 @@
      * @return Whether or not the back press event is consumed here.
      */
     public boolean onBackPressed() {
-        return mTabGroupUi != null && mTabGroupUi.onBackPressed();
+        return mContentDelegate != null && mContentDelegate.onBackPressed();
     }
 
     /**
      * Clean up any state when the bottom controls component is destroyed.
      */
     public void destroy() {
-        if (mTabGroupUi != null) mTabGroupUi.destroy();
+        if (mContentDelegate != null) mContentDelegate.destroy();
         mMediator.destroy();
     }
-
-    /**
-     * @return {@link Supplier} that provides dialog visibility.
-     */
-    public Supplier<Boolean> getTabGridDialogVisibilitySupplier() {
-        if (mTabGroupUi == null) {
-            return null;
-        }
-        return mTabGroupUi.getTabGridDialogVisibilitySupplier();
-    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
rename to chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
similarity index 100%
rename from chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
rename to chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsProperties.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
similarity index 97%
rename from chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
rename to chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
index 4a27d7e..3508835 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsViewBinder.java
@@ -6,7 +6,7 @@
 
 import android.view.View;
 
-import org.chromium.chrome.R;
+import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc b/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc
index 67a277d..fee8cca 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc
@@ -22,6 +22,8 @@
 #include "chrome/browser/ui/app_list/page_break_constants.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
+#include "chrome/browser/web_applications/components/web_app_id_constants.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -38,6 +40,8 @@
 
 namespace {
 
+const char kOsSettingsUrl[] = "chrome://os-settings/";
+
 scoped_refptr<extensions::Extension> MakeApp(
     const std::string& name,
     const std::string& id,
@@ -466,10 +470,14 @@
   AppListInternalAppSyncableServiceTest() {
     chrome::SettingsWindowManager::ForceDeprecatedSettingsWindowForTesting();
   }
-  ~AppListInternalAppSyncableServiceTest() override = default;
 
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  void SetUp() override {
+    AppListSyncableServiceTest::SetUp();
+    web_app::InstallDummyWebApp(testing_profile(), kOsSettingsUrl,
+                                GURL(kOsSettingsUrl));
+  }
+
+  ~AppListInternalAppSyncableServiceTest() override = default;
 };
 
 TEST_F(AppListInternalAppSyncableServiceTest, ExistingDefaultPageBreak) {
@@ -515,7 +523,7 @@
 
   // Since internal apps are added by default, we'll use the settings apps to
   // test the ordering.
-  auto* settings_app_sync_item = GetSyncItem(ash::kInternalAppIdSettings);
+  auto* settings_app_sync_item = GetSyncItem(web_app::kOsSettingsAppId);
   auto* hosted_app_sync_item = GetSyncItem(kHostedAppId);
   ASSERT_TRUE(settings_app_sync_item);
   ASSERT_TRUE(hosted_app_sync_item);
@@ -1084,9 +1092,8 @@
   auto ordered_items = GetIdsOfSortedItemsFromModelUpdater();
   EXPECT_THAT(
       ordered_items,
-      ElementsAre(kItemIdA1, kItemIdA2, kPageBreakItemId1,
-                  kItemIdB1, kItemIdB2, kItemIdB3, kPageBreakItemId2,
-                  kItemIdC1, kPageBreakItemId3));
+      ElementsAre(kItemIdA1, kItemIdA2, kPageBreakItemId1, kItemIdB1, kItemIdB2,
+                  kItemIdB3, kPageBreakItemId2, kItemIdC1, kPageBreakItemId3));
 
   // On device 1, move A1 from page 1 to page 2 and insert it between B1 and B2.
   // Device 2 should get the following 3 sync changes from device 1:
@@ -1141,9 +1148,9 @@
   // B3 C1 [pagebreak 3]
   auto ordered_items_after_sync = GetIdsOfSortedItemsFromModelUpdater();
   EXPECT_THAT(ordered_items_after_sync,
-              ElementsAre(kItemIdA2, kPageBreakItemId1,
-                          kItemIdB1, kItemIdA1, kItemIdB2, kNewPageBreakItemId,
-                          kItemIdB3, kItemIdC1, kPageBreakItemId3));
+              ElementsAre(kItemIdA2, kPageBreakItemId1, kItemIdB1, kItemIdA1,
+                          kItemIdB2, kNewPageBreakItemId, kItemIdB3, kItemIdC1,
+                          kPageBreakItemId3));
 }
 
 TEST_F(AppListSyncableServiceTest, FirstAvailablePosition) {
diff --git a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
index aaf4217..b295c9aa 100644
--- a/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
+++ b/chrome/browser/ui/app_list/internal_app/internal_app_metadata.cc
@@ -69,24 +69,7 @@
             /*searchable=*/false,
             /*show_in_launcher=*/false, apps::BuiltInAppName::kContinueReading,
             /*searchable_string_resource_id=*/0}});
-
-  static base::NoDestructor<std::vector<InternalApp>> internal_app_list;
-  internal_app_list->clear();
-  internal_app_list->insert(internal_app_list->begin(),
-                            internal_app_list_static->begin(),
-                            internal_app_list_static->end());
-
-  if (chrome::SettingsWindowManager::UseDeprecatedSettingsWindow(profile)) {
-    internal_app_list->push_back(
-        {ash::kInternalAppIdSettings, IDS_INTERNAL_APP_SETTINGS,
-         IDR_SETTINGS_LOGO_192,
-         /*recommendable=*/true,
-         /*searchable=*/true,
-         /*show_in_launcher=*/true, apps::BuiltInAppName::kSettings,
-         /*searchable_string_resource_id=*/0});
-  }
-
-  return *internal_app_list;
+  return *internal_app_list_static;
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index 331ca503..6219f403 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -660,7 +660,8 @@
 
   if (web_app::SystemWebAppManager::IsAppEnabled(
           web_app::SystemAppType::CAMERA)) {
-    ChromeCameraAppUIDelegate::ShowIntent(queries, arc::GetArcWindow(task_id));
+    ChromeCameraAppUIDelegate::CameraAppDialog::ShowIntent(
+        queries, arc::GetArcWindow(task_id));
     return;
   }
 
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 3f8fd4f5..1dc2353 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -411,28 +411,26 @@
 // Browser, Constructors, Creation, Showing:
 
 // static
-Browser::BrowserCreationStatus Browser::GetBrowserCreationStatusForProfile(
-    Profile* profile) {
+Browser::CreationStatus Browser::GetCreationStatusForProfile(Profile* profile) {
   if (!g_browser_process || g_browser_process->IsShuttingDown())
-    return BrowserCreationStatus::kErrorNoProcess;
+    return CreationStatus::kErrorNoProcess;
 
   if (!IncognitoModePrefs::CanOpenBrowser(profile) ||
       (profile->IsGuestSession() && !profile->IsOffTheRecord()) ||
       !profile->AllowsBrowserWindows() ||
       ProfileManager::IsProfileDirectoryMarkedForDeletion(profile->GetPath())) {
-    return BrowserCreationStatus::kErrorProfileUnsuitable;
+    return CreationStatus::kErrorProfileUnsuitable;
   }
 
   if (IsOnKioskSplashScreen())
-    return BrowserCreationStatus::kErrorLoadingKiosk;
+    return CreationStatus::kErrorLoadingKiosk;
 
-  return BrowserCreationStatus::kOk;
+  return CreationStatus::kOk;
 }
 
 // static
 Browser* Browser::Create(const CreateParams& params) {
-  CHECK_EQ(BrowserCreationStatus::kOk,
-           GetBrowserCreationStatusForProfile(params.profile));
+  CHECK_EQ(CreationStatus::kOk, GetCreationStatusForProfile(params.profile));
   return new Browser(params);
 }
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 62b62d2..7287ec0 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -195,7 +195,7 @@
   enum class WarnBeforeClosingResult { kOkToClose, kDoNotClose };
 
   // Represents the result of a browser creation request.
-  enum class BrowserCreationStatus {
+  enum class CreationStatus {
     kOk,
     kErrorNoProcess,
     kErrorProfileUnsuitable,
@@ -309,8 +309,7 @@
   static Browser* Create(const CreateParams& params);
 
   // Returns whether a browser window can be created for the specified profile.
-  static BrowserCreationStatus GetBrowserCreationStatusForProfile(
-      Profile* profile);
+  static CreationStatus GetCreationStatusForProfile(Profile* profile);
 
   ~Browser() override;
 
diff --git a/chrome/browser/ui/browser_navigator.cc b/chrome/browser/ui/browser_navigator.cc
index 6ccb154..4bf8544 100644
--- a/chrome/browser/ui/browser_navigator.cc
+++ b/chrome/browser/ui/browser_navigator.cc
@@ -110,8 +110,8 @@
 Browser* GetOrCreateBrowser(Profile* profile, bool user_gesture) {
   Browser* browser = chrome::FindTabbedBrowser(profile, false);
 
-  if (!browser && Browser::GetBrowserCreationStatusForProfile(profile) ==
-                      Browser::BrowserCreationStatus::kOk) {
+  if (!browser && Browser::GetCreationStatusForProfile(profile) ==
+                      Browser::CreationStatus::kOk) {
     browser = Browser::Create(Browser::CreateParams(profile, user_gesture));
   }
   return browser;
@@ -169,8 +169,8 @@
     if (app_id) {
       std::string app_name = web_app::GenerateApplicationNameFromAppId(*app_id);
       Browser* browser = nullptr;
-      if (Browser::GetBrowserCreationStatusForProfile(profile) ==
-          Browser::BrowserCreationStatus::kOk) {
+      if (Browser::GetCreationStatusForProfile(profile) ==
+          Browser::CreationStatus::kOk) {
         browser = Browser::Create(Browser::CreateParams::CreateForApp(
             app_name,
             true,  // trusted_source. Installed PWAs are considered trusted.
@@ -237,8 +237,8 @@
         app_name = params.browser->app_name();
       }
 #endif
-      if (Browser::GetBrowserCreationStatusForProfile(profile) !=
-          Browser::BrowserCreationStatus::kOk) {
+      if (Browser::GetCreationStatusForProfile(profile) !=
+          Browser::CreationStatus::kOk) {
         return {nullptr, -1};
       }
       if (app_name.empty()) {
@@ -256,8 +256,8 @@
     case WindowOpenDisposition::NEW_WINDOW: {
       // Make a new normal browser window.
       Browser* browser = nullptr;
-      if (Browser::GetBrowserCreationStatusForProfile(profile) ==
-          Browser::BrowserCreationStatus::kOk) {
+      if (Browser::GetCreationStatusForProfile(profile) ==
+          Browser::CreationStatus::kOk) {
         browser = Browser::Create(
             Browser::CreateParams(profile, params.user_gesture));
       }
diff --git a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
index 18c1ce2..a3216802 100644
--- a/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
+++ b/chrome/browser/ui/browser_navigator_browsertest_chromeos.cc
@@ -86,8 +86,8 @@
 IN_PROC_BROWSER_TEST_F(BrowserNavigatorTestChromeOS, RestrictSigninProfile) {
   EXPECT_EQ(chrome::GetTotalBrowserCount(), 1u);
 
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-            Browser::GetBrowserCreationStatusForProfile(
+  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+            Browser::GetCreationStatusForProfile(
                 chromeos::ProfileHelper::GetSigninProfile()));
 }
 
diff --git a/chrome/browser/ui/browser_unittest.cc b/chrome/browser/ui/browser_unittest.cc
index e4dc414..1b314e6 100644
--- a/chrome/browser/ui/browser_unittest.cc
+++ b/chrome/browser/ui/browser_unittest.cc
@@ -173,10 +173,10 @@
 
   // Verify creating browser fails in both original and OTR version of the
   // profile.
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-            Browser::GetBrowserCreationStatusForProfile(test_profile.get()));
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-            Browser::GetBrowserCreationStatusForProfile(
+  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+            Browser::GetCreationStatusForProfile(test_profile.get()));
+  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+            Browser::GetCreationStatusForProfile(
                 test_profile->GetPrimaryOTRProfile()));
 }
 
@@ -189,8 +189,8 @@
 
   // Creating a browser window in OTR profile should fail if incognito is
   // disabled.
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-            Browser::GetBrowserCreationStatusForProfile(
+  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+            Browser::GetCreationStatusForProfile(
                 test_profile->GetPrimaryOTRProfile()));
 
   // Verify creating a browser in the original profile succeeds.
@@ -210,8 +210,8 @@
 
   // Creating a browser window in the original profile should fail if incognito
   // is forced.
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-            Browser::GetBrowserCreationStatusForProfile(test_profile.get()));
+  EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+            Browser::GetCreationStatusForProfile(test_profile.get()));
 
   // Creating a browser in OTR test profile should succeed.
   Browser::CreateParams off_the_record_create_params(
@@ -266,8 +266,8 @@
 
   session_manager.SetSessionState(SessionState::LOGIN_PRIMARY);
   // Browser should not be created during login session state.
-  EXPECT_EQ(Browser::BrowserCreationStatus::kErrorLoadingKiosk,
-            Browser::GetBrowserCreationStatusForProfile(&profile));
+  EXPECT_EQ(Browser::CreationStatus::kErrorLoadingKiosk,
+            Browser::GetCreationStatusForProfile(&profile));
 
   Browser::CreateParams create_params = Browser::CreateParams(&profile, false);
   std::unique_ptr<BrowserWindow> window = CreateBrowserWindow();
@@ -421,8 +421,8 @@
     guest_profile = test_profile.get();
   } else {
     // Try creating a browser in original guest profile - it should fail.
-    EXPECT_EQ(Browser::BrowserCreationStatus::kErrorProfileUnsuitable,
-              Browser::GetBrowserCreationStatusForProfile(test_profile.get()));
+    EXPECT_EQ(Browser::CreationStatus::kErrorProfileUnsuitable,
+              Browser::GetCreationStatusForProfile(test_profile.get()));
 
     // Create OTR profile for the Guest profile.
     EXPECT_TRUE(otr_profile_builder.BuildIncognito(test_profile.get()));
diff --git a/chrome/browser/ui/cocoa/applescript/window_applescript.mm b/chrome/browser/ui/cocoa/applescript/window_applescript.mm
index 376bc50..2c649293 100644
--- a/chrome/browser/ui/cocoa/applescript/window_applescript.mm
+++ b/chrome/browser/ui/cocoa/applescript/window_applescript.mm
@@ -79,8 +79,8 @@
   if ((self = [super init])) {
     // TODO(https://crbug.com/1144992): If crash fixed, investigate why browser
     // cannot be created here.
-    if (Browser::GetBrowserCreationStatusForProfile(aProfile) !=
-        Browser::BrowserCreationStatus::kOk) {
+    if (Browser::GetCreationStatusForProfile(aProfile) !=
+        Browser::CreationStatus::kOk) {
       NOTREACHED();
       [self release];
       return nil;
diff --git a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
index 1ad8380..47a7c1cc 100644
--- a/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
+++ b/chrome/browser/ui/settings_window_manager_browsertest_chromeos.cc
@@ -187,17 +187,3 @@
   chrome::ShowSettingsSubPage(browser(), chrome::kAutofillSubPage);
   EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
 }
-
-IN_PROC_BROWSER_TEST_F(SettingsWindowManagerTest, KioskMode) {
-  base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kForceAppMode);
-
-  // Open settings window.
-  settings_manager_->ShowOSSettings(browser()->profile());
-  Browser* settings_browser =
-      settings_manager_->FindBrowserForProfile(browser()->profile());
-  ASSERT_TRUE(settings_browser);
-
-  // In kiosk mode, browser should be created, but it should not be a system web
-  // app.
-  EXPECT_EQ(settings_browser->app_controller(), nullptr);
-}
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_dialog_view.cc b/chrome/browser/ui/views/apps/app_dialog/app_dialog_view.cc
index 277d3245..87fc5f7 100644
--- a/chrome/browser/ui/views/apps/app_dialog/app_dialog_view.cc
+++ b/chrome/browser/ui/views/apps/app_dialog/app_dialog_view.cc
@@ -17,7 +17,7 @@
   SetIcon(image);
   SetShowIcon(true);
   SetShowCloseButton(false);
-  SetModalType(ui::MODAL_TYPE_WINDOW);
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
 }
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc b/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
index 1ceb903..fc90dae 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views_unittest.cc
@@ -314,3 +314,23 @@
       origin_before_layout);
   EXPECT_FALSE(overlay_window().IsLayoutPendingForTesting());
 }
+
+TEST_F(OverlayWindowViewsTest, UpdateVideoSizeDoesNotMoveWindow) {
+  // Enter PiP.
+  overlay_window().UpdateVideoSize({300, 200});
+  overlay_window().ShowInactive();
+
+  // Resize the window and move it toward the top-left corner of the work area.
+  // In production, resizing preserves the aspect ratio if possible, so we
+  // preserve it here too.
+  overlay_window().SetBounds({100, 100, 450, 300});
+
+  // Simulate a new surface layer and a change in the aspect ratio.
+  overlay_window().UpdateVideoSize({400, 200});
+
+  // The window should not move.
+  // The window size will be adjusted according to the new aspect ratio, and
+  // clamped to 500x250 to fit within the maximum size for the work area of
+  // 1000x1000.
+  EXPECT_EQ(gfx::Rect(100, 100, 500, 250), overlay_window().GetBounds());
+}
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index 5922e02..a1fe350 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -801,8 +801,13 @@
     return;
   }
 
-  // Unmark this profile ephemeral so that it is not deleted upon next startup.
-  entry->SetIsEphemeral(false);
+  if (!signed_in_profile_being_created_->GetPrefs()->GetBoolean(
+          prefs::kForceEphemeralProfiles)) {
+    // Unmark this profile ephemeral so that it isn't deleted upon next startup.
+    // Profiles should never be made non-ephemeral if ephemeral mode is forced
+    // by policy.
+    entry->SetIsEphemeral(false);
+  }
   entry->SetLocalProfileName(name_for_signed_in_profile_);
   ProfileMetrics::LogProfileAddNewUser(
       ProfileMetrics::ADD_NEW_PROFILE_PICKER_SIGNED_IN);
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
index 530de3b8..fac855c4f 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -28,10 +28,15 @@
 #include "chrome/browser/ui/views/profiles/profile_picker_test_base.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/management/management_service.h"
 #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
@@ -48,8 +53,12 @@
 
 namespace {
 
+// State of the the ForceEphemeralProfiles policy.
+enum class ForceEphemeralProfilesPolicy { kUnset, kEnabled, kDisabled };
+
 const SkColor kProfileColor = SK_ColorRED;
 const char kWork[] = "Work";
+const char kOriginalProfileName[] = "OriginalProfile";
 
 AccountInfo FillAccountInfo(
     const CoreAccountInfo& core_info,
@@ -221,6 +230,26 @@
         context, base::BindRepeating(&FakeUserPolicySigninService::Build));
   }
 
+  // Opens the Gaia signin page in the profile creation flow. Returns the new
+  // profile that was created.
+  Profile* StartSigninFlow() {
+    ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
+    WaitForLayoutWithoutToolbar();
+
+    // Simulate a click on the signin button.
+    base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
+    EXPECT_CALL(switch_finished_callback, Run(true));
+    ProfilePicker::SwitchToSignIn(kProfileColor,
+                                  switch_finished_callback.Get());
+
+    // The DICE navigation happens in a new web contents (for the profile being
+    // created), wait for it.
+    WaitForLayoutWithToolbar();
+    WaitForFirstPaint(web_contents(),
+                      GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+    return static_cast<Profile*>(web_contents()->GetBrowserContext());
+  }
+
  private:
   base::CallbackListSubscription create_services_subscription_;
   base::test::ScopedFeatureList feature_list_;
@@ -253,24 +282,9 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        CreateSignedInProfile) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  Profile* profile_being_created = StartSigninFlow();
 
   // Add an account - simulate a successful Gaia sign-in.
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_being_created);
   CoreAccountInfo core_account_info =
@@ -312,24 +326,9 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        CreateSignedInProfileWithSyncDisabled) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  Profile* profile_being_created = StartSigninFlow();
 
   // Disable sync by setting the device as managed in prefs.
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   syncer::SyncPrefs prefs(profile_being_created->GetPrefs());
   prefs.SetManagedForTest(true);
   syncer::SyncService* sync_service =
@@ -376,24 +375,9 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        CreateSignedInProfileSettings) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  Profile* profile_being_created = StartSigninFlow();
 
   // Add an account - simulate a successful Gaia sign-in.
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_being_created);
   CoreAccountInfo core_account_info =
@@ -437,20 +421,7 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        CreateSignedInProfileOpenLink) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  StartSigninFlow();
 
   // Simulate clicking on a link that opens in a new window.
   const GURL kURL("https://foo.google.com");
@@ -498,23 +469,9 @@
                            base::UTF8ToUTF16("joe.consumer@gmail.com"),
                            /*is_consented_primary_account=*/true);
 
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  Profile* profile_being_created = StartSigninFlow();
 
   // Add an account - simulate a successful Gaia sign-in.
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_being_created);
   CoreAccountInfo core_account_info =
@@ -547,23 +504,8 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        CreateSignedInProfileExtendedInfoTimeout) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
+  Profile* profile_being_created = StartSigninFlow();
 
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
-
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_being_created);
 
@@ -623,24 +565,9 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest,
                        CreateSignedInProfile) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  Profile* profile_being_created = StartSigninFlow();
 
   // Add an account - simulate a successful Gaia sign-in.
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_being_created);
   // Consumer-looking gmail address avoids code that forces the sync service to
@@ -695,24 +622,9 @@
 IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest,
                        CreateSignedInProfileSettings) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-
-  ProfilePicker::Show(ProfilePicker::EntryPoint::kProfileMenuAddNewProfile);
-  WaitForLayoutWithoutToolbar();
-
-  // Simulate a click on the signin button.
-  base::MockCallback<base::OnceCallback<void(bool)>> switch_finished_callback;
-  EXPECT_CALL(switch_finished_callback, Run(true));
-  ProfilePicker::SwitchToSignIn(kProfileColor, switch_finished_callback.Get());
-
-  // The DICE navigation happens in a new web contents (for the profile being
-  // created), wait for it.
-  WaitForLayoutWithToolbar();
-  WaitForFirstPaint(web_contents(),
-                    GaiaUrls::GetInstance()->signin_chrome_sync_dice());
+  Profile* profile_being_created = StartSigninFlow();
 
   // Add an account - simulate a successful Gaia sign-in.
-  Profile* profile_being_created =
-      static_cast<Profile*>(web_contents()->GetBrowserContext());
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile_being_created);
   // Consumer-looking gmail address avoids code that forces the sync service to
@@ -759,4 +671,184 @@
                    ->UsingAutogeneratedTheme());
 }
 
+class ProfilePickerCreationFlowEphemeralProfileBrowserTest
+    : public ProfilePickerCreationFlowBrowserTest,
+      public testing::WithParamInterface<ForceEphemeralProfilesPolicy> {
+ public:
+  ProfilePickerCreationFlowEphemeralProfileBrowserTest() = default;
+
+  ForceEphemeralProfilesPolicy GetForceEphemeralProfilesPolicy() const {
+    return GetParam();
+  }
+
+  bool AreEphemeralProfilesForced() const {
+    return GetForceEphemeralProfilesPolicy() ==
+           ForceEphemeralProfilesPolicy::kEnabled;
+  }
+
+  // Check that the policy was correctly applied to the preference.
+  void CheckPolicyApplied(Profile* profile) {
+    EXPECT_EQ(profile->GetPrefs()->GetBoolean(prefs::kForceEphemeralProfiles),
+              AreEphemeralProfilesForced());
+  }
+
+  static ProfileManager* profile_manager() {
+    return g_browser_process->profile_manager();
+  }
+
+  // Checks if a profile matching `name` exists in the profile manager.
+  bool ProfileWithNameExists(const std::string& name) {
+    for (const auto* entry : profile_manager()
+                                 ->GetProfileAttributesStorage()
+                                 .GetAllProfilesAttributes()) {
+      if (entry->GetLocalProfileName() == base::UTF8ToUTF16(name))
+        return true;
+    }
+    return false;
+  }
+
+  // Checks if the original profile (the initial profile existing at the start
+  // of the test) exists in the profile manager.
+  bool OriginalProfileExists() {
+    return ProfileWithNameExists(kOriginalProfileName);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    ForceEphemeralProfilesPolicy policy = GetForceEphemeralProfilesPolicy();
+
+    if (policy != ForceEphemeralProfilesPolicy::kUnset) {
+      policy::PolicyMap policy_map;
+      policy_map.Set(
+          policy::key::kForceEphemeralProfiles, policy::POLICY_LEVEL_MANDATORY,
+          policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
+          base::Value(policy == ForceEphemeralProfilesPolicy::kEnabled),
+          nullptr);
+      policy_provider_.UpdateChromePolicy(policy_map);
+
+      ON_CALL(policy_provider_, IsInitializationComplete(testing::_))
+          .WillByDefault(testing::Return(true));
+      ON_CALL(policy_provider_, IsFirstPolicyLoadComplete(testing::_))
+          .WillByDefault(testing::Return(true));
+      policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+          &policy_provider_);
+    }
+    ProfilePickerCreationFlowBrowserTest::SetUpInProcessBrowserTestFixture();
+  }
+
+  void SetUpOnMainThread() override {
+    ProfilePickerCreationFlowBrowserTest::SetUpInProcessBrowserTestFixture();
+    if (GetTestPreCount() == 1) {
+      // Only called in "PRE_" tests, to set a name to the starting profile.
+      ProfileAttributesEntry* entry = nullptr;
+      ASSERT_TRUE(profile_manager()
+                      ->GetProfileAttributesStorage()
+                      .GetProfileAttributesWithPath(
+                          browser()->profile()->GetPath(), &entry));
+      entry->SetLocalProfileName(base::UTF8ToUTF16(kOriginalProfileName));
+    }
+    CheckPolicyApplied(browser()->profile());
+  }
+
+ private:
+  testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
+};
+
+// Checks that the new profile is no longer ephemeral at the end of the flow and
+// still exists after restart.
+IN_PROC_BROWSER_TEST_P(ProfilePickerCreationFlowEphemeralProfileBrowserTest,
+                       PRE_Signin) {
+  ASSERT_EQ(1u, BrowserList::GetInstance()->size());
+  ASSERT_EQ(1u, profile_manager()->GetNumberOfProfiles());
+  ASSERT_TRUE(OriginalProfileExists());
+  Profile* profile_being_created = StartSigninFlow();
+
+  // Check that the profile is ephemeral, regardless of the policy.
+  ProfileAttributesEntry* entry = nullptr;
+  ASSERT_TRUE(profile_manager()
+                  ->GetProfileAttributesStorage()
+                  .GetProfileAttributesWithPath(
+                      profile_being_created->GetPath(), &entry));
+  EXPECT_TRUE(entry->IsEphemeral());
+  // Add an account - simulate a successful Gaia sign-in.
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile_being_created);
+  CoreAccountInfo core_account_info =
+      signin::MakeAccountAvailable(identity_manager, "joe.consumer@gmail.com");
+  ASSERT_TRUE(identity_manager->HasAccountWithRefreshToken(
+      core_account_info.account_id));
+
+  AccountInfo account_info = FillAccountInfo(core_account_info, "Joe");
+  signin::UpdateAccountInfoForAccount(identity_manager, account_info);
+
+  // Wait for the sign-in to propagate to the flow, resulting in sync
+  // confirmation screen getting displayed.
+  WaitForFirstPaint(web_contents(), GURL("chrome://sync-confirmation/"));
+
+  // Simulate closing the UI with "No, thanks".
+  LoginUIServiceFactory::GetForProfile(profile_being_created)
+      ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC);
+  Browser* new_browser = BrowserAddedWaiter(2u).Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
+
+  EXPECT_FALSE(ProfilePicker::IsOpen());
+  EXPECT_EQ(2u, profile_manager()->GetNumberOfProfiles());
+  EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe"));
+  // The profile is no longer ephemeral, unless the policy is enabled.
+  EXPECT_EQ(entry->IsEphemeral(), AreEphemeralProfilesForced());
+  // The preference is consistent with the policy.
+  CheckPolicyApplied(profile_being_created);
+}
+
+IN_PROC_BROWSER_TEST_P(ProfilePickerCreationFlowEphemeralProfileBrowserTest,
+                       Signin) {
+  if (AreEphemeralProfilesForced()) {
+    // If the policy is set, all profiles should have been deleted.
+    EXPECT_EQ(1u, profile_manager()->GetNumberOfProfiles());
+    // The current profile is not the one that was created in the previous run.
+    EXPECT_FALSE(ProfileWithNameExists("Joe"));
+    EXPECT_FALSE(OriginalProfileExists());
+    return;
+  }
+
+  // If the policy is disabled or unset, the two profiles are still here.
+  EXPECT_EQ(2u, profile_manager()->GetNumberOfProfiles());
+  EXPECT_TRUE(ProfileWithNameExists("Joe"));
+  EXPECT_TRUE(OriginalProfileExists());
+}
+
+// Checks that the new profile is deleted on next startup if Chrome exits during
+// the signin flow.
+IN_PROC_BROWSER_TEST_P(ProfilePickerCreationFlowEphemeralProfileBrowserTest,
+                       PRE_ExitDuringSignin) {
+  ASSERT_EQ(1u, BrowserList::GetInstance()->size());
+  ASSERT_EQ(1u, profile_manager()->GetNumberOfProfiles());
+  ASSERT_TRUE(OriginalProfileExists());
+  Profile* profile_being_created = StartSigninFlow();
+
+  // Check that the profile is ephemeral, regardless of the policy.
+  ProfileAttributesEntry* entry = nullptr;
+  ASSERT_TRUE(profile_manager()
+                  ->GetProfileAttributesStorage()
+                  .GetProfileAttributesWithPath(
+                      profile_being_created->GetPath(), &entry));
+  EXPECT_TRUE(entry->IsEphemeral());
+  // Exit Chrome while still in the signin flow.
+}
+
+IN_PROC_BROWSER_TEST_P(ProfilePickerCreationFlowEphemeralProfileBrowserTest,
+                       ExitDuringSignin) {
+  // The profile was deleted, regardless of the policy.
+  EXPECT_EQ(1u, profile_manager()->GetNumberOfProfiles());
+  // The other profile still exists.
+  EXPECT_NE(AreEphemeralProfilesForced(), OriginalProfileExists());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    ProfilePickerCreationFlowEphemeralProfileBrowserTest,
+    testing::Values(ForceEphemeralProfilesPolicy::kUnset,
+                    ForceEphemeralProfilesPolicy::kDisabled,
+                    ForceEphemeralProfilesPolicy::kEnabled));
+
 }  // namespace
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc
index 496eae75..8164843 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_interactive_uitest.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_test_helper.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "content/public/test/browser_test.h"
@@ -21,23 +22,32 @@
 #include "ui/views/view.h"
 #include "url/gurl.h"
 
+// Param: DesktopPWAsElidedExtensionsMenu feature.
 class WebAppFrameToolbarInteractiveUITest
-    : public extensions::ExtensionBrowserTest {
+    : public extensions::ExtensionBrowserTest,
+      public testing::WithParamInterface<bool> {
  public:
-  WebAppFrameToolbarInteractiveUITest() = default;
+  WebAppFrameToolbarInteractiveUITest() {
+    feature_list_.InitWithFeatureState(
+        ::features::kDesktopPWAsElidedExtensionsMenu, IsExtensionsMenuElided());
+  }
 
   WebAppFrameToolbarTestHelper* helper() {
     return &web_app_frame_toolbar_helper_;
   }
 
+ protected:
+  bool IsExtensionsMenuElided() const { return GetParam(); }
+
  private:
   WebAppFrameToolbarTestHelper web_app_frame_toolbar_helper_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Verifies that for minimal-ui web apps, the toolbar keyboard focus cycles
 // among the toolbar buttons: the reload button, the extensions menu button, and
 // the app menu button, in that order.
-IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarInteractiveUITest, CycleFocus) {
+IN_PROC_BROWSER_TEST_P(WebAppFrameToolbarInteractiveUITest, CycleFocus) {
   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("simple_with_icon/")));
 
   const GURL app_url("https://test.org");
@@ -66,9 +76,11 @@
 
   // Press Tab to cycle through controls until we end up back where we started.
   // This approach is similar to ToolbarViewTest::RunToolbarCycleFocusTest().
-  focus_manager->AdvanceFocus(false);
-  EXPECT_EQ(focus_manager->GetFocusedView()->GetID(),
-            VIEW_ID_EXTENSIONS_MENU_BUTTON);
+  if (!IsExtensionsMenuElided()) {
+    focus_manager->AdvanceFocus(false);
+    EXPECT_EQ(focus_manager->GetFocusedView()->GetID(),
+              VIEW_ID_EXTENSIONS_MENU_BUTTON);
+  }
   focus_manager->AdvanceFocus(false);
   EXPECT_EQ(focus_manager->GetFocusedView()->GetID(), VIEW_ID_APP_MENU);
   focus_manager->AdvanceFocus(false);
@@ -77,9 +89,11 @@
   // Now press Shift-Tab to cycle backwards.
   focus_manager->AdvanceFocus(true);
   EXPECT_EQ(focus_manager->GetFocusedView()->GetID(), VIEW_ID_APP_MENU);
-  focus_manager->AdvanceFocus(true);
-  EXPECT_EQ(focus_manager->GetFocusedView()->GetID(),
-            VIEW_ID_EXTENSIONS_MENU_BUTTON);
+  if (!IsExtensionsMenuElided()) {
+    focus_manager->AdvanceFocus(true);
+    EXPECT_EQ(focus_manager->GetFocusedView()->GetID(),
+              VIEW_ID_EXTENSIONS_MENU_BUTTON);
+  }
   focus_manager->AdvanceFocus(true);
   EXPECT_EQ(focus_manager->GetFocusedView()->GetID(), VIEW_ID_RELOAD_BUTTON);
 
@@ -90,3 +104,7 @@
       IDC_FOCUS_TOOLBAR);
   EXPECT_EQ(focus_manager->GetFocusedView()->GetID(), VIEW_ID_BACK_BUTTON);
 }
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebAppFrameToolbarInteractiveUITest,
+                         ::testing::Bool());
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 a3f18586..328a5e0 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
@@ -179,8 +179,8 @@
       return nullptr;
   }
 
-  if (Browser::GetBrowserCreationStatusForProfile(profile_for_launch) !=
-      Browser::BrowserCreationStatus::kOk) {
+  if (Browser::GetCreationStatusForProfile(profile_for_launch) !=
+      Browser::CreationStatus::kOk) {
     return nullptr;
   }
 
diff --git a/chrome/browser/ui/web_applications/web_app_launch_manager.cc b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
index 312b269..126f17e 100644
--- a/chrome/browser/ui/web_applications/web_app_launch_manager.cc
+++ b/chrome/browser/ui/web_applications/web_app_launch_manager.cc
@@ -140,8 +140,8 @@
 
 content::WebContents* WebAppLaunchManager::OpenApplication(
     apps::AppLaunchParams&& params) {
-  if (Browser::GetBrowserCreationStatusForProfile(profile_) !=
-          Browser::BrowserCreationStatus::kOk ||
+  if (Browser::GetCreationStatusForProfile(profile_) !=
+          Browser::CreationStatus::kOk ||
       !provider_->registrar().IsInstalled(params.app_id)) {
     return nullptr;
   }
diff --git a/chrome/browser/ui/webui/chrome_web_contents_handler.cc b/chrome/browser/ui/webui/chrome_web_contents_handler.cc
index 6a398159..c7e3359 100644
--- a/chrome/browser/ui/webui/chrome_web_contents_handler.cc
+++ b/chrome/browser/ui/webui/chrome_web_contents_handler.cc
@@ -91,8 +91,8 @@
   if (!browser) {
     // The request can be triggered by Captive portal when browser is not ready
     // (https://crbug.com/1141608).
-    if (Browser::GetBrowserCreationStatusForProfile(profile) !=
-        Browser::BrowserCreationStatus::kOk) {
+    if (Browser::GetCreationStatusForProfile(profile) !=
+        Browser::CreationStatus::kOk) {
       return;
     }
     browser = Browser::Create(
diff --git a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
index 9b25742..fcad654a 100644
--- a/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.cc
@@ -13,6 +13,8 @@
 #include "base/containers/flat_set.h"
 #include "base/feature_list.h"
 #include "base/guid.h"
+#include "base/i18n/message_formatter.h"
+#include "base/i18n/number_formatting.h"
 #include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
@@ -65,6 +67,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/installer/util/google_update_settings.h"
 #include "chromeos/components/security_token_pin/constants.h"
+#include "chromeos/components/security_token_pin/error_generator.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/constants/devicetype.h"
@@ -270,8 +273,17 @@
   base::Value params(base::Value::Type::DICTIONARY);
   params.SetIntKey("codeType", static_cast<int>(code_type));
   params.SetBoolKey("enableUserInput", enable_user_input);
-  params.SetIntKey("errorLabel", static_cast<int>(error_label));
   params.SetIntKey("attemptsLeft", attempts_left);
+  params.SetBoolKey("hasError",
+                    error_label != security_token_pin::ErrorLabel::kNone);
+  params.SetStringKey(
+      "formattedError",
+      GenerateErrorMessage(error_label, attempts_left, enable_user_input));
+  params.SetStringKey(
+      "formattedAttemptsLeft",
+      base::i18n::MessageFormatter::FormatWithNumberedArgs(
+          l10n_util::GetStringUTF16(IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT),
+          attempts_left));
   return params;
 }
 
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index d9693c2..89a6747f 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1610294646-d9496012030808fbbd5c6d71395353a15075c9f2.profdata
+chrome-linux-master-1610338418-506ef60e799a83be30c02d3485d0519970a70817.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 3384c90..bcacc638 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1610294646-5cd814ecb87ad7a408cdeda7da36a6886e7402c5.profdata
+chrome-win32-master-1610338418-0a30eb701be1732e98bc8c78e37a5b6340c67f55.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 089ab8f..84330c4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1610294646-193d19344cbd53e124bebc9434b6d5bafe44fd51.profdata
+chrome-win64-master-1610338418-1bf01a8f47d2b9b410b39a25bf178b364099d014.profdata
diff --git a/chrome/common/channel_info.h b/chrome/common/channel_info.h
index b9e0d89..5958587 100644
--- a/chrome/common/channel_info.h
+++ b/chrome/common/channel_info.h
@@ -42,12 +42,6 @@
 version_info::Channel GetChannel();
 
 #if defined(OS_MAC)
-// Because the channel information on the Mac is baked into the Info.plist file,
-// and that file may change during an update, this function must be called
-// early in startup to cache the channel info so that the correct channel info
-// can be returned later.
-void CacheChannelInfo();
-
 // Maps the name of the channel to version_info::Channel, always returning
 // Channel::UNKNOWN for unbranded builds. For branded builds defaults to
 // Channel::STABLE, if channel is empty, else matches the name and returns
diff --git a/chrome/common/channel_info_mac.mm b/chrome/common/channel_info_mac.mm
index e9e7931..848ef7e 100644
--- a/chrome/common/channel_info_mac.mm
+++ b/chrome/common/channel_info_mac.mm
@@ -7,101 +7,53 @@
 #import <Foundation/Foundation.h>
 
 #include "base/mac/bundle_locations.h"
-#include "base/macros.h"
-#include "base/no_destructor.h"
 #include "base/strings/sys_string_conversions.h"
 #include "build/branding_buildflags.h"
 #include "components/version_info/version_info.h"
 
 namespace chrome {
 
-namespace {
-
-std::string ChannelName() {
+std::string GetChannelName() {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  static const base::NoDestructor<std::string> channel([] {
-    // Use the main Chrome application bundle and not the framework bundle.
-    // Keystone keys don't live in the framework.
-    NSBundle* bundle = base::mac::OuterBundle();
-    NSString* channel = [bundle objectForInfoDictionaryKey:@"KSChannelID"];
+  // Use the main Chrome application bundle and not the framework bundle.
+  // Keystone keys don't live in the framework.
+  NSBundle* bundle = base::mac::OuterBundle();
+  NSString* channel = [bundle objectForInfoDictionaryKey:@"KSChannelID"];
 
-    // Only ever return "", "unknown", "beta", "dev", or "canary" in a branded
-    // build.
-    // KSProductID is not set (for stable) or "beta", "dev" or "canary" for
-    // the intel-only build.
-    // KSProductID is "arm64" (for stable) or "arm64-beta", "arm64-dev" or
-    // "arm64-canary" for the arm-only build.
-    // KSProductID is "universal" (for stable) or "universal-beta",
-    // "universal-dev" or "universal-canary" for the arm+intel universal binary.
-    if (![bundle objectForInfoDictionaryKey:@"KSProductID"]) {
-      // This build is not Keystone-enabled, it can't have a channel.
-      channel = @"unknown";
-    } else if (!channel || [channel isEqual:@"arm64"] ||
-               [channel isEqual:@"universal"]) {
-      // For the intel stable channel, KSChannelID is not set.
-      channel = @"";
+  // Only ever return "", "unknown", "beta", "dev", or "canary" in a branded
+  // build.
+  // KSProductID is not set (for stable) or "beta", "dev" or "canary" for
+  // the intel-only build.
+  // KSProductID is "arm64" (for stable) or "arm64-beta", "arm64-dev" or
+  // "arm64-canary" for the arm-only build.
+  // KSProductID is "universal" (for stable) or "universal-beta",
+  // "universal-dev" or "universal-canary" for the arm+intel universal binary.
+  if (![bundle objectForInfoDictionaryKey:@"KSProductID"]) {
+    // This build is not Keystone-enabled, it can't have a channel.
+    channel = @"unknown";
+  } else if (!channel || [channel isEqual:@"arm64"] ||
+             [channel isEqual:@"universal"]) {
+    // For the intel stable channel, KSChannelID is not set.
+    channel = @"";
+  } else {
+    if ([channel hasPrefix:@"arm64-"])
+      channel = [channel substringFromIndex:[@"arm64-" length]];
+    else if ([channel hasPrefix:@"universal-"])
+      channel = [channel substringFromIndex:[@"universal-" length]];
+    if ([channel isEqual:@"beta"] || [channel isEqual:@"dev"] ||
+        [channel isEqual:@"canary"]) {
+      // do nothing.
     } else {
-      if ([channel hasPrefix:@"arm64-"])
-        channel = [channel substringFromIndex:[@"arm64-" length]];
-      else if ([channel hasPrefix:@"universal-"])
-        channel = [channel substringFromIndex:[@"universal-" length]];
-      if ([channel isEqual:@"beta"] || [channel isEqual:@"dev"] ||
-          [channel isEqual:@"canary"]) {
-        // Do nothing.
-      } else {
-        channel = @"unknown";
-      }
+      channel = @"unknown";
     }
+  }
 
-    return base::SysNSStringToUTF8(channel);
-  }());
-  return *channel;
+  return base::SysNSStringToUTF8(channel);
 #else
   return std::string();
 #endif
 }
 
-bool SideBySideCapable() {
-#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  static const base::NoDestructor<bool> capable([] {
-    // Use the main Chrome application bundle and not the framework bundle.
-    // Keystone keys don't live in the framework.
-    NSBundle* bundle = base::mac::OuterBundle();
-    if (![bundle objectForInfoDictionaryKey:@"KSProductID"]) {
-      // This build is not Keystone-enabled, and without a channel assume it is
-      // side-by-side capable.
-      return true;
-    }
-
-    if (GetChannelName().empty()) {
-      // For the stable channel, GetChannelName() returns the empty string.
-      // Stable Chromes are what side-by-side capable Chromes are running
-      // side-by-side *to* and by definition are side-by-side capable.
-      return true;
-    }
-
-    // If there is a CrProductDirName key, then the user data dir of this
-    // beta/dev/canary Chrome is separate, and it can run side-by-side to the
-    // stable Chrome.
-    return [bundle objectForInfoDictionaryKey:@"CrProductDirName"];
-  }());
-  return *capable;
-#else
-  return true;
-#endif
-}
-
-}  // namespace
-
-void CacheChannelInfo() {
-  ignore_result(ChannelName());
-  ignore_result(SideBySideCapable());
-}
-
-std::string GetChannelName() {
-  return ChannelName();
-}
-
 version_info::Channel GetChannelByName(const std::string& channel) {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
   if (channel.empty())
@@ -117,11 +69,34 @@
 }
 
 bool IsSideBySideCapable() {
-  return SideBySideCapable();
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  // Use the main Chrome application bundle and not the framework bundle.
+  // Keystone keys don't live in the framework.
+  NSBundle* bundle = base::mac::OuterBundle();
+  if (![bundle objectForInfoDictionaryKey:@"KSProductID"]) {
+    // This build is not Keystone-enabled, and without a channel assume it is
+    // side-by-side capable.
+    return true;
+  }
+
+  if (GetChannelName().empty()) {
+    // For the stable channel, GetChannelName() returns the empty string.
+    // Stable Chromes are what side-by-side capable Chromes are running
+    // side-by-side *to* and by definition are side-by-side capable.
+    return true;
+  }
+
+  // If there is a CrProductDirName key, then the user data dir of this
+  // beta/dev/canary Chrome is separate, and it can run side-by-side to the
+  // stable Chrome.
+  return [bundle objectForInfoDictionaryKey:@"CrProductDirName"];
+#else
+  return true;
+#endif
 }
 
 version_info::Channel GetChannel() {
-  return GetChannelByName(ChannelName());
+  return GetChannelByName(GetChannelName());
 }
 
 }  // namespace chrome
diff --git a/chrome/test/data/webui/settings/chromeos/device_page_tests.js b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
index 8fd944262..9ce429ed 100644
--- a/chrome/test/data/webui/settings/chromeos/device_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/device_page_tests.js
@@ -1048,6 +1048,7 @@
         MockInteractions.pressAndReleaseKeyOn(
             keyboardPage.$$('#repeatRateSlider').$$('cr-slider'), 39, [],
             'ArrowRight');
+        await test_util.flushTasks();
         expectEquals(1000, get('xkb_auto_repeat_delay_r2'));
         expectEquals(300, get('xkb_auto_repeat_interval_r2'));
 
diff --git a/chromecast/browser/exo/cast_wm_helper.cc b/chromecast/browser/exo/cast_wm_helper.cc
index 593bb46..a751b18 100644
--- a/chromecast/browser/exo/cast_wm_helper.cc
+++ b/chromecast/browser/exo/cast_wm_helper.cc
@@ -103,9 +103,10 @@
 
 void CastWMHelper::OnDragEntered(const ui::DropTargetEvent& event) {}
 
-int CastWMHelper::OnDragUpdated(const ui::DropTargetEvent& event) {
+aura::client::DragUpdateInfo CastWMHelper::OnDragUpdated(
+    const ui::DropTargetEvent& event) {
   NOTIMPLEMENTED();
-  return 0;
+  return aura::client::DragUpdateInfo();
 }
 
 void CastWMHelper::OnDragExited() {}
diff --git a/chromecast/browser/exo/cast_wm_helper.h b/chromecast/browser/exo/cast_wm_helper.h
index 30960115..e0d287d 100644
--- a/chromecast/browser/exo/cast_wm_helper.h
+++ b/chromecast/browser/exo/cast_wm_helper.h
@@ -105,7 +105,8 @@
 
   // Overridden from aura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index f696aa7..804b9c0 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13716.0.0
\ No newline at end of file
+13717.0.0
\ No newline at end of file
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 290923f..fd031a5 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -322,10 +322,14 @@
         Unknown error.
       </message>
       <message name="IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT" desc="The text displayed in the certificate provider PIN request dialog about the number of attempts left">
-        <ph name="ATTEMPTS_LEFT">$1<ex>3</ex></ph> attempts left
+        {ATTEMPTS_LEFT, plural,
+        =1 {{0} attempt left}
+        other {{0} attempts left}}
       </message>
       <message name="IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS" desc="The text displayed in the certificate provider PIN request dialog when the previous login attempt was unsuccessful but there are more attempts remaining. Includes the reason for the previous failure.">
-        <ph name="ERROR_MESSAGE">$1<ex>Invalid PIN.</ex></ph> <ph name="ATTEMPTS_LEFT">$2<ex>3</ex></ph> attempts left
+        {ATTEMPTS_LEFT, plural,
+        =1 {<ph name="ERROR_MESSAGE">{1}<ex>Invalid PIN.</ex></ph> {0} attempt left}
+        other {<ph name="ERROR_MESSAGE">{1}<ex>Invalid PIN.</ex></ph> {0} attempts left}}
       </message>
 
       <!-- Print Management App -->
diff --git a/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT.png.sha1
new file mode 100644
index 0000000..ec5958ae
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT.png.sha1
@@ -0,0 +1 @@
+3d613d7a3005a79de03176b1adcd93150b92b64c
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS.png.sha1 b/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS.png.sha1
index a9d5735..cad40522 100644
--- a/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS.png.sha1
+++ b/chromeos/chromeos_strings_grd/IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS.png.sha1
@@ -1 +1 @@
-bff3e9c779eeec926cb2a4e6483f615b8b797900
\ No newline at end of file
+81517747d7433f99ec8277b79a4654f45d72ea68
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/resources/css/main.css b/chromeos/components/camera_app_ui/resources/css/main.css
index 9fd93a0..41adc66 100644
--- a/chromeos/components/camera_app_ui/resources/css/main.css
+++ b/chromeos/components/camera_app_ui/resources/css/main.css
@@ -1186,7 +1186,8 @@
   }
   8%,
   92% {
-    animation-timing-function: var(--exit-easing);
+    /* exit-easing. We cannot use variable here (https://crbug.com/1066798) */
+    animation-timing-function: cubic-bezier(0.4, 0, 1, 1);
     bottom: 31px;
   }
 }
@@ -1549,16 +1550,22 @@
   --border-distance: 8px;
   --border-width: 4px;
   --inner-border-radius: 16px;
+  opacity: 0;
   /* Use padding-top instead of height to make a responsive square, since the
    * percentage in padding-top is relative to the width of the containing
    * block. */
-  padding-top: calc(100% / 3);
+  padding-top: calc(100% / 2);
+  transition: all var(--exit-easing) var(--moderate1-duration);
   visibility: hidden;
-  width: calc(100% / 3);
+  width: calc(100% / 2);
 }
 
 body.photo.scan-barcode .barcode-scan-box {
+  opacity: 1;
+  padding-top: calc(100% / 3);
+  transition: all var(--enter-easing) var(--moderate1-duration);
   visibility: visible;
+  width: calc(100% / 3);
 }
 
 /* The inner scan box with a translucent overlay. */
@@ -1602,6 +1609,7 @@
   left: 50%;
   max-width: min(80%, var(--chip-max-width));
   opacity: 1;
+  pointer-events: auto;
   position: absolute;
   /* Avoid collision with scan-box and preview-box */
   top: max(8px, min(10%, 33.33% - var(--text-line-height) -
diff --git a/chromeos/components/camera_app_ui/resources/js/main.js b/chromeos/components/camera_app_ui/resources/js/main.js
index 750c0d6..f371da3 100644
--- a/chromeos/components/camera_app_ui/resources/js/main.js
+++ b/chromeos/components/camera_app_ui/resources/js/main.js
@@ -116,6 +116,14 @@
 
     document.body.addEventListener('keydown', this.onKeyPressed_.bind(this));
 
+    // Disable the zoom in-out gesture which is triggered by wheel and pinch on
+    // trackpad.
+    document.body.addEventListener('wheel', (event) => {
+      if (event.ctrlKey) {
+        event.preventDefault();
+      }
+    }, {passive: false, capture: true});
+
     document.title = browserProxy.getI18nMessage('name');
     util.setupI18nElements(document.body);
     this.setupToggles_();
diff --git a/chromeos/components/security_token_pin/error_generator.cc b/chromeos/components/security_token_pin/error_generator.cc
index 2bbe4cd..fa1a1c40 100644
--- a/chromeos/components/security_token_pin/error_generator.cc
+++ b/chromeos/components/security_token_pin/error_generator.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/components/security_token_pin/error_generator.h"
 
+#include "base/i18n/message_formatter.h"
 #include "base/i18n/number_formatting.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -48,12 +49,13 @@
     return error_message;
   }
   if (error_message.empty()) {
-    return l10n_util::GetStringFUTF16(IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT,
-                                      base::FormatNumber(attempts_left));
+    return base::i18n::MessageFormatter::FormatWithNumberedArgs(
+        l10n_util::GetStringUTF16(IDS_REQUEST_PIN_DIALOG_ATTEMPTS_LEFT),
+        attempts_left);
   }
-  return l10n_util::GetStringFUTF16(IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS,
-                                    error_message,
-                                    base::FormatNumber(attempts_left));
+  return base::i18n::MessageFormatter::FormatWithNumberedArgs(
+      l10n_util::GetStringUTF16(IDS_REQUEST_PIN_DIALOG_ERROR_ATTEMPTS),
+      attempts_left, error_message);
 }
 
 }  // namespace security_token_pin
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index ccd20939..a9a4247 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-89-4367.0-1609757097-benchmark-89.0.4383.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-89-4367.0-1609757097-benchmark-89.0.4384.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 62663a2..97ec8d9 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-89-4367.0-1609764762-benchmark-89.0.4383.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-89-4367.0-1609764762-benchmark-89.0.4384.0-r1-redacted.afdo.xz
diff --git a/components/autofill/core/browser/autofill_regex_constants.cc b/components/autofill/core/browser/autofill_regex_constants.cc
index 3f4b0491..ae58f14 100644
--- a/components/autofill/core/browser/autofill_regex_constants.cc
+++ b/components/autofill/core/browser/autofill_regex_constants.cc
@@ -334,13 +334,14 @@
     "|(?:사용자.?)?아이디|사용자.?ID";  // ko-KR
 const char kNameRe[] =
     "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name"
-    "|name.*first.*last|firstandlastname"
+    "|name.*first.*last|firstandlastname|contact.?(name|person)"
     "|nombre.*y.*apellidos"                    // es
     "|^nom(?![a-zA-Z])"                        // fr-FR
     "|お名前|氏名"                             // ja-JP
     "|^nome"                                   // pt-BR, pt-PT
     "|نام.*نام.*خانوادگی"                      // fa
     "|姓名"                                    // zh-CN
+    "|контактное.?лицо"                        // ru
     "|(\\b|_|\\*)ad[ı]? soyad[ı]?(\\b|_|\\*)"  // tr
     "|성명"                                    // ko-KR
     "|nama.?(lengkap|penerima|kamu)";          // id
diff --git a/components/autofill/core/browser/form_parsing/name_field_unittest.cc b/components/autofill/core/browser/form_parsing/name_field_unittest.cc
index 294f3e74..2bd72ccf 100644
--- a/components/autofill/core/browser/form_parsing/name_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/name_field_unittest.cc
@@ -567,4 +567,23 @@
   ASSERT_EQ(nullptr, field_.get());
 }
 
+// Tests that contact name is classified as full name.
+TEST_F(NameFieldTest, ContactNameFull) {
+  FormFieldData field;
+  field.form_control_type = "text";
+
+  field.label = base::UTF8ToUTF16("Контактное лицо");
+  field.name = base::UTF8ToUTF16("contact person");
+  field.unique_renderer_id = MakeFieldRendererId();
+  list_.push_back(std::make_unique<AutofillField>(field));
+  FieldRendererId name = list_.back()->unique_renderer_id;
+
+  AutofillScanner scanner(list_);
+  field_ = Parse(&scanner);
+  ASSERT_NE(nullptr, field_.get());
+  field_->AddClassificationsForTesting(&field_candidates_map_);
+  ASSERT_TRUE(field_candidates_map_.find(name) != field_candidates_map_.end());
+  EXPECT_EQ(NAME_FULL, field_candidates_map_[name].BestHeuristicType());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
index 95b1c0a..0c2e1d5e 100644
--- a/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
+++ b/components/autofill/core/browser/pattern_provider/resources/regex_patterns.json
@@ -2223,7 +2223,7 @@
     "en": [
       {
         "pattern_identifier": "en_full_name_preserving",
-        "positive_pattern": "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name|name.*first.*last|firstandlastname",
+        "positive_pattern": "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name|name.*first.*last|firstandlastname|contact.?(name|person)",
         "positive_score": 0.9,
         "negative_pattern": null,
         "match_field_attributes": 3,
@@ -2290,6 +2290,16 @@
         "match_field_input_types": 1
       }
     ],
+    "ru": [
+      {
+        "pattern_identifier": "ru_full_name_preserving",
+        "positive_pattern": "контактное.?лицо",
+        "positive_score": 0.9,
+        "negative_pattern": null,
+        "match_field_attributes": 3,
+        "match_field_input_types": 1
+      }
+    ],
     "tr": [
       {
         "pattern_identifier": "tr_full_name_preserving",
diff --git a/components/exo/data_device.cc b/components/exo/data_device.cc
index 091ebfb..f3e81b1 100644
--- a/components/exo/data_device.cc
+++ b/components/exo/data_device.cc
@@ -12,8 +12,10 @@
 #include "components/exo/seat.h"
 #include "components/exo/shell_surface_util.h"
 #include "components/exo/surface.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/clipboard_monitor.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/drop_target_event.h"
 
@@ -97,15 +99,29 @@
   delegate_->OnEnter(surface, event.location_f(), *data_offer_->get());
 }
 
-int DataDevice::OnDragUpdated(const ui::DropTargetEvent& event) {
+aura::client::DragUpdateInfo DataDevice::OnDragUpdated(
+    const ui::DropTargetEvent& event) {
   if (!data_offer_)
-    return ui::DragDropTypes::DRAG_NONE;
+    return aura::client::DragUpdateInfo();
+
+  ui::EndpointType endpoint_type = ui::EndpointType::kDefault;
+  Surface* surface = GetEffectiveTargetForEvent(event);
+  if (surface) {
+    endpoint_type =
+        seat_->data_exchange_delegate()->GetDataTransferEndpointType(
+            surface->window());
+  }
+  aura::client::DragUpdateInfo drag_info(
+      ui::DragDropTypes::DRAG_NONE, ui::DataTransferEndpoint(endpoint_type));
 
   delegate_->OnMotion(event.time_stamp(), event.location_f());
 
   // TODO(hirono): dnd_action() here may not be updated. Chrome needs to provide
   // a way to update DND action asynchronously.
-  return DndActionToDragOperation(data_offer_->get()->dnd_action());
+  drag_info.drag_operation =
+      DndActionToDragOperation(data_offer_->get()->dnd_action());
+
+  return drag_info;
 }
 
 void DataDevice::OnDragExited() {
diff --git a/components/exo/data_device.h b/components/exo/data_device.h
index cf66a180..b174dd2 100644
--- a/components/exo/data_device.h
+++ b/components/exo/data_device.h
@@ -58,7 +58,8 @@
 
   // Overridden from WMHelper::DragDropObserver:
   void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event) override;
 
diff --git a/components/exo/data_device_unittest.cc b/components/exo/data_device_unittest.cc
index fbe7d49f..3768410 100644
--- a/components/exo/data_device_unittest.cc
+++ b/components/exo/data_device_unittest.cc
@@ -166,7 +166,8 @@
   EXPECT_EQ(DataEvent::kOffer, events[0]);
   EXPECT_EQ(DataEvent::kEnter, events[1]);
 
-  EXPECT_EQ(ui::DragDropTypes::DRAG_LINK, device_->OnDragUpdated(event));
+  EXPECT_EQ(ui::DragDropTypes::DRAG_LINK,
+            device_->OnDragUpdated(event).drag_operation);
   ASSERT_EQ(1u, delegate_.PopEvents(&events));
   EXPECT_EQ(DataEvent::kMotion, events[0]);
 
@@ -191,7 +192,8 @@
   EXPECT_EQ(DataEvent::kOffer, events[0]);
   EXPECT_EQ(DataEvent::kEnter, events[1]);
 
-  EXPECT_EQ(ui::DragDropTypes::DRAG_LINK, device_->OnDragUpdated(event));
+  EXPECT_EQ(ui::DragDropTypes::DRAG_LINK,
+            device_->OnDragUpdated(event).drag_operation);
   ASSERT_EQ(1u, delegate_.PopEvents(&events));
   EXPECT_EQ(DataEvent::kMotion, events[0]);
 
@@ -224,7 +226,8 @@
 
   delegate_.DeleteDataOffer(false);
 
-  EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, device_->OnDragUpdated(event));
+  EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
+            device_->OnDragUpdated(event).drag_operation);
   EXPECT_EQ(0u, delegate_.PopEvents(&events));
 
   device_->OnPerformDrop(event);
@@ -242,7 +245,8 @@
   EXPECT_EQ(DataEvent::kOffer, events[0]);
   EXPECT_EQ(DataEvent::kEnter, events[1]);
 
-  EXPECT_EQ(ui::DragDropTypes::DRAG_LINK, device_->OnDragUpdated(event));
+  EXPECT_EQ(ui::DragDropTypes::DRAG_LINK,
+            device_->OnDragUpdated(event).drag_operation);
   ASSERT_EQ(1u, delegate_.PopEvents(&events));
   EXPECT_EQ(DataEvent::kMotion, events[0]);
 
@@ -267,7 +271,8 @@
   device_->OnDragEntered(event);
   EXPECT_EQ(0u, delegate_.PopEvents(&events));
 
-  EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, device_->OnDragUpdated(event));
+  EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
+            device_->OnDragUpdated(event).drag_operation);
   EXPECT_EQ(0u, delegate_.PopEvents(&events));
 
   device_->OnPerformDrop(event);
diff --git a/components/exo/test/exo_test_base_views.cc b/components/exo/test/exo_test_base_views.cc
index 0852235..14893ec 100644
--- a/components/exo/test/exo_test_base_views.cc
+++ b/components/exo/test/exo_test_base_views.cc
@@ -7,6 +7,7 @@
 #include "components/exo/vsync_timing_manager.h"
 #include "components/exo/wm_helper.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/base/ime/init/input_method_factory.h"
 #include "ui/display/manager/managed_display_info.h"
 #include "ui/wm/core/wm_core_switches.h"
@@ -81,7 +82,10 @@
 
   // Overridden from aura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override {}
-  int OnDragUpdated(const ui::DropTargetEvent& event) override { return 0; }
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override {
+    return aura::client::DragUpdateInfo();
+  }
   void OnDragExited() override {}
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override {
diff --git a/components/exo/wm_helper.h b/components/exo/wm_helper.h
index 421a75e..9c4f0cfc 100644
--- a/components/exo/wm_helper.h
+++ b/components/exo/wm_helper.h
@@ -52,7 +52,8 @@
   class DragDropObserver {
    public:
     virtual void OnDragEntered(const ui::DropTargetEvent& event) = 0;
-    virtual int OnDragUpdated(const ui::DropTargetEvent& event) = 0;
+    virtual aura::client::DragUpdateInfo OnDragUpdated(
+        const ui::DropTargetEvent& event) = 0;
     virtual void OnDragExited() = 0;
     virtual int OnPerformDrop(const ui::DropTargetEvent& event) = 0;
 
@@ -147,7 +148,8 @@
 
   // Overridden from aura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override = 0;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override = 0;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override = 0;
   void OnDragExited() override = 0;
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override = 0;
diff --git a/components/exo/wm_helper_chromeos.cc b/components/exo/wm_helper_chromeos.cc
index 6bb5b710..9231d73 100644
--- a/components/exo/wm_helper_chromeos.cc
+++ b/components/exo/wm_helper_chromeos.cc
@@ -12,6 +12,7 @@
 #include "base/memory/singleton.h"
 #include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/client/focus_client.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/display/manager/display_configurator.h"
 #include "ui/display/manager/display_manager.h"
@@ -117,11 +118,21 @@
     observer.OnDragEntered(event);
 }
 
-int WMHelperChromeOS::OnDragUpdated(const ui::DropTargetEvent& event) {
-  int valid_operation = ui::DragDropTypes::DRAG_NONE;
-  for (DragDropObserver& observer : drag_drop_observers_)
-    valid_operation = valid_operation | observer.OnDragUpdated(event);
-  return valid_operation;
+aura::client::DragUpdateInfo WMHelperChromeOS::OnDragUpdated(
+    const ui::DropTargetEvent& event) {
+  aura::client::DragUpdateInfo drag_info(
+      ui::DragDropTypes::DRAG_NONE,
+      ui::DataTransferEndpoint(ui::EndpointType::kUnknownVm));
+
+  for (DragDropObserver& observer : drag_drop_observers_) {
+    auto observer_drag_info = observer.OnDragUpdated(event);
+    drag_info.drag_operation =
+        drag_info.drag_operation | observer_drag_info.drag_operation;
+    if (observer_drag_info.data_endpoint.type() !=
+        drag_info.data_endpoint.type())
+      drag_info.data_endpoint = observer_drag_info.data_endpoint;
+  }
+  return drag_info;
 }
 
 void WMHelperChromeOS::OnDragExited() {
diff --git a/components/exo/wm_helper_chromeos.h b/components/exo/wm_helper_chromeos.h
index ddc78df..b0e0d62b 100644
--- a/components/exo/wm_helper_chromeos.h
+++ b/components/exo/wm_helper_chromeos.h
@@ -107,7 +107,8 @@
 
   // Overridden from aura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override;
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index a3fdb14a..d329f92 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -462,13 +462,8 @@
   NotifyObservers();
 }
 
-void AboutSigninInternals::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  NotifyObservers();
-}
-
-void AboutSigninInternals::OnPrimaryAccountCleared(
-    const CoreAccountInfo& primary_account_info) {
+void AboutSigninInternals::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
   NotifyObservers();
 }
 
diff --git a/components/signin/core/browser/about_signin_internals.h b/components/signin/core/browser/about_signin_internals.h
index 289afad..06167d2a2 100644
--- a/components/signin/core/browser/about_signin_internals.h
+++ b/components/signin/core/browser/about_signin_internals.h
@@ -207,10 +207,8 @@
   // IdentityManager::Observer implementations.
   void OnRefreshTokensLoaded() override;
   void OnEndBatchOfRefreshTokenStateChanges() override;
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
 
   void NotifyTimedSigninFieldValueChanged(
       const signin_internals_util::TimedSigninStatusField& field,
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java
index 35c0f0a..a3c49c0 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ProfileDataSource.java
@@ -8,10 +8,8 @@
 
 import androidx.annotation.Nullable;
 
-import java.util.Map;
-
 /**
- * Observable source of profile data for accounts on device. Must be used from UI thread only.
+ * Observable source of profile data for accounts on device.
  */
 public interface ProfileDataSource {
     /**
@@ -81,16 +79,6 @@
     }
 
     /**
-     * Gets ProfileData for all accounts. There must be at least one active observer when this
-     * method is invoked (see {@link #addObserver}).
-     * @return unmodifiable map of ProfileData for all accounts (keyed by account name).
-     * TODO(crbug/1136452): Remove this method after removing the downstream implementation.
-     */
-    default Map<String, ProfileData> getProfileDataMap() {
-        return null;
-    }
-
-    /**
      * Gets ProfileData for single account. There must be at least one active observer when this
      * method is invoked (see {@link #addObserver}).
      * @param accountEmail account email to get ProfileData for.
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
index 6178299..806dcce3 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
@@ -77,25 +77,8 @@
                                            gaia_accounts);
 }
 
-// TODO(https://crbug.com/1046746): Replace separate IdentityManager::Observer
-// method overrides below with a single OnPrimaryAccountChanged method and
-// inline UpdateReconcilorStatus.
-void MirrorAccountReconcilorDelegate::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  UpdateReconcilorStatus();
-}
-
-void MirrorAccountReconcilorDelegate::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  UpdateReconcilorStatus();
-}
-
-void MirrorAccountReconcilorDelegate::OnUnconsentedPrimaryAccountChanged(
-    const CoreAccountInfo& unconsented_primary_account_info) {
-  UpdateReconcilorStatus();
-}
-
-void MirrorAccountReconcilorDelegate::UpdateReconcilorStatus() {
+void MirrorAccountReconcilorDelegate::OnPrimaryAccountChanged(
+    const PrimaryAccountChangeEvent& event) {
   // Have to check whether the state has actually changed, as calling
   // DisableReconcile logs out all accounts even if it was already disabled.
   bool should_enable_reconcile =
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
index 87db817..d7a11c66 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
@@ -50,12 +50,7 @@
       const gaia::MultiloginMode mode) const override;
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
-  void OnUnconsentedPrimaryAccountChanged(
-      const CoreAccountInfo& unconsented_primary_account_info) override;
+  void OnPrimaryAccountChanged(const PrimaryAccountChangeEvent& event) override;
 
   void UpdateReconcilorStatus();
 
diff --git a/components/signin/core/browser/signin_error_controller.cc b/components/signin/core/browser/signin_error_controller.cc
index 9490cd5..05a7139 100644
--- a/components/signin/core/browser/signin_error_controller.cc
+++ b/components/signin/core/browser/signin_error_controller.cc
@@ -149,17 +149,12 @@
   Update();
 }
 
-void SigninErrorController::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  // Ignore updates to the primary account if not in PRIMARY_ACCOUNT mode.
-  if (account_mode_ != AccountMode::PRIMARY_ACCOUNT)
+void SigninErrorController::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
+  if (event.GetEventTypeFor(signin::ConsentLevel::kSync) ==
+      signin::PrimaryAccountChangeEvent::Type::kNone) {
     return;
-
-  Update();
-}
-
-void SigninErrorController::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
+  }
   // Ignore updates to the primary account if not in PRIMARY_ACCOUNT mode.
   if (account_mode_ != AccountMode::PRIMARY_ACCOUNT)
     return;
diff --git a/components/signin/core/browser/signin_error_controller.h b/components/signin/core/browser/signin_error_controller.h
index fccbb40..8a5fcfb 100644
--- a/components/signin/core/browser/signin_error_controller.h
+++ b/components/signin/core/browser/signin_error_controller.h
@@ -78,10 +78,8 @@
   void OnErrorStateOfRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info,
       const GoogleServiceAuthError& error) override;
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
 
   const AccountMode account_mode_;
   signin::IdentityManager* identity_manager_;
diff --git a/components/signin/core/browser/signin_status_metrics_provider.cc b/components/signin/core/browser/signin_status_metrics_provider.cc
index f5d509b8..26e1034 100644
--- a/components/signin/core/browser/signin_status_metrics_provider.cc
+++ b/components/signin/core/browser/signin_status_metrics_provider.cc
@@ -72,26 +72,30 @@
     scoped_observations_.RemoveObservation(identity_manager);
 }
 
-void SigninStatusMetricsProvider::OnPrimaryAccountSet(
-    const CoreAccountInfo& account_info) {
-  SigninStatus recorded_signin_status = signin_status();
-  if (recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN) {
-    UpdateSigninStatus(MIXED_SIGNIN_STATUS);
-  } else if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
-    // There should have at least one browser opened if the user can sign in, so
-    // signin_status_ value should not be unknown.
-    UpdateSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
+void SigninStatusMetricsProvider::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
+  if (event.GetEventTypeFor(signin::ConsentLevel::kSync) ==
+      signin::PrimaryAccountChangeEvent::Type::kNone) {
+    return;
   }
-}
 
-void SigninStatusMetricsProvider::OnPrimaryAccountCleared(
-    const CoreAccountInfo& account_info) {
   SigninStatus recorded_signin_status = signin_status();
-  if (recorded_signin_status == ALL_PROFILES_SIGNED_IN) {
-    UpdateSigninStatus(MIXED_SIGNIN_STATUS);
-  } else if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
-    // There should have at least one browser opened if the user can sign out,
-    // so signin_status_ value should not be unknown.
+  switch (event.GetEventTypeFor(signin::ConsentLevel::kSync)) {
+    case signin::PrimaryAccountChangeEvent::Type::kSet:
+      if (recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN)
+        UpdateSigninStatus(MIXED_SIGNIN_STATUS);
+      break;
+    case signin::PrimaryAccountChangeEvent::Type::kCleared:
+      if (recorded_signin_status == ALL_PROFILES_SIGNED_IN)
+        UpdateSigninStatus(MIXED_SIGNIN_STATUS);
+      break;
+    case signin::PrimaryAccountChangeEvent::Type::kNone:
+      NOTREACHED() << "See return statement above";
+      break;
+  }
+  if (recorded_signin_status == UNKNOWN_SIGNIN_STATUS) {
+    // There should have at least one browser opened if the user can sign in or
+    // out, so signin_status_ value should not be unknown.
     UpdateSigninStatus(ERROR_GETTING_SIGNIN_STATUS);
   }
 }
diff --git a/components/signin/core/browser/signin_status_metrics_provider.h b/components/signin/core/browser/signin_status_metrics_provider.h
index 094ad210..ce32d68 100644
--- a/components/signin/core/browser/signin_status_metrics_provider.h
+++ b/components/signin/core/browser/signin_status_metrics_provider.h
@@ -70,8 +70,8 @@
       bool is_test);
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
-  void OnPrimaryAccountCleared(const CoreAccountInfo& account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
 
   // Obtain sign-in status and add observers.
   void Initialize();
diff --git a/components/signin/core/browser/signin_status_metrics_provider_unittest.cc b/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
index 974ad479..832813d 100644
--- a/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
+++ b/components/signin/core/browser/signin_status_metrics_provider_unittest.cc
@@ -3,11 +3,22 @@
 // found in the LICENSE file.
 
 #include "components/signin/core/browser/signin_status_metrics_provider.h"
+#include "components/signin/public/identity_manager/identity_test_utils.h"
 
 #include <string>
 
 #include "testing/gtest/include/gtest/gtest.h"
 
+using signin::PrimaryAccountChangeEvent;
+
+CoreAccountInfo TestAccount() {
+  CoreAccountInfo account;
+  account.email = "test@gmail.com";
+  account.gaia = signin::GetTestGaiaIdForEmail(account.email);
+  account.account_id = CoreAccountId::FromEmail(account.email);
+  return account;
+}
+
 TEST(SigninStatusMetricsProviderTest, UpdateInitialSigninStatus) {
   SigninStatusMetricsProvider metrics_provider(nullptr, true);
 
@@ -24,32 +35,46 @@
 
 TEST(SigninStatusMetricsProviderTest, OnPrimaryAccountSet) {
   SigninStatusMetricsProvider metrics_provider(nullptr, true);
+  CoreAccountInfo account_info = TestAccount();
 
   // Initial status is all signed out and then one of the profiles is signed in.
   metrics_provider.UpdateInitialSigninStatus(2, 0);
-  metrics_provider.OnPrimaryAccountSet(AccountInfo());
+  metrics_provider.OnPrimaryAccountChanged(PrimaryAccountChangeEvent(
+      PrimaryAccountChangeEvent::State(),
+      PrimaryAccountChangeEvent::State(account_info,
+                                       signin::ConsentLevel::kSync)));
   EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
             metrics_provider.GetSigninStatusForTesting());
 
   // Initial status is mixed and then one of the profiles is signed in.
   metrics_provider.UpdateInitialSigninStatus(2, 1);
-  metrics_provider.OnPrimaryAccountSet(AccountInfo());
+  metrics_provider.OnPrimaryAccountChanged(PrimaryAccountChangeEvent(
+      PrimaryAccountChangeEvent::State(),
+      PrimaryAccountChangeEvent::State(account_info,
+                                       signin::ConsentLevel::kSync)));
   EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
             metrics_provider.GetSigninStatusForTesting());
 }
 
 TEST(SigninStatusMetricsProviderTest, OnPrimaryAccountCleared) {
   SigninStatusMetricsProvider metrics_provider(nullptr, true);
+  CoreAccountInfo account_info = TestAccount();
 
   // Initial status is all signed in and then one of the profiles is signed out.
   metrics_provider.UpdateInitialSigninStatus(2, 2);
-  metrics_provider.OnPrimaryAccountCleared(AccountInfo());
+  metrics_provider.OnPrimaryAccountChanged(
+      PrimaryAccountChangeEvent(PrimaryAccountChangeEvent::State(
+                                    account_info, signin::ConsentLevel::kSync),
+                                PrimaryAccountChangeEvent::State()));
   EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
             metrics_provider.GetSigninStatusForTesting());
 
   // Initial status is mixed and then one of the profiles is signed out.
   metrics_provider.UpdateInitialSigninStatus(2, 1);
-  metrics_provider.OnPrimaryAccountCleared(AccountInfo());
+  metrics_provider.OnPrimaryAccountChanged(
+      PrimaryAccountChangeEvent(PrimaryAccountChangeEvent::State(
+                                    account_info, signin::ConsentLevel::kSync),
+                                PrimaryAccountChangeEvent::State()));
   EXPECT_EQ(SigninStatusMetricsProviderBase::MIXED_SIGNIN_STATUS,
             metrics_provider.GetSigninStatusForTesting());
 }
diff --git a/components/signin/public/identity_manager/identity_manager.cc b/components/signin/public/identity_manager/identity_manager.cc
index ae85f6a..0f64d03 100644
--- a/components/signin/public/identity_manager/identity_manager.cc
+++ b/components/signin/public/identity_manager/identity_manager.cc
@@ -503,13 +503,20 @@
     observer.OnPrimaryAccountChanged(event_details);
 
 #if defined(OS_ANDROID)
-  if (!java_identity_manager_)
-    return;
-  JNIEnv* env = base::android::AttachCurrentThread();
-  Java_IdentityManager_onPrimaryAccountChanged(
-      env, java_identity_manager_,
-      ConvertToJavaPrimaryAccountChangeEvent(env, event_details));
+  if (java_identity_manager_) {
+    JNIEnv* env = base::android::AttachCurrentThread();
+    Java_IdentityManager_onPrimaryAccountChanged(
+        env, java_identity_manager_,
+        ConvertToJavaPrimaryAccountChangeEvent(env, event_details));
+  }
 #endif
+
+  if (event_details.GetEventTypeFor(ConsentLevel::kSync) ==
+      PrimaryAccountChangeEvent::Type::kCleared) {
+    for (auto& observer : observer_list_) {
+      observer.AfterSyncPrimaryAccountCleared();
+    }
+  }
 }
 
 void IdentityManager::FirePrimaryAccountSet(
@@ -537,10 +544,6 @@
   DCHECK(!HasPrimaryAccount());
   DCHECK(!account_info.IsEmpty());
   for (auto& observer : observer_list_) {
-    observer.BeforePrimaryAccountCleared(account_info);
-  }
-
-  for (auto& observer : observer_list_) {
     observer.OnPrimaryAccountCleared(account_info);
   }
 }
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index 2fe9f4bb..57317ff3 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -94,12 +94,12 @@
         const CoreAccountInfo& previous_primary_account_info) {}
 
     // TODO(crbug.com/1046746): Move to |SigninClient|.
-    // Called Before notifying all the observers of |OnPrimaryAccountCleared|.
-    // |OnPrimaryAccountCleared| should be used instead in general.This function
-    // should be used carefully, as the value of the unconsented primary account
-    // is not properly defined when it is run and can be changed meanwhile.
-    virtual void BeforePrimaryAccountCleared(
-        const CoreAccountInfo& previous_primary_account_info) {}
+    // Called After notifying all the observers of |OnPrimaryAccountChanged|
+    // if the sync primary account was cleared.
+    //
+    // Note: This function is only intended to be used by the signin code.
+    // General observers should use |OnPrimaryAccountChanged|.
+    virtual void AfterSyncPrimaryAccountCleared() {}
 
     // When the unconsented primary account (see ./README.md) of the user
     // changes, this callback gets called with the new account as
diff --git a/components/url_formatter/spoof_checks/top_domains/domains.list b/components/url_formatter/spoof_checks/top_domains/domains.list
index 6ce1810..173141a 100644
--- a/components/url_formatter/spoof_checks/top_domains/domains.list
+++ b/components/url_formatter/spoof_checks/top_domains/domains.list
@@ -2693,6 +2693,7 @@
 lecker.de
 lecturas.com
 ledauphine.com
+ledger.com
 lefigaro.fr
 legacy.com
 leggo.it
diff --git a/components/url_formatter/spoof_checks/top_domains/domains.skeletons b/components/url_formatter/spoof_checks/top_domains/domains.skeletons
index a96c5cb..b673ba7e 100644
--- a/components/url_formatter/spoof_checks/top_domains/domains.skeletons
+++ b/components/url_formatter/spoof_checks/top_domains/domains.skeletons
@@ -2713,6 +2713,7 @@
 lecker.de, lecker.de
 lecturas.corn, lecturas.com
 ledauphine.corn, ledauphine.com
+ledger.corn, ledger.com
 lefigaro.fr, lefigaro.fr
 legacy.corn, legacy.com
 leggo.it, leggo.it
diff --git a/components/url_formatter/spoof_checks/top_domains/make_alexa_top_list.py b/components/url_formatter/spoof_checks/top_domains/make_alexa_top_list.py
index 9877ec3..8f79fc7 100755
--- a/components/url_formatter/spoof_checks/top_domains/make_alexa_top_list.py
+++ b/components/url_formatter/spoof_checks/top_domains/make_alexa_top_list.py
@@ -46,6 +46,6 @@
   # Add some popular domains if they're missing.
   # TODO(jshin): Find a way to update the list. (crbug.com/722022)
   for domain in ["gmail.com", "hotmail.com", "360.cn", "ntd.tv", "onclkds.com",
-                 "uber.com", "lyft.com", "ok.ru", "stripe.com"]:
+                 "uber.com", "lyft.com", "ok.ru", "stripe.com", "ledger.com"]:
     if domain not in domains:
       outfile.write(domain + "\n")
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc
index 7a729b9..2e0dd90 100644
--- a/content/browser/devtools/protocol/network_handler.cc
+++ b/content/browser/devtools/protocol/network_handler.cc
@@ -155,6 +155,7 @@
           .SetSecure(cookie.IsSecure())
           .SetSession(!cookie.IsPersistent())
           .SetPriority(cp)
+          .SetSameParty(cookie.IsSameParty())
           .Build();
 
   switch (cookie.SameSite()) {
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index b68f773..6c527e2 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -29,6 +29,7 @@
 #include "content/browser/renderer_host/input/touch_selection_controller_client_aura.h"
 #include "content/browser/renderer_host/navigation_entry_impl.h"
 #include "content/browser/renderer_host/overscroll_controller.h"
+#include "content/browser/renderer_host/render_frame_host_impl.h"
 #include "content/browser/renderer_host/render_view_host_factory.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
@@ -68,6 +69,7 @@
 #include "ui/aura/window_tree_host_observer.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/custom_data_helper.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/drop_target_event.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
@@ -1353,9 +1355,17 @@
     drag_dest_delegate_->OnDragOver();
 }
 
-int WebContentsViewAura::OnDragUpdated(const ui::DropTargetEvent& event) {
+aura::client::DragUpdateInfo WebContentsViewAura::OnDragUpdated(
+    const ui::DropTargetEvent& event) {
   if (web_contents_->ShouldIgnoreInputEvents())
-    return ui::DragDropTypes::DRAG_NONE;
+    return aura::client::DragUpdateInfo();
+
+  aura::client::DragUpdateInfo drag_info;
+  auto* focused_frame = web_contents_->GetFocusedFrame();
+  if (focused_frame) {
+    drag_info.data_endpoint = ui::DataTransferEndpoint(
+        web_contents_->GetFocusedFrame()->GetLastCommittedOrigin());
+  }
 
   std::unique_ptr<DropData> drop_data = std::make_unique<DropData>();
   // Calling this here as event.data might become invalid inside the callback.
@@ -1368,8 +1378,10 @@
           base::BindOnce(&WebContentsViewAura::DragUpdatedCallback,
                          weak_ptr_factory_.GetWeakPtr(), event,
                          std::move(drop_data)));
-  return ConvertFromDragOperationsMask(
+
+  drag_info.drag_operation = ConvertFromDragOperationsMask(
       static_cast<blink::DragOperationsMask>(current_drag_op_));
+  return drag_info;
 }
 
 void WebContentsViewAura::OnDragExited() {
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index 583f3ce..24b4e6ba 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -216,7 +216,8 @@
 
   // Overridden from aura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override;
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 51b1046..d69110ff 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -290,6 +290,8 @@
     "render_view_host.h",
     "render_widget_host.h",
     "render_widget_host_iterator.h",
+    "render_widget_host_observer.cc",
+    "render_widget_host_observer.h",
     "render_widget_host_view.h",
     "render_widget_host_view_mac_delegate.h",
     "renderer_preferences_util.cc",
diff --git a/content/public/browser/render_widget_host_observer.cc b/content/public/browser/render_widget_host_observer.cc
new file mode 100644
index 0000000..ecc744e9
--- /dev/null
+++ b/content/public/browser/render_widget_host_observer.cc
@@ -0,0 +1,16 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/render_widget_host_observer.h"
+#include "base/check.h"
+
+namespace content {
+
+RenderWidgetHostObserver::~RenderWidgetHostObserver() {
+  // TODO(https://crbug.com/1153966): Instrumentation. When fixed, decide if
+  // this CHECK should be removed or turned into a DCHECK.
+  CHECK(!IsInObserverList());
+}
+
+}  // namespace content
diff --git a/content/public/browser/render_widget_host_observer.h b/content/public/browser/render_widget_host_observer.h
index e1609ff..da2cae2 100644
--- a/content/public/browser/render_widget_host_observer.h
+++ b/content/public/browser/render_widget_host_observer.h
@@ -26,7 +26,7 @@
   virtual void RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) {}
 
  protected:
-  ~RenderWidgetHostObserver() override = default;
+  ~RenderWidgetHostObserver() override;
 };
 
 }  // namespace content
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 4e6a693..c5352e8 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -2895,7 +2895,7 @@
   // During initial setup, the tab strip view may be nil, but the missing
   // snapshot will never be visible because all three animation methods are
   // called in succession.
-  if (self.tabStripView) {
+  if (self.tabStripView && !base::FeatureList::IsEnabled(kModernTabStrip)) {
     self.tabStripSnapshot = [self.tabStripView screenshotForAnimation];
     self.tabStripSnapshot.translatesAutoresizingMaskIntoConstraints = NO;
     self.tabStripSnapshot.transform =
@@ -2956,26 +2956,29 @@
     case ViewRevealState::Hidden:
       self.view.superview.transform = CGAffineTransformIdentity;
       self.view.transform = CGAffineTransformIdentity;
-      self.tabStripSnapshot.transform =
-          [self.tabStripView adjustTransformForRTL:CGAffineTransformIdentity];
+      if (!base::FeatureList::IsEnabled(kModernTabStrip))
+        self.tabStripSnapshot.transform =
+            [self.tabStripView adjustTransformForRTL:CGAffineTransformIdentity];
       break;
     case ViewRevealState::Peeked:
       self.view.superview.transform = CGAffineTransformMakeTranslation(
           0, self.thumbStripPanHandler.peekedHeight);
       self.view.transform = CGAffineTransformMakeTranslation(
           0, -self.tabStripView.frame.size.height - self.headerOffset);
-      self.tabStripSnapshot.transform = [self.tabStripView
-          adjustTransformForRTL:CGAffineTransformMakeTranslation(
-                                    0, self.tabStripView.frame.size.height)];
+      if (!base::FeatureList::IsEnabled(kModernTabStrip))
+        self.tabStripSnapshot.transform = [self.tabStripView
+            adjustTransformForRTL:CGAffineTransformMakeTranslation(
+                                      0, self.tabStripView.frame.size.height)];
       break;
     case ViewRevealState::Revealed:
       self.view.superview.transform = CGAffineTransformMakeTranslation(
           0, self.thumbStripPanHandler.revealedHeight);
       self.view.transform = CGAffineTransformMakeTranslation(
           0, -self.tabStripView.frame.size.height - self.headerOffset);
-      self.tabStripSnapshot.transform = [self.tabStripView
-          adjustTransformForRTL:CGAffineTransformMakeTranslation(
-                                    0, self.tabStripView.frame.size.height)];
+      if (!base::FeatureList::IsEnabled(kModernTabStrip))
+        self.tabStripSnapshot.transform = [self.tabStripView
+            adjustTransformForRTL:CGAffineTransformMakeTranslation(
+                                      0, self.tabStripView.frame.size.height)];
       break;
   }
 }
diff --git a/ios/chrome/browser/ui/gestures/BUILD.gn b/ios/chrome/browser/ui/gestures/BUILD.gn
index 1a1e136..1d33e666f4 100644
--- a/ios/chrome/browser/ui/gestures/BUILD.gn
+++ b/ios/chrome/browser/ui/gestures/BUILD.gn
@@ -16,6 +16,7 @@
   ]
   deps = [
     "//base",
+    "//ios/chrome/browser/ui/util",
     "//ios/web/public/ui",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.mm b/ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.mm
index ad032e5..c8c741c5 100644
--- a/ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.mm
+++ b/ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.mm
@@ -9,6 +9,7 @@
 #include "base/numerics/ranges.h"
 #import "ios/chrome/browser/ui/gestures/layout_switcher.h"
 #import "ios/chrome/browser/ui/gestures/pan_handler_scroll_view.h"
+#include "ios/chrome/browser/ui/util/ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -166,13 +167,14 @@
   }
   [self willAnimateViewReveal];
   [self.animator stopAnimation:YES];
+
+  __weak ViewRevealingVerticalPanHandler* weakSelf = self;
   self.animator = [[UIViewPropertyAnimator alloc]
       initWithDuration:kAnimationDuration
           dampingRatio:1
             animations:^() {
-              [self animateToNextViewRevealState];
+              [weakSelf animateToNextViewRevealState];
             }];
-  __weak ViewRevealingVerticalPanHandler* weakSelf = self;
   [self.animator addCompletion:^(UIViewAnimatingPosition finalPosition) {
     if (!weakSelf.animator.reversed) {
       weakSelf.currentState = weakSelf.nextState;
@@ -435,7 +437,7 @@
       // starts dragging from the top.
       CGFloat contentOffsetY =
           scrollView.contentOffset.y + scrollView.contentInset.top;
-      if (contentOffsetY != 0) {
+      if (!AreCGFloatsEqual(contentOffsetY, 0.0)) {
         return;
       }
       break;
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index fd5e604..e9c91fca 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-19980861b244b1c9f0c3f389bb8d8a1ef5272dca
\ No newline at end of file
+97bd8c22b109a06bc07e3188ab17b40d5eb6d546
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 936790f..1efb172 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-a9906b12ff830faee298d693baea0493d2d8f8c8
\ No newline at end of file
+fba615f8993ab6651d3e60c05fd10b83ed2605eb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index 4954a6e..7bd09d1 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-01a08f8e8f7482c18d3f9b00a3a5ecdc34d03fd0
\ No newline at end of file
+7ac6bbb0de0095e9eca88d08f62ca14807aa441d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index ff692916..90bbd65 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-63b933988525e9ed3d348e1a947681aa491b3820
\ No newline at end of file
+10a2c26eb1e6ae8a3e42ca2923865fbd20230e94
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 51ca470..c4c3f60 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-911143fdb76c9713567da7140dd9a34572052047
\ No newline at end of file
+50adac2cf3a84bdce0d024f50358959f35659011
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index d67b437..1b0d6f1 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-8e900c2655f9577fdc8a03d798dac807166d789a
\ No newline at end of file
+cf5ed70b85953ee3d39a4e452602bfb117fae227
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index d19efd0..8fe89a0 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-c4623b87c0e7c17033cfacfb53fd9cf20436d43e
\ No newline at end of file
+016a6695d81394a5303c310ab195361386497796
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index ccf9941..f91de88 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-d124cc513ed5a625cd8f77851e0f8623831cbe21
\ No newline at end of file
+f34f81a272298718e0b70885a189d8cbfefbc8b3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index eb1c154..902e7aa 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-9f56cf69aaecaac6af268031fdf1909bd79fa6df
\ No newline at end of file
+fe119dec8450c08edf43c047f88729cea523badb
\ No newline at end of file
diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm
index 9663d103..85e2c1f 100644
--- a/ios/web/web_state/ui/crw_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_context_menu_controller.mm
@@ -5,6 +5,9 @@
 #import "ios/web/web_state/ui/crw_context_menu_controller.h"
 
 #import "ios/web/public/ui/context_menu_params.h"
+#import "ios/web/public/web_state.h"
+#import "ios/web/public/web_state_delegate.h"
+#import "ios/web/web_state/context_menu_params_utils.h"
 #import "ios/web/web_state/ui/crw_context_menu_element_fetcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -22,6 +25,12 @@
 // The context menu responsible for the interaction.
 @property(nonatomic, strong) UIContextMenuInteraction* contextMenu;
 
+// Views used to do the highlight/dismiss animation. Those view are empty and
+// are used to override the default animation which is to focus the whole
+// WebView (as the interaction is used on the whole WebView).
+@property(nonatomic, strong, readonly) UIView* highlightView;
+@property(nonatomic, strong, readonly) UIView* dismissView;
+
 @property(nonatomic, strong) WKWebView* webView;
 
 @property(nonatomic, assign) web::WebState* webState;
@@ -32,6 +41,9 @@
 
 @implementation CRWContextMenuController
 
+@synthesize highlightView = _highlightView;
+@synthesize dismissView = _dismissView;
+
 - (instancetype)initWithWebView:(WKWebView*)webView
                        webState:(web::WebState*)webState {
   self = [super init];
@@ -50,6 +62,28 @@
   return self;
 }
 
+#pragma mark - Property
+
+- (UIView*)highlightView {
+  if (!_highlightView) {
+    // If the views have a CGRectZero size, it is not taken into account.
+    CGRect rectSizedOne = CGRectMake(0, 0, 1, 1);
+    _highlightView = [[UIView alloc] initWithFrame:rectSizedOne];
+    _highlightView.backgroundColor = UIColor.clearColor;
+  }
+  return _highlightView;
+}
+
+- (UIView*)dismissView {
+  if (!_dismissView) {
+    // If the views have a CGRectZero size, it is not taken into account.
+    CGRect rectSizedOne = CGRectMake(0, 0, 1, 1);
+    _dismissView = [[UIView alloc] initWithFrame:rectSizedOne];
+    _dismissView.backgroundColor = UIColor.clearColor;
+  }
+  return _dismissView;
+}
+
 #pragma mark - UIContextMenuInteractionDelegate
 
 - (UIContextMenuConfiguration*)contextMenuInteraction:
@@ -105,12 +139,38 @@
 
   isRunLoopComplete = YES;
 
+  if (!web::CanShowContextMenuForParams(self.params))
+    return nil;
+
+  // Adding the highlight/dismiss view here so they can be used in the
+  // delegate's methods.
+  [interaction.view addSubview:self.highlightView];
+  [interaction.view addSubview:self.dismissView];
+
   self.params.location = [self.webView convertPoint:location
                                            fromView:interaction.view];
 
-  // TODO(crbug.com/1140387): Present the context menu with the params.
+  __block UIContextMenuConfiguration* configuration;
+  self.webState->GetDelegate()->ContextMenuConfiguration(
+      self.webState, self.params, ^(UIContextMenuConfiguration* conf) {
+        configuration = conf;
+      });
 
-  return nil;
+  return configuration;
+}
+
+- (UITargetedPreview*)contextMenuInteraction:
+                          (UIContextMenuInteraction*)interaction
+    previewForHighlightingMenuWithConfiguration:
+        (UIContextMenuConfiguration*)configuration {
+  return [[UITargetedPreview alloc] initWithView:self.highlightView];
+}
+
+- (UITargetedPreview*)contextMenuInteraction:
+                          (UIContextMenuInteraction*)interaction
+    previewForDismissingMenuWithConfiguration:
+        (UIContextMenuConfiguration*)configuration {
+  return [[UITargetedPreview alloc] initWithView:self.dismissView];
 }
 
 @end
diff --git a/media/capture/video/chromeos/camera_app_device_bridge_impl.cc b/media/capture/video/chromeos/camera_app_device_bridge_impl.cc
index 4fec380f4..e71efb4 100644
--- a/media/capture/video/chromeos/camera_app_device_bridge_impl.cc
+++ b/media/capture/video/chromeos/camera_app_device_bridge_impl.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/command_line.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/media_switches.h"
 #include "media/capture/video/chromeos/public/cros_features.h"
 #include "media/capture/video/chromeos/video_capture_device_chromeos_halv3.h"
@@ -69,8 +70,9 @@
   auto device_info = camera_info_getter_.Run(device_id);
   auto device_impl = std::make_unique<media::CameraAppDeviceImpl>(
       device_id, std::move(device_info),
-      base::BindOnce(&CameraAppDeviceBridgeImpl::OnDeviceClosed,
-                     base::Unretained(this), device_id));
+      media::BindToCurrentLoop(
+          base::BindOnce(&CameraAppDeviceBridgeImpl::OnDeviceClosed,
+                         base::Unretained(this), device_id)));
   auto result = camera_app_devices_.emplace(device_id, std::move(device_impl));
   return result.first->second.get();
 }
diff --git a/media/capture/video/chromeos/camera_app_device_impl.cc b/media/capture/video/chromeos/camera_app_device_impl.cc
index 709a1b9..a7bc71d 100644
--- a/media/capture/video/chromeos/camera_app_device_impl.cc
+++ b/media/capture/video/chromeos/camera_app_device_impl.cc
@@ -295,7 +295,11 @@
 }
 
 void CameraAppDeviceImpl::OnMojoConnectionError() {
-  std::move(cleanup_callback_).Run();
+  // Since it is the error handler of a receiver set, it might be triggered more
+  // than once.
+  if (!cleanup_callback_.is_null()) {
+    std::move(cleanup_callback_).Run();
+  }
 }
 
 void CameraAppDeviceImpl::SetReprocessResultOnMojoThread(
diff --git a/media/capture/video/chromeos/camera_hal_delegate.h b/media/capture/video/chromeos/camera_hal_delegate.h
index 34d4ddd..a5f7a29 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.h
+++ b/media/capture/video/chromeos/camera_hal_delegate.h
@@ -191,7 +191,8 @@
   // updated in GetDeviceDescriptors() and queried in
   // GetCameraIdFromDeviceId().
   base::Lock device_id_to_camera_id_lock_;
-  std::map<std::string, int> device_id_to_camera_id_;
+  std::map<std::string, int> device_id_to_camera_id_
+      GUARDED_BY(device_id_to_camera_id_lock_);
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/media/capture/video/chromeos/camera_metadata_utils.cc b/media/capture/video/chromeos/camera_metadata_utils.cc
index 0690ff2..6111666 100644
--- a/media/capture/video/chromeos/camera_metadata_utils.cc
+++ b/media/capture/video/chromeos/camera_metadata_utils.cc
@@ -56,7 +56,15 @@
   if (iter == camera_metadata->entries.value().end()) {
     return nullptr;
   }
-  return &(camera_metadata->entries.value()[(*iter)->index]);
+
+  auto* entry_ptr = &(camera_metadata->entries.value()[(*iter)->index]);
+  if (!(*entry_ptr)->data.data()) {
+    // Metadata tag found with no valid data.
+    LOG(WARNING) << "Found tag " << static_cast<int>(tag)
+                 << " but with invalid data";
+    return nullptr;
+  }
+  return entry_ptr;
 }
 
 void AddOrUpdateMetadataEntry(cros::mojom::CameraMetadataPtr* to,
diff --git a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
index c272d15..feca687 100644
--- a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
+++ b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
@@ -25,15 +25,16 @@
       decode_done_cb_(std::move(decode_done_cb)),
       send_log_message_cb_(std::move(send_log_message_cb)),
       has_received_decoded_frame_(false),
+      decoder_status_(INIT_PENDING),
       next_task_id_(0),
-      task_id_(chromeos_camera::MjpegDecodeAccelerator::kInvalidTaskId),
-      decoder_status_(INIT_PENDING) {}
+      task_id_(chromeos_camera::MjpegDecodeAccelerator::kInvalidTaskId) {}
 
 VideoCaptureJpegDecoderImpl::~VideoCaptureJpegDecoderImpl() {
   DCHECK(decoder_task_runner_->RunsTasksInCurrentSequence());
 }
 
 void VideoCaptureJpegDecoderImpl::Initialize() {
+  base::AutoLock lock(lock_);
   if (!IsVideoCaptureAcceleratedJpegDecodingEnabled()) {
     decoder_status_ = FAILED;
     RecordInitDecodeUMA_Locked();
@@ -236,6 +237,7 @@
 }
 
 void VideoCaptureJpegDecoderImpl::RecordInitDecodeUMA_Locked() {
+  lock_.AssertAcquired();
   UMA_HISTOGRAM_BOOLEAN("Media.VideoCaptureGpuJpegDecoder.InitDecodeSuccess",
                         decoder_status_ == INIT_PASSED);
 }
diff --git a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h
index 7bb0296..fbc3a41 100644
--- a/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h
+++ b/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.h
@@ -87,11 +87,10 @@
   const base::RepeatingCallback<void(const std::string&)> send_log_message_cb_;
   bool has_received_decoded_frame_;
 
-  // Guards |decode_done_closure_| and |decoder_status_|.
-  mutable base::Lock lock_;
-
   // The closure of |decode_done_cb_| with bound parameters.
-  base::OnceClosure decode_done_closure_;
+  mutable base::Lock lock_;
+  STATUS decoder_status_ GUARDED_BY(lock_);
+  base::OnceClosure decode_done_closure_ GUARDED_BY(lock_);
 
   // Next id for input BitstreamBuffer.
   int32_t next_task_id_;
@@ -104,8 +103,6 @@
   base::UnsafeSharedMemoryRegion in_shared_region_;
   base::WritableSharedMemoryMapping in_shared_mapping_;
 
-  STATUS decoder_status_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   base::WeakPtrFactory<VideoCaptureJpegDecoderImpl> weak_ptr_factory_{this};
diff --git a/services/network/public/cpp/is_potentially_trustworthy_unittest.cc b/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
index ee4c69d..2b72d8fb 100644
--- a/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
+++ b/services/network/public/cpp/is_potentially_trustworthy_unittest.cc
@@ -51,7 +51,6 @@
   EXPECT_FALSE(IsOriginPotentiallyTrustworthy("javascript:alert('blah')"));
   EXPECT_FALSE(IsOriginPotentiallyTrustworthy("data:test/plain;blah"));
 
-  EXPECT_FALSE(IsOriginPotentiallyTrustworthy("custom-scheme://example.com"));
   EXPECT_TRUE(
       IsOriginPotentiallyTrustworthy("quic-transport://example.com/counter"));
 }
@@ -150,18 +149,39 @@
 
   EXPECT_TRUE(
       IsUrlPotentiallyTrustworthy("quic-transport://example.com/counter"));
-  EXPECT_FALSE(IsUrlPotentiallyTrustworthy("custom-scheme://example.com"));
 }
 
+// Tests the trustworthiness of an URL and origin whose scheme was added to the
+// custom sets of standard and secure schemes. A scheme must be added to both
+// to be considered trustworthy.
 TEST(IsPotentiallyTrustworthy, CustomScheme) {
-  url::ScopedSchemeRegistryForTests scoped_registry;
-  url::AddSecureScheme("custom-scheme");
+  const char* custom_scheme = "custom-scheme";
+  const char* custom_scheme_example = "custom-scheme://example.com";
 
-  // TODO(crbug.com/1159371): These tests should return true.
-  EXPECT_FALSE(IsOriginPotentiallyTrustworthy(
-      "custom-scheme://578223a1-8c13-17b3-84d5-eca045ae384a/fun.js"));
-  EXPECT_FALSE(IsUrlPotentiallyTrustworthy(
-      "custom-scheme://578223a1-8c13-17b3-84d5-eca045ae384a/fun.js"));
+  EXPECT_FALSE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
+  EXPECT_FALSE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
+
+  {
+    url::ScopedSchemeRegistryForTests scoped_registry;
+    url::AddSecureScheme(custom_scheme);
+    EXPECT_FALSE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
+    EXPECT_FALSE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
+  }
+
+  {
+    url::ScopedSchemeRegistryForTests scoped_registry;
+    url::AddStandardScheme(custom_scheme, url::SchemeType::SCHEME_WITH_HOST);
+    EXPECT_FALSE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
+    EXPECT_FALSE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
+  }
+
+  {
+    url::ScopedSchemeRegistryForTests scoped_registry;
+    url::AddStandardScheme(custom_scheme, url::SchemeType::SCHEME_WITH_HOST);
+    url::AddSecureScheme(custom_scheme);
+    EXPECT_TRUE(IsOriginPotentiallyTrustworthy(custom_scheme_example));
+    EXPECT_TRUE(IsUrlPotentiallyTrustworthy(custom_scheme_example));
+  }
 }
 
 // Tests that were for the removed blink::network_utils::IsOriginSecure.
diff --git a/services/tracing/public/cpp/perfetto/shared_memory.cc b/services/tracing/public/cpp/perfetto/shared_memory.cc
index 55ef1384..a1bb544 100644
--- a/services/tracing/public/cpp/perfetto/shared_memory.cc
+++ b/services/tracing/public/cpp/perfetto/shared_memory.cc
@@ -47,8 +47,4 @@
   return shared_buffer_->GetSize();
 }
 
-int MojoSharedMemory::fd() const {
-  return -1;
-}
-
 }  // namespace tracing
diff --git a/services/tracing/public/cpp/perfetto/shared_memory.h b/services/tracing/public/cpp/perfetto/shared_memory.h
index ea7bd275..548e64d 100644
--- a/services/tracing/public/cpp/perfetto/shared_memory.h
+++ b/services/tracing/public/cpp/perfetto/shared_memory.h
@@ -42,7 +42,6 @@
   // classes.
   void* start() const override;
   size_t size() const override;
-  int fd() const override;
 
  private:
   mojo::ScopedSharedBufferHandle shared_buffer_;
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index f98cac5..403b3d5 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -240,11 +240,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -254,7 +254,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -317,11 +317,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -331,7 +331,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -394,11 +394,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -408,7 +408,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -471,11 +471,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -485,7 +485,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -769,11 +769,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -783,7 +783,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -846,11 +846,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -860,7 +860,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -923,11 +923,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -937,7 +937,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1000,11 +1000,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -1014,7 +1014,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1298,11 +1298,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -1312,7 +1312,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1375,11 +1375,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -1389,7 +1389,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1452,11 +1452,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -1466,7 +1466,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1529,11 +1529,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -1543,7 +1543,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1827,11 +1827,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -1841,7 +1841,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1904,11 +1904,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -1918,7 +1918,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -1981,11 +1981,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.146",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.147",
         "resultdb": {
           "enable": true
         },
@@ -1995,7 +1995,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M87",
-              "revision": "version:87.0.4280.146"
+              "revision": "version:87.0.4280.147"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -2058,11 +2058,11 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83"
+            "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
         },
-        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.83",
+        "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.84",
         "resultdb": {
           "enable": true
         },
@@ -2072,7 +2072,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M88",
-              "revision": "version:88.0.4324.83"
+              "revision": "version:88.0.4324.84"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index ba85f789..45e49019 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -319,13 +319,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=88',
     ],
-    'identifier': 'Implementation Tests For 88.0.4324.83',
+    'identifier': 'Implementation Tests For 88.0.4324.84',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.83',
+          'revision': 'version:88.0.4324.84',
         }
       ],
     },
@@ -342,13 +342,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--impl-version=87',
     ],
-    'identifier': 'Implementation Tests For 87.0.4280.146',
+    'identifier': 'Implementation Tests For 87.0.4280.147',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M87',
-          'revision': 'version:87.0.4280.146',
+          'revision': 'version:87.0.4280.147',
         }
       ],
     },
@@ -388,13 +388,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=88',
     ],
-    'identifier': 'Client Tests For 88.0.4324.83',
+    'identifier': 'Client Tests For 88.0.4324.84',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M88',
-          'revision': 'version:88.0.4324.83',
+          'revision': 'version:88.0.4324.84',
         }
       ],
     },
@@ -411,13 +411,13 @@
       '../../weblayer/browser/android/javatests/skew/expectations.txt',
       '--client-version=87',
     ],
-    'identifier': 'Client Tests For 87.0.4280.146',
+    'identifier': 'Client Tests For 87.0.4280.147',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M87',
-          'revision': 'version:87.0.4280.146',
+          'revision': 'version:87.0.4280.147',
         }
       ],
     },
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index dbbad849..af1c31a5 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -4749,6 +4749,8 @@
       optional CookieSameSite sameSite
       # Cookie Priority
       experimental CookiePriority priority
+      # True if cookie is SameParty.
+      experimental boolean sameParty
 
   # Types of reasons why a cookie may not be stored from a response.
   experimental type SetCookieBlockedReason extends string
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index dbb154e..075f4cb 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3064,10 +3064,11 @@
   kCrossOriginSubframeWithoutEmbeddingControl = 3742,
   kReadableStreamWithByteSource = 3743,
   kReadableStreamBYOBReader = 3744,
-  kEmbedElementWithoutTypeSrcChanged = 3745,
   kSamePartyCookieAttribute = 3746,
   kSamePartyCookieExclusionOverruledSameSite = 3747,
   kSamePartyCookieInclusionOverruledSameSite = 3748,
+  kEmbedElementWithoutTypeSrcChanged = 3749,
+  kPaymentHandlerStandardizedPaymentMethodIdentifier = 3750,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 6b3cea9..3c09d254 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -122,7 +122,9 @@
     : Supplement<Navigator>(navigator),
       ExecutionContextLifecycleObserver(navigator.DomWindow()),
       PageVisibilityObserver(navigator.DomWindow()->GetFrame()->GetPage()),
+      one_shots_(MakeGarbageCollected<GeoNotifierSet>()),
       watchers_(MakeGarbageCollected<GeolocationWatchers>()),
+      one_shots_being_invoked_(MakeGarbageCollected<GeoNotifierSet>()),
       geolocation_(navigator.DomWindow()),
       geolocation_service_(navigator.DomWindow()) {}
 
@@ -148,7 +150,7 @@
 
 void Geolocation::ContextDestroyed() {
   StopTimers();
-  one_shots_.clear();
+  one_shots_->clear();
   watchers_->Clear();
 
   StopUpdating();
@@ -198,7 +200,7 @@
   auto* notifier = MakeGarbageCollected<GeoNotifier>(this, success_callback,
                                                      error_callback, options);
 
-  one_shots_.insert(notifier);
+  one_shots_->insert(notifier);
 
   StartRequest(notifier);
 }
@@ -260,7 +262,7 @@
   DCHECK(!notifier->IsTimerActive());
 
   // This request has failed fatally. Remove it from our lists.
-  one_shots_.erase(notifier);
+  one_shots_->erase(notifier);
   watchers_->Remove(notifier);
 
   if (!HasListeners())
@@ -274,8 +276,8 @@
 
   // If this is a one-shot request, stop it. Otherwise, if the watch still
   // exists, start the service to get updates.
-  if (one_shots_.Contains(notifier)) {
-    one_shots_.erase(notifier);
+  if (one_shots_->Contains(notifier)) {
+    one_shots_->erase(notifier);
   } else if (watchers_->Contains(notifier)) {
     StartUpdating(notifier);
   }
@@ -288,15 +290,15 @@
   DCHECK(!notifier->IsTimerActive());
 
   // If this is a one-shot request, stop it.
-  one_shots_.erase(notifier);
+  one_shots_->erase(notifier);
 
   if (!HasListeners())
     StopUpdating();
 }
 
 bool Geolocation::DoesOwnNotifier(GeoNotifier* notifier) const {
-  return one_shots_.Contains(notifier) ||
-         one_shots_being_invoked_.Contains(notifier) ||
+  return one_shots_->Contains(notifier) ||
+         one_shots_being_invoked_->Contains(notifier) ||
          watchers_->Contains(notifier) ||
          watchers_being_invoked_.Contains(notifier);
 }
@@ -328,7 +330,7 @@
 }
 
 void Geolocation::StopTimers() {
-  for (const auto& notifier : one_shots_) {
+  for (const auto& notifier : *one_shots_) {
     notifier->StopTimer();
   }
 
@@ -340,7 +342,7 @@
 void Geolocation::HandleError(GeolocationPositionError* error) {
   DCHECK(error);
 
-  DCHECK(one_shots_being_invoked_.IsEmpty());
+  DCHECK(one_shots_being_invoked_->IsEmpty());
   DCHECK(watchers_being_invoked_.IsEmpty());
 
   if (error->IsFatal()) {
@@ -370,7 +372,7 @@
   // already scheduled must be immediately cancelled according to the spec. But
   // the current implementation doesn't support such case.
   // TODO(mattreynolds): Support watcher cancellation inside notifier callbacks.
-  for (auto& notifier : one_shots_being_invoked_) {
+  for (auto& notifier : *one_shots_being_invoked_) {
     if (error->IsFatal() || !notifier->UseCachedPosition())
       notifier->RunErrorCallback(error);
   }
@@ -387,23 +389,23 @@
 
   if (!error->IsFatal()) {
     // Keep the notifiers that are okay with a cached position in |one_shots_|.
-    for (const auto& notifier : one_shots_being_invoked_) {
+    for (const auto& notifier : *one_shots_being_invoked_) {
       if (notifier->UseCachedPosition())
-        one_shots_.InsertWithoutTimerCheck(notifier.Get());
+        one_shots_->InsertWithoutTimerCheck(notifier.Get());
       else
         notifier->StopTimer();
     }
-    one_shots_being_invoked_.ClearWithoutTimerCheck();
+    one_shots_being_invoked_->ClearWithoutTimerCheck();
   }
 
-  one_shots_being_invoked_.clear();
+  one_shots_being_invoked_->clear();
   watchers_being_invoked_.clear();
 }
 
 void Geolocation::MakeSuccessCallbacks() {
   DCHECK(last_position_);
 
-  DCHECK(one_shots_being_invoked_.IsEmpty());
+  DCHECK(one_shots_being_invoked_->IsEmpty());
   DCHECK(watchers_being_invoked_.IsEmpty());
 
   // Set |one_shots_being_invoked_| and |watchers_being_invoked_| to the
@@ -420,7 +422,7 @@
   // already scheduled must be immediately cancelled according to the spec. But
   // the current implementation doesn't support such case.
   // TODO(mattreynolds): Support watcher cancellation inside notifier callbacks.
-  for (auto& notifier : one_shots_being_invoked_)
+  for (auto& notifier : *one_shots_being_invoked_)
     notifier->RunSuccessCallback(last_position_);
   for (auto& notifier : watchers_being_invoked_)
     notifier->RunSuccessCallback(last_position_);
@@ -428,7 +430,7 @@
   if (!HasListeners())
     StopUpdating();
 
-  one_shots_being_invoked_.clear();
+  one_shots_being_invoked_->clear();
   watchers_being_invoked_.clear();
 }
 
@@ -511,7 +513,7 @@
 }
 
 bool Geolocation::HasPendingActivity() const {
-  return !one_shots_.IsEmpty() || !one_shots_being_invoked_.IsEmpty() ||
+  return !one_shots_->IsEmpty() || !one_shots_being_invoked_->IsEmpty() ||
          !watchers_->IsEmpty() || !watchers_being_invoked_.IsEmpty();
 }
 
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.h b/third_party/blink/renderer/modules/geolocation/geolocation.h
index 86f6281..7b86f1d 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.h
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.h
@@ -120,49 +120,47 @@
  private:
   // Customized HeapHashSet class that checks notifiers' timers. Notifier's
   // timer may be active only when the notifier is owned by the Geolocation.
-  class GeoNotifierSet : private HeapHashSet<Member<GeoNotifier>> {
-    using BaseClass = HeapHashSet<Member<GeoNotifier>>;
-
+  class GeoNotifierSet final : public GarbageCollected<GeoNotifierSet> {
    public:
-    using BaseClass::Trace;
+    void Trace(Visitor* visitor) const { visitor->Trace(set_); }
 
-    using BaseClass::const_iterator;
-    using BaseClass::iterator;
-
-    using BaseClass::begin;
-    using BaseClass::end;
-    using BaseClass::size;
+    auto begin() const { return set_.begin(); }
+    auto end() const { return set_.end(); }
+    auto size() const { return set_.size(); }
 
     auto insert(GeoNotifier* value) {
       DCHECK(!value->IsTimerActive());
-      return BaseClass::insert(value);
+      return set_.insert(value);
     }
 
     void erase(GeoNotifier* value) {
       DCHECK(!value->IsTimerActive());
-      return BaseClass::erase(value);
+      return set_.erase(value);
     }
 
     void clear() {
 #if DCHECK_IS_ON()
-      for (const auto& notifier : *this) {
+      for (const auto& notifier : set_) {
         DCHECK(!notifier->IsTimerActive());
       }
 #endif
-      BaseClass::clear();
+      set_.clear();
     }
 
-    using BaseClass::Contains;
-    using BaseClass::IsEmpty;
+    auto Contains(GeoNotifier* value) const { return set_.Contains(value); }
+    auto IsEmpty() const { return set_.IsEmpty(); }
 
     auto InsertWithoutTimerCheck(GeoNotifier* value) {
-      return BaseClass::insert(value);
+      return set_.insert(value);
     }
-    void ClearWithoutTimerCheck() { BaseClass::clear(); }
+    void ClearWithoutTimerCheck() { set_.clear(); }
+
+   private:
+    HeapHashSet<Member<GeoNotifier>> set_;
   };
 
   bool HasListeners() const {
-    return !one_shots_.IsEmpty() || !watchers_->IsEmpty();
+    return !one_shots_->IsEmpty() || !watchers_->IsEmpty();
   }
 
   void StopTimers();
@@ -204,7 +202,7 @@
   void OnGeolocationPermissionStatusUpdated(GeoNotifier*,
                                             mojom::PermissionStatus);
 
-  GeoNotifierSet one_shots_;
+  Member<GeoNotifierSet> one_shots_;
   Member<GeolocationWatchers> watchers_;
   // GeoNotifiers that are in the middle of invocation.
   //
@@ -217,7 +215,7 @@
   // wrapper-tracing.
   // TODO(https://crbug.com/796145): Remove this hack once on-stack objects
   // get supported by either of wrapper-tracing or unified GC.
-  GeoNotifierSet one_shots_being_invoked_;
+  Member<GeoNotifierSet> one_shots_being_invoked_;
   HeapVector<Member<GeoNotifier>> watchers_being_invoked_;
   Member<Geoposition> last_position_;
 
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index 23a1a29..9298c7f 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -613,8 +613,12 @@
       agc2_properties->max_output_noise_level_dbfs =
           base::GetFieldTrialParamByFeatureAsInt(
               features::kWebRtcHybridAgc, "max_output_noise_level_dbfs", -50);
+      agc2_properties->sse2_allowed = base::GetFieldTrialParamByFeatureAsBool(
+          features::kWebRtcHybridAgc, "sse2_allowed", true);
       agc2_properties->avx2_allowed = base::GetFieldTrialParamByFeatureAsBool(
           features::kWebRtcHybridAgc, "avx2_allowed", true);
+      agc2_properties->neon_allowed = base::GetFieldTrialParamByFeatureAsBool(
+          features::kWebRtcHybridAgc, "neon_allowed", true);
     }
     blink::ConfigAutomaticGainControl(
         properties.goog_auto_gain_control,
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
index fd583fa..a82b231 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
@@ -122,7 +122,7 @@
   FRIEND_TEST_ALL_PREFIXES(MediaStreamAudioProcessorTest,
                            TestAgcEnableHybridAgc);
   FRIEND_TEST_ALL_PREFIXES(MediaStreamAudioProcessorTest,
-                           TestAgcEnableHybridAgcAvx2NotAllowed);
+                           TestAgcEnableHybridAgcSimdNotAllowed);
 
   // WebRtcPlayoutDataSource::Sink implementation.
   void OnPlayoutData(media::AudioBus* audio_bus,
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
index 55387b3..4d33df8 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor_test.cc
@@ -434,7 +434,9 @@
        {"gain_applier_speech_frames_threshold", "5"},
        {"max_gain_change_db_per_second", "4"},
        {"max_output_noise_level_dbfs", "-22"},
-       {"avx2_allowed", "true"}});
+       {"sse2_allowed", "true"},
+       {"avx2_allowed", "true"},
+       {"neon_allowed", "true"}});
 
   blink::AudioProcessingProperties properties;
   properties.goog_auto_gain_control = true;
@@ -475,13 +477,17 @@
   EXPECT_EQ(adaptive_digital.gain_applier_adjacent_speech_frames_threshold, 5);
   EXPECT_FLOAT_EQ(adaptive_digital.max_gain_change_db_per_second, 4);
   EXPECT_FLOAT_EQ(adaptive_digital.max_output_noise_level_dbfs, -22);
+  EXPECT_TRUE(adaptive_digital.sse2_allowed);
   EXPECT_TRUE(adaptive_digital.avx2_allowed);
+  EXPECT_TRUE(adaptive_digital.neon_allowed);
 }
 
-TEST_F(MediaStreamAudioProcessorTest, TestAgcEnableHybridAgcAvx2NotAllowed) {
+TEST_F(MediaStreamAudioProcessorTest, TestAgcEnableHybridAgcSimdNotAllowed) {
   ::base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeatureWithParameters(features::kWebRtcHybridAgc,
-                                                  {{"avx2_allowed", "false"}});
+                                                  {{"sse2_allowed", "false"},
+                                                   {"avx2_allowed", "false"},
+                                                   {"neon_allowed", "false"}});
 
   blink::AudioProcessingProperties properties;
   properties.goog_auto_gain_control = true;
@@ -497,7 +503,9 @@
       audio_processor->GetAudioProcessingModuleConfig();
   ASSERT_TRUE(apm_config);
 
+  EXPECT_FALSE(apm_config->gain_controller2.adaptive_digital.sse2_allowed);
   EXPECT_FALSE(apm_config->gain_controller2.adaptive_digital.avx2_allowed);
+  EXPECT_FALSE(apm_config->gain_controller2.adaptive_digital.neon_allowed);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index f008cde..dc30c93f 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
 
+#include <algorithm>
 #include <memory>
 #include <utility>
 
@@ -47,6 +48,7 @@
 #include "third_party/blink/public/mojom/appcache/appcache.mojom-blink.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/timing/worker_timing_container.mojom-blink.h"
+#include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/mojom/worker/subresource_loader_updater.mojom.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_fetch_context.h"
@@ -2406,6 +2408,18 @@
         event_data->payment_options->request_shipping);
   }
 
+  // Count standardized payment method identifiers, such as "basic-card" or
+  // "tokenized-card". Omit counting the URL-based payment method identifiers,
+  // such as "https://bobpay.xyz".
+  if (std::any_of(
+          event_data->method_data.begin(), event_data->method_data.end(),
+          [](const payments::mojom::blink::PaymentMethodDataPtr& datum) {
+            return datum && !datum->supported_method.StartsWith("http");
+          })) {
+    UseCounter::Count(
+        this, WebFeature::kPaymentHandlerStandardizedPaymentMethodIdentifier);
+  }
+
   mojo::PendingRemote<payments::mojom::blink::PaymentHandlerHost>
       payment_handler_host = std::move(event_data->payment_handler_host);
   Event* event = PaymentRequestEvent::Create(
@@ -2413,8 +2427,7 @@
       PaymentEventDataConversion::ToPaymentRequestEventInit(
           ScriptController()->GetScriptState(), std::move(event_data)),
       std::move(payment_handler_host), respond_with_observer,
-      wait_until_observer,
-      ExecutionContext::From(ScriptController()->GetScriptState()));
+      wait_until_observer, this);
 
   DispatchExtendableEventWithRespondWith(event, wait_until_observer,
                                          respond_with_observer);
diff --git a/third_party/blink/renderer/platform/bindings/scoped_persistent.h b/third_party/blink/renderer/platform/bindings/scoped_persistent.h
index fd3267a..f21756c3b 100644
--- a/third_party/blink/renderer/platform/bindings/scoped_persistent.h
+++ b/third_party/blink/renderer/platform/bindings/scoped_persistent.h
@@ -51,12 +51,6 @@
   ScopedPersistent(v8::Isolate* isolate, v8::Local<T> handle)
       : handle_(isolate, handle) {}
 
-  ScopedPersistent(v8::Isolate* isolate, v8::MaybeLocal<T> maybe) {
-    v8::Local<T> local;
-    if (maybe.ToLocal(&local))
-      handle_.Reset(isolate, local);
-  }
-
   ~ScopedPersistent() { Clear(); }
 
   ALWAYS_INLINE v8::Local<T> NewLocal(v8::Isolate* isolate) const {
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_deque.h b/third_party/blink/renderer/platform/heap/collection_support/heap_deque.h
index 323d0190..be2ffec1 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_deque.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_deque.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_DEQUE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_DEQUE_H_
 
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator_impl.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/deque.h"
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_hash_counted_set.h b/third_party/blink/renderer/platform/heap/collection_support/heap_hash_counted_set.h
index ed1034f9..fa2d44e 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_hash_counted_set.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_hash_counted_set.h
@@ -5,7 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_COUNTED_SET_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_COUNTED_SET_H_
 
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator_impl.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/hash_counted_set.h"
 
 namespace blink {
@@ -14,11 +16,20 @@
           typename HashFunctions = typename DefaultHash<Value>::Hash,
           typename Traits = HashTraits<Value>>
 class HeapHashCountedSet final
-    : public HashCountedSet<Value, HashFunctions, Traits, HeapAllocator> {
-  IS_GARBAGE_COLLECTED_CONTAINER_TYPE();
+    : public GarbageCollected<HeapHashCountedSet<Value, HashFunctions, Traits>>,
+      public HashCountedSet<Value, HashFunctions, Traits, HeapAllocator> {
   DISALLOW_NEW();
 
-  static void CheckType() {
+ public:
+  HeapHashCountedSet() = default;
+
+  void Trace(Visitor* visitor) const {
+    CheckType();
+    HashCountedSet<Value, HashFunctions, Traits, HeapAllocator>::Trace(visitor);
+  }
+
+ private:
+  static constexpr void CheckType() {
     static_assert(WTF::IsMemberOrWeakMemberType<Value>::value,
                   "HeapHashCountedSet supports only Member and WeakMember.");
     static_assert(std::is_trivially_destructible<HeapHashCountedSet>::value,
@@ -27,21 +38,8 @@
                   "For counted sets without traceable elements, use "
                   "HashCountedSet<> instead of HeapHashCountedSet<>.");
   }
-
- public:
-  template <typename>
-  static void* AllocateObject(size_t size) {
-    return ThreadHeap::Allocate<
-        HeapHashCountedSet<Value, HashFunctions, Traits>>(size);
-  }
-
-  HeapHashCountedSet() { CheckType(); }
 };
 
-template <typename T, typename U, typename V>
-struct GCInfoTrait<HeapHashCountedSet<T, U, V>>
-    : public GCInfoTrait<HashCountedSet<T, U, V, HeapAllocator>> {};
-
 }  // namespace blink
 
 namespace WTF {
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h b/third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h
index a184316d..0d37a73 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h
@@ -5,7 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_MAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_MAP_H_
 
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator_impl.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
 namespace blink {
@@ -15,16 +17,30 @@
           typename HashArg = typename DefaultHash<KeyArg>::Hash,
           typename KeyTraitsArg = HashTraits<KeyArg>,
           typename MappedTraitsArg = HashTraits<MappedArg>>
-class HeapHashMap final : public HashMap<KeyArg,
+class HeapHashMap final : public GarbageCollected<HeapHashMap<KeyArg,
+                                                              MappedArg,
+                                                              HashArg,
+                                                              KeyTraitsArg,
+                                                              MappedTraitsArg>>,
+                          public HashMap<KeyArg,
                                          MappedArg,
                                          HashArg,
                                          KeyTraitsArg,
                                          MappedTraitsArg,
                                          HeapAllocator> {
-  IS_GARBAGE_COLLECTED_CONTAINER_TYPE();
   DISALLOW_NEW();
 
-  static void CheckType() {
+ public:
+  HeapHashMap() = default;
+
+  void Trace(Visitor* visitor) const {
+    CheckType();
+    HashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg,
+            HeapAllocator>::Trace(visitor);
+  }
+
+ private:
+  static constexpr void CheckType() {
     static_assert(std::is_trivially_destructible<HeapHashMap>::value,
                   "HeapHashMap must be trivially destructible.");
     static_assert(
@@ -43,22 +59,8 @@
                   "TraceWrapperV8Reference and "
                   "non-traceable types as values.");
   }
-
- public:
-  template <typename>
-  static void* AllocateObject(size_t size) {
-    return ThreadHeap::Allocate<
-        HeapHashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg>>(
-        size);
-  }
-
-  HeapHashMap() { CheckType(); }
 };
 
-template <typename T, typename U, typename V, typename W, typename X>
-struct GCInfoTrait<HeapHashMap<T, U, V, W, X>>
-    : public GCInfoTrait<HashMap<T, U, V, W, X, HeapAllocator>> {};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_MAP_H_
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h b/third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h
index 03207de75..47b58a7 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h
@@ -5,7 +5,9 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_SET_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_SET_H_
 
+#include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator_impl.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
 namespace blink {
@@ -13,12 +15,21 @@
 template <typename ValueArg,
           typename HashArg = typename DefaultHash<ValueArg>::Hash,
           typename TraitsArg = HashTraits<ValueArg>>
-class HeapHashSet
-    : public HashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
-  IS_GARBAGE_COLLECTED_CONTAINER_TYPE();
+class HeapHashSet final
+    : public GarbageCollected<HeapHashSet<ValueArg, HashArg, TraitsArg>>,
+      public HashSet<ValueArg, HashArg, TraitsArg, HeapAllocator> {
   DISALLOW_NEW();
 
-  static void CheckType() {
+ public:
+  HeapHashSet() = default;
+
+  void Trace(Visitor* visitor) const {
+    CheckType();
+    HashSet<ValueArg, HashArg, TraitsArg, HeapAllocator>::Trace(visitor);
+  }
+
+ private:
+  static constexpr void CheckType() {
     static_assert(WTF::IsMemberOrWeakMemberType<ValueArg>::value,
                   "HeapHashSet supports only Member and WeakMember.");
     static_assert(std::is_trivially_destructible<HeapHashSet>::value,
@@ -27,21 +38,8 @@
                   "For hash sets without traceable elements, use HashSet<> "
                   "instead of HeapHashSet<>.");
   }
-
- public:
-  template <typename>
-  static void* AllocateObject(size_t size) {
-    return ThreadHeap::Allocate<HeapHashSet<ValueArg, HashArg, TraitsArg>>(
-        size);
-  }
-
-  HeapHashSet() { CheckType(); }
 };
 
-template <typename T, typename U, typename V>
-struct GCInfoTrait<HeapHashSet<T, U, V>>
-    : public GCInfoTrait<HashSet<T, U, V, HeapAllocator>> {};
-
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_COLLECTION_SUPPORT_HEAP_HASH_SET_H_
diff --git a/third_party/blink/renderer/platform/heap/member.h b/third_party/blink/renderer/platform/heap/member.h
index d01f2714..0bf9d7e 100644
--- a/third_party/blink/renderer/platform/heap/member.h
+++ b/third_party/blink/renderer/platform/heap/member.h
@@ -14,6 +14,15 @@
 #include "third_party/blink/renderer/platform/heap/impl/member.h"
 #endif  // !USE_V8_OILPAN
 
+namespace blink {
+
+template <typename T>
+inline void swap(Member<T>& a, Member<T>& b) {
+  a.Swap(b);
+}
+
+}  // namespace blink
+
 namespace WTF {
 
 template <typename T>
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc
index 6e21bfa..92b97b09 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.cc
@@ -247,7 +247,9 @@
       adaptive_digital.max_output_noise_level_dbfs =
           agc2_properties->max_output_noise_level_dbfs;
 
+      adaptive_digital.sse2_allowed = agc2_properties->sse2_allowed;
       adaptive_digital.avx2_allowed = agc2_properties->avx2_allowed;
+      adaptive_digital.neon_allowed = agc2_properties->neon_allowed;
     }
   } else if (use_fixed_digital_agc2) {
     // Experimental AGC is disabled, thus hybrid AGC is disabled. Config AGC2
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h
index 55e75da..53f5718 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options.h
@@ -125,7 +125,9 @@
   int gain_applier_speech_frames_threshold;
   int max_gain_change_db_per_second;
   int max_output_noise_level_dbfs;
+  bool sse2_allowed;
   bool avx2_allowed;
+  bool neon_allowed;
 };
 
 // Configures automatic gain control in `apm_config`. If `agc_enabled` is true
diff --git a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc
index ea93c103..e2ce2e3b1 100644
--- a/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc
+++ b/third_party/blink/renderer/platform/mediastream/media_stream_audio_processor_options_test.cc
@@ -51,7 +51,9 @@
   agc2_properties.gain_applier_speech_frames_threshold = 5;
   agc2_properties.max_gain_change_db_per_second = 4;
   agc2_properties.max_output_noise_level_dbfs = -22;
+  agc2_properties.sse2_allowed = true;
   agc2_properties.avx2_allowed = true;
+  agc2_properties.neon_allowed = true;
   const double compression_gain_db = 10.0;
 
   ConfigAutomaticGainControl(
@@ -89,7 +91,9 @@
                   agc2_properties.max_gain_change_db_per_second);
   EXPECT_FLOAT_EQ(adaptive_digital.max_output_noise_level_dbfs,
                   agc2_properties.max_output_noise_level_dbfs);
+  EXPECT_TRUE(adaptive_digital.sse2_allowed);
   EXPECT_TRUE(adaptive_digital.avx2_allowed);
+  EXPECT_TRUE(adaptive_digital.neon_allowed);
 }
 
 TEST(PopulateApmConfigTest, DefaultWithoutConfigJson) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index aeafd89b..37bbbeb9 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2185,7 +2185,7 @@
     },
     {
       name: "WebNFC",
-      status: "experimental",
+      status: {"Android": "stable", "default": "test"},
     },
     {
       name: "WebOTP",
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
index ccbc3f3..1752b009 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_origin_test.cc
@@ -159,6 +159,8 @@
       {false, false, "http://foobar.com"},
       {false, false, "http://foobar.com:443"},
       {false, false, "ws://foobar.com"},
+      // TODO(crbug.com/1163060): Test registration of secure schemes.
+      {false, false, "custom-scheme://example.com"},
 
       // Local files are considered trustworthy.
       {true, false, "file:///home/foobar/index.html"},
@@ -238,6 +240,7 @@
       {false,
        "filesystem:blob:https://example.com/"
        "578223a1-8c13-17b3-84d5-eca045ae384a"},
+      // TODO(crbug.com/1163060): Test registration of secure schemes.
       {false, "custom-scheme://example.com"},
       {true, "quic-transport://example.com/counter"},
       {false, ""},
@@ -251,12 +254,6 @@
   EXPECT_FALSE(SecurityOrigin::IsSecure(NullURL()));
 }
 
-TEST_F(SecurityOriginTest, IsCustomSchemeSecure) {
-  url::ScopedSchemeRegistryForTests scoped_registry;
-  url::AddSecureScheme("custom-scheme");
-  EXPECT_TRUE(SecurityOrigin::IsSecure(KURL("custom-scheme://example.com")));
-}
-
 TEST_F(SecurityOriginTest, IsSecureViaTrustworthy) {
   // TODO(crbug.com/1153336): Should SecurityOrigin::IsSecure be aligned with
   // network::IsURLPotentiallyTrustworthy?
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 31e6d605..dbd53100 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -70,6 +70,7 @@
 crbug.com/1132260 http/tests/devtools/console/console-bad-stacktrace.js [ Pass Failure ]
 crbug.com/1132260 http/tests/devtools/console/console-uncaught-promise.js [ Pass Failure ]
 crbug.com/1132260 http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js [ Pass Failure ]
+crbug.com/1132260 inspector-protocol/page/frameAttachedStacktrace.js [ Pass Failure ]
 
 # With --enable-display-compositor-pixel-dump enabled by default, these three
 # tests fail. See crbug.com/887140 for more info.
diff --git a/third_party/blink/web_tests/WebDriverExpectations b/third_party/blink/web_tests/WebDriverExpectations
index c1d6c9ff..9ad8e13 100644
--- a/third_party/blink/web_tests/WebDriverExpectations
+++ b/third_party/blink/web_tests/WebDriverExpectations
@@ -78,6 +78,8 @@
 crbug.com/1020018 [ Linux ] external/wpt/webdriver/tests/get_active_element/get.py>>test_sucess_input_non_interactable [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_frame/switch.py>>test_frame_id_null [ Failure ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_parent_frame/switch.py>>test_switch_from_iframe [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/switch_to_window/switch.py>>test_element_not_found_after_tab_switch [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/element_click/click.py>>test_no_top_browsing_context [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/is_element_enabled/enabled.py>>test_no_top_browsing_context [ 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 fc1dcaa2..ad2b2f7 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
@@ -375317,7 +375317,7 @@
         ]
        ],
        "radio.html": [
-        "7dcc9a14a8980898917d8d771041bd691e99785f",
+        "7f183f8367db079cd3b8e6b7a0dc741e874c4d0f",
         [
          null,
          {}
@@ -413498,6 +413498,15 @@
      ]
     ],
     "textcontrols": {
+     "focus.html": [
+      "8c2e0b55914e765bb8b56154aa6638dd0d9f863f",
+      [
+       null,
+       {
+        "testdriver": true
+       }
+      ]
+     ],
      "selectionchange.tentative.html": [
       "adec8b31f3e096eb28ae0246164d637da10bd5ee",
       [
@@ -464945,7 +464954,7 @@
        ]
       ],
       "switch.py": [
-       "07994bed34931940c15aa4f722809c809fd6d20c",
+       "df7e5b73af419ab464b3a787eee514575af70c37",
        [
         null,
         {}
@@ -464968,7 +464977,7 @@
      },
      "switch_to_parent_frame": {
       "switch.py": [
-       "e05e11953c971b0d45bf24b899d5f0342f5ea305",
+       "9c6db8d2cd61d6e92f45ac1843dbe0f43f69af76",
        [
         null,
         {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-001.html
index 33036d99..0322f51 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-001.html
@@ -1,6 +1,5 @@
 <!DOCTYPE html>
-<link rel="author" title="Benjamin Beaudry" href="mailto:bebeaudr@microsoft.com">
-<link rel="help" href="href=https://www.w3.org/TR/css-break-3/#break-decoration">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#break-decoration">
 <link rel="match" href="out-of-flow-in-multicolumn-001-ref.html">
 
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-002.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-002.html
index b5fa72b3..0138a3a4 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-002.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-break-3/#break-decoration">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#break-decoration">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Tests fragmentation when a positioned node's child overflows. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-003.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-003.html
index c8e1b6f..b37bfad 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-003.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-break-3/#break-decoration">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#break-decoration">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Tests that empty column fragments are added if an OOF element begins layout
      in a fragmentainer that is more than one index beyond the last existing
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-004.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-004.html
index 86ffa50d..f2e4aa97 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-004.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-break-3/#breaking-controls">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#breaking-controls">
 <link rel="match" href="out-of-flow-in-multicolumn-004-ref.html">
 <!-- break-inside: avoid and break-before: column do not apply to absolute positioned elements. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-005.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-005.html
index 3917db4..a87a074 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-005.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-005.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-break-3/#breaking-controls">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#breaking-controls">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- break-after: column does not apply to absolute positioned elements. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-006.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-006.html
index 7d1803e..d724ad0 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-006.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-006.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Tests that the fragments of a positioned element are added to the right fragmentainer despite the presence of column spanners. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-007.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-007.html
index a6e68a0..0e6988c 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-007.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-007.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Tests that a positioned element without a top or bottom property uses the static position -
      even though it's treated as an OOF element. -->
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-008.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-008.html
index 0c1405d..98c7764 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-008.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-008.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Tests fragmented abspos elements with a spanner nested inside. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-009.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-009.html
index 88b8d48..4db33ef 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-009.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-009.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Tests that empty column fragments are added if an OOF element begins layout in a fragmentainer that is more than one index beyond the last existing column fragmentainer in the presence of a spanner. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-010.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-010.html
index 708b134..5ae87e7 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-010.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-010.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Fragmented OOF element with block-size percentage resolution. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-011.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-011.html
index 9335089..17e8908 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-011.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-011.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Fragmented OOF element with block-size percentage resolution and overflow. -->
 <style>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-012.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-012.html
index 349d059b..4e59979 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-012.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-012.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Fragmented OOF with `height: auto`, positioned with the bottom property and
      the second child fragment has a 'break-inside: avoid'. The abspos fragment
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-013.html b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-013.html
index 3bf06df..6580a65 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-013.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/out-of-flow-in-multicolumn-013.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<link rel="help" href="href=https://www.w3.org/TR/css-position-3/#abspos-breaking">
+<link rel="help" href="https://www.w3.org/TR/css-position-3/#abspos-breaking">
 <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
 <!-- Nested abspos with `height: auto` and positioned with the bottom property.
      The innermost abspos only is fragmented. -->
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/radio.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/radio.html
index 7dcc9a14..7f183f8 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/radio.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/radio.html
@@ -204,4 +204,148 @@
     radios[0].checked = false;
     assert_true(radios[0].validity.valueMissing, 'The first radio should be invalid because of required');
   }, 'Radio buttons in an orphan tree should make a group');
+
+  test(() => {
+    const container = document.createElement('div');
+    container.innerHTML =
+        '<form>' +
+          '<input type=radio name=group1 id=radio1 checked>' +
+          '<input type=radio name=group1 id=radio2>' +
+        '</form>' +
+        '<form>' +
+          '<input type=radio name=group1 id=radio3 checked>' +
+          '<input type=radio name=group1 id=radio4>' +
+        '</form>' +
+        '<input type=radio name=group1 id=radio5 checked>' +
+        '<input type=radio name=group1 id=radio6>';
+    const radio1 = container.querySelector('#radio1');
+    const radio2 = container.querySelector('#radio2');
+    const radio3 = container.querySelector('#radio3');
+    const radio4 = container.querySelector('#radio4');
+    const radio5 = container.querySelector('#radio5');
+    const radio6 = container.querySelector('#radio6');
+
+    // initial conditions
+    assert_true(radio1.checked, 'radio1 should be checked');
+    assert_false(radio2.checked, 'radio2 should be unchecked');
+    assert_true(radio3.checked, 'radio3 should be checked');
+    assert_false(radio4.checked, 'radio4 should be unchecked');
+    assert_true(radio5.checked, 'radio5 should be checked');
+    assert_false(radio6.checked, 'radio6 should be unchecked');
+
+    radio2.checked = true;
+    assert_false(radio1.checked, 'radio1 should be unchecked');
+    assert_true(radio2.checked, 'radio2 should be checked');
+    assert_true(radio3.checked, 'radio3 should remain checked');
+    assert_true(radio5.checked, 'radio5 should remain checked');
+
+    radio4.checked = true;
+    assert_false(radio1.checked, 'radio1 should remain unchecked');
+    assert_true(radio2.checked, 'radio2 should remain checked');
+    assert_false(radio3.checked, 'radio3 should be unchecked');
+    assert_true(radio4.checked, 'radio4 should be checked');
+    assert_true(radio5.checked, 'radio5 should remain checked');
+
+    radio6.checked = true;
+    assert_false(radio1.checked, 'radio1 should remain unchecked');
+    assert_true(radio2.checked, 'radio2 should remain checked');
+    assert_false(radio3.checked, 'radio3 should remain unchecked');
+    assert_true(radio4.checked, 'radio4 should remain checked');
+    assert_false(radio5.checked, 'radio5 should be unchecked');
+    assert_true(radio6.checked, 'radio6 should be checked');
+  }, "Radio buttons in different groups (because they have different form owners or no form owner) do not affect each other's checkedness");
+
+  test(() => {
+    const container = document.createElement('div');
+    container.innerHTML =
+        '<form>' +
+          '<input type=radio name=group1 id=radio1 checked>' +
+          '<input type=radio name=group1 id=radio2>' +
+          '<input type=radio name=group1 id=radio3>' +
+          '<input type=radio name=group1 id=radio4>' +
+        '</form>';
+    const radio1 = container.querySelector('#radio1');
+    const radio2 = container.querySelector('#radio2');
+    const radio3 = container.querySelector('#radio3');
+    const radio4 = container.querySelector('#radio4');
+
+    // initial conditions
+    assert_true(radio1.checked, 'radio1 should be checked');
+    assert_false(radio2.checked, 'radio2 should be unchecked');
+    assert_false(radio3.checked, 'radio3 should be unchecked');
+    assert_false(radio4.checked, 'radio4 should be unchecked');
+
+    radio3.remove();
+    radio4.remove();
+    radio3.checked = true;
+    radio4.checked = true;
+    assert_true(radio1.checked, 'radio1 should remain checked');
+    assert_false(radio2.checked, 'radio2 should remain unchecked');
+    assert_true(radio3.checked, 'radio3 should be checked');
+    assert_true(radio4.checked, 'radio4 should be checked');
+  }, "Radio buttons in different groups (because they are not in the same tree) do not affect each other's checkedness");
+
+  test(() => {
+    const container = document.createElement('div');
+    container.innerHTML =
+        '<form>' +
+          '<input type=radio name=group1 id=radio1 checked>' +
+          '<input type=radio name=group1 id=radio2>' +
+          '<input type=radio name=group2 id=radio3 checked>' +
+          '<input type=radio name=group2 id=radio4>' +
+          '<input type=radio name="" id=radio5 checked>' +
+          '<input type=radio name="" id=radio6>' +
+          '<input type=radio id=radio7 checked>' +
+          '<input type=radio id=radio8>' +
+        '</form>';
+    const radio1 = container.querySelector('#radio1');
+    const radio2 = container.querySelector('#radio2');
+    const radio3 = container.querySelector('#radio3');
+    const radio4 = container.querySelector('#radio4');
+    const radio5 = container.querySelector('#radio5');
+    const radio6 = container.querySelector('#radio6');
+    const radio7 = container.querySelector('#radio7');
+    const radio8 = container.querySelector('#radio8');
+
+    // initial conditions
+    assert_true(radio1.checked, 'radio1 should be checked');
+    assert_false(radio2.checked, 'radio2 should be unchecked');
+    assert_true(radio3.checked, 'radio3 should be checked');
+    assert_false(radio4.checked, 'radio4 should be unchecked');
+    assert_true(radio5.checked, 'radio5 should be checked');
+    assert_false(radio6.checked, 'radio6 should be unchecked');
+    assert_true(radio7.checked, 'radio7 should be checked');
+    assert_false(radio8.checked, 'radio8 should be unchecked');
+
+    radio2.checked = true;
+    assert_false(radio1.checked, 'radio1 should be unchecked');
+    assert_true(radio2.checked, 'radio2 should be checked');
+    assert_true(radio3.checked, 'radio3 should remain checked');
+    assert_false(radio4.checked, 'radio4 should remain unchecked');
+    assert_true(radio5.checked, 'radio5 should remain checked');
+    assert_false(radio6.checked, 'radio6 should remain unchecked');
+    assert_true(radio7.checked, 'radio7 should remain checked');
+    assert_false(radio8.checked, 'radio8 should remain unchecked');
+
+    radio6.checked = true;
+    assert_false(radio1.checked, 'radio1 should remain unchecked');
+    assert_true(radio2.checked, 'radio2 should remain checked');
+    assert_true(radio3.checked, 'radio3 should remain checked');
+    assert_false(radio4.checked, 'radio4 should remain unchecked');
+    assert_true(radio5.checked, 'radio5 should remain checked');
+    assert_true(radio6.checked, 'radio6 should be checked');
+    assert_true(radio7.checked, 'radio7 should remain checked');
+
+    radio8.checked = true;
+    assert_false(radio1.checked, 'radio1 should remain unchecked');
+    assert_true(radio2.checked, 'radio2 should remain checked');
+    assert_true(radio3.checked, 'radio3 should remain checked');
+    assert_false(radio4.checked, 'radio4 should remain unchecked');
+    assert_true(radio5.checked, 'radio5 should remain checked');
+    assert_true(radio6.checked, 'radio6 should remain checked');
+    assert_true(radio7.checked, 'radio7 should remain checked');
+    assert_true(radio8.checked, 'radio8 should be checked');
+
+  }, "Radio buttons in different groups (because they have different name attribute values, or no name attribute) do not affect each other's checkedness");
+
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/selection/textcontrols/focus.html b/third_party/blink/web_tests/external/wpt/selection/textcontrols/focus.html
new file mode 100644
index 0000000..8c2e0b5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/selection/textcontrols/focus.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Stop selection extension when focus changes</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+#p {
+  font: 16px/1 Ahem;
+}
+</style>
+<p id="p">
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
+  aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+  Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+  occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+</p>
+<textarea id="textarea">Hello</textarea>
+<script>
+  const selection = getSelection();
+  const { p, textarea } = document.all;
+  document.addEventListener("selectionchange", ev => {
+    if (p.contains(selection.focusNode)) {
+      textarea.focus();
+    }
+  });
+
+  promise_test(async () => {
+    await new test_driver.Actions()
+      .pointerMove(5, 5, {origin: p})
+      .pointerDown()
+      .pointerMove(50, 50)
+      .pointerUp()
+      .send();
+    assert_equals(selection.focusNode, document.body);
+    assert_equals(selection.focusOffset, 2);
+    assert_equals(selection.anchorNode, document.body);
+    assert_equals(selection.focusOffset, 2);
+  }, "focus() should cancel selection extension by pointer device");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/svg/animations/use-animate-display-none-symbol.html b/third_party/blink/web_tests/external/wpt/svg/animations/use-animate-display-none-symbol.html
new file mode 100644
index 0000000..6ae10ca8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/svg/animations/use-animate-display-none-symbol.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Animate a symbol in a display: none subtree</title>
+<link rel="match" href="../struct/reftests/reference/green-100x100.html">
+<script src="/common/reftest-wait.js"></script>
+<svg>
+  <use href="#hidden"/>
+</svg>
+<svg style="display: none">
+  <symbol id="hidden">
+    <rect width="100" height="100" fill="red">
+      <animate attributeName="fill" values="red; green" calcMode="discrete"
+               dur="1ms" fill="freeze" onend="takeScreenshot()"/>
+    </rect>
+  </symbol>
+</svg>
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch.py
index 07994be..df7e5b7 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_frame/switch.py
@@ -2,7 +2,7 @@
 
 import webdriver.protocol as protocol
 
-from webdriver import StaleElementReferenceException
+from webdriver import NoSuchElementException
 from webdriver.transport import Response
 
 from tests.support.asserts import assert_error, assert_same_element, assert_success
@@ -90,9 +90,9 @@
     response = switch_to_frame(session, None)
     assert_success(response)
 
-    with pytest.raises(StaleElementReferenceException):
+    with pytest.raises(NoSuchElementException):
         element2.text
-    with pytest.raises(StaleElementReferenceException):
+    with pytest.raises(NoSuchElementException):
         element1.text
 
     frame = session.find.css("iframe", all=False)
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_parent_frame/switch.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_parent_frame/switch.py
index e05e1195..9c6db8d2 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_parent_frame/switch.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/switch_to_parent_frame/switch.py
@@ -1,6 +1,6 @@
 import pytest
 
-from webdriver import StaleElementReferenceException
+from webdriver import NoSuchElementException
 
 from tests.support.asserts import assert_error, assert_success
 
@@ -66,13 +66,13 @@
     session.url = inline(iframe("<p>foo"))
     frame_element = session.find.css("iframe", all=False)
     session.switch_frame(frame_element)
-    stale_element = session.find.css("p", all=False)
+    element = session.find.css("p", all=False)
 
     result = switch_to_parent_frame(session)
     assert_success(result)
 
-    with pytest.raises(StaleElementReferenceException):
-        stale_element.text
+    with pytest.raises(NoSuchElementException):
+        element.text
 
 
 def test_switch_from_top_level(session, inline):
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js
index 5c286a90..141943c 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js
@@ -21,7 +21,8 @@
         SourcesTestRunner.waitUntilPaused(didPause);
         SourcesTestRunner.createNewBreakpoint(currentSourceFrame, 13, '', true)
             .then(() => SourcesTestRunner.waitBreakpointSidebarPane())
-            .then(() => SourcesTestRunner.runTestFunction());
+            .then(() => setTimeout(() =>
+                SourcesTestRunner.runTestFunction(), 1));
       }
 
       async function didPause(callFrames) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js
index bb25488..cd6d6f4 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js
@@ -22,7 +22,8 @@
         SourcesTestRunner
             .createNewBreakpoint(currentSourceFrame, 13, 'true', true)
             .then(() => SourcesTestRunner.waitBreakpointSidebarPane())
-            .then(() => SourcesTestRunner.runTestFunction());
+            .then(() => setTimeout(() =>
+                SourcesTestRunner.runTestFunction(), 1));
       }
 
       async function didPause(callFrames) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js
index ab7efb6c..1fea9ee 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js
@@ -40,7 +40,7 @@
 
       function evaluateF2() {
         SourcesTestRunner.waitUntilPaused(pausedInF2);
-        TestRunner.evaluateInPageWithTimeout('f2()');
+        setTimeout(() => TestRunner.evaluateInPageAnonymously('f2()'), 1);
       }
 
       async function pausedInF2(callFrames) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js
index b09ce02..c6e5fce 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js
@@ -39,7 +39,7 @@
 
       function evaluateF2() {
         SourcesTestRunner.waitUntilPaused(pausedInF2);
-        TestRunner.evaluateInPageWithTimeout('f2()');
+        setTimeout(() => TestRunner.evaluateInPageAnonymously('f2()'), 1);
       }
 
 
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-domain-mismatch-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-domain-mismatch-expected.txt
index 55f29b1..41bc4ee 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-domain-mismatch-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-domain-mismatch-expected.txt
@@ -12,6 +12,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : false
             session : true
             size : 9
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-not-on-path-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-not-on-path-expected.txt
index 9f5bbeee..5e0f94b28 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-not-on-path-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-not-on-path-expected.txt
@@ -12,6 +12,7 @@
             name : name
             path : /inspector-protocol/network/resources/set-cookie.php
             priority : Medium
+            sameParty : false
             secure : false
             session : true
             size : 9
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-lax-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-lax-expected.txt
index 1e30fae..4c6f2c6 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-lax-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-lax-expected.txt
@@ -11,6 +11,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Lax
             secure : false
             session : true
@@ -30,6 +31,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Lax
             secure : false
             session : true
@@ -50,6 +52,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Lax
             secure : false
             session : true
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-strict-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-strict-expected.txt
index 46209a6b..ef9d7c1 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-strict-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-same-site-strict-expected.txt
@@ -11,6 +11,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Strict
             secure : false
             session : true
@@ -31,6 +32,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Strict
             secure : false
             session : true
@@ -51,6 +53,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Strict
             secure : false
             session : true
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-secure-only-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-secure-only-expected.txt
index 1be6a37..99fcb01 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-secure-only-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-cookie-secure-only-expected.txt
@@ -12,6 +12,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : true
             session : true
             size : 9
@@ -32,6 +33,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : true
             session : true
             size : 9
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-multiple-reasons-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-multiple-reasons-expected.txt
index 87306866..24724bfd 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-multiple-reasons-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-multiple-reasons-expected.txt
@@ -12,6 +12,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : true
             session : true
             size : 9
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-overwrite-secure-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-overwrite-secure-expected.txt
index 96abb98d..37a1b08 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-overwrite-secure-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-overwrite-secure-expected.txt
@@ -11,6 +11,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : false
             session : true
             size : 9
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-lax-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-lax-expected.txt
index 91b798c..386dabe9 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-lax-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-lax-expected.txt
@@ -12,6 +12,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Lax
             secure : false
             session : true
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-strict-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-strict-expected.txt
index 5ce0fd37..cb06cf0 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-strict-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/blocked-setcookie-same-site-strict-expected.txt
@@ -12,6 +12,7 @@
             name : name
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : Strict
             secure : false
             session : true
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-expected.txt
index 784cf5eb0..92826cf 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-expected.txt
@@ -11,6 +11,7 @@
             name : name1
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : None
             secure : true
             session : true
@@ -28,6 +29,7 @@
             name : name2
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : false
             session : true
             size : 11
@@ -47,6 +49,7 @@
             name : name2
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             secure : false
             session : true
             size : 11
@@ -63,6 +66,7 @@
             name : name1
             path : /inspector-protocol/network/resources
             priority : Medium
+            sameParty : false
             sameSite : None
             secure : true
             session : true
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-same-party-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-same-party-expected.txt
new file mode 100644
index 0000000..979e9fec
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-same-party-expected.txt
@@ -0,0 +1,22 @@
+Verifies that Network.RequestWillBeSentExtraInfo events report structured request cookies with the correct SameParty attribute.
+
+requestWillBeSentExtraInfo request cookies on same domain:[
+    [0] : {
+        blockedReasons : [
+        ]
+        cookie : {
+            domain : .cookie.test
+            expires : -1
+            httpOnly : false
+            name : name1
+            path : /inspector-protocol/network/resources
+            priority : Medium
+            sameParty : true
+            secure : true
+            session : true
+            size : 11
+            value : value1
+        }
+    }
+]
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-same-party.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-same-party.js
new file mode 100644
index 0000000..8c0bf4c8
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-request-cookies-same-party.js
@@ -0,0 +1,18 @@
+(async function(testRunner) {
+  const {page, session, dp} = await testRunner.startBlank(
+      `Verifies that Network.RequestWillBeSentExtraInfo events report structured request cookies with the correct SameParty attribute.\n`);
+  await dp.Network.enable();
+  const helper = (await testRunner.loadScript('resources/extra-info-helper.js'))(dp, session);
+
+  const setCookieUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/set-cookie.php?cookie='
+      + encodeURIComponent('name1=value1;SameParty;Secure;Domain=cookie.test');
+
+  // Set cookies in a domain.
+  await helper.navigateWithExtraInfo(setCookieUrl);
+
+  // Navigate to a the same domain to see that the cookie is reported.
+  const sendCookieUrl = 'https://cookie.test:8443/inspector-protocol/network/resources/hello-world.html';
+  const response = await helper.navigateWithExtraInfo(sendCookieUrl);
+  testRunner.log(response.requestExtraInfo.params.associatedCookies, 'requestWillBeSentExtraInfo request cookies on same domain:');
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/plugins/embed-src-change-usecounter.html b/third_party/blink/web_tests/plugins/embed-src-change-usecounter.html
index 771ed59..bd35b2d 100644
--- a/third_party/blink/web_tests/plugins/embed-src-change-usecounter.html
+++ b/third_party/blink/web_tests/plugins/embed-src-change-usecounter.html
@@ -7,6 +7,6 @@
 <script>
 test(() => {
   embedid.src = '../http/tests/resources/test.webm';
-  assert_true(internals.isUseCounted(document, 3745));
+  assert_true(internals.isUseCounted(document, 3749));
 }, 'Verifies that changing embed src gets use counted.');
 </script>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 809d60a..12e1cf6e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -30822,6 +30822,8 @@
   <int value="3746" label="SamePartyCookieAttribute"/>
   <int value="3747" label="SamePartyCookieExclusionOverruledSameSite"/>
   <int value="3748" label="SamePartyCookieInclusionOverruledSameSite"/>
+  <int value="3749" label="EmbedElementWithoutTypeSrcChanged"/>
+  <int value="3750" label="PaymentHandlerStandardizedPaymentMethodIdentifier"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
diff --git a/tools/metrics/histograms/histograms_xml/v8/histograms.xml b/tools/metrics/histograms/histograms_xml/v8/histograms.xml
index d138055..a56c1789 100644
--- a/tools/metrics/histograms/histograms_xml/v8/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/v8/histograms.xml
@@ -916,8 +916,9 @@
 </histogram>
 
 <histogram name="V8.TurboFanOptimizeNonConcurrentTotalTime"
-    units="microseconds" expires_after="2020-12-31">
-  <owner>bmeurer@chromium.org</owner>
+    units="microseconds" expires_after="2021-05-09">
+  <owner>neis@chromium.org</owner>
+  <owner>mvstanton@chromium.org</owner>
   <owner>mslekova@chromium.org</owner>
   <summary>
     Total time from starting optimizing to installing the code object. Recorded
diff --git a/tools/perf/benchmarks/v8_browsing.py b/tools/perf/benchmarks/v8_browsing.py
index 282f398..1b17073 100644
--- a/tools/perf/benchmarks/v8_browsing.py
+++ b/tools/perf/benchmarks/v8_browsing.py
@@ -66,6 +66,7 @@
       'expectedQueueingTimeMetric',
       'gcMetric',
       'memoryMetric',
+      'pcscanMetric',
       'reportedByPageMetric',
       'umaMetric',
       'wasmMetric',
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 3e9044e3..1a4bc6f 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/35f78f4a4562f61cde3b096492628e1d47acb04e/trace_processor_shell"
         },
         "linux": {
-            "hash": "4910c2bfbdf2ab1a735e227bb0d279ccdad8d311",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/35f78f4a4562f61cde3b096492628e1d47acb04e/trace_processor_shell"
+            "hash": "5fc878a0256dfd62f3cac5b7384536e0902e23bd",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/eddc7d0df5d1b1fc6a35162da39375cbaa809fe7/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/perf/page_sets/tab_search/tab_search_story.py b/tools/perf/page_sets/tab_search/tab_search_story.py
index f865710..57759ffc 100644
--- a/tools/perf/page_sets/tab_search/tab_search_story.py
+++ b/tools/perf/page_sets/tab_search/tab_search_story.py
@@ -104,7 +104,10 @@
     tab.WaitForDocumentReadyStateToBeComplete()
 
     # Send key navigation to Tab Search bubble.
+    action_runner.ExecuteJavaScript(MEASURE_JS_MEMORY %
+                                    'used_js_heap_size_begin')
     self.InteractWithPage(action_runner)
+    action_runner.ExecuteJavaScript(MEASURE_JS_MEMORY % 'used_js_heap_size_end')
 
   def InteractWithPage(self, action_runner):
     self.ScrollTabs(action_runner)
@@ -294,3 +297,8 @@
 STOP_MEASURING_FRAME_TIME = '''
 window.__webui_stopMeasuringFrameTime()
 '''
+
+MEASURE_JS_MEMORY = '''
+performance.mark(
+    `%s:${performance.memory.usedJSHeapSize}:benchmark_value`);
+'''
diff --git a/ui/aura/client/drag_drop_delegate.cc b/ui/aura/client/drag_drop_delegate.cc
index 37529f9..2e250bc 100644
--- a/ui/aura/client/drag_drop_delegate.cc
+++ b/ui/aura/client/drag_drop_delegate.cc
@@ -12,6 +12,14 @@
 namespace aura {
 namespace client {
 
+DragUpdateInfo::DragUpdateInfo() = default;
+
+DragUpdateInfo::DragUpdateInfo(int op, ui::DataTransferEndpoint endpoint)
+    : drag_operation(op), data_endpoint(endpoint) {}
+
+DragUpdateInfo& DragUpdateInfo::operator=(const DragUpdateInfo& update_info) =
+    default;
+
 DEFINE_UI_CLASS_PROPERTY_KEY(DragDropDelegate*, kDragDropDelegateKey, nullptr)
 
 void SetDragDropDelegate(Window* window, DragDropDelegate* delegate) {
diff --git a/ui/aura/client/drag_drop_delegate.h b/ui/aura/client/drag_drop_delegate.h
index 82a2dc6..2308cae0 100644
--- a/ui/aura/client/drag_drop_delegate.h
+++ b/ui/aura/client/drag_drop_delegate.h
@@ -9,6 +9,8 @@
 
 #include "ui/aura/aura_export.h"
 #include "ui/aura/window.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
+#include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 
 namespace ui {
@@ -19,6 +21,18 @@
 class Window;
 namespace client {
 
+struct AURA_EXPORT DragUpdateInfo {
+  DragUpdateInfo();
+  DragUpdateInfo(int op, ui::DataTransferEndpoint endpoint);
+
+  DragUpdateInfo& operator=(const DragUpdateInfo& update_info);
+
+  // A bitmask of the DragDropTypes::DragOperation supported.
+  int drag_operation = ui::DragDropTypes::DRAG_NONE;
+  // An object representing the destination window.
+  ui::DataTransferEndpoint data_endpoint{ui::EndpointType::kDefault};
+};
+
 // Delegate interface for drag and drop actions on aura::Window.
 class AURA_EXPORT DragDropDelegate {
  public:
@@ -28,10 +42,9 @@
   virtual void OnDragEntered(const ui::DropTargetEvent& event) = 0;
 
   // Invoked during a drag and drop session while the mouse is over the window.
-  // This should return a bitmask of the DragDropTypes::DragOperation supported
-  // based on the location of the event. Return 0 to indicate the drop should
-  // not be accepted.
-  virtual int OnDragUpdated(const ui::DropTargetEvent& event) = 0;
+  // This should return DragUpdateInfo object based on the location of the
+  // event.
+  virtual DragUpdateInfo OnDragUpdated(const ui::DropTargetEvent& event) = 0;
 
   // Invoked during a drag and drop session when the mouse exits the window, or
   // when the drag session was canceled and the mouse was over the window.
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint.cc b/ui/base/data_transfer_policy/data_transfer_endpoint.cc
index a5f3c7bd..6acbcb8a 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint.cc
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint.cc
@@ -30,6 +30,12 @@
 DataTransferEndpoint::DataTransferEndpoint(DataTransferEndpoint&& other) =
     default;
 
+DataTransferEndpoint& DataTransferEndpoint::operator=(
+    const DataTransferEndpoint& other) = default;
+
+DataTransferEndpoint& DataTransferEndpoint::operator=(
+    DataTransferEndpoint&& other) = default;
+
 bool DataTransferEndpoint::operator==(const DataTransferEndpoint& other) const {
   return origin_ == other.origin_ && type_ == other.type_;
 }
diff --git a/ui/base/data_transfer_policy/data_transfer_endpoint.h b/ui/base/data_transfer_policy/data_transfer_endpoint.h
index 64332c4..ffd31c3 100644
--- a/ui/base/data_transfer_policy/data_transfer_endpoint.h
+++ b/ui/base/data_transfer_policy/data_transfer_endpoint.h
@@ -50,8 +50,8 @@
   DataTransferEndpoint(const DataTransferEndpoint& other);
   DataTransferEndpoint(DataTransferEndpoint&& other);
 
-  DataTransferEndpoint& operator=(const DataTransferEndpoint& other) = delete;
-  DataTransferEndpoint& operator=(DataTransferEndpoint&& other) = delete;
+  DataTransferEndpoint& operator=(const DataTransferEndpoint& other);
+  DataTransferEndpoint& operator=(DataTransferEndpoint&& other);
 
   bool operator==(const DataTransferEndpoint& other) const;
 
@@ -67,10 +67,10 @@
 
  private:
   // This variable should always have a value representing the object type.
-  const EndpointType type_;
+  EndpointType type_;
   // The url::Origin of the data endpoint. It always has a value if `type_` ==
   // EndpointType::kUrl, otherwise it's empty.
-  const base::Optional<url::Origin> origin_;
+  base::Optional<url::Origin> origin_;
   // This variable should be set to true, if paste is initiated by the user.
   // Otherwise it should be set to false, so the user won't see a notification
   // when the data is restricted by the rules of data leak prevention policy
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index 876192939..b1fdf499 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -31,14 +31,19 @@
     ":android_app_list_model.m",
     ":constants.m",
     ":crossover_search_utils.m",
+    ":dialog_type.m",
     ":directory_contents.m",
     ":directory_model.m",
+    ":directory_tree_naming_controller.m",
+    ":drop_effect_and_label.m",
+    ":empty_folder_controller.m",
     ":fake_android_app_list_model.m",
     ":file_list_model.m",
     ":file_watcher.m",
     ":folder_shortcuts_data_model.m",
     ":mock_directory_model.m",
     ":mock_folder_shortcut_data_model.m",
+    ":mock_navigation_list_model.m",
     ":navigation_list_model.m",
     ":thumbnail_loader.m",
   ]
@@ -262,6 +267,17 @@
   deps = [ ":navigation_list_model" ]
 }
 
+js_library("mock_navigation_list_model.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/mock_navigation_list_model.m.js" ]
+  deps = [
+    ":navigation_list_model.m",
+    "//ui/file_manager/externs:volume_manager.m",
+    "//ui/webui/resources/js/cr:event_target.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("mock_thumbnail_loader") {
   testonly = true
   deps = [ ":thumbnail_loader" ]
@@ -346,6 +362,14 @@
 js_library("dialog_type") {
 }
 
+js_library("dialog_type.m") {
+  sources = [
+    "$root_gen_dir/ui/file_manager/file_manager/foreground/js/dialog_type.m.js",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("directory_contents") {
   deps = [
     ":constants",
@@ -448,9 +472,29 @@
   ]
 }
 
+js_library("directory_tree_naming_controller.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.m.js" ]
+  deps = [
+    ":directory_model.m",
+    "ui:directory_tree.m",
+    "//ui/file_manager/externs:volume_info.m",
+    "//ui/file_manager/file_manager/common/js:util.m",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js/cr/ui:dialogs.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("drop_effect_and_label") {
 }
 
+js_library("drop_effect_and_label.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.m.js" ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("elements_importer") {
 }
 
@@ -462,6 +506,21 @@
   ]
 }
 
+js_library("empty_folder_controller.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/empty_folder_controller.m.js" ]
+  deps = [
+    ":constants.m",
+    ":directory_model.m",
+    ":file_list_model.m",
+    "ui:empty_folder.m",
+    "ui:files_alert_dialog.m",
+    "//ui/file_manager/file_manager/common/js:util.m",
+    "//ui/webui/resources/js:assert.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("file_list_model") {
   deps = [
     "metadata:metadata_model",
@@ -1111,18 +1170,23 @@
 js_modulizer("modulize") {
   input_files = [
     "android_app_list_model.js",
-    "thumbnail_loader.js",
     "constants.js",
-    "directory_model.js",
+    "crossover_search_utils.js",
+    "dialog_type.js",
     "directory_contents.js",
+    "directory_model.js",
+    "directory_tree_naming_controller.js",
+    "drop_effect_and_label.js",
+    "empty_folder_controller.js",
+    "fake_android_app_list_model.js",
     "file_list_model.js",
     "file_watcher.js",
-    "crossover_search_utils.js",
-    "navigation_list_model.js",
     "folder_shortcuts_data_model.js",
     "mock_directory_model.js",
-    "fake_android_app_list_model.js",
     "mock_folder_shortcut_data_model.js",
+    "mock_navigation_list_model.js",
+    "navigation_list_model.js",
+    "thumbnail_loader.js",
   ]
 
   namespace_rewrites = cr_namespace_rewrites
diff --git a/ui/file_manager/file_manager/foreground/js/dialog_type.js b/ui/file_manager/file_manager/foreground/js/dialog_type.js
index b0e4721..8b6e2e1 100644
--- a/ui/file_manager/file_manager/foreground/js/dialog_type.js
+++ b/ui/file_manager/file_manager/foreground/js/dialog_type.js
@@ -3,6 +3,11 @@
 // found in the LICENSE file.
 
 /**
+ * @fileoverview
+ * @suppress {uselessCode} Temporary suppress because of the line exporting.
+ */
+
+/**
  * List of dialog types.
  *
  * Keep this in sync with FileManagerDialog::GetDialogTypeAsString, except
@@ -60,3 +65,6 @@
   return type == DialogType.SELECT_FOLDER ||
       type == DialogType.SELECT_UPLOAD_FOLDER;
 };
+
+// eslint-disable-next-line semi,no-extra-semi
+/* #export */ {DialogType};
diff --git a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
index e6604389..cd93ad06 100644
--- a/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/directory_tree_naming_controller.js
@@ -2,10 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {DirectoryTree, DirectoryItem} from './ui/directory_tree.m.js';
+// #import {VolumeInfo} from '../../../externs/volume_info.m.js';
+// #import {AlertDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
+// #import {DirectoryModel} from './directory_model.m.js';
+// #import {util} from '../../common/js/util.m.js';
+// #import {assert} from 'chrome://resources/js/assert.m.js';
+
 /**
  * Naming controller for directory tree.
  */
-class DirectoryTreeNamingController {
+/* #export */ class DirectoryTreeNamingController {
   /**
    * @param {!DirectoryModel} directoryModel
    * @param {!DirectoryTree} directoryTree
@@ -147,7 +154,8 @@
     if (this.isRemovableRoot_) {
       // Validate new name.
       util.validateExternalDriveName(
-              newName, assert(this.volumeInfo_).diskFileSystemType)
+              newName,
+              assert(this.volumeInfo_ && this.volumeInfo_.diskFileSystemType))
           .then(
               this.performExternalDriveRename_.bind(this, entry, newName),
               errorMessage => {
diff --git a/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js b/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js
index fa5529e..65a3924 100644
--- a/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js
+++ b/ui/file_manager/file_manager/foreground/js/drop_effect_and_label.js
@@ -6,17 +6,13 @@
  * Drop effect names supported as a value of DataTransfer.dropEffect.
  * @enum {string}
  */
-const DropEffectType = {
-  NONE: 'none',
-  COPY: 'copy',
-  MOVE: 'move',
-  LINK: 'link'
-};
+/* #export */ const DropEffectType =
+    {NONE: 'none', COPY: 'copy', MOVE: 'move', LINK: 'link'};
 
 /**
  * Represents a drop effect and a label to describe it.
  */
-class DropEffectAndLabel {
+/* #export */ class DropEffectAndLabel {
   /**
    * @param {!DropEffectType} dropEffect
    * @param {?string} label
diff --git a/ui/file_manager/file_manager/foreground/js/empty_folder_controller.js b/ui/file_manager/file_manager/foreground/js/empty_folder_controller.js
index e2d9150..1b53f53 100644
--- a/ui/file_manager/file_manager/foreground/js/empty_folder_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/empty_folder_controller.js
@@ -2,10 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {FilesAlertDialog} from './ui/files_alert_dialog.m.js';
+// #import {EmptyFolder} from './ui/empty_folder.m.js';
+// #import {FileListModel} from './file_list_model.m.js';
+// #import {DirectoryModel} from './directory_model.m.js';
+// #import {str, strf, util} from '../../common/js/util.m.js';
+// #import {constants} from './constants.m.js';
+// #import {assert} from 'chrome://resources/js/assert.m.js';
+
 /**
  * Empty folder controller.
  */
-class EmptyFolderController {
+/* #export */ class EmptyFolderController {
   /**
    * @param {!EmptyFolder} emptyFolder Empty folder ui.
    * @param {!DirectoryModel} directoryModel Directory model.
diff --git a/ui/file_manager/file_manager/foreground/js/mock_navigation_list_model.js b/ui/file_manager/file_manager/foreground/js/mock_navigation_list_model.js
index 25139d9..59a8798d1 100644
--- a/ui/file_manager/file_manager/foreground/js/mock_navigation_list_model.js
+++ b/ui/file_manager/file_manager/foreground/js/mock_navigation_list_model.js
@@ -2,6 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {VolumeManager} from '../../../externs/volume_manager.m.js';
+// #import {NavigationModelVolumeItem, NavigationModelItem} from './navigation_list_model.m.js';
+// #import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
+// clang-format on
+
 /**
  * Container for a NavigationModelVolumeItem, allowing it to be reused for a
  * given volumeInfo.
@@ -25,7 +31,7 @@
  * Mock class for NavigationListModel.
  * Current implementation of mock class cannot handle shortcut list.
  */
-class MockNavigationListModel extends cr.EventTarget {
+/* #export */ class MockNavigationListModel extends cr.EventTarget {
   /**
    * @param {VolumeManager} volumeManager A volume manager.
    */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
index 3d1e045..5f0c8fb 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn
@@ -23,7 +23,18 @@
 
 js_type_check("closure_compile_jsmodules") {
   uses_js_modules = true
-  deps = [ ":file_list_selection_model.m" ]
+  deps = [
+    ":directory_tree.m",
+    ":empty_folder.m",
+    ":file_list_selection_model.m",
+    ":files_alert_dialog.m",
+    "table:table.m",
+    "table:table_column.m",
+    "table:table_column_model.m",
+    "table:table_header.m",
+    "table:table_list.m",
+    "table:table_splitter.m",
+  ]
 }
 
 js_type_check("closure_compile_module") {
@@ -195,16 +206,53 @@
   ]
 }
 
-js_unittest("directory_tree_unittest") {
+js_library("directory_tree.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/directory_tree.m.js" ]
   deps = [
-    ":directory_tree",
-    "//ui/file_manager/base/js:mock_chrome",
-    "//ui/file_manager/base/js:test_error_reporting",
-    "//ui/file_manager/file_manager/background/js:mock_volume_manager",
-    "//ui/file_manager/file_manager/foreground/js:fake_android_app_list_model",
-    "//ui/file_manager/file_manager/foreground/js:mock_directory_model",
-    "//ui/file_manager/file_manager/foreground/js:mock_folder_shortcut_data_model",
-    "//ui/file_manager/file_manager/foreground/js:mock_navigation_list_model",
+    "//ui/file_manager/base/js:volume_manager_types.m",
+    "//ui/file_manager/externs:files_app_entry_interfaces.m",
+    "//ui/file_manager/externs:volume_info.m",
+    "//ui/file_manager/externs:volume_manager.m",
+    "//ui/file_manager/externs/background:file_operation_manager.m",
+    "//ui/file_manager/file_manager/common/js:file_type.m",
+    "//ui/file_manager/file_manager/common/js:metrics.m",
+    "//ui/file_manager/file_manager/common/js:util.m",
+    "//ui/file_manager/file_manager/foreground/js:constants.m",
+    "//ui/file_manager/file_manager/foreground/js:directory_contents.m",
+    "//ui/file_manager/file_manager/foreground/js:directory_model.m",
+    "//ui/file_manager/file_manager/foreground/js:navigation_list_model.m",
+    "//ui/file_manager/file_manager/foreground/js/metadata:metadata_model.m",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr/ui:command.m",
+    "//ui/webui/resources/js/cr/ui:context_menu_handler.m",
+    "//ui/webui/resources/js/cr/ui:menu.m",
+    "//ui/webui/resources/js/cr/ui:tree.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_unittest("directory_tree_unittest.m") {
+  deps = [
+    ":directory_tree.m",
+    "//chrome/test/data/webui:chai_assert",
+    "//ui/file_manager/base/js:mock_chrome.m",
+    "//ui/file_manager/base/js:test_error_reporting.m",
+    "//ui/file_manager/base/js:volume_manager_types.m",
+    "//ui/file_manager/externs/background:file_operation_manager.m",
+    "//ui/file_manager/file_manager/background/js:mock_volume_manager.m",
+    "//ui/file_manager/file_manager/common/js:files_app_entry_types.m",
+    "//ui/file_manager/file_manager/common/js:mock_entry.m",
+    "//ui/file_manager/file_manager/common/js:util.m",
+    "//ui/file_manager/file_manager/foreground/js:directory_model.m",
+    "//ui/file_manager/file_manager/foreground/js:fake_android_app_list_model.m",
+    "//ui/file_manager/file_manager/foreground/js:mock_directory_model.m",
+    "//ui/file_manager/file_manager/foreground/js:mock_folder_shortcut_data_model.m",
+    "//ui/file_manager/file_manager/foreground/js:mock_navigation_list_model.m",
+    "//ui/file_manager/file_manager/foreground/js:navigation_list_model.m",
+    "//ui/file_manager/file_manager/foreground/js/metadata:metadata_model.m",
+    "//ui/webui/resources/js:assert.m",
   ]
 }
 
@@ -220,6 +268,13 @@
   deps = [ "//ui/webui/resources/js:util" ]
 }
 
+js_library("empty_folder.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/empty_folder.m.js" ]
+  deps = [ "//ui/webui/resources/js:util.m" ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("file_grid") {
   deps = [
     ":drag_selector",
@@ -394,6 +449,16 @@
   deps = [ "//ui/webui/resources/js/cr/ui:dialogs" ]
 }
 
+js_library("files_alert_dialog.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.m.js" ]
+  deps = [
+    "//ui/file_manager/file_manager/common/js:util.m",
+    "//ui/webui/resources/js/cr/ui:dialogs.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 # TODO(tapted): Move this into //ui/file_manager/base.
 js_library("files_confirm_dialog") {
   visibility += [ "//ui/file_manager/gallery/*" ]
@@ -551,14 +616,19 @@
 }
 
 js_test_gen_html("js_test_gen_html_modules") {
-  deps = [ ":file_list_selection_model_unittest.m" ]
+  deps = [
+    ":directory_tree_unittest.m",
+    ":file_list_selection_model_unittest.m",
+  ]
   js_module = true
 
   closure_flags =
       strict_error_checking_closure_args + [
         "js_module_root=./gen/ui",
         "js_module_root=../../ui",
+        "jscomp_off=duplicate",
         "browser_resolver_prefix_replacements=\"chrome://test/=./\"",
+        "hide_warnings_for=third_party/",
       ]
 }
 
@@ -566,7 +636,6 @@
   deps = [
     ":actions_submenu_unittest",
     ":breadcrumb_unittest",
-    ":directory_tree_unittest",
     ":file_manager_dialog_base_unittest",
     ":file_table_list_unittest",
     ":file_table_unittest",
@@ -577,7 +646,12 @@
 }
 
 js_modulizer("modulize") {
-  input_files = [ "file_list_selection_model.js" ]
+  input_files = [
+    "directory_tree.js",
+    "empty_folder.js",
+    "file_list_selection_model.js",
+    "files_alert_dialog.js",
+  ]
 
   namespace_rewrites = cr_namespace_rewrites
 }
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index c37fc9c8..abe6fb9 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -2,6 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {FileOperationManager} from '../../../../externs/background/file_operation_manager.m.js';
+// #import {FileFilter} from '../directory_contents.m.js';
+// #import {MetadataModel} from '../metadata/metadata_model.m.js';
+// #import {VolumeManager} from '../../../../externs/volume_manager.m.js';
+// #import {DirectoryModel} from '../directory_model.m.js';
+// #import {VolumeInfo} from '../../../../externs/volume_info.m.js';
+// #import {Menu} from 'chrome://resources/js/cr/ui/menu.m.js';
+// #import {Command} from 'chrome://resources/js/cr/ui/command.m.js';
+// #import {FilesAppDirEntry} from '../../../../externs/files_app_entry_interfaces.m.js';
+// #import {NavigationModelItemType, NavigationSection, NavigationModelFakeItem, NavigationModelVolumeItem, NavigationModelShortcutItem, NavigationModelAndroidAppItem, NavigationListModel, NavigationModelItem} from '../navigation_list_model.m.js';
+// #import {FileType} from '../../../common/js/file_type.m.js';
+// #import {contextMenuHandler} from 'chrome://resources/js/cr/ui/context_menu_handler.m.js';
+// #import {constants} from '../constants.m.js';
+// #import {TreeItem, Tree} from 'chrome://resources/js/cr/ui/tree.m.js';
+// #import {VolumeManagerCommon} from '../../../../base/js/volume_manager_types.m.js';
+// #import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+// #import {dispatchSimpleEvent, getPropertyDescriptor, PropertyKind} from 'chrome://resources/js/cr.m.js';
+// #import {metrics} from '../../../common/js/metrics.m.js';
+// #import {util, str} from '../../../common/js/util.m.js';
+// clang-format on
+
 // Namespace
 const directorytree = {};
 
@@ -151,21 +173,6 @@
  */
 directorytree.createRowElementContent = (id, label) => {
   return `
-    <paper-ripple fit class='recenteringTouch'></paper-ripple>
-    <span class='expand-icon'></span>
-    <span class='icon'></span>
-    <span class='label entry-name' id='${id}'>${label}</span>`;
-};
-
-/**
- * Create tree rowElement content: returns a string of HTML used to innerHTML
- * a tree item rowElement for FILES_NG_ENABLED case.
- * @param {string} id The tree rowElement label Id.
- * @param {string} label The tree rowElement label.
- * @return {string}
- */
-directorytree.createRowElementContentFilesNG = (id, label) => {
-  return `
     <div class='file-row'>
      <span class='expand-icon'></span>
      <span class='icon'></span>
@@ -174,19 +181,12 @@
 };
 
 /**
- * An optional rowElement depth (indent) style handler where undefined uses the
- * default cr.ui.TreeItem indent styling.
- * @type {function(!cr.ui.TreeItem,number)|undefined}
- */
-directorytree.styleRowElementDepth = undefined;
-
-/**
  * Custom tree row style handler: called when the item's |rowElement| should be
  * styled to indent |depth| in the tree for FILES_NG_ENABLED case.
  * @param {!cr.ui.TreeItem} item cr.ui.TreeItem.
  * @param {number} depth Indent depth (>=0).
  */
-directorytree.styleRowElementDepthFilesNG = (item, depth) => {
+directorytree.styleRowElementDepth = (item, depth) => {
   const fileRowElement = item.rowElement.firstElementChild;
 
   const indent = depth * 22;
@@ -198,16 +198,9 @@
 };
 
 /**
- * The iron-icon-set prefix for tree rows that have an .align-right-icon class
- * element added to the row (eject icon, AndroidAppItem launch icon).
- * @type {string}
- */
-directorytree.rightIconSetPrefix = 'files16';
-
-/**
  * A tree item has a tree row with a text label.
  */
-class TreeItem extends cr.ui.TreeItem {
+class FilesTreeItem extends cr.ui.TreeItem {
   /**
    * @param {string} label Label for this item.
    * @param {DirectoryTree} tree Tree that contains this item.
@@ -217,7 +210,7 @@
 
     // Save the cr.ui.TreeItem label id before overwriting the prototype.
     const id = this.labelElement.id;
-    this.__proto__ = TreeItem.prototype;
+    this.__proto__ = FilesTreeItem.prototype;
 
     if (window.IN_TEST) {
       this.setAttribute('entry-label', label);
@@ -274,7 +267,7 @@
  * An expandable directory in the tree. Each element represents one folder (sub
  * directory) or one volume (root directory).
  */
-class DirectoryItem extends TreeItem {
+/* #export */ class DirectoryItem extends FilesTreeItem {
   /**
    * @param {string} label Label for this item.
    * @param {DirectoryTree} tree Current tree, which contains this item.
@@ -821,8 +814,7 @@
 
     // Append eject iron-icon.
     const ironIcon = document.createElement('iron-icon');
-    const iconSet = directorytree.rightIconSetPrefix;
-    ironIcon.setAttribute('icon', `${iconSet}:eject`);
+    ironIcon.setAttribute('icon', `files20:eject`);
     ejectButton.appendChild(ironIcon);
 
     // Add the eject button as the last element of the tree row content.
@@ -830,9 +822,7 @@
     label.parentElement.appendChild(ejectButton);
 
     // Ensure the eject icon shows when the directory tree is too narrow.
-    if (directorytree.FILES_NG_ENABLED) {
-      label.setAttribute('style', 'margin-inline-end: 2px; min-width: 0;');
-    }
+    label.setAttribute('style', 'margin-inline-end: 2px; min-width: 0;');
   }
 
   /**
@@ -852,7 +842,7 @@
  * A subdirectory in the tree. Each element represents a directory that is not
  * a volume's root.
  */
-class SubDirectoryItem extends DirectoryItem {
+/* #export */ class SubDirectoryItem extends DirectoryItem {
   /**
    * @param {string} label Label for this item.
    * @param {DirectoryEntry} dirEntry DirectoryEntry of this item.
@@ -958,7 +948,7 @@
 /**
  * A directory of entries. Each element represents an entry.
  */
-class EntryListItem extends DirectoryItem {
+/* #export */ class EntryListItem extends DirectoryItem {
   /**
    * @param {VolumeManagerCommon.RootType} rootType The root type to record.
    * @param {!NavigationModelFakeItem} modelItem NavigationModelItem of this
@@ -1214,20 +1204,22 @@
         util.iconSetToCSSBackgroundImageValue(volumeInfo.iconSet);
     if (backgroundImage !== 'none') {
       icon.setAttribute('style', 'background-image: ' + backgroundImage);
-    } else if (directorytree.FILES_NG_ENABLED) {
-      if (VolumeManagerCommon.shouldProvideIcons(volumeInfo.volumeType)) {
-        icon.setAttribute('use-generic-provided-icon', '');
-      }
+    } else if (VolumeManagerCommon.shouldProvideIcons(
+                   assert(volumeInfo.volumeType))) {
+      icon.setAttribute('use-generic-provided-icon', '');
     }
 
-    icon.setAttribute('volume-type-icon', volumeInfo.volumeType);
+    icon.setAttribute(
+        'volume-type-icon', /** @type {string} */ (volumeInfo.volumeType));
 
     if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.MEDIA_VIEW) {
       const subtype = VolumeManagerCommon.getMediaViewRootTypeFromVolumeId(
           volumeInfo.volumeId);
       icon.setAttribute('volume-subtype', subtype);
     } else {
-      icon.setAttribute('volume-subtype', volumeInfo.deviceType || '');
+      icon.setAttribute(
+          'volume-subtype',
+          /** @type {string} */ (volumeInfo.deviceType) || '');
     }
   }
 
@@ -1275,7 +1267,7 @@
  * A TreeItem which represents a Drive volume. Drive volume has fake entries
  * such as Shared Drives, Shared with me, and Offline in it.
  */
-class DriveVolumeItem extends VolumeItem {
+/* #export */ class DriveVolumeItem extends VolumeItem {
   /**
    * @param {!NavigationModelVolumeItem} modelItem NavigationModelItem of this
    *     volume.
@@ -1621,7 +1613,7 @@
  * A TreeItem which represents a shortcut for Drive folder.
  * Shortcut items are displayed as top-level children of DirectoryTree.
  */
-class ShortcutItem extends TreeItem {
+/* #export */ class ShortcutItem extends FilesTreeItem {
   /**
    * @param {!NavigationModelShortcutItem} modelItem NavigationModelItem of this
    *     volume.
@@ -1748,7 +1740,7 @@
  * A TreeItem representing an Android picker app. These Android app items are
  * shown as top-level volume entries of the DirectoryTree.
  */
-class AndroidAppItem extends TreeItem {
+class AndroidAppItem extends FilesTreeItem {
   /**
    * @param {!NavigationModelAndroidAppItem} modelItem NavigationModelItem
    *     associated with this volume.
@@ -1775,9 +1767,7 @@
       }
     }
 
-    if (directorytree.FILES_NG_ENABLED && !icon.hasAttribute('style')) {
-      icon.setAttribute('use-generic-provided-icon', '');
-    }
+    icon.setAttribute('use-generic-provided-icon', '');
 
     // Use aria-describedby attribute to let ChromeVox users know that the link
     // launches an external app window.
@@ -1789,8 +1779,7 @@
 
     // Append external-link iron-icon.
     const ironIcon = document.createElement('iron-icon');
-    const iconSet = directorytree.rightIconSetPrefix;
-    ironIcon.setAttribute('icon', `${iconSet}:external-link`);
+    ironIcon.setAttribute('icon', `files20:external-link`);
     externalLinkIcon.appendChild(ironIcon);
 
     // Add the external-link as the last element of the tree row content.
@@ -1798,9 +1787,7 @@
     label.parentElement.appendChild(externalLinkIcon);
 
     // Ensure the link icon shows when the directory tree is too narrow.
-    if (directorytree.FILES_NG_ENABLED) {
-      label.setAttribute('style', 'margin-inline-end: 2px; min-width: 0;');
-    }
+    label.setAttribute('style', 'margin-inline-end: 2px; min-width: 0;');
   }
 
   /**
@@ -1829,7 +1816,7 @@
 /**
  * FakeItem is used by Recent and Linux files.
  */
-class FakeItem extends TreeItem {
+/* #export */ class FakeItem extends FilesTreeItem {
   /**
    * @param {!VolumeManagerCommon.RootType} rootType root type.
    * @param {!NavigationModelFakeItem} modelItem
@@ -1937,7 +1924,7 @@
  * Tree of directories on the middle bar. This element is also the root of
  * items, in other words, this is the parent of the top-level items.
  */
-class DirectoryTree extends cr.ui.Tree {
+/* #export */ class DirectoryTree extends cr.ui.Tree {
   constructor() {
     super();
 
@@ -2484,33 +2471,33 @@
     (el, directoryModel, volumeManager, metadataModel, fileOperationManager,
      fakeEntriesVisible) => {
       el.__proto__ = DirectoryTree.prototype;
-
-      if (util.isFilesNg()) {
-        directorytree.FILES_NG_ENABLED = true;
-        directorytree.rightIconSetPrefix = 'files20';
-        directorytree.createRowElementContent =
-            directorytree.createRowElementContentFilesNG;
-        directorytree.styleRowElementDepth =
-            directorytree.styleRowElementDepthFilesNG;
-        el.setAttribute('files-ng', '');
-      } else {
-        el.removeAttribute('files-ng');
-      }
-
+      el.setAttribute('files-ng', '');
       Object.freeze(directorytree);
 
       /** @type {DirectoryTree} */ (el).decorateDirectoryTree(
           directoryModel, volumeManager, metadataModel, fileOperationManager,
           fakeEntriesVisible);
 
-      if (directorytree.FILES_NG_ENABLED) {
-        el.rowElementDepthStyleHandler = directorytree.styleRowElementDepth;
-      }
+      el.rowElementDepthStyleHandler = directorytree.styleRowElementDepth;
     };
 
-cr.defineProperty(DirectoryTree, 'contextMenuForSubitems', cr.PropertyKind.JS);
-cr.defineProperty(DirectoryTree, 'contextMenuForRootItems', cr.PropertyKind.JS);
-cr.defineProperty(DirectoryTree, 'disabledContextMenu', cr.PropertyKind.JS);
+/** @type {?cr.ui.Menu} */
+DirectoryTree.prototype.contextMenuForSubitems;
+Object.defineProperty(
+    DirectoryTree.prototype, 'contextMenuForSubitems',
+    cr.getPropertyDescriptor('contextMenuForSubitems', cr.PropertyKind.JS));
+
+/** @type {?cr.ui.Menu} */
+DirectoryTree.prototype.contextMenuForRootItems;
+Object.defineProperty(
+    DirectoryTree.prototype, 'contextMenuForRootItems',
+    cr.getPropertyDescriptor('contextMenuForRootItems', cr.PropertyKind.JS));
+
+/** @type {?cr.ui.Menu} */
+DirectoryTree.prototype.disabledContextMenu;
+Object.defineProperty(
+    DirectoryTree.prototype, 'disabledContextMenu',
+    cr.getPropertyDescriptor('disabledContextMenu', cr.PropertyKind.JS));
 
 /**
  * Creates a new DirectoryItem based on |modelItem|.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.m.js
similarity index 92%
rename from ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js
rename to ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.m.js
index cd6f6ef..a2ec1454 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.m.js
@@ -2,6 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {assertArrayEquals,assertEquals, assertFalse, assertTrue} from 'chrome://test/chai_assert.js';
+import {installMockChrome, MockCommandLinePrivate} from '../../../../base/js/mock_chrome.m.js';
+import {reportPromise, waitUntil} from '../../../../base/js/test_error_reporting.m.js';
+import {VolumeManagerCommon} from '../../../../base/js/volume_manager_types.m.js';
+import {FileOperationManager} from '../../../../externs/background/file_operation_manager.m.js';
+import {MockVolumeManager} from '../../../background/js/mock_volume_manager.m.js';
+import {EntryList} from '../../../common/js/files_app_entry_types.m.js';
+import {metrics} from '../../../common/js/metrics.m.js';
+import {MockDirectoryEntry} from '../../../common/js/mock_entry.m.js';
+import {str} from '../../../common/js/util.m.js';
+import {DirectoryModel} from '../directory_model.m.js';
+import {createFakeAndroidAppListModel} from '../fake_android_app_list_model.m.js';
+import {MetadataModel} from '../metadata/metadata_model.m.js';
+import {createFakeDirectoryModel} from '../mock_directory_model.m.js';
+import {MockFolderShortcutDataModel} from '../mock_folder_shortcut_data_model.m.js';
+import {MockNavigationListModel} from '../mock_navigation_list_model.m.js';
+import {NavigationListModel, NavigationModelFakeItem, NavigationModelItemType, NavigationSection} from '../navigation_list_model.m.js';
+
+import {DirectoryTree, EntryListItem} from './directory_tree.m.js';
+// clang-format on
+
 /** @type {!MockVolumeManager} */
 let volumeManager;
 
@@ -24,13 +47,19 @@
 let driveFileSystem;
 
 /**
- * Mock metrics.
- * @type {!Object}
+ * Mock metrics.recordEnum.
+ * @param {string} name
+ * @param {*} value
+ * @param {Array<*>|number=} opt_validValues
  */
-window.metrics = {
-  recordSmallCount: function() {},
-  recordEnum: function() {},
-};
+metrics.recordEnum = function(name, value, opt_validValues) {};
+
+/**
+ * Mock metrics.recordSmallCount.
+ * @param {string} name Short metric name.
+ * @param {number} value Value to be recorded.
+ */
+metrics.recordSmallCount = function(name, value) {};
 
 /**
  * Mock Chrome APIs
@@ -39,7 +68,7 @@
 let mockChrome;
 
 // Set up test components.
-function setUp() {
+export function setUp() {
   // Mock LoadTimeData strings.
   window.loadTimeData.getString = id => id;
   window.loadTimeData.data = {};
@@ -137,7 +166,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testCreateDirectoryTree(callback) {
+export function testCreateDirectoryTree(callback) {
   // Populate the directory tree with the mock filesystem.
   let directoryTree = createElements();
   DirectoryTree.decorate(
@@ -186,7 +215,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testCreateDirectoryTreeWithTeamDrive(callback) {
+export function testCreateDirectoryTreeWithTeamDrive(callback) {
   // Setup entries returned by fakeFileSystemURLResults.
   const driveFileSystem = volumeManager.volumeInfoList.item(0).fileSystem;
   fakeFileSystemURLEntries['filesystem:drive/team_drives/a'] =
@@ -236,7 +265,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testCreateDirectoryTreeWithEmptyTeamDrive(callback) {
+export function testCreateDirectoryTreeWithEmptyTeamDrive(callback) {
   // No directories exist under Team Drives
 
   // Populate the directory tree with the mock filesystem.
@@ -286,7 +315,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testCreateDirectoryTreeWithComputers(callback) {
+export function testCreateDirectoryTreeWithComputers(callback) {
   // Setup entries returned by fakeFileSystemURLResults.
   fakeFileSystemURLEntries['filesystem:drive/Comuters/My Laptop'] =
       MockDirectoryEntry.create(driveFileSystem, '/Computers/My Laptop');
@@ -334,7 +363,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testCreateDirectoryTreeWithEmptyComputers(callback) {
+export function testCreateDirectoryTreeWithEmptyComputers(callback) {
   // No directories exist under Team Drives
 
   // Populate the directory tree with the mock filesystem.
@@ -387,7 +416,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testCreateDirectoryTreeWithTeamDrivesAndComputers(callback) {
+export function testCreateDirectoryTreeWithTeamDrivesAndComputers(callback) {
   // Setup entries returned by fakeFileSystemURLResults.
   fakeFileSystemURLEntries['filesystem:drive/team_drives/a'] =
       MockDirectoryEntry.create(driveFileSystem, '/team_drives/a');
@@ -438,7 +467,7 @@
  * 'section-start' attribute is used to display a line divider between
  * "sections" in the directory tree. This is calculated in NavigationListModel.
  */
-function testUpdateSubElementsFromListSections() {
+export function testUpdateSubElementsFromListSections() {
   const recentItem = null;
   const shortcutListModel = new MockFolderShortcutDataModel([]);
   const androidAppListModel = createFakeAndroidAppListModel(['android:app1']);
@@ -490,7 +519,7 @@
  * Mounts/unmounts removable and archive volumes, and checks these volumes come
  * up to/disappear from the list correctly.
  */
-function testUpdateSubElementsFromList() {
+export function testUpdateSubElementsFromList() {
   // Populate the directory tree with the mock filesystem.
   let directoryTree = createElements();
   DirectoryTree.decorate(
@@ -592,7 +621,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testAddFirstTeamDrive(callback) {
+export function testAddFirstTeamDrive(callback) {
   // No directories exist under Team Drives
 
   // Populate the directory tree with the mock filesystem.
@@ -646,7 +675,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testRemoveLastTeamDrive(callback) {
+export function testRemoveLastTeamDrive(callback) {
   // Setup entries returned by fakeFileSystemURLResults.
   const driveFileSystem = volumeManager.volumeInfoList.item(0).fileSystem;
   fakeFileSystemURLEntries['filesystem:drive/team_drives/a'] =
@@ -708,7 +737,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testAddFirstComputer(callback) {
+export function testAddFirstComputer(callback) {
   // No directories exist under Computers
 
   // Populate the directory tree with the mock filesystem.
@@ -765,7 +794,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testRemoveLastComputer(callback) {
+export function testRemoveLastComputer(callback) {
   // Setup entries returned by fakeFileSystemURLResults.
   const driveFileSystem = volumeManager.volumeInfoList.item(0).fileSystem;
   fakeFileSystemURLEntries['filesystem:drive/Computers/a'] =
@@ -828,7 +857,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testInsideMyDriveAndInsideDrive(callback) {
+export function testInsideMyDriveAndInsideDrive(callback) {
   // Setup My Drive and Downloads and one folder inside each of them.
   fakeFileSystemURLEntries['filesystem:drive/root/folder1'] =
       MockDirectoryEntry.create(driveFileSystem, '/root/folder1');
@@ -883,7 +912,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testAddProviders(callback) {
+export function testAddProviders(callback) {
   // Add a volume representing a non-Smb provider to the mock filesystem.
   volumeManager.createVolumeInfo(
       VolumeManagerCommon.VolumeType.PROVIDED, 'not_smb', 'NOT_SMB_LABEL');
@@ -954,7 +983,7 @@
  * @param {!function(boolean)} callback A callback function which is called with
  *     test result.
  */
-function testSmbNotFetchedUntilClick(callback) {
+export function testSmbNotFetchedUntilClick(callback) {
   // Add a volume representing an Smb provider to the mock filesystem.
   volumeManager.createVolumeInfo(
       VolumeManagerCommon.VolumeType.PROVIDED, 'smb', 'SMB_LABEL', '@smb');
@@ -1005,7 +1034,7 @@
 }
 
 /** Test EntryListItem.sortEntries doesn't fail sorting empty array. */
-function testEntryListItemSortEntriesEmpty() {
+export function testEntryListItemSortEntriesEmpty() {
   const rootType = VolumeManagerCommon.RootType.MY_FILES;
   const entryList = new EntryList(str('MY_FILES_ROOT_LABEL'), rootType);
   const modelItem = new NavigationModelFakeItem(
@@ -1026,7 +1055,7 @@
 
 
 /** Test EntryListItem.sortEntries doesn't fail sorting empty array. */
-function testAriaExpanded(callback) {
+export function testAriaExpanded(callback) {
   // Setup My Drive and Downloads and one folder inside each of them.
   fakeFileSystemURLEntries['filesystem:drive/root/folder1'] =
       MockDirectoryEntry.create(driveFileSystem, '/root/folder1');
diff --git a/ui/file_manager/file_manager/foreground/js/ui/empty_folder.js b/ui/file_manager/file_manager/foreground/js/ui/empty_folder.js
index 1abccef..45157a6 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/empty_folder.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/empty_folder.js
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-class EmptyFolder {
+// #import {queryRequiredElement} from 'chrome://resources/js/util.m.js';
+
+/* #export */ class EmptyFolder {
   /**
    * Empty folder UI.
    * @param {!HTMLElement} emptyFolder DOM element of empty folder.
diff --git a/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js b/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
index affa944..668229f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/files_alert_dialog.js
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// #import {util} from '../../../common/js/util.m.js';
+// #import {AlertDialog} from 'chrome://resources/js/cr/ui/dialogs.m.js';
+
 /**
  * Alert dialog.
  */
-class FilesAlertDialog extends cr.ui.dialogs.AlertDialog {
+/* #export */ class FilesAlertDialog extends cr.ui.dialogs.AlertDialog {
   /**
    * @param {!HTMLElement} parentNode
    */
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn b/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn
index 04c89fe..0623368 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/BUILD.gn
@@ -3,6 +3,8 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//ui/webui/resources/js/cr.gni")
+import("//ui/webui/resources/tools/js_modulizer.gni")
 
 js_library("table_column") {
   deps = [
@@ -11,6 +13,16 @@
   ]
 }
 
+js_library("table_column.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/table/table_column.m.js" ]
+  deps = [
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr:event_target.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("table") {
   deps = [
     ":table_column_model",
@@ -21,6 +33,23 @@
   ]
 }
 
+js_library("table.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/table/table.m.js" ]
+  deps = [
+    ":table_column.m",
+    ":table_column_model.m",
+    ":table_header.m",
+    ":table_list.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr/ui:array_data_model.m",
+    "//ui/webui/resources/js/cr/ui:list.m",
+    "//ui/webui/resources/js/cr/ui:list_item.m",
+    "//ui/webui/resources/js/cr/ui:list_single_selection_model.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("table_column_model") {
   deps = [
     ":table_column",
@@ -28,6 +57,17 @@
   ]
 }
 
+js_library("table_column_model.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/table/table_column_model.m.js" ]
+  deps = [
+    ":table_column.m",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr:event_target.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("table_header") {
   deps = [
     ":table_splitter",
@@ -35,6 +75,16 @@
   ]
 }
 
+js_library("table_header.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/table/table_header.m.js" ]
+  deps = [
+    ":table_splitter.m",
+    "//ui/webui/resources/js:cr.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("table_list") {
   deps = [
     ":table_column_model",
@@ -43,6 +93,17 @@
   ]
 }
 
+js_library("table_list.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/table/table_list.m.js" ]
+  deps = [
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr/ui:list.m",
+    "//ui/webui/resources/js/cr/ui:list_item.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
 js_library("table_splitter") {
   deps = [
     ":table_column_model",
@@ -50,3 +111,27 @@
     "//ui/webui/resources/js/cr/ui:splitter",
   ]
 }
+
+js_library("table_splitter.m") {
+  sources = [ "$root_gen_dir/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.m.js" ]
+  deps = [
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js/cr:event_target.m",
+    "//ui/webui/resources/js/cr/ui:splitter.m",
+  ]
+
+  extra_deps = [ ":modulize" ]
+}
+
+js_modulizer("modulize") {
+  input_files = [
+    "table.js",
+    "table_column.js",
+    "table_column_model.js",
+    "table_header.js",
+    "table_list.js",
+    "table_splitter.js",
+  ]
+
+  namespace_rewrites = cr_namespace_rewrites + [ "cr.ui.table.Table|Table" ]
+}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table.js b/ui/file_manager/file_manager/foreground/js/ui/table/table.js
index 8f58514..49f3e8f 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table.js
@@ -6,12 +6,24 @@
  * @fileoverview This implements a table control.
  */
 
+// clang-format off
+// #import {getPropertyDescriptor, PropertyKind, dispatchSimpleEvent} from 'chrome://resources/js/cr.m.js';
+// #import {ArrayDataModel} from 'chrome://resources/js/cr/ui/array_data_model.m.js';
+// #import {List} from 'chrome://resources/js/cr/ui/list.m.js';
+// #import {ListItem} from 'chrome://resources/js/cr/ui/list_item.m.js';
+// #import {ListSelectionModel} from 'chrome://resources/js/cr/ui/list_selection_model.m.js';
+// #import {ListSingleSelectionModel} from 'chrome://resources/js/cr/ui/list_single_selection_model.m.js';
+// #import {TableColumnModel} from './table_column_model.m.js';
+// #import {TableList} from './table_list.m.js';
+// #import {TableHeader} from './table_header.m.js';
+// clang-format on
+
 cr.define('cr.ui', function() {
   /**
    * Creates a new table element.
    * @extends {HTMLDivElement}
    */
-  class Table {
+  /* #export */ class Table {
     constructor() {
       /** @private {cr.ui.table.TableColumnModel} */
       this.columnModel_;
@@ -421,8 +433,13 @@
    * because table contents can contain controls that can be focused, and for
    * some purposes (e.g., styling), the table can still be conceptually focused
    * at that point even though it doesn't actually have the page focus.
+   * @type {boolean}
    */
-  cr.defineProperty(Table, 'hasElementFocus', cr.PropertyKind.BOOL_ATTR);
+  Table.prototype.hasElementFocus;
+  Object.defineProperty(
+      Table.prototype, 'hasElementFocus',
+      cr.getPropertyDescriptor('hasElementFocus', cr.PropertyKind.BOOL_ATTR));
 
+  // #cr_define_end
   return {Table: Table};
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_column.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_column.js
index 6d23f0dc..32dd894 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_column.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_column.js
@@ -6,11 +6,16 @@
  * @fileoverview This is a table column representation
  */
 
+// clang-format off
+// #import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
+// #import {getPropertyDescriptor, dispatchPropertyChange} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 cr.define('cr.ui.table', function() {
   /**
    * A table column that wraps column ids and settings.
    */
-  class TableColumn extends cr.EventTarget {
+  /* #export */ class TableColumn extends cr.EventTarget {
     /**
      * @param {string} id
      * @param {string} name
@@ -76,6 +81,14 @@
       return this.visible_ ? this.width_ : 0;
     }
 
+    set width(value) {
+      const oldValue = this.width;
+      if (value !== oldValue) {
+        this.width_ = value;
+        cr.dispatchPropertyChange(this, 'width', value, oldValue);
+      }
+    }
+
     /**
      * The width of the column, disregarding visibility.  For hidden columns,
      * this would be the width of the column if it were to be made visible.
@@ -90,49 +103,61 @@
    * The column id.
    * @type {string}
    */
-  cr.defineProperty(TableColumn, 'id');
+  TableColumn.prototype.id;
+  Object.defineProperty(
+      TableColumn.prototype, 'id', cr.getPropertyDescriptor('id'));
 
   /**
    * The column name
    * @type {string}
    */
-  cr.defineProperty(TableColumn, 'name');
-
-  /**
-   * The column width.
-   * @type {number}
-   */
-  cr.defineProperty(TableColumn, 'width');
+  TableColumn.prototype.name;
+  Object.defineProperty(
+      TableColumn.prototype, 'name', cr.getPropertyDescriptor('name'));
 
   /**
    * The column visibility.
    * @type {boolean}
    */
-  cr.defineProperty(TableColumn, 'visible');
+  TableColumn.prototype.visible;
+  Object.defineProperty(
+      TableColumn.prototype, 'visible', cr.getPropertyDescriptor('visible'));
 
   /**
    * True if the column is aligned to end.
    * @type {boolean}
    */
-  cr.defineProperty(TableColumn, 'endAlign');
+  TableColumn.prototype.endAlign;
+  Object.defineProperty(
+      TableColumn.prototype, 'endAlign', cr.getPropertyDescriptor('endAlign'));
 
   /**
    * The column render function.
    * @type {function(*, string, Element): HTMLElement}
    */
-  cr.defineProperty(TableColumn, 'renderFunction');
+  TableColumn.prototype.renderFunction;
+  Object.defineProperty(
+      TableColumn.prototype, 'renderFunction',
+      cr.getPropertyDescriptor('renderFunction'));
 
   /**
    * The column header render function.
    * @type {function(Element):Node}
    */
-  cr.defineProperty(TableColumn, 'headerRenderFunction');
+  TableColumn.prototype.headerRenderFunction;
+  Object.defineProperty(
+      TableColumn.prototype, 'headerRenderFunction',
+      cr.getPropertyDescriptor('headerRenderFunction'));
 
   /**
    * Default sorting order for the column ('asc' or 'desc').
    * @type {string}
    */
-  cr.defineProperty(TableColumn, 'defaultOrder');
+  TableColumn.prototype.defaultOrder;
+  Object.defineProperty(
+      TableColumn.prototype, 'defaultOrder',
+      cr.getPropertyDescriptor('defaultOrder'));
 
+  // #cr_define_end
   return {TableColumn: TableColumn};
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_column_model.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_column_model.js
index 6d0f715..eac9eb3b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_column_model.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_column_model.js
@@ -5,6 +5,13 @@
 /**
  * @fileoverview This is a table column model
  */
+
+// clang-format off
+// #import {NativeEventTarget as EventTarget} from 'chrome://resources/js/cr/event_target.m.js';
+// #import {TableColumn} from './table_column.m.js';
+// #import {dispatchSimpleEvent} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 cr.define('cr.ui.table', function() {
   /** @type {number} */
   const MIMIMAL_WIDTH = 10;
@@ -13,7 +20,7 @@
    * A table column model that wraps table columns array
    * This implementation supports widths in percents.
    */
-  class TableColumnModel extends cr.EventTarget {
+  /* #export */ class TableColumnModel extends cr.EventTarget {
     /**
      * @param {!Array<cr.ui.table.TableColumn>} tableColumns Array of table
      *     columns.
@@ -229,5 +236,6 @@
     }
   }
 
+  // #cr_define_end
   return {TableColumnModel: TableColumnModel};
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_header.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_header.js
index dc4f7d97..1426f93 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_header.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_header.js
@@ -6,12 +6,16 @@
  * @fileoverview This implements a table header.
  */
 
+// #import {getPropertyDescriptor} from 'chrome://resources/js/cr.m.js';
+// #import {TableSplitter} from './table_splitter.m.js';
+// #import {Table} from './table.m.js';
+
 cr.define('cr.ui.table', function() {
   /**
    * Creates a new table header.
    * @extends {HTMLDivElement}
    */
-  class TableHeader {
+  /* #export */ class TableHeader {
     constructor() {
       /** @private {cr.ui.Table} */
       this.table_ = null;
@@ -261,7 +265,9 @@
    * The table associated with the header.
    * @type {Element}
    */
-  cr.defineProperty(TableHeader, 'table');
+  TableHeader.prototype.table;
+  Object.defineProperty(
+      TableHeader.prototype, 'table', cr.getPropertyDescriptor('table'));
 
   /**
    * Rectangular area around the splitters sensitive to touch events
@@ -269,5 +275,6 @@
    */
   TableHeader.TOUCH_DRAG_AREA_WIDTH = 30;
 
+  // #cr_define_end
   return {TableHeader: TableHeader};
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_list.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_list.js
index cc9835b..da92129 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_list.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_list.js
@@ -6,13 +6,18 @@
  * @fileoverview This extends cr.ui.List for use in the table.
  */
 
+// #import {List} from 'chrome://resources/js/cr/ui/list.m.js';
+// #import {ListItem} from 'chrome://resources/js/cr/ui/list_item.m.js';
+// #import {Table} from './table.m.js';
+// #import {getPropertyDescriptor} from 'chrome://resources/js/cr.m.js';
+
 cr.define('cr.ui.table', function() {
-  /** @const */ const List = cr.ui.List;
+  /* #ignore */ /** @const */ const List = cr.ui.List;
 
   /**
    * Creates a new table list element.
    */
-  class TableList extends cr.ui.List {
+  /* #export */ class TableList extends cr.ui.List {
     /**
      * @param {Object=} opt_propertyBag Optional properties.
      */
@@ -30,7 +35,10 @@
     }
 
     static decorate(el) {
-      cr.ui.List.decorate(el);
+      if (cr.ui.List.decorate) {
+        cr.ui.List.decorate(el);
+      }
+
       el.__proto__ = TableList.prototype;
       el.className = 'list';
     }
@@ -202,8 +210,11 @@
    * The table associated with the list.
    * @type {Element}
    */
-  cr.defineProperty(TableList, 'table');
+  TableList.prototype.table;
+  Object.defineProperty(
+      TableList.prototype, 'table', cr.getPropertyDescriptor('table'));
 
+  // #cr_define_end
   return {
     TableList: TableList,
   };
diff --git a/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js b/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
index 0062edb..52d0ac4 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/table/table_splitter.js
@@ -10,10 +10,16 @@
  * It is column model responsibility to resize other columns accordingly.
  */
 
+// clang-format off
+// #import {Splitter} from 'chrome://resources/js/cr/ui/splitter.m.js';
+// #import {Table} from './table.m.js';
+// #import {getPropertyDescriptor, dispatchSimpleEvent} from 'chrome://resources/js/cr.m.js';
+// clang-format on
+
 /**
  * Creates a new table splitter element.
  */
-class TableSplitter extends cr.ui.Splitter {
+/* #export */ class TableSplitter extends cr.ui.Splitter {
   /**
    * @param {Object=} opt_propertyBag Optional properties.
    */
@@ -44,14 +50,12 @@
   decorate() {
     super.decorate();
 
-    if (util.isFilesNg()) {
-      const icon = document.createElement('cr-icon-button');
-      icon.setAttribute('iron-icon', 'files32:small-dragger');
-      icon.setAttribute('tabindex', '-1');
-      icon.setAttribute('aria-hidden', 'true');
-      icon.classList.add('splitter-icon');
-      this.appendChild(icon);
-    }
+    const icon = document.createElement('cr-icon-button');
+    icon.setAttribute('iron-icon', 'files32:small-dragger');
+    icon.setAttribute('tabindex', '-1');
+    icon.setAttribute('aria-hidden', 'true');
+    icon.classList.add('splitter-icon');
+    this.appendChild(icon);
 
     this.classList.add('table-header-splitter');
   }
@@ -76,8 +80,10 @@
    * @override
    */
   handleSplitterDragMove(deltaX) {
-    this.table_.columnModel.setWidthAndKeepTotal(
-        this.columnIndex, this.columnWidth_ + deltaX, true);
+    if (this.table_.columnModel.setWidthAndKeepTotal) {
+      this.table_.columnModel.setWidthAndKeepTotal(
+          this.columnIndex, this.columnWidth_ + deltaX, true);
+    }
   }
 
   /**
@@ -95,10 +101,15 @@
  * The column index.
  * @type {number}
  */
-cr.defineProperty(TableSplitter, 'columnIndex');
+TableSplitter.prototype.columnIndex;
+Object.defineProperty(
+    TableSplitter.prototype, 'columnIndex',
+    cr.getPropertyDescriptor('columnIndex'));
 
 /**
  * The table associated with the splitter.
  * @type {Element}
  */
-cr.defineProperty(TableSplitter, 'table');
+TableSplitter.prototype.table;
+Object.defineProperty(
+    TableSplitter.prototype, 'table', cr.getPropertyDescriptor('table'));
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index 041d0f9..04e0c8d 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -241,6 +241,7 @@
                                                    bool is_activated) {
   // Store the old state to propagte state changes if Wayland decides to change
   // the state to something else.
+  PlatformWindowState old_state = state_;
   if (state_ == PlatformWindowState::kMinimized && !is_activated) {
     state_ = PlatformWindowState::kMinimized;
   } else if (is_fullscreen) {
@@ -253,6 +254,8 @@
 
   const bool is_normal = state_ == PlatformWindowState::kNormal;
 
+  const bool did_window_show_state_change = old_state != state_;
+
   // Update state before notifying delegate.
   const bool did_active_change = is_active_ != is_activated;
   is_active_ = is_activated;
@@ -288,8 +291,10 @@
   SetOrResetRestoredBounds();
   ApplyPendingBounds();
 
-  // Notify the delegate about the latest state set.
-  delegate()->OnWindowStateChanged(state_);
+  if (did_window_show_state_change) {
+    previous_state_ = old_state;
+    delegate()->OnWindowStateChanged(state_);
+  }
 
   if (did_active_change)
     delegate()->OnActivationChanged(is_active_);
@@ -361,6 +366,8 @@
     shell_toplevel_->UnSetMaximized();
   }
 
+  delegate()->OnWindowStateChanged(state_);
+
   connection()->ScheduleFlush();
 }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index 94e31f1..b98d6e5 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -476,12 +476,11 @@
   // Make sure the window is initialized to normal state from the beginning.
   EXPECT_EQ(PlatformWindowState::kNormal, window_->GetPlatformWindowState());
 
-  // The state must not be changed to the fullscreen before the surface is
-  // activated.
+  // The state gets changed to maximize and the delegate notified.
   auto* mock_surface = server_.GetObject<wl::MockSurface>(
       window->root_surface()->GetSurfaceId());
   EXPECT_FALSE(mock_surface->xdg_surface());
-  EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
+  EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1);
 
   window_->Maximize();
   // The state of the window must already be fullscreen one.
@@ -489,8 +488,9 @@
 
   Sync();
 
-  // Once the surface will be activated, the window state gets updated.
-  EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(1);
+  // Window show state should be already up to date, so delegate is not
+  // notified.
+  EXPECT_CALL(delegate_, OnWindowStateChanged(_)).Times(0);
   EXPECT_EQ(window_->GetPlatformWindowState(), PlatformWindowState::kMaximized);
 
   // Activate the surface.
@@ -529,6 +529,8 @@
   EXPECT_CALL(*xdg_surface_, SetWindowGeometry(0, 0, normal_bounds.width(),
                                                normal_bounds.height()));
 
+  Sync();
+
   // Now, set to fullscreen.
   AddStateToWlArray(XDG_TOPLEVEL_STATE_FULLSCREEN, states.get());
   SendConfigureEvent(xdg_surface_, 2005, 2005, 3, states.get());
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
index 420b91a..f435f1de 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone.cc
@@ -237,8 +237,10 @@
   int client_operation = ui::DragDropTypes::DRAG_NONE;
   std::unique_ptr<ui::DropTargetEvent> event =
       UpdateTargetAndCreateDropEvent(point, modifiers);
-  if (drag_drop_delegate_ && event)
-    client_operation = drag_drop_delegate_->OnDragUpdated(*event);
+  if (drag_drop_delegate_ && event) {
+    client_operation =
+        drag_drop_delegate_->OnDragUpdated(*event).drag_operation;
+  }
   return client_operation;
 }
 
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
index 9c6073c..7e78082 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_ozone_unittest.cc
@@ -10,8 +10,10 @@
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/platform_window/platform_window.h"
@@ -166,14 +168,18 @@
     last_event_flags_ = event.flags();
   }
 
-  int OnDragUpdated(const ui::DropTargetEvent& event) override {
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override {
     // The event must always have valid data.  This will crash if it doesn't.
     // See crbug.com/1151836.
     auto dummy_copy = event.data().provider().Clone();
 
     ++num_updates_;
     last_event_flags_ = event.flags();
-    return destination_operation_;
+
+    return aura::client::DragUpdateInfo(
+        destination_operation_,
+        ui::DataTransferEndpoint(ui::EndpointType::kDefault));
   }
 
   void OnDragExited() override { ++num_exits_; }
diff --git a/ui/views/widget/desktop_aura/desktop_drop_target_win.cc b/ui/views/widget/desktop_aura/desktop_drop_target_win.cc
index eac8953..1e27a8c 100644
--- a/ui/views/widget/desktop_aura/desktop_drop_target_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_drop_target_win.cc
@@ -77,7 +77,7 @@
   DragDropDelegate* delegate;
   Translate(data_object, key_state, position, effect, &data, &event, &delegate);
   if (delegate)
-    drag_operation = delegate->OnDragUpdated(*event);
+    drag_operation = delegate->OnDragUpdated(*event).drag_operation;
 
   return ui::DragDropTypes::DragOperationToDropEffect(drag_operation);
 }
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
index 01fd7f8..3dfe3fc8 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc
@@ -17,6 +17,7 @@
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/window_parenting_client.h"
 #include "ui/aura/env.h"
@@ -25,6 +26,7 @@
 #include "ui/aura/window_occlusion_tracker.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/class_property.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/hit_test.h"
 #include "ui/base/ime/input_method.h"
@@ -1227,11 +1229,15 @@
       event.data(), event.location(), event.source_operations());
 }
 
-int DesktopNativeWidgetAura::OnDragUpdated(const ui::DropTargetEvent& event) {
+aura::client::DragUpdateInfo DesktopNativeWidgetAura::OnDragUpdated(
+    const ui::DropTargetEvent& event) {
   DCHECK(drop_helper_.get() != nullptr);
   last_drop_operation_ = drop_helper_->OnDragOver(
       event.data(), event.location(), event.source_operations());
-  return last_drop_operation_;
+
+  return aura::client::DragUpdateInfo(
+      last_drop_operation_,
+      ui::DataTransferEndpoint(ui::EndpointType::kDefault));
 }
 
 void DesktopNativeWidgetAura::OnDragExited() {
diff --git a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
index 5ab952e..ca861a1 100644
--- a/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
+++ b/ui/views/widget/desktop_aura/desktop_native_widget_aura.h
@@ -245,7 +245,8 @@
 
   // ura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override;
diff --git a/ui/views/widget/native_widget_aura.cc b/ui/views/widget/native_widget_aura.cc
index b5d6fe4a..ee7e955 100644
--- a/ui/views/widget/native_widget_aura.cc
+++ b/ui/views/widget/native_widget_aura.cc
@@ -18,6 +18,7 @@
 #include "ui/aura/client/capture_client.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/drag_drop_client.h"
+#include "ui/aura/client/drag_drop_delegate.h"
 #include "ui/aura/client/focus_client.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/client/window_parenting_client.h"
@@ -28,6 +29,7 @@
 #include "ui/aura/window_observer.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/class_property.h"
+#include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/base/ui_base_types.h"
 #include "ui/compositor/layer.h"
@@ -1065,11 +1067,14 @@
       event.data(), event.location(), event.source_operations());
 }
 
-int NativeWidgetAura::OnDragUpdated(const ui::DropTargetEvent& event) {
+aura::client::DragUpdateInfo NativeWidgetAura::OnDragUpdated(
+    const ui::DropTargetEvent& event) {
   DCHECK(drop_helper_.get() != nullptr);
   last_drop_operation_ = drop_helper_->OnDragOver(
       event.data(), event.location(), event.source_operations());
-  return last_drop_operation_;
+  return aura::client::DragUpdateInfo(
+      last_drop_operation_,
+      ui::DataTransferEndpoint(ui::EndpointType::kDefault));
 }
 
 void NativeWidgetAura::OnDragExited() {
diff --git a/ui/views/widget/native_widget_aura.h b/ui/views/widget/native_widget_aura.h
index d70aa0a..e524189 100644
--- a/ui/views/widget/native_widget_aura.h
+++ b/ui/views/widget/native_widget_aura.h
@@ -210,7 +210,8 @@
 
   // aura::client::DragDropDelegate:
   void OnDragEntered(const ui::DropTargetEvent& event) override;
-  int OnDragUpdated(const ui::DropTargetEvent& event) override;
+  aura::client::DragUpdateInfo OnDragUpdated(
+      const ui::DropTargetEvent& event) override;
   void OnDragExited() override;
   int OnPerformDrop(const ui::DropTargetEvent& event,
                     std::unique_ptr<ui::OSExchangeData> data) override;