diff --git a/AUTHORS b/AUTHORS
index 31ddb044..9778c49 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -964,6 +964,7 @@
 Sergey Shekyan <shekyan@gmail.com>
 Sergey Talantov <sergey.talantov@gmail.com>
 Sergio Carlos Morales Angeles <carloschilazo@gmail.com>
+Sergio Garcia Murillo <sergio.garcia.murillo@gmail.com>
 Sergiy Belozorov <rryk.ua@gmail.com>
 Seshadri Mahalingam <seshadri.mahalingam@gmail.com>
 Seungkyu Lee <zx6658@gmail.com>
diff --git a/DEPS b/DEPS
index ab86b11..51640850 100644
--- a/DEPS
+++ b/DEPS
@@ -213,7 +213,7 @@
   # 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': '55e37b474d17543e9a8b6bcba71a1c53b2852da8',
+  'v8_revision': '9b373ece78ec52eeee18e248c4d76696ce6e335a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -221,7 +221,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'ab3b50bcdeef82814e45cc263b6ccb0e6179dbdf',
+  'angle_revision': 'adc9033be9174ca30e65ddd3f8986ddad60de60f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -280,7 +280,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '7318bc06728f464275b8605e5c0da3f45b56710c',
+  'catapult_revision': '1ae270e66814fe4d968ec699f21b75205e2d7a83',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,7 +288,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'c01495c05ce3b1117b39ab4da19bf226ad7dd6b5',
+  'devtools_frontend_revision': '6d08302be136ec5a68f6c7731644ad2c33e511e6',
   # 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.
@@ -328,7 +328,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': 'a7f1c50f498e8d0655061e1530984449b8eba5e1',
+  'dawn_revision': 'c63ac30826671e49173d18afdb9ae4b31bdaa6be',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -384,7 +384,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.
-  'libjxl_revision': 'e5ce94456581d43f8a52c8100c726a0d079f65e7',
+  'libjxl_revision': '9a8f5195e4d1c45112fd65f184ebe115f4163ba2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -730,7 +730,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'F-KyxlnPsilCF8hZJ4eXLycPHgTbV09Ch5FXUOHjzWMC',
+          'version': 'RjeRyfAqXQf6VfPsfovn5_088ot2hkVyL1FaGWMsxI0C',
       },
     ],
     'condition': 'checkout_android',
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6b022d1efb1559ab795a1c91014a2a6fb7efba88',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '3da91715d3b7447cc012b3e38901b9e9a54039eb',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1416,7 +1416,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'Q9wrtYCFy4whHc75FrdwzygrqI5DSmX_tuj8UJUcrckC'
+              'version': '42zBK32YxEB18xY1NCrwmj25vLkVlpXgaCil3aOhi6EC'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1552,7 +1552,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '239db71432f4e4fe1f6192a7d54717701ef84f66',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'da3dc149b20576a9bae09b0d7a4ecfe819829c1f',
+    Var('webrtc_git') + '/src.git' + '@' + 'c51ce06208e516ce80539a100a5d1f41ff360c75',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1613,7 +1613,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@4b648bf36750515bb4f2b451924d5535b8ab1e81',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@48aeba5a7e1ef4b213c51d909971831d3278d23a',
     'condition': 'checkout_src_internal',
   },
 
@@ -1621,7 +1621,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/eche_app/app',
-        'version': 'yY4G9__wVxlswvn7cNuQGTFi4WP3FitAGp1I_1AgGagC',
+        'version': '6mLyz6teriSC8d5wsJf-A5ZsxuzknycldIZl8mGcJBUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 76c87f5..159d798 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -327,30 +327,6 @@
     ),
 )
 
-# Directories that contain deprecated Bind() or Callback types.
-# Find sub-directories from a given directory by running:
-# for i in `find . -maxdepth 1 -type d|sort`; do
-#   echo "-- $i"
-#   (cd $i; git grep -nP \
-#     'base::(Bind\(|(Cancelable)?(Callback<|Closure))'|wc -l)
-# done
-#
-# TODO(crbug.com/714018): Remove (or narrow the scope of) paths from this list
-# when they have been converted to modern callback types (OnceCallback,
-# RepeatingCallback, BindOnce, BindRepeating) in order to enable presubmit
-# checks for them and prevent regressions.
-_NOT_CONVERTED_TO_MODERN_BIND_AND_CALLBACK = '|'.join((
-  '^base/callback.h',  # Intentional.
-  '^base/cancelable_callback.h',  # Intentional.
-  "^docs/callback\\.md",  # Intentional
-  "^docs/process/lsc/large_scale_changes\\.md",  # Intentional
-  '^extensions/browser/api/webcam_private',
-  '^third_party/blink/PRESUBMIT_test.py', # Intentional.
-  '^third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py' # Intentional pylint: disable=line-too-long
-  '^tools/clang/base_bind_rewriters/',  # Intentional.
-  '^tools/gdb/gdb_chrome.py',  # Intentional.
-))
-
 # Format: Sequence of tuples containing:
 # * String pattern or, if starting with a slash, a regular expression.
 # * Sequence of strings to show when the pattern matches.
@@ -763,51 +739,6 @@
       (),
     ),
     (
-      r'/\bbase::Bind\(',
-      (
-          'Please use base::Bind{Once,Repeating} instead',
-          'of base::Bind. (crbug.com/714018)',
-      ),
-      False,
-      (_NOT_CONVERTED_TO_MODERN_BIND_AND_CALLBACK,),
-    ),
-    (
-      r'/\bbase::Callback[<:]',
-      (
-          'Please use base::{Once,Repeating}Callback instead',
-          'of base::Callback. (crbug.com/714018)',
-      ),
-      False,
-      (_NOT_CONVERTED_TO_MODERN_BIND_AND_CALLBACK,),
-    ),
-    (
-      r'/\bbase::Closure\b',
-      (
-          'Please use base::{Once,Repeating}Closure instead',
-          'of base::Closure. (crbug.com/714018)',
-      ),
-      False,
-      (_NOT_CONVERTED_TO_MODERN_BIND_AND_CALLBACK,),
-    ),
-    (
-      r'/\bbase::CancelableCallback[<:]',
-      (
-          'Please use base::Cancelable{Once,Repeating}Callback instead',
-          'of base::CancelableCallback. (crbug.com/714018)',
-      ),
-      False,
-      (_NOT_CONVERTED_TO_MODERN_BIND_AND_CALLBACK,),
-    ),
-    (
-      r'/\bbase::CancelableClosure\b',
-      (
-          'Please use base::Cancelable{Once,Repeating}Closure instead',
-          'of base::CancelableClosure. (crbug.com/714018)',
-      ),
-      False,
-      (_NOT_CONVERTED_TO_MODERN_BIND_AND_CALLBACK,),
-    ),
-    (
       r'/\bRunMessageLoop\b',
       (
           'RunMessageLoop is deprecated, use RunLoop instead.',
diff --git a/android_webview/OWNERS b/android_webview/OWNERS
index 324db12..6956a3e 100644
--- a/android_webview/OWNERS
+++ b/android_webview/OWNERS
@@ -3,6 +3,7 @@
 michaelbai@chromium.org
 ntfschr@chromium.org
 torne@chromium.org
+nator@chromium.org
 
 # Documentation changes:
 per-file *.md=file://android_webview/docs/OWNERS
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 717993e..54ad96e8 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -63,7 +63,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/autofill/android/provider/autofill_provider_android.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
-#include "components/autofill/core/browser/android_autofill_manager.h"
+#include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/navigation_interception/intercept_navigation_delegate.h"
@@ -327,8 +327,7 @@
                    is_download_manager_disabled_for_testing())
           ? autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER
           : autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
-      base::BindRepeating(&autofill::AndroidAutofillManager::Create,
-                          autofill_provider_.get()));
+      autofill_provider_.get());
 }
 
 void AwContents::SetAwAutofillClient(const JavaRef<jobject>& client) {
diff --git a/android_webview/docs/test-instructions.md b/android_webview/docs/test-instructions.md
index f6b06c2..d2d595e 100644
--- a/android_webview/docs/test-instructions.md
+++ b/android_webview/docs/test-instructions.md
@@ -87,19 +87,29 @@
 
 ### Layout tests and page cycler tests
 
-WebView's layout tests and page cycler tests exercise the WebView installed on
-the system, instrumenting the WebView shell (`system_webview_shell_apk`,
-`org.chromium.webview_shell`). These test cases are defined in
+WebView's layout tests and page cycler tests exercise the **WebView installed on
+the system** and instrument the [system WebView shell app](webview-shell.md)
+(`system_webview_shell_apk`). These test cases are defined in
 `//android_webview/tools/system_webview_shell/`.
 
+*** note
+**Important:** because these tests run against the WebView installed on the
+system, both these test targets automatically compile and install
+`system_webview_apk` and switch the WebView provider. This means you need to
+configure this target to be compatible with your system by following the
+[full build instructions](build-instructions.md).
+
+**Note:** we do not currently support running these tests on the emulator due to
+signing key mismatches with the preinstalled WebView shell
+(https://crbug.com/1205665 tracks supporting this).
+***
+
 ```sh
-# Build
+# Build (this also compiles system_webview_shell_apk and system_webview_apk)
 $ autoninja -C out/Default system_webview_shell_layout_test_apk
 
-# Install the desired WebView APK
-...
-
-# Run layout tests (installs WebView shell):
+# Run layout tests (installs the test APK, WebView shell, and
+# system_webview_apk, and also switches your WebView provider)
 $ out/Default/bin/run_system_webview_shell_layout_test_apk
 
 # Print both Java and C++ log messages to the console (optional):
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 67bb0b49..fa58ffe 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -12,7 +12,6 @@
 #include "ash/capture_mode/capture_mode_util.h"
 #include "ash/capture_mode/video_recording_watcher.h"
 #include "ash/display/window_tree_host_manager.h"
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/capture_mode_delegate.h"
 #include "ash/public/cpp/holding_space/holding_space_client.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
@@ -929,11 +928,9 @@
   CopyImageToClipboard(image);
   ShowPreviewNotification(path, image, CaptureModeType::kImage);
 
-  if (features::IsTemporaryHoldingSpaceEnabled()) {
-    HoldingSpaceClient* client = HoldingSpaceController::Get()->client();
-    if (client)  // May be `nullptr` in tests.
-      client->AddScreenshot(path);
-  }
+  HoldingSpaceClient* client = HoldingSpaceController::Get()->client();
+  if (client)  // May be `nullptr` in tests.
+    client->AddScreenshot(path);
 }
 
 void CaptureModeController::OnVideoFileStatus(bool success) {
@@ -960,11 +957,9 @@
     RecordCaptureModeRecordTime(
         (base::TimeTicks::Now() - recording_start_time_).InSeconds());
 
-    if (features::IsTemporaryHoldingSpaceEnabled()) {
-      HoldingSpaceClient* client = HoldingSpaceController::Get()->client();
-      if (client)  // May be `nullptr` in tests.
-        client->AddScreenRecording(current_video_file_path_);
-    }
+    HoldingSpaceClient* client = HoldingSpaceController::Get()->client();
+    if (client)  // May be `nullptr` in tests.
+      client->AddScreenRecording(current_video_file_path_);
   }
 
   if (!on_file_saved_callback_.is_null())
diff --git a/ash/login/ui/OWNERS b/ash/login/ui/OWNERS
index 5634885..2d6ea2a 100644
--- a/ash/login/ui/OWNERS
+++ b/ash/login/ui/OWNERS
@@ -3,6 +3,8 @@
 
 per-file lock_screen.*=set noparent
 per-file lock_screen.*=file://ash/login/LOGIN_LOCK_OWNERS
+per-file lock_screen.*=tellier@google.com
 
 per-file lock_contents_view.*=set noparent
 per-file lock_contents_view.*=file://ash/login/LOGIN_LOCK_OWNERS
+per-file lock_contents_view.*=tellier@google.com
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 7046d443..a2ff930b 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -126,11 +126,8 @@
 const base::Feature kNotificationsInContextMenu{
     "NotificationsInContextMenu", base::FEATURE_DISABLED_BY_DEFAULT};
 
-const base::Feature kTemporaryHoldingSpace{"TemporaryHoldingSpace",
-                                           base::FEATURE_ENABLED_BY_DEFAULT};
-
-const base::Feature kTemporaryHoldingSpaceArcIntegration{
-    "TemporaryHoldingSpaceArcIntegration", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kHoldingSpaceArcIntegration{
+    "HoldingSpaceArcIntegration", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kDragUnpinnedAppToPin{"DragUnpinnedAppToPin",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
@@ -298,13 +295,8 @@
   return base::FeatureList::IsEnabled(kNotificationsInContextMenu);
 }
 
-bool IsTemporaryHoldingSpaceEnabled() {
-  return base::FeatureList::IsEnabled(kTemporaryHoldingSpace);
-}
-
-bool IsTemporaryHoldingSpaceArcIntegrationEnabled() {
-  return IsTemporaryHoldingSpaceEnabled() &&
-         base::FeatureList::IsEnabled(kTemporaryHoldingSpaceArcIntegration);
+bool IsHoldingSpaceArcIntegrationEnabled() {
+  return base::FeatureList::IsEnabled(kHoldingSpaceArcIntegration);
 }
 
 bool IsDragUnpinnedAppToPinEnabled() {
diff --git a/ash/public/cpp/ash_features.h b/ash/public/cpp/ash_features.h
index 2d54ef5..401b669 100644
--- a/ash/public/cpp/ash_features.h
+++ b/ash/public/cpp/ash_features.h
@@ -172,15 +172,10 @@
 // Enables notifications to be shown within context menus.
 ASH_PUBLIC_EXPORT extern const base::Feature kNotificationsInContextMenu;
 
-// Enables the productivity feature that aims to reduce context switching by
-// enabling users to collect content and transfer or access it later.
-ASH_PUBLIC_EXPORT extern const base::Feature kTemporaryHoldingSpace;
-
 // Enables ARC integration with the productivity feature that aims to reduce
 // context switching by enabling users to collect content and transfer or access
-// it later. Note that this flag has no effect w/o `kTemporaryHoldingSpace`.
-ASH_PUBLIC_EXPORT extern const base::Feature
-    kTemporaryHoldingSpaceArcIntegration;
+// it later.
+ASH_PUBLIC_EXPORT extern const base::Feature kHoldingSpaceArcIntegration;
 
 // Enables dragging an unpinned open app to pinned app side to pin.
 ASH_PUBLIC_EXPORT extern const base::Feature kDragUnpinnedAppToPin;
@@ -261,9 +256,7 @@
 
 ASH_PUBLIC_EXPORT bool IsNotificationsInContextMenuEnabled();
 
-ASH_PUBLIC_EXPORT bool IsTemporaryHoldingSpaceEnabled();
-
-ASH_PUBLIC_EXPORT bool IsTemporaryHoldingSpaceArcIntegrationEnabled();
+ASH_PUBLIC_EXPORT bool IsHoldingSpaceArcIntegrationEnabled();
 
 ASH_PUBLIC_EXPORT bool IsDragUnpinnedAppToPinEnabled();
 
diff --git a/ash/shell.cc b/ash/shell.cc
index 0390850..56e6206 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -1054,8 +1054,7 @@
   }
 
   // `HoldingSpaceController` must be instantiated before the shelf.
-  if (features::IsTemporaryHoldingSpaceEnabled())
-    holding_space_controller_ = std::make_unique<HoldingSpaceController>();
+  holding_space_controller_ = std::make_unique<HoldingSpaceController>();
 
   shelf_config_ = std::make_unique<ShelfConfig>();
   shelf_controller_ = std::make_unique<ShelfController>();
diff --git a/ash/system/holding_space/holding_space_tray_unittest.cc b/ash/system/holding_space/holding_space_tray_unittest.cc
index 62329979..ea23dbc 100644
--- a/ash/system/holding_space/holding_space_tray_unittest.cc
+++ b/ash/system/holding_space/holding_space_tray_unittest.cc
@@ -8,7 +8,6 @@
 #include <deque>
 #include <vector>
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_client.h"
 #include "ash/public/cpp/holding_space/holding_space_constants.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
@@ -35,7 +34,6 @@
 #include "base/strings/strcat.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
@@ -272,9 +270,7 @@
 
 class HoldingSpaceTrayTest : public AshTestBase {
  public:
-  HoldingSpaceTrayTest() {
-    scoped_feature_list_.InitAndEnableFeature(features::kTemporaryHoldingSpace);
-  }
+  HoldingSpaceTrayTest() = default;
 
   // AshTestBase:
   void SetUp() override {
@@ -413,7 +409,6 @@
   std::unique_ptr<HoldingSpaceTestApi> test_api_;
   testing::NiceMock<MockHoldingSpaceClient> holding_space_client_;
   HoldingSpaceModel holding_space_model_;
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Tests -----------------------------------------------------------------------
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 21508dc..ca5b54b 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -102,10 +102,8 @@
       std::make_unique<StatusAreaOverflowButtonTray>(shelf_);
   AddTrayButton(overflow_button_tray_.get());
 
-  if (features::IsTemporaryHoldingSpaceEnabled()) {
-    holding_space_tray_ = std::make_unique<HoldingSpaceTray>(shelf_);
-    AddTrayButton(holding_space_tray_.get());
-  }
+  holding_space_tray_ = std::make_unique<HoldingSpaceTray>(shelf_);
+  AddTrayButton(holding_space_tray_.get());
 
   logout_button_tray_ = std::make_unique<LogoutButtonTray>(shelf_);
   AddTrayButton(logout_button_tray_.get());
diff --git a/base/allocator/partition_allocator/partition_alloc_perftest.cc b/base/allocator/partition_allocator/partition_alloc_perftest.cc
index 2c0bb0ab..0e51fb57 100644
--- a/base/allocator/partition_allocator/partition_alloc_perftest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_perftest.cc
@@ -282,6 +282,20 @@
   return timer.LapsPerSecond() * kMultiBucketRounds;
 }
 
+float DirectMapped(Allocator* allocator) {
+  constexpr size_t kSize = 2 * 1000 * 1000;
+
+  LapTimer timer(kWarmupRuns, kTimeLimit, kTimeCheckInterval);
+  do {
+    void* cur = allocator->Alloc(kSize);
+    CHECK_NE(cur, nullptr);
+    allocator->Free(cur);
+    timer.NextLap();
+  } while (!timer.HasTimeLimitExpired());
+
+  return timer.LapsPerSecond();
+}
+
 std::unique_ptr<Allocator> CreateAllocator(AllocatorType type) {
   switch (type) {
     case AllocatorType::kSystem:
@@ -305,9 +319,16 @@
 void RunTest(int thread_count,
              AllocatorType alloc_type,
              float (*test_fn)(Allocator*),
+             float (*noisy_neighbor_fn)(Allocator*),
              const char* story_base_name) {
   auto alloc = CreateAllocator(alloc_type);
 
+  std::unique_ptr<TestLoopThread> noisy_neighbor_thread = nullptr;
+  if (noisy_neighbor_fn) {
+    noisy_neighbor_thread = std::make_unique<TestLoopThread>(
+        BindOnce(noisy_neighbor_fn, Unretained(alloc.get())));
+  }
+
   std::vector<std::unique_ptr<TestLoopThread>> threads;
   for (int i = 0; i < thread_count; ++i) {
     threads.push_back(std::make_unique<TestLoopThread>(
@@ -322,6 +343,9 @@
     total_laps_per_second += laps_per_second;
   }
 
+  if (noisy_neighbor_thread)
+    noisy_neighbor_thread->Run();
+
   char const* alloc_type_str;
   switch (alloc_type) {
     case AllocatorType::kSystem:
@@ -368,7 +392,7 @@
 #if !defined(MEMORY_CONSTRAINED)
 TEST_P(MemoryAllocationPerfTest, SingleBucket) {
   auto params = GetParam();
-  RunTest(std::get<0>(params), std::get<1>(params), SingleBucket,
+  RunTest(std::get<0>(params), std::get<1>(params), SingleBucket, nullptr,
           "SingleBucket");
 }
 #endif  // defined(MEMORY_CONSTRAINED)
@@ -376,22 +400,37 @@
 TEST_P(MemoryAllocationPerfTest, SingleBucketWithFree) {
   auto params = GetParam();
   RunTest(std::get<0>(params), std::get<1>(params), SingleBucketWithFree,
-          "SingleBucketWithFree");
+          nullptr, "SingleBucketWithFree");
 }
 
 #if !defined(MEMORY_CONSTRAINED)
 TEST_P(MemoryAllocationPerfTest, MultiBucket) {
   auto params = GetParam();
-  RunTest(std::get<0>(params), std::get<1>(params), MultiBucket, "MultiBucket");
+  RunTest(std::get<0>(params), std::get<1>(params), MultiBucket, nullptr,
+          "MultiBucket");
 }
 #endif  // defined(MEMORY_CONSTRAINED)
 
 TEST_P(MemoryAllocationPerfTest, MultiBucketWithFree) {
   auto params = GetParam();
   RunTest(std::get<0>(params), std::get<1>(params), MultiBucketWithFree,
-          "MultiBucketWithFree");
+          nullptr, "MultiBucketWithFree");
 }
 
+TEST_P(MemoryAllocationPerfTest, DirectMapped) {
+  auto params = GetParam();
+  RunTest(std::get<0>(params), std::get<1>(params), DirectMapped, nullptr,
+          "DirectMapped");
+}
+
+#if !defined(MEMORY_CONSTRAINED)
+TEST_P(MemoryAllocationPerfTest, MultiBucketWithNoisyNeighbor) {
+  auto params = GetParam();
+  RunTest(std::get<0>(params), std::get<1>(params), MultiBucket, DirectMapped,
+          "MultiBucketWithNoisyNeighbor");
+}
+#endif  // !defined(MEMORY_CONSTRAINED)
+
 }  // namespace
 
 }  // namespace base
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index 7b259f6..d796c0e3 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -27,10 +27,15 @@
 namespace {
 
 template <bool thread_safe>
-SlotSpanMetadata<thread_safe>*
-PartitionDirectMap(PartitionRoot<thread_safe>* root, int flags, size_t raw_size)
-    EXCLUSIVE_LOCKS_REQUIRED(root->lock_) {
-  bool return_null = flags & PartitionAllocReturnNull;
+SlotSpanMetadata<thread_safe>* PartitionDirectMap(
+    PartitionRoot<thread_safe>* root,
+    int flags,
+    size_t raw_size) {
+  // No static EXCLUSIVE_LOCKS_REQUIRED(), as the checker doesn't understand
+  // scoped unlocking.
+  root->lock_.AssertAcquired();
+
+  const bool return_null = flags & PartitionAllocReturnNull;
   if (UNLIKELY(raw_size > MaxDirectMapped())) {
     if (return_null)
       return nullptr;
@@ -60,110 +65,134 @@
     IMMEDIATE_CRASH();  // Not required, kept as documentation.
   }
 
-  size_t slot_size = PartitionRoot<thread_safe>::GetDirectMapSlotSize(raw_size);
-  size_t reserved_size = root->GetDirectMapReservedSize(raw_size);
-  size_t map_size =
-      reserved_size -
-      PartitionRoot<thread_safe>::GetDirectMapMetadataAndGuardPagesSize();
-  PA_DCHECK(slot_size <= map_size);
+  PartitionDirectMapExtent<thread_safe>* map_extent = nullptr;
+  PartitionPage<thread_safe>* page = nullptr;
 
-  char* ptr = nullptr;
-  // Allocate from GigaCage, if enabled. In this case, use non-BRP pool, because
-  // BackupRefPtr isn't supported in direct maps.
-  bool with_giga_cage = features::IsPartitionAllocGigaCageEnabled();
-  if (with_giga_cage) {
-    ptr = internal::AddressPoolManager::GetInstance()->Reserve(
-        GetNonBRPPool(), nullptr, reserved_size);
-  } else {
-    ptr = reinterpret_cast<char*>(
-        AllocPages(nullptr, reserved_size, kSuperPageAlignment,
-                   PageInaccessible, PageTag::kPartitionAlloc));
-  }
-  if (UNLIKELY(!ptr)) {
-    if (return_null)
-      return nullptr;
-
-    // Crash handling is split on purpose in this function:
-    // - Crashing here likely means that Chrome is out of address space (on 32
-    //   bit platforms), or out of GigaCage space (on 64 bit ones).
-    // - Crashing below would likely mean out of commit charge.
+  {
+    // Getting memory for direct-mapped allocations doesn't interact with the
+    // rest of the allocator, but takes a long time, as it involves several
+    // system calls. With GigaCage, no mmap() (or equivalent) call is made on 64
+    // bit systems, but page permissions are changed with mprotect(), which is a
+    // syscall.
     //
-    // See comment above regarding unlocking,
-    ScopedUnlockGuard<thread_safe> unlock{root->lock_};
-    root->OutOfMemory(raw_size);
-    IMMEDIATE_CRASH();  // Not required, kept as documentation.
-  }
+    // These calls are almost always slow (at least a couple us per syscall on a
+    // desktop Linux machine), and they also have a very long latency tail,
+    // possibly from getting descheduled. As a consequence, we should not hold
+    // the lock when performing a syscall. This is not the only problematic
+    // location, but since this one doesn't interact with the rest of the
+    // allocator, we can safely drop and then re-acquire the lock.
+    //
+    // Note that this only affects allocations that are not served out of the
+    // thread cache, but as a simple example the buffer partition in blink is
+    // frequently used for large allocations (e.g. ArrayBuffer), and frequent,
+    // small ones (e.g. WTF::String), and does not have a thread cache.
+    ScopedUnlockGuard<thread_safe> scoped_unlock{root->lock_};
 
-  root->total_size_of_direct_mapped_pages.fetch_add(reserved_size,
-                                                    std::memory_order_relaxed);
+    const size_t slot_size =
+        PartitionRoot<thread_safe>::GetDirectMapSlotSize(raw_size);
+    const size_t reserved_size =
+        PartitionRoot<thread_safe>::GetDirectMapReservedSize(raw_size);
+    const size_t map_size =
+        reserved_size -
+        PartitionRoot<thread_safe>::GetDirectMapMetadataAndGuardPagesSize();
+    PA_DCHECK(slot_size <= map_size);
 
-  char* slot = ptr + PartitionPageSize();
-  RecommitSystemPages(ptr + SystemPageSize(), SystemPageSize(), PageReadWrite,
-                      PageUpdatePermissions);
-  // It is typically possible to map a large range of inaccessible pages, and
-  // this is leveraged in multiple places, including the GigaCage. However, this
-  // doesn't mean that we can commit all this memory.  For the vast majority of
-  // allocations, this just means that we crash in a slightly different places,
-  // but for callers ready to handle failures, we have to return nullptr.
-  // See crbug.com/1187404.
-  //
-  // Note that we didn't check above, because if we cannot even commit a single
-  // page, then this is likely hopeless anyway, and we will crash very soon.
-  bool ok = root->TryRecommitSystemPagesForData(slot, slot_size,
-                                                PageUpdatePermissions);
-  if (!ok) {
+    char* ptr = nullptr;
+    // Allocate from GigaCage, if enabled. In this case, use non-BRP pool,
+    // because BackupRefPtr isn't supported in direct maps.
+    const bool with_giga_cage = features::IsPartitionAllocGigaCageEnabled();
     if (with_giga_cage) {
-      internal::AddressPoolManager::GetInstance()->UnreserveAndDecommit(
-          GetNonBRPPool(), ptr, reserved_size);
+      ptr = internal::AddressPoolManager::GetInstance()->Reserve(
+          GetNonBRPPool(), nullptr, reserved_size);
     } else {
-      FreePages(ptr, reserved_size);
+      ptr = reinterpret_cast<char*>(
+          AllocPages(nullptr, reserved_size, kSuperPageAlignment,
+                     PageInaccessible, PageTag::kPartitionAlloc));
+    }
+    if (UNLIKELY(!ptr)) {
+      if (return_null)
+        return nullptr;
+
+      // Crash handling is split on purpose in this function:
+      // - Crashing here likely means that Chrome is out of address space (on 32
+      //   bit platforms), or out of GigaCage space (on 64 bit ones).
+      // - Crashing below would likely mean out of commit charge.
+      root->OutOfMemory(raw_size);
+      IMMEDIATE_CRASH();  // Not required, kept as documentation.
     }
 
-    if (return_null)
-      return nullptr;
+    root->total_size_of_direct_mapped_pages.fetch_add(
+        reserved_size, std::memory_order_relaxed);
 
-    // See comment above.
-    ScopedUnlockGuard<thread_safe> unlock{root->lock_};
-    root->OutOfMemory(raw_size);
-    IMMEDIATE_CRASH();  // Not required, kept as documentation.
+    char* const slot = ptr + PartitionPageSize();
+    RecommitSystemPages(ptr + SystemPageSize(), SystemPageSize(), PageReadWrite,
+                        PageUpdatePermissions);
+    // It is typically possible to map a large range of inaccessible pages, and
+    // this is leveraged in multiple places, including the GigaCage. However,
+    // this doesn't mean that we can commit all this memory.  For the vast
+    // majority of allocations, this just means that we crash in a slightly
+    // different place, but for callers ready to handle failures, we have to
+    // return nullptr. See crbug.com/1187404.
+    //
+    // Note that we didn't check above, because if we cannot even commit a
+    // single page, then this is likely hopeless anyway, and we will crash very
+    // soon.
+    const bool ok = root->TryRecommitSystemPagesForData(slot, slot_size,
+                                                        PageUpdatePermissions);
+    if (!ok) {
+      if (!return_null) {
+        root->OutOfMemory(raw_size);
+        IMMEDIATE_CRASH();  // Not required, kept as documentation.
+      }
+
+      if (with_giga_cage) {
+        internal::AddressPoolManager::GetInstance()->UnreserveAndDecommit(
+            GetNonBRPPool(), ptr, reserved_size);
+      } else {
+        FreePages(ptr, reserved_size);
+      }
+      return nullptr;
+    }
+
+    auto* metadata = reinterpret_cast<PartitionDirectMapMetadata<thread_safe>*>(
+        PartitionSuperPageToMetadataArea(ptr));
+    metadata->extent.root = root;
+    // The new structures are all located inside a fresh system page so they
+    // will all be zeroed out. These DCHECKs are for documentation and to assert
+    // our expectations of the kernel.
+    PA_DCHECK(!metadata->extent.super_page_base);
+    PA_DCHECK(!metadata->extent.super_pages_end);
+    PA_DCHECK(!metadata->extent.next);
+    // Call FromSlotInnerPtr instead of FromSlotStartPtr, because the bucket
+    // isn't set up yet to properly assert the slot start.
+    PA_DCHECK(PartitionPage<thread_safe>::FromSlotInnerPtr(slot) ==
+              &metadata->page);
+
+    page = &metadata->page;
+    PA_DCHECK(!page->slot_span_metadata_offset);
+    PA_DCHECK(!page->slot_span_metadata.next_slot_span);
+    PA_DCHECK(!page->slot_span_metadata.num_allocated_slots);
+    PA_DCHECK(!page->slot_span_metadata.num_unprovisioned_slots);
+    PA_DCHECK(!page->slot_span_metadata.empty_cache_index);
+
+    PA_DCHECK(!metadata->bucket.active_slot_spans_head);
+    PA_DCHECK(!metadata->bucket.empty_slot_spans_head);
+    PA_DCHECK(!metadata->bucket.decommitted_slot_spans_head);
+    PA_DCHECK(!metadata->bucket.num_system_pages_per_slot_span);
+    PA_DCHECK(!metadata->bucket.num_full_slot_spans);
+    metadata->bucket.slot_size = slot_size;
+
+    new (&page->slot_span_metadata)
+        SlotSpanMetadata<thread_safe>(&metadata->bucket);
+    auto* next_entry = new (slot) PartitionFreelistEntry();
+    page->slot_span_metadata.SetFreelistHead(next_entry);
+
+    map_extent = &metadata->direct_map_extent;
+    map_extent->map_size = map_size;
+    map_extent->bucket = &metadata->bucket;
   }
 
-  auto* metadata = reinterpret_cast<PartitionDirectMapMetadata<thread_safe>*>(
-      PartitionSuperPageToMetadataArea(ptr));
-  metadata->extent.root = root;
-  // The new structures are all located inside a fresh system page so they
-  // will all be zeroed out. These DCHECKs are for documentation.
-  PA_DCHECK(!metadata->extent.super_page_base);
-  PA_DCHECK(!metadata->extent.super_pages_end);
-  PA_DCHECK(!metadata->extent.next);
-  // Call FromSlotInnerPtr instead of FromSlotStartPtr, because the bucket isn't
-  // set up yet to properly assert the slot start.
-  PA_DCHECK(PartitionPage<thread_safe>::FromSlotInnerPtr(slot) ==
-            &metadata->page);
-
-  auto* page = &metadata->page;
-  PA_DCHECK(!page->slot_span_metadata_offset);
-  PA_DCHECK(!page->slot_span_metadata.next_slot_span);
-  PA_DCHECK(!page->slot_span_metadata.num_allocated_slots);
-  PA_DCHECK(!page->slot_span_metadata.num_unprovisioned_slots);
-  PA_DCHECK(!page->slot_span_metadata.empty_cache_index);
-
-  PA_DCHECK(!metadata->bucket.active_slot_spans_head);
-  PA_DCHECK(!metadata->bucket.empty_slot_spans_head);
-  PA_DCHECK(!metadata->bucket.decommitted_slot_spans_head);
-  PA_DCHECK(!metadata->bucket.num_system_pages_per_slot_span);
-  PA_DCHECK(!metadata->bucket.num_full_slot_spans);
-  metadata->bucket.slot_size = slot_size;
-
-  new (&page->slot_span_metadata)
-      SlotSpanMetadata<thread_safe>(&metadata->bucket);
-  auto* next_entry = new (slot) PartitionFreelistEntry();
-  page->slot_span_metadata.SetFreelistHead(next_entry);
-
-  auto* map_extent = &metadata->direct_map_extent;
-  map_extent->map_size = map_size;
-  map_extent->bucket = &metadata->bucket;
-
+  root->lock_.AssertAcquired();
   // Maintain the doubly-linked list of all direct mappings.
   map_extent->next_extent = root->direct_map_list;
   if (map_extent->next_extent)
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index 582e072..3fc1425 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -233,7 +233,7 @@
   char* next_partition_page_end = nullptr;
   SuperPageExtentEntry* current_extent = nullptr;
   SuperPageExtentEntry* first_extent = nullptr;
-  DirectMapExtent* direct_map_list = nullptr;
+  DirectMapExtent* direct_map_list GUARDED_BY(lock_) = nullptr;
   SlotSpan* global_empty_slot_span_ring[kMaxFreeableSpans] = {};
   int16_t global_empty_slot_span_ring_index = 0;
 
@@ -279,8 +279,7 @@
   ALWAYS_INLINE bool TryRecommitSystemPagesForData(
       void* address,
       size_t length,
-      PageAccessibilityDisposition accessibility_disposition)
-      EXCLUSIVE_LOCKS_REQUIRED(lock_);
+      PageAccessibilityDisposition accessibility_disposition);
 
   [[noreturn]] NOINLINE void OutOfMemory(size_t size);
 
@@ -401,7 +400,7 @@
     return bits::AlignUp(raw_size, SystemPageSize());
   }
 
-  ALWAYS_INLINE size_t GetDirectMapReservedSize(size_t raw_size) {
+  static ALWAYS_INLINE size_t GetDirectMapReservedSize(size_t raw_size) {
     // Caller must check that the size is not above the MaxDirectMapped()
     // limit before calling. This also guards against integer overflow in the
     // calculation here.
diff --git a/base/bind.h b/base/bind.h
index 359e3533..c9a5ea8f 100644
--- a/base/bind.h
+++ b/base/bind.h
@@ -45,9 +45,6 @@
 //   auto cb = base::BindOnce(&C::F, instance);
 //   std::move(cb).Run();  // Identical to instance->F()
 //
-// base::Bind is currently a type alias for base::BindRepeating(). In the
-// future, we expect to flip this to default to base::BindOnce().
-//
 // See //docs/callback.md for the full documentation.
 //
 // -----------------------------------------------------------------------------
@@ -91,17 +88,6 @@
                                                std::forward<Args>(args)...);
 }
 
-// Unannotated Bind.
-// TODO(tzik): Deprecate this and migrate to OnceCallback and
-// RepeatingCallback, once they get ready.
-template <typename Functor, typename... Args>
-inline Callback<internal::MakeUnboundRunType<Functor, Args...>> Bind(
-    Functor&& functor,
-    Args&&... args) {
-  return base::BindRepeating(std::forward<Functor>(functor),
-                             std::forward<Args>(args)...);
-}
-
 // Special cases for binding to a base::Callback without extra bound arguments.
 // We CHECK() the validity of callback to guard against null pointers
 // accidentally ending up in posted tasks, causing hard-to-debug crashes.
@@ -124,12 +110,6 @@
   return callback;
 }
 
-template <typename Signature>
-Callback<Signature> Bind(Callback<Signature> callback) {
-  CHECK(callback);
-  return callback;
-}
-
 // Unretained() allows binding a non-refcounted class, and to disable
 // refcounting on arguments that are refcounted objects.
 //
diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc
index 7154194a..ec7433a 100644
--- a/base/bind_unittest.cc
+++ b/base/bind_unittest.cc
@@ -1282,38 +1282,40 @@
   int assigns = 0;
 
   CopyCounter counter(&copies, &assigns);
-  Bind(&VoidPolymorphic<CopyCounter>::Run, counter);
+  BindRepeating(&VoidPolymorphic<CopyCounter>::Run, counter);
   EXPECT_EQ(1, copies);
   EXPECT_EQ(0, assigns);
 
   copies = 0;
   assigns = 0;
-  Bind(&VoidPolymorphic<CopyCounter>::Run, CopyCounter(&copies, &assigns));
+  BindRepeating(&VoidPolymorphic<CopyCounter>::Run,
+                CopyCounter(&copies, &assigns));
   EXPECT_EQ(1, copies);
   EXPECT_EQ(0, assigns);
 
   copies = 0;
   assigns = 0;
-  Bind(&VoidPolymorphic<CopyCounter>::Run).Run(counter);
+  BindRepeating(&VoidPolymorphic<CopyCounter>::Run).Run(counter);
   EXPECT_EQ(2, copies);
   EXPECT_EQ(0, assigns);
 
   copies = 0;
   assigns = 0;
-  Bind(&VoidPolymorphic<CopyCounter>::Run).Run(CopyCounter(&copies, &assigns));
+  BindRepeating(&VoidPolymorphic<CopyCounter>::Run)
+      .Run(CopyCounter(&copies, &assigns));
   EXPECT_EQ(1, copies);
   EXPECT_EQ(0, assigns);
 
   copies = 0;
   assigns = 0;
   DerivedCopyMoveCounter derived(&copies, &assigns, nullptr, nullptr);
-  Bind(&VoidPolymorphic<CopyCounter>::Run).Run(CopyCounter(derived));
+  BindRepeating(&VoidPolymorphic<CopyCounter>::Run).Run(CopyCounter(derived));
   EXPECT_EQ(2, copies);
   EXPECT_EQ(0, assigns);
 
   copies = 0;
   assigns = 0;
-  Bind(&VoidPolymorphic<CopyCounter>::Run)
+  BindRepeating(&VoidPolymorphic<CopyCounter>::Run)
       .Run(CopyCounter(
           DerivedCopyMoveCounter(&copies, &assigns, nullptr, nullptr)));
   EXPECT_EQ(2, copies);
@@ -1326,8 +1328,8 @@
   int move_constructs = 0;
   int move_assigns = 0;
 
-  Bind(&VoidPolymorphic<const MoveCounter&>::Run,
-       MoveCounter(&move_constructs, &move_assigns));
+  BindRepeating(&VoidPolymorphic<const MoveCounter&>::Run,
+                MoveCounter(&move_constructs, &move_assigns));
   EXPECT_EQ(1, move_constructs);
   EXPECT_EQ(0, move_assigns);
 
@@ -1336,14 +1338,14 @@
 
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<MoveCounter>::Run)
+  BindRepeating(&VoidPolymorphic<MoveCounter>::Run)
       .Run(MoveCounter(&move_constructs, &move_assigns));
   EXPECT_EQ(1, move_constructs);
   EXPECT_EQ(0, move_assigns);
 
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<MoveCounter>::Run)
+  BindRepeating(&VoidPolymorphic<MoveCounter>::Run)
       .Run(MoveCounter(DerivedCopyMoveCounter(
           nullptr, nullptr, &move_constructs, &move_assigns)));
   EXPECT_EQ(2, move_constructs);
@@ -1362,7 +1364,7 @@
   int move_assigns = 0;
 
   CopyMoveCounter counter(&copies, &assigns, &move_constructs, &move_assigns);
-  Bind(&VoidPolymorphic<CopyMoveCounter>::Run, counter);
+  BindRepeating(&VoidPolymorphic<CopyMoveCounter>::Run, counter);
   EXPECT_EQ(1, copies);
   EXPECT_EQ(0, assigns);
   EXPECT_EQ(0, move_constructs);
@@ -1372,8 +1374,9 @@
   assigns = 0;
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<CopyMoveCounter>::Run,
-       CopyMoveCounter(&copies, &assigns, &move_constructs, &move_assigns));
+  BindRepeating(
+      &VoidPolymorphic<CopyMoveCounter>::Run,
+      CopyMoveCounter(&copies, &assigns, &move_constructs, &move_assigns));
   EXPECT_EQ(0, copies);
   EXPECT_EQ(0, assigns);
   EXPECT_EQ(1, move_constructs);
@@ -1383,7 +1386,7 @@
   assigns = 0;
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<CopyMoveCounter>::Run).Run(counter);
+  BindRepeating(&VoidPolymorphic<CopyMoveCounter>::Run).Run(counter);
   EXPECT_EQ(1, copies);
   EXPECT_EQ(0, assigns);
   EXPECT_EQ(1, move_constructs);
@@ -1393,7 +1396,7 @@
   assigns = 0;
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<CopyMoveCounter>::Run)
+  BindRepeating(&VoidPolymorphic<CopyMoveCounter>::Run)
       .Run(CopyMoveCounter(&copies, &assigns, &move_constructs, &move_assigns));
   EXPECT_EQ(0, copies);
   EXPECT_EQ(0, assigns);
@@ -1406,7 +1409,7 @@
   assigns = 0;
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<CopyMoveCounter>::Run)
+  BindRepeating(&VoidPolymorphic<CopyMoveCounter>::Run)
       .Run(CopyMoveCounter(derived_counter));
   EXPECT_EQ(1, copies);
   EXPECT_EQ(0, assigns);
@@ -1417,7 +1420,7 @@
   assigns = 0;
   move_constructs = 0;
   move_assigns = 0;
-  Bind(&VoidPolymorphic<CopyMoveCounter>::Run)
+  BindRepeating(&VoidPolymorphic<CopyMoveCounter>::Run)
       .Run(CopyMoveCounter(DerivedCopyMoveCounter(
           &copies, &assigns, &move_constructs, &move_assigns)));
   EXPECT_EQ(0, copies);
@@ -1444,8 +1447,8 @@
       char(int, double),
       internal::ExtractCallableRunType<decltype(h)>>::value));
 
-  EXPECT_EQ(42, Bind([] { return 42; }).Run());
-  EXPECT_EQ(42, Bind([](int i) { return i * 7; }, 6).Run());
+  EXPECT_EQ(42, BindRepeating([] { return 42; }).Run());
+  EXPECT_EQ(42, BindRepeating([](int i) { return i * 7; }, 6).Run());
 
   int x = 1;
   base::RepeatingCallback<void(int)> cb =
diff --git a/base/callback.h b/base/callback.h
index b1f4ec4..c7b76c0 100644
--- a/base/callback.h
+++ b/base/callback.h
@@ -48,9 +48,6 @@
 // will be a no-op. Note that |IsCancelled()| and |is_null()| are distinct:
 // simply cancelling a callback will not also make it null.
 //
-// base::Callback is currently a type alias for base::RepeatingCallback. In the
-// future, we expect to flip this to default to base::OnceCallback.
-//
 // See //docs/callback.md for the full documentation.
 
 namespace base {
diff --git a/base/callback_forward.h b/base/callback_forward.h
index d0f230c..8435348 100644
--- a/base/callback_forward.h
+++ b/base/callback_forward.h
@@ -13,15 +13,11 @@
 template <typename Signature>
 class RepeatingCallback;
 
-template <typename Signature>
-using Callback = RepeatingCallback<Signature>;
-
 // Syntactic sugar to make OnceClosure<void()> and RepeatingClosure<void()>
 // easier to declare since they will be used in a lot of APIs with delayed
 // execution.
 using OnceClosure = OnceCallback<void()>;
 using RepeatingClosure = RepeatingCallback<void()>;
-using Closure = Callback<void()>;
 
 }  // namespace base
 
diff --git a/base/cancelable_callback.h b/base/cancelable_callback.h
index c19e9a36..44ed6ad5 100644
--- a/base/cancelable_callback.h
+++ b/base/cancelable_callback.h
@@ -147,10 +147,6 @@
     internal::CancelableCallbackImpl<RepeatingCallback<Signature>>;
 using CancelableRepeatingClosure = CancelableRepeatingCallback<void()>;
 
-template <typename Signature>
-using CancelableCallback = CancelableRepeatingCallback<Signature>;
-using CancelableClosure = CancelableCallback<void()>;
-
 }  // namespace base
 
 #endif  // BASE_CANCELABLE_CALLBACK_H_
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 03d4beb..3960226 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -7,7 +7,6 @@
 #include <algorithm>
 #include <utility>
 
-#include "base/auto_reset.h"
 #include "base/base_switches.h"
 #include "base/command_line.h"
 #include "base/debug/activity_tracker.h"
@@ -18,7 +17,6 @@
 #include "base/process/process_handle.h"
 #include "base/process/process_info.h"
 #include "base/rand_util.h"
-#include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_split.h"
@@ -442,7 +440,9 @@
 
 FieldTrialList::FieldTrialList(
     std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider)
-    : entropy_provider_(std::move(entropy_provider)) {
+    : entropy_provider_(std::move(entropy_provider)),
+      observer_list_(new ObserverListThreadSafe<FieldTrialList::Observer>(
+          ObserverListPolicy::EXISTING_ONLY)) {
   DCHECK(!global_);
   DCHECK(!used_without_global_);
   global_ = this;
@@ -917,8 +917,7 @@
 bool FieldTrialList::AddObserver(Observer* observer) {
   if (!global_)
     return false;
-  AutoLock auto_lock(global_->lock_);
-  global_->observers_.push_back(observer);
+  global_->observer_list_->AddObserver(observer);
   return true;
 }
 
@@ -926,10 +925,19 @@
 void FieldTrialList::RemoveObserver(Observer* observer) {
   if (!global_)
     return;
-  AutoLock auto_lock(global_->lock_);
-  Erase(global_->observers_, observer);
-  DCHECK_EQ(global_->num_ongoing_notify_field_trial_group_selection_calls_, 0)
-      << "Cannot call RemoveObserver while accessing FieldTrial::group().";
+  global_->observer_list_->RemoveObserver(observer);
+}
+
+// static
+void FieldTrialList::SetSynchronousObserver(Observer* observer) {
+  DCHECK(!global_->synchronous_observer_);
+  global_->synchronous_observer_ = observer;
+}
+
+// static
+void FieldTrialList::RemoveSynchronousObserver(Observer* observer) {
+  DCHECK_EQ(global_->synchronous_observer_, observer);
+  global_->synchronous_observer_ = nullptr;
 }
 
 // static
@@ -951,8 +959,6 @@
   if (!global_)
     return;
 
-  std::vector<Observer*> local_observers;
-
   {
     AutoLock auto_lock(global_->lock_);
     if (field_trial->group_reported_)
@@ -962,24 +968,17 @@
     if (!field_trial->enable_field_trial_)
       return;
 
-    ++global_->num_ongoing_notify_field_trial_group_selection_calls_;
-
     ActivateFieldTrialEntryWhileLocked(field_trial);
-
-    // Copy observers to a local variable to access outside the scope of the
-    // lock. Since removing observers concurrently with this method is
-    // disallowed, pointers should remain valid while observers are notified.
-    local_observers = global_->observers_;
   }
 
-  for (Observer* observer : local_observers) {
-    observer->OnFieldTrialGroupFinalized(field_trial->trial_name(),
-                                         field_trial->group_name_internal());
+  if (global_->synchronous_observer_) {
+    global_->synchronous_observer_->OnFieldTrialGroupFinalized(
+        field_trial->trial_name(), field_trial->group_name_internal());
   }
 
-  int previous_num_ongoing_notify_field_trial_group_selection_calls =
-      global_->num_ongoing_notify_field_trial_group_selection_calls_--;
-  DCHECK_GT(previous_num_ongoing_notify_field_trial_group_selection_calls, 0);
+  global_->observer_list_->NotifySynchronously(
+      FROM_HERE, &FieldTrialList::Observer::OnFieldTrialGroupFinalized,
+      field_trial->trial_name(), field_trial->group_name_internal());
 }
 
 // static
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 1c2460c..9b61c1e 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -57,7 +57,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <atomic>
 #include <map>
 #include <memory>
 #include <string>
@@ -269,7 +268,6 @@
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_NonDefault);
-  FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ObserveReentrancy);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DoesNotSurpassTotalProbability);
   FRIEND_TEST_ALL_PREFIXES(FieldTrialListTest,
@@ -610,15 +608,26 @@
   // Add an observer to be notified when a field trial is irrevocably committed
   // to being part of some specific field_group (and hence the group_name is
   // also finalized for that field_trial). Returns false and does nothing if
-  // there is no FieldTrialList singleton. The observer can be notified on any
-  // sequence; it must be thread-safe.
+  // there is no FieldTrialList singleton.
   static bool AddObserver(Observer* observer);
 
-  // Remove an observer. This cannot be invoked concurrently with
-  // FieldTrial::group() (typically, this means that no other thread should be
-  // running when this is invoked).
+  // Remove an observer.
   static void RemoveObserver(Observer* observer);
 
+  // Similar to AddObserver(), but the passed observer will be notified
+  // synchronously when a field trial is activated and its group selected. It
+  // will be notified synchronously on the same thread where the activation and
+  // group selection happened. It is the responsibility of the observer to make
+  // sure that this is a safe operation and the operation must be fast, as this
+  // work is done synchronously as part of group() or related APIs. Only a
+  // single such observer is supported, exposed specifically for crash
+  // reporting. Must be called on the main thread before any other threads
+  // have been started.
+  static void SetSynchronousObserver(Observer* observer);
+
+  // Removes the single synchronous observer.
+  static void RemoveSynchronousObserver(Observer* observer);
+
   // Grabs the lock if necessary and adds the field trial to the allocator. This
   // should only be called from FinalizeGroupChoice().
   static void OnGroupFinalized(bool is_locked, FieldTrial* field_trial);
@@ -741,7 +750,7 @@
   typedef std::map<std::string, FieldTrial*, std::less<>> RegistrationMap;
 
   // Helper function should be called only while holding lock_.
-  FieldTrial* PreLockedFind(StringPiece name) EXCLUSIVE_LOCKS_REQUIRED(lock_);
+  FieldTrial* PreLockedFind(StringPiece name);
 
   // Register() stores a pointer to the given trial in a global map.
   // This method also AddRef's the indicated trial.
@@ -759,22 +768,19 @@
   // FieldTrialList is created after that.
   static bool used_without_global_;
 
-  // Lock for access to |registered_|, |observers_| and
-  // |field_trial_allocator_|.
+  // Lock for access to registered_ and field_trial_allocator_.
   Lock lock_;
-  RegistrationMap registered_ GUARDED_BY(lock_);
+  RegistrationMap registered_;
 
   // Entropy provider to be used for one-time randomized field trials. If NULL,
   // one-time randomization is not supported.
   std::unique_ptr<const FieldTrial::EntropyProvider> entropy_provider_;
 
   // List of observers to be notified when a group is selected for a FieldTrial.
-  std::vector<Observer*> observers_ GUARDED_BY(lock_);
+  scoped_refptr<ObserverListThreadSafe<Observer> > observer_list_;
 
-  // Counts the ongoing calls to
-  // FieldTrialList::NotifyFieldTrialGroupSelection(). Used to ensure that
-  // RemoveObserver() isn't called while notifying observers.
-  std::atomic_int num_ongoing_notify_field_trial_group_selection_calls_{0};
+  // Single synchronous observer to be notified when a trial group is chosen.
+  Observer* synchronous_observer_ = nullptr;
 
   // Allocator in shared memory containing field trial data. Used in both
   // browser and child processes, but readonly in the child.
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index 6e70648c..07fbdd2 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -55,15 +55,29 @@
       FieldTrial::SESSION_RANDOMIZED, default_group_number);
 }
 
-// A FieldTrialList::Observer implementation which stores the trial name and
-// group name received via OnFieldTrialGroupFinalized() for later inspection.
+// FieldTrialList::Observer implementation for testing.
 class TestFieldTrialObserver : public FieldTrialList::Observer {
  public:
-  TestFieldTrialObserver() { FieldTrialList::AddObserver(this); }
+  enum Type {
+    ASYNCHRONOUS,
+    SYNCHRONOUS,
+  };
+
+  explicit TestFieldTrialObserver(Type type) : type_(type) {
+    if (type == SYNCHRONOUS)
+      FieldTrialList::SetSynchronousObserver(this);
+    else
+      FieldTrialList::AddObserver(this);
+  }
   TestFieldTrialObserver(const TestFieldTrialObserver&) = delete;
   TestFieldTrialObserver& operator=(const TestFieldTrialObserver&) = delete;
 
-  ~TestFieldTrialObserver() override { FieldTrialList::RemoveObserver(this); }
+  ~TestFieldTrialObserver() override {
+    if (type_ == SYNCHRONOUS)
+      FieldTrialList::RemoveSynchronousObserver(this);
+    else
+      FieldTrialList::RemoveObserver(this);
+  }
 
   void OnFieldTrialGroupFinalized(const std::string& trial,
                                   const std::string& group) override {
@@ -75,39 +89,11 @@
   const std::string& group_name() const { return group_name_; }
 
  private:
+  const Type type_;
   std::string trial_name_;
   std::string group_name_;
 };
 
-// A FieldTrialList::Observer implementation which accesses the group of a
-// FieldTrial from OnFieldTrialGroupFinalized(). Used to test reentrancy.
-class FieldTrialObserverAccessingGroup : public FieldTrialList::Observer {
- public:
-  // |trial_to_access| is the FieldTrial on which to invoke group() when
-  // receiving an OnFieldTrialGroupFinalized() notification.
-  explicit FieldTrialObserverAccessingGroup(
-      scoped_refptr<FieldTrial> trial_to_access)
-      : trial_to_access_(trial_to_access) {
-    FieldTrialList::AddObserver(this);
-  }
-  FieldTrialObserverAccessingGroup(const FieldTrialObserverAccessingGroup&) =
-      delete;
-  FieldTrialObserverAccessingGroup& operator=(
-      const FieldTrialObserverAccessingGroup&) = delete;
-
-  ~FieldTrialObserverAccessingGroup() override {
-    FieldTrialList::RemoveObserver(this);
-  }
-
-  void OnFieldTrialGroupFinalized(const std::string& trial,
-                                  const std::string& group) override {
-    trial_to_access_->group();
-  }
-
- private:
-  scoped_refptr<FieldTrial> trial_to_access_;
-};
-
 std::string MockEscapeQueryParamValue(const std::string& input) {
   return input;
 }
@@ -628,7 +614,7 @@
 TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) {
   ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
 
-  TestFieldTrialObserver observer;
+  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
   ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/"));
   RunLoop().RunUntilIdle();
   // Observer shouldn't be notified.
@@ -637,6 +623,7 @@
   // Check that the values still get returned and querying them activates them.
   EXPECT_EQ("def", FieldTrialList::FindFullName("Abc"));
 
+  RunLoop().RunUntilIdle();
   EXPECT_EQ("Abc", observer.trial_name());
   EXPECT_EQ("def", observer.group_name());
 }
@@ -901,7 +888,26 @@
   const char kTrialName[] = "TrialToObserve1";
   const char kSecondaryGroupName[] = "SecondaryGroup";
 
-  TestFieldTrialObserver observer;
+  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
+  int default_group = -1;
+  scoped_refptr<FieldTrial> trial =
+      CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
+  const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50);
+  const int chosen_group = trial->group();
+  EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group);
+
+  EXPECT_EQ(kTrialName, observer.trial_name());
+  if (chosen_group == default_group)
+    EXPECT_EQ(kDefaultGroupName, observer.group_name());
+  else
+    EXPECT_EQ(kSecondaryGroupName, observer.group_name());
+}
+
+TEST_F(FieldTrialTest, SynchronousObserver) {
+  const char kTrialName[] = "TrialToObserve1";
+  const char kSecondaryGroupName[] = "SecondaryGroup";
+
+  TestFieldTrialObserver observer(TestFieldTrialObserver::SYNCHRONOUS);
   int default_group = -1;
   scoped_refptr<FieldTrial> trial =
       CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
@@ -917,39 +923,10 @@
     EXPECT_EQ(kSecondaryGroupName, observer.group_name());
 }
 
-// Verify that no hang occurs when a FieldTrial group is selected from a
-// FieldTrialList::Observer::OnFieldTrialGroupFinalized() notification. If the
-// FieldTrialList's lock is held when observers are notified, this test will
-// hang due to reentrant lock acquisition when selecting the FieldTrial group.
-TEST_F(FieldTrialTest, ObserveReentrancy) {
-  const char kTrialName1[] = "TrialToObserve1";
-  const char kTrialName2[] = "TrialToObserve2";
-
-  int default_group_1 = -1;
-  scoped_refptr<FieldTrial> trial_1 =
-      CreateFieldTrial(kTrialName1, 100, kDefaultGroupName, &default_group_1);
-
-  FieldTrialObserverAccessingGroup observer(trial_1);
-
-  int default_group_2 = -1;
-  scoped_refptr<FieldTrial> trial_2 =
-      CreateFieldTrial(kTrialName2, 100, kDefaultGroupName, &default_group_2);
-
-  // No group should be selected for |trial_1| yet.
-  EXPECT_EQ(FieldTrial::kNotFinalized, trial_1->group_);
-
-  // Force selection of a group for |trial_2|. This will notify |observer| which
-  // will force the selection of a group for |trial_1|. This should not hang.
-  trial_2->group();
-
-  // The above call should have selected a group for |trial_1|.
-  EXPECT_NE(FieldTrial::kNotFinalized, trial_1->group_);
-}
-
 TEST_F(FieldTrialTest, ObserveDisabled) {
   const char kTrialName[] = "TrialToObserve2";
 
-  TestFieldTrialObserver observer;
+  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
   int default_group = -1;
   scoped_refptr<FieldTrial> trial =
       CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
@@ -973,7 +950,7 @@
 TEST_F(FieldTrialTest, ObserveForcedDisabled) {
   const char kTrialName[] = "TrialToObserve3";
 
-  TestFieldTrialObserver observer;
+  TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
   int default_group = -1;
   scoped_refptr<FieldTrial> trial =
       CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group);
@@ -1069,13 +1046,14 @@
     { 0.95, kDefaultGroupName },
   };
 
-  for (auto& test_case : test_cases) {
-    TestFieldTrialObserver observer;
-    scoped_refptr<FieldTrial> trial(FieldTrial::CreateSimulatedFieldTrial(
-        kTrialName, 100, kDefaultGroupName, test_case.entropy_value));
+  for (size_t i = 0; i < base::size(test_cases); ++i) {
+    TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS);
+    scoped_refptr<FieldTrial> trial(
+       FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, kDefaultGroupName,
+                                             test_cases[i].entropy_value));
     trial->AppendGroup("A", 80);
     trial->AppendGroup("B", 10);
-    EXPECT_EQ(test_case.expected_group, trial->group_name());
+    EXPECT_EQ(test_cases[i].expected_group, trial->group_name());
 
     // Field trial shouldn't have been registered with the list.
     EXPECT_FALSE(FieldTrialList::TrialExists(kTrialName));
diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h
index c750bbb0..dc6b7bb9d 100644
--- a/base/observer_list_threadsafe.h
+++ b/base/observer_list_threadsafe.h
@@ -7,6 +7,7 @@
 
 #include <unordered_map>
 #include <utility>
+#include <vector>
 
 #include "base/base_export.h"
 #include "base/bind.h"
@@ -42,11 +43,7 @@
 //   The drawback of the threadsafe observer list is that notifications are not
 //   as real-time as the non-threadsafe version of this class. Notifications
 //   will always be done via PostTask() to another sequence, whereas with the
-//   non-thread-safe ObserverList, notifications happen synchronously.
-//
-//   Note: this class previously supported synchronous notifications for
-//   same-sequence observers, but it was error-prone and removed in
-//   crbug.com/1193750, think twice before re-considering this paradigm.
+//   non-thread-safe observer_list, notifications happen synchronously.
 //
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -202,6 +199,49 @@
     }
   }
 
+  // Like Notify() but attempts to synchronously invoke callbacks if they are
+  // associated with this thread.
+  template <typename Method, typename... Params>
+  void NotifySynchronously(const Location& from_here,
+                           Method m,
+                           Params&&... params) {
+    RepeatingCallback<void(ObserverType*)> method =
+        BindRepeating(&Dispatcher<ObserverType, Method>::Run, m,
+                      std::forward<Params>(params)...);
+
+    // The observers may make reentrant calls (which can be a problem due to the
+    // lock), so we extract a list to call synchronously.
+    struct PendingNotificationData {
+      ObserverType* observer;
+      size_t observer_id;
+    };
+    std::vector<PendingNotificationData> current_sequence_observers;
+
+    {
+      AutoLock lock(lock_);
+      current_sequence_observers.reserve(observers_.size());
+      for (const auto& observer : observers_) {
+        if (observer.second.task_runner->RunsTasksInCurrentSequence()) {
+          current_sequence_observers.emplace_back(PendingNotificationData{
+              observer.first, observer.second.observer_id});
+        } else {
+          observer.second.task_runner->PostTask(
+              from_here,
+              BindOnce(&ObserverListThreadSafe<ObserverType>::NotifyWrapper,
+                       this, observer.first,
+                       NotificationData(this, observer.second.observer_id,
+                                        from_here, method)));
+        }
+      }
+    }
+
+    for (const auto& pending_notification : current_sequence_observers) {
+      NotifyWrapper(pending_notification.observer,
+                    NotificationData(this, pending_notification.observer_id,
+                                     from_here, method));
+    }
+  }
+
  private:
   friend class RefCountedThreadSafe<ObserverListThreadSafeBase>;
 
diff --git a/base/observer_list_threadsafe_unittest.cc b/base/observer_list_threadsafe_unittest.cc
index 3dee47f..08f4aa4 100644
--- a/base/observer_list_threadsafe_unittest.cc
+++ b/base/observer_list_threadsafe_unittest.cc
@@ -514,4 +514,59 @@
   EXPECT_EQ(1, c.total);
 }
 
+TEST(ObserverListThreadSafeTest, NotifySynchronously) {
+  test::TaskEnvironment task_environment;
+
+  scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
+      new ObserverListThreadSafe<Foo>);
+  Adder a(1);
+  Adder b(-1);
+  Adder c(1);
+  Adder d(-1);
+
+  observer_list->AddObserver(&a);
+  observer_list->AddObserver(&b);
+
+  observer_list->NotifySynchronously(FROM_HERE, &Foo::Observe, 10);
+
+  observer_list->AddObserver(&c);
+  observer_list->AddObserver(&d);
+
+  observer_list->NotifySynchronously(FROM_HERE, &Foo::Observe, 10);
+
+  EXPECT_EQ(20, a.total);
+  EXPECT_EQ(-20, b.total);
+  EXPECT_EQ(10, c.total);
+  EXPECT_EQ(-10, d.total);
+}
+
+TEST(ObserverListThreadSafeTest, NotifySynchronouslyCrossSequence) {
+  test::TaskEnvironment task_environment;
+
+  scoped_refptr<ObserverListThreadSafe<Foo>> observer_list(
+      new ObserverListThreadSafe<Foo>);
+  Adder a(1);
+  observer_list->AddObserver(&a);
+
+  WaitableEvent event(WaitableEvent::ResetPolicy::AUTOMATIC,
+                      WaitableEvent::InitialState::NOT_SIGNALED);
+  // Call NotifySynchronously on a different sequence.
+  ThreadPool::PostTask(FROM_HERE, {}, BindLambdaForTesting([&]() {
+                         observer_list->NotifySynchronously(FROM_HERE,
+                                                            &Foo::Observe, 10);
+                         event.Signal();
+                       }));
+
+  event.Wait();
+
+  // Because it was run on a different sequence NotifySynchronously should have
+  // posted a task which hasn't run yet.
+  EXPECT_EQ(0, a.total);
+
+  RunLoop().RunUntilIdle();
+
+  // Verify the task has now run.
+  EXPECT_EQ(10, a.total);
+}
+
 }  // namespace base
diff --git a/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index 618ffb8..254a77e 100644
--- a/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -4823,9 +4823,9 @@
 
   std::vector<int> order;
 
-  CancelableClosure cancelable_closure1(
+  CancelableRepeatingClosure cancelable_closure1(
       BindLambdaForTesting([&]() { order.push_back(10); }));
-  CancelableClosure cancelable_closure2(
+  CancelableRepeatingClosure cancelable_closure2(
       BindLambdaForTesting([&]() { order.push_back(11); }));
   queue1->task_runner()->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
                                     order.push_back(1);
diff --git a/base/task/sequence_manager/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h
index 0276b03..8d40248 100644
--- a/base/task/sequence_manager/thread_controller_impl.h
+++ b/base/task/sequence_manager/thread_controller_impl.h
@@ -120,7 +120,7 @@
   const TickClock* time_source_;
   RepeatingClosure immediate_do_work_closure_;
   RepeatingClosure delayed_do_work_closure_;
-  CancelableClosure cancelable_delayed_do_work_closure_;
+  CancelableRepeatingClosure cancelable_delayed_do_work_closure_;
   SequencedTaskSource* sequence_ = nullptr;  // Not owned.
   TaskAnnotator task_annotator_;
   WorkDeduplicator work_deduplicator_;
diff --git a/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java b/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java
index 3264014..2f04d86 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/util/CallbackHelper.java
@@ -228,6 +228,20 @@
     }
 
     /**
+     * Blocks until the next time the callback is called.
+     * @param msg The error message to use if the callback times out.
+     * @throws TimeoutException
+     */
+    public void waitForNext(String msg) throws TimeoutException {
+        waitForCallback(msg, mCallCount, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+    }
+
+    /** @see #waitForNext(String) */
+    public void waitForNext() throws TimeoutException {
+        waitForNext(null);
+    }
+
+    /**
      * Wait until the callback has been called once.
      */
     public void waitForFirst(String msg, long timeout, TimeUnit unit) throws TimeoutException {
diff --git a/base/test/task_environment_unittest.cc b/base/test/task_environment_unittest.cc
index 61c620b..4b813713 100644
--- a/base/test/task_environment_unittest.cc
+++ b/base/test/task_environment_unittest.cc
@@ -1147,20 +1147,20 @@
   EXPECT_EQ(TimeDelta::Max(),
             task_environment.NextMainThreadPendingTaskDelay());
 
-  CancelableClosure task2(BindRepeating([]() {}));
+  CancelableRepeatingClosure task2(BindRepeating([]() {}));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, task2.callback(),
                                                  TimeDelta::FromSeconds(1));
   task2.Cancel();
   EXPECT_EQ(0u, task_environment.GetPendingMainThreadTaskCount());
 
-  CancelableClosure task3(BindRepeating([]() {}));
+  CancelableRepeatingClosure task3(BindRepeating([]() {}));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, task3.callback(),
                                                  TimeDelta::FromSeconds(1));
   task3.Cancel();
   EXPECT_EQ(TimeDelta::Max(),
             task_environment.NextMainThreadPendingTaskDelay());
 
-  CancelableClosure task4(BindRepeating([]() {}));
+  CancelableRepeatingClosure task4(BindRepeating([]() {}));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, task4.callback(),
                                                  TimeDelta::FromSeconds(1));
   task4.Cancel();
@@ -1185,7 +1185,7 @@
       TaskEnvironment::ThreadPoolExecutionMode::QUEUED);
 
   TimeTicks start_time = task_environment.NowTicks();
-  CancelableClosure task(BindRepeating([]() {}));
+  CancelableRepeatingClosure task(BindRepeating([]() {}));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, task.callback(),
                                                  TimeDelta::FromSeconds(1));
   EXPECT_EQ(TimeDelta::FromSeconds(1),
@@ -1199,7 +1199,7 @@
   TaskEnvironment task_environment(TaskEnvironment::TimeSource::MOCK_TIME);
 
   EXPECT_FALSE(task_environment.NextTaskIsDelayed());
-  CancelableClosure task(BindRepeating([]() {}));
+  CancelableRepeatingClosure task(BindRepeating([]() {}));
   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, task.callback(),
                                                  TimeDelta::FromSeconds(1));
   EXPECT_TRUE(task_environment.NextTaskIsDelayed());
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 3a15d060..307ecc1 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-4.20210504.3.1
+4.20210505.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 3a15d060..307ecc1 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-4.20210504.3.1
+4.20210505.0.1
diff --git a/build/toolchain/apple/toolchain.gni b/build/toolchain/apple/toolchain.gni
index 3474a31..4191f9bf 100644
--- a/build/toolchain/apple/toolchain.gni
+++ b/build/toolchain/apple/toolchain.gni
@@ -513,10 +513,13 @@
         _tool = rebase_path("//build/toolchain/ios/compile_xcassets.py",
                             root_build_dir)
 
+        _env_vars = "TOOL_VERSION=${tool_versions.compile_xcassets}"
+        if (invoker.sdk_developer_dir != "") {
+          _env_vars += " DEVELOPER_DIR=${toolchain_args.sdk_developer_dir}"
+        }
+
         command =
-            "rm -f \"{{output}}\" && " +
-            "TOOL_VERSION=${tool_versions.compile_xcassets} " +
-            "$python_path $_tool -p \"${invoker.sdk_name}\" " +
+            "$_env_vars $python_path $_tool -p \"${invoker.sdk_name}\" " +
             "-t \"${invoker.deployment_target}\" " +
             "-T \"{{bundle_product_type}}\" " +
             "-P \"{{bundle_partial_info_plist}}\" " + "-o {{output}} {{inputs}}"
diff --git a/build/toolchain/ios/compile_xcassets.py b/build/toolchain/ios/compile_xcassets.py
index e160665..2241f148 100644
--- a/build/toolchain/ios/compile_xcassets.py
+++ b/build/toolchain/ios/compile_xcassets.py
@@ -2,12 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import argparse
-import os
-import re
-import subprocess
-import sys
-import tempfile
 """Wrapper around actool to compile assets catalog.
 
 The script compile_xcassets.py is a wrapper around actool to compile
@@ -22,6 +16,14 @@
 an option to fail with non-zero error code when there are warnings.
 """
 
+import argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
 # Pattern matching a section header in the output of actool.
 SECTION_HEADER = re.compile('^/\\* ([^ ]*) \\*/$')
 
@@ -62,6 +64,15 @@
   return False
 
 
+def FixAbsolutePathInLine(line, relative_paths):
+  """Fix absolute paths present in |line| to relative paths."""
+  absolute_path = line.split(':')[0]
+  relative_path = relative_paths.get(absolute_path, absolute_path)
+  if absolute_path == relative_path:
+    return line
+  return relative_path + line[len(absolute_path):]
+
+
 def FilterCompilerOutput(compiler_output, relative_paths):
   """Filers actool compilation output.
 
@@ -102,14 +113,12 @@
     if current_section and current_section != NOTICE_SECTION:
       if IsSpuriousMessage(line):
         continue
-      absolute_path = line.split(':')[0]
-      relative_path = relative_paths.get(absolute_path, absolute_path)
-      if absolute_path != relative_path:
-        line = relative_path + line[len(absolute_path):]
       if not data_in_section:
         data_in_section = True
         filtered_output.append('/* %s */\n' % current_section)
-      filtered_output.append(line + '\n')
+
+      fixed_line = FixAbsolutePathInLine(line, relative_paths)
+      filtered_output.append(fixed_line + '\n')
 
   return ''.join(filtered_output)
 
@@ -168,7 +177,7 @@
 
       command.extend([ACTOOL_FLAG_FOR_ASSET_TYPE[asset_type], asset_name])
 
-  # Always ask actool to generate a partial Info.plist file. If not path
+  # Always ask actool to generate a partial Info.plist file. If no path
   # has been given by the caller, use a temporary file name.
   temporary_file = None
   if not partial_info_plist:
@@ -202,10 +211,21 @@
                                stderr=subprocess.STDOUT)
     stdout, _ = process.communicate()
 
-    # Filter the output to remove all garbarge and to fix the paths.
-    stdout = FilterCompilerOutput(stdout.decode('UTF-8'), relative_paths)
+    # If the invocation of `actool` failed, copy all the compiler output to
+    # the standard error stream and exit. See https://crbug.com/1205775 for
+    # example of compilation that failed with no error message due to filter.
+    if process.returncode:
+      for line in stdout.splitlines():
+        fixed_line = FixAbsolutePathInLine(line, relative_paths)
+        sys.stderr.write(fixed_line + '\n')
+      sys.exit(1)
 
-    if process.returncode or stdout:
+    # Filter the output to remove all garbage and to fix the paths. If the
+    # output is not empty after filtering, then report the compilation as a
+    # failure (as some version of `actool` report error to stdout, yet exit
+    # with an return code of zero).
+    stdout = FilterCompilerOutput(stdout.decode('UTF-8'), relative_paths)
+    if stdout:
       sys.stderr.write(stdout)
       sys.exit(1)
 
@@ -252,6 +272,12 @@
                      'to the containing bundle: %s\n' % (args.output, ))
     sys.exit(1)
 
+  if os.path.exists(args.output):
+    if os.path.isfile(args.output):
+      os.unlink(args.output)
+    else:
+      shutil.rmtree(args.output)
+
   CompileAssetCatalog(args.output, args.platform, args.product_type,
                       args.minimum_deployment_target, args.inputs,
                       args.compress_pngs, args.partial_info_plist)
diff --git a/cc/trees/frame_rate_estimator.cc b/cc/trees/frame_rate_estimator.cc
index 577b6a1..0ba9354 100644
--- a/cc/trees/frame_rate_estimator.cc
+++ b/cc/trees/frame_rate_estimator.cc
@@ -49,7 +49,7 @@
   // The delta below is to account for minor offsets in frame times.
   constexpr auto kFudgeDelta = base::TimeDelta::FromMilliseconds(1);
   constexpr auto kMinDelta =
-      (viz::BeginFrameArgs::DefaultInterval() * 2) + kFudgeDelta;
+      (viz::BeginFrameArgs::DefaultInterval() * 2) - kFudgeDelta;
   if (draw_delta < kMinDelta)
     num_of_consecutive_frames_with_min_delta_++;
   else
diff --git a/cc/trees/frame_rate_estimator_unittest.cc b/cc/trees/frame_rate_estimator_unittest.cc
index d99fabf8..03df3952 100644
--- a/cc/trees/frame_rate_estimator_unittest.cc
+++ b/cc/trees/frame_rate_estimator_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/trees/frame_rate_estimator.h"
 
+#include "base/stl_util.h"
 #include "base/test/test_simple_task_runner.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -69,5 +70,28 @@
   EXPECT_NE(estimator_->GetPreferredInterval(),
             viz::BeginFrameArgs::MinInterval());
 }
+
+TEST_F(FrameRateEstimatorTest, RafAtHalfFps) {
+  estimator_->SetFrameEstimationEnabled(true);
+  // Recorded rAF intervals at 30 fps.
+  const base::TimeDelta kIntervals[] = {
+      base::TimeDelta::FromMicroseconds(33425),
+      base::TimeDelta::FromMicroseconds(33298),
+      base::TimeDelta::FromMicroseconds(33396),
+      base::TimeDelta::FromMicroseconds(33339),
+      base::TimeDelta::FromMicroseconds(33431),
+      base::TimeDelta::FromMicroseconds(33320),
+      base::TimeDelta::FromMicroseconds(33364),
+      base::TimeDelta::FromMicroseconds(33360)};
+  const base::TimeDelta kIntervalForHalfFps =
+      viz::BeginFrameArgs::DefaultInterval() * 2;
+  base::TimeTicks time;
+  for (size_t i = 0; i <= base::size(kIntervals); ++i) {
+    estimator_->WillDraw(time);
+    EXPECT_EQ(kIntervalForHalfFps, estimator_->GetPreferredInterval());
+    if (i < base::size(kIntervals))
+      time += kIntervals[i];
+  }
+}
 }  // namespace
 }  // namespace cc
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index 24b4105..d10441c 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -142,6 +142,7 @@
   "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabExternalNavigationTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabFromChromeExternalNavigationTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabLaunchCauseMetricsTest.java",
+  "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabPostMessageTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistenceIntegrationTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTabPersistencePolicyTest.java",
   "javatests/src/org/chromium/chrome/browser/customtabs/CustomTabTaskDescriptionHelperTest.java",
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 867a3b6..019e75d 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -316,6 +316,7 @@
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTestUtil.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/BottomSheetOnboardingCoordinatorTest.java",
+    "javatests/src/org/chromium/chrome/browser/autofill_assistant/InChromeTriggeringTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureParameters.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/PasswordChangeFixtureTestUtils.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
index 110f6d0..297447a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScript.java
@@ -184,11 +184,6 @@
     }
 
     @VisibleForTesting
-    public void disableBottomSheetAnimationsForTesting(boolean disable) {
-        mAnimateBottomSheet = !disable;
-    }
-
-    @VisibleForTesting
     public List<AssistantChip> getLeftAlignedChipsForTest() {
         return mLeftAlignedChips;
     }
@@ -225,6 +220,7 @@
                 AssistantHeaderModel.FEEDBACK_BUTTON_CALLBACK, mDelegate::onFeedbackButtonClicked);
         if (AutofillAssistantServiceInjector.hasServiceRequestSenderToInject()) {
             mHeaderModel.set(AssistantHeaderModel.DISABLE_ANIMATIONS_FOR_TESTING, true);
+            mAnimateBottomSheet = false;
         }
         return mHeaderModel;
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
index 511cec04..12dd8e2 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/trigger_scripts/AssistantTriggerScriptBridge.java
@@ -9,14 +9,12 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.autofill_assistant.AssistantCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.AssistantDependenciesImpl;
 import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiController;
 import org.chromium.chrome.browser.autofill_assistant.carousel.AssistantChip;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
 import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncherImpl;
-import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.ui.KeyboardVisibilityDelegate;
 
@@ -33,7 +31,6 @@
     private final AssistantTriggerScript mTriggerScript;
     private long mNativeBridge;
     private KeyboardVisibilityDelegate.KeyboardVisibilityListener mKeyboardVisibilityListener;
-    private ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
 
     @CalledByNative
     public AssistantTriggerScriptBridge(AssistantDependenciesImpl startupDependencies) {
@@ -70,14 +67,6 @@
                 mStartupDependencies.getBottomInsetProvider());
 
         mKeyboardVisibilityListener = this::safeNativeOnKeyboardVisibilityChanged;
-        mActivityTabObserver =
-                new ActivityTabProvider.ActivityTabTabObserver(
-                        mStartupDependencies.getActivityTabProvider(), true) {
-                    @Override
-                    public void onInteractabilityChanged(Tab tab, boolean isInteractable) {
-                        safeNativeOnTabInteractabilityChanged(isInteractable);
-                    }
-                };
     }
 
     /**
@@ -146,7 +135,6 @@
         mTriggerScript.destroy();
         mStartupDependencies.getKeyboardVisibilityDelegate().removeKeyboardVisibilityListener(
                 mKeyboardVisibilityListener);
-        mActivityTabObserver.destroy();
     }
 
     private void safeNativeOnTriggerScriptAction(int action) {
@@ -178,13 +166,6 @@
         }
     }
 
-    private void safeNativeOnTabInteractabilityChanged(boolean interactable) {
-        if (mNativeBridge != 0) {
-            AssistantTriggerScriptBridgeJni.get().onTabInteractabilityChanged(
-                    mNativeBridge, AssistantTriggerScriptBridge.this, interactable);
-        }
-    }
-
     @NativeMethods
     interface Natives {
         void onTriggerScriptAction(long nativeTriggerScriptBridgeAndroid,
@@ -195,7 +176,5 @@
                 long nativeTriggerScriptBridgeAndroid, AssistantTriggerScriptBridge caller);
         void onKeyboardVisibilityChanged(long nativeTriggerScriptBridgeAndroid,
                 AssistantTriggerScriptBridge caller, boolean visible);
-        void onTabInteractabilityChanged(long nativeTriggerScriptBridgeAndroid,
-                AssistantTriggerScriptBridge caller, boolean interactable);
     }
 }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/InChromeTriggeringTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/InChromeTriggeringTest.java
new file mode 100644
index 0000000..f3391427
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/InChromeTriggeringTest.java
@@ -0,0 +1,130 @@
+// 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.autofill_assistant;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.chromium.base.test.util.CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL;
+import static org.chromium.base.test.util.CriteriaHelper.DEFAULT_POLLING_INTERVAL;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.createDefaultTriggerScriptUI;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewAssertionTrue;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
+
+import androidx.test.filters.MediumTest;
+
+import com.google.protobuf.GeneratedMessageLite;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.autofill_assistant.proto.GetTriggerScriptsResponseProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.TriggerScriptProto;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+
+/**
+ * Tests for TriggerContext.
+ */
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@RunWith(ChromeJUnit4ClassRunner.class)
+public class InChromeTriggeringTest {
+    private static final String HTML_DIRECTORY = "/components/test/data/autofill_assistant/html/";
+    private static final String TEST_PAGE_UNSUPPORTED = "autofill_assistant_target_website.html";
+    private static final String TEST_PAGE_SUPPORTED = "cart.html";
+
+    @Rule
+    public ChromeTabbedActivityTestRule mTestRule = new ChromeTabbedActivityTestRule();
+
+    private String getTargetWebsiteUrl(String testPage) {
+        return mTestRule.getTestServer().getURL(HTML_DIRECTORY + testPage);
+    }
+
+    private void setupTriggerScripts(GetTriggerScriptsResponseProto triggerScripts) {
+        AutofillAssistantTestServiceRequestSender testServiceRequestSender =
+                new AutofillAssistantTestServiceRequestSender();
+        testServiceRequestSender.setNextResponse(/* httpStatus = */ 200, triggerScripts);
+        testServiceRequestSender.scheduleForInjection();
+    }
+
+    @Before
+    public void setUp() {
+        mTestRule.startMainActivityOnBlankPage();
+
+        // Enable MSBB.
+        AutofillAssistantPreferencesUtil.setProactiveHelpSwitch(true);
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            UnifiedConsentServiceBridge.setUrlKeyedAnonymizedDataCollectionEnabled(
+                    AutofillAssistantUiController.getProfile(), true);
+
+            // Force native to pick up the changes we made to Chrome preferences.
+            AutofillAssistantTabHelper
+                    .get(TabModelUtils.getCurrentTab(mTestRule.getActivity().getCurrentTabModel()))
+                    .forceSettingsChangeNotificationForTesting();
+        });
+    }
+
+    private GeneratedMessageLite createDefaultTriggerScriptResponse(String statusMessage) {
+        return GetTriggerScriptsResponseProto.newBuilder()
+                .addTriggerScripts(TriggerScriptProto.newBuilder().setUserInterface(
+                        createDefaultTriggerScriptUI(statusMessage,
+                                /* bubbleMessage = */ "",
+                                /* withProgressBar = */ false)))
+                .build();
+    }
+
+    @Test
+    @MediumTest
+    @Features.EnableFeatures("AutofillAssistantInChromeTriggering")
+    public void triggerImplicitlyOnSupportedSite() {
+        AutofillAssistantTestServiceRequestSender testServiceRequestSender =
+                new AutofillAssistantTestServiceRequestSender();
+        testServiceRequestSender.setNextResponse(
+                /* httpStatus = */ 200, createDefaultTriggerScriptResponse("TriggerScript"));
+        testServiceRequestSender.scheduleForInjection();
+
+        mTestRule.loadUrl(getTargetWebsiteUrl(TEST_PAGE_UNSUPPORTED));
+        onView(withText("TriggerScript")).check(doesNotExist());
+
+        mTestRule.loadUrl(getTargetWebsiteUrl(TEST_PAGE_SUPPORTED));
+        // Note: allow for some extra time here to account for both the navigation and the start.
+        waitUntilViewMatchesCondition(
+                withText("TriggerScript"), isDisplayed(), 2 * DEFAULT_MAX_TIME_TO_POLL);
+
+        // Disabling the proactive help setting should stop the trigger script.
+        AutofillAssistantPreferencesUtil.setProactiveHelpSwitch(false);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> AutofillAssistantTabHelper
+                                   .get(TabModelUtils.getCurrentTab(
+                                           mTestRule.getActivity().getCurrentTabModel()))
+                                   .forceSettingsChangeNotificationForTesting());
+        waitUntilViewAssertionTrue(
+                withText("TriggerScript"), doesNotExist(), DEFAULT_POLLING_INTERVAL);
+
+        // Re-enabling the proactive help setting should restart the trigger script, since we are
+        // still on a supported URL.
+        testServiceRequestSender.setNextResponse(
+                /* httpStatus = */ 200, createDefaultTriggerScriptResponse("TriggerScript"));
+        AutofillAssistantPreferencesUtil.setProactiveHelpSwitch(true);
+        TestThreadUtils.runOnUiThreadBlocking(
+                ()
+                        -> AutofillAssistantTabHelper
+                                   .get(TabModelUtils.getCurrentTab(
+                                           mTestRule.getActivity().getCurrentTabModel()))
+                                   .forceSettingsChangeNotificationForTesting());
+        waitUntilViewMatchesCondition(withText("TriggerScript"), isDisplayed());
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
index 2b07e20a..205148f 100644
--- a/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
+++ b/chrome/android/features/autofill_assistant/public/java/src/org/chromium/chrome/browser/autofill_assistant/Starter.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.autofill_assistant;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.UserData;
 import org.chromium.base.annotations.CalledByNative;
@@ -139,6 +140,15 @@
         safeNativeOnInteractabilityChanged(isInteractable);
     }
 
+    /**
+     * Forces native to re-evaluate the Chrome settings. Integration tests may need to call this to
+     * ensure that programmatic updates to the Chrome settings are received by the native starter.
+     */
+    @VisibleForTesting
+    public void forceSettingsChangeNotificationForTesting() {
+        safeNativeOnInteractabilityChanged(true);
+    }
+
     private void safeNativeDetach() {
         if (mNativeStarter == 0) {
             return;
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
index 40ba6878..f45b9bcf 100644
--- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
+++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -2013,10 +2013,9 @@
     }
 
     private void waitForOverviewVisible() {
-        int callCount = mLayoutChangedCallbackHelper.getCallCount();
         if (mCurrentlyActiveLayout == LayoutType.TAB_SWITCHER) return;
         try {
-            mLayoutChangedCallbackHelper.waitForCallback(callCount);
+            mLayoutChangedCallbackHelper.waitForNext();
         } catch (TimeoutException ex) {
             assert false : "Timeout waiting for browser to enter tab switcher.";
         }
diff --git a/chrome/android/java/res/layout/autofill_save_address_profile_prompt.xml b/chrome/android/java/res/layout/autofill_save_address_profile_prompt.xml
index dc17d1f..aec3414 100644
--- a/chrome/android/java/res/layout/autofill_save_address_profile_prompt.xml
+++ b/chrome/android/java/res/layout/autofill_save_address_profile_prompt.xml
@@ -13,6 +13,7 @@
       android:layout_width="48dp"
       android:layout_height="48dp"
       android:layout_alignParentEnd="true"
+      android:layout_marginEnd="8dp"
       android:padding="12dp"
       android:background="?attr/selectableItemBackground"
       android:contentDescription="@string/payments_edit_address"
diff --git a/chrome/android/java/res/layout/autofill_update_address_profile_prompt.xml b/chrome/android/java/res/layout/autofill_update_address_profile_prompt.xml
index 2131ada..6d1b634 100644
--- a/chrome/android/java/res/layout/autofill_update_address_profile_prompt.xml
+++ b/chrome/android/java/res/layout/autofill_update_address_profile_prompt.xml
@@ -7,13 +7,13 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:paddingBottom="12dp">
 
   <TextView
       android:id="@+id/subtitle"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:layout_marginBottom="16dp"
       android:paddingStart="@dimen/dialog_padding_sides"
       android:paddingEnd="@dimen/dialog_padding_sides"
       android:ellipsize="end"
@@ -25,11 +25,20 @@
       android:layout_height="wrap_content"
       android:paddingStart="@dimen/dialog_padding_sides"
       tools:ignore="RtlSymmetry">
+
+    <Space
+        android:id="@+id/no_header_space"
+        android:layout_width="match_parent"
+        android:layout_height="25dp"
+        tools:visibility="gone" />
+
     <!-- TODO(crbug.com/1167061): Replace with proper localized string. -->
     <TextView
         android:id="@+id/header_new"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_below="@id/no_header_space"
         android:layout_toStartOf="@id/edit_button"
         android:text="New"
         android:textAppearance="@style/TextAppearance.TextMedium.Blue"
@@ -40,6 +49,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginBottom="12dp"
+        android:layout_marginTop="3dp"
         android:layout_below="@id/header_new"
         android:layout_toStartOf="@id/edit_button"
         android:textAppearance="@style/TextAppearance.TextLarge.Primary"
@@ -50,7 +60,10 @@
         android:id="@+id/edit_button"
         android:layout_width="48dp"
         android:layout_height="48dp"
+        android:layout_alignBaseline="@id/details_new"
         android:layout_alignParentEnd="true"
+        android:layout_marginEnd="8dp"
+        android:baseline="32dp"
         android:padding="12dp"
         android:background="?attr/selectableItemBackground"
         android:contentDescription="@string/payments_edit_address"
@@ -60,12 +73,6 @@
         tools:src="@drawable/edit_icon" />
   </RelativeLayout>
 
-  <View
-      android:id="@+id/details_separator"
-      style="@style/HorizontalDivider"
-      android:layout_marginStart="@dimen/dialog_padding_sides"
-      android:layout_marginEnd="@dimen/dialog_padding_sides" />
-
   <!-- TODO(crbug.com/1167061): Replace with proper localized string. -->
   <TextView
       android:id="@+id/header_old"
@@ -81,7 +88,7 @@
       android:id="@+id/details_old"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:layout_marginBottom="12dp"
+      android:layout_marginTop="3dp"
       android:paddingStart="@dimen/dialog_padding_sides"
       android:paddingEnd="@dimen/dialog_padding_sides"
       android:textAppearance="@style/TextAppearance.TextLarge.Primary"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveAddressProfilePrompt.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveAddressProfilePrompt.java
index 81bf568a..851651f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveAddressProfilePrompt.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/SaveAddressProfilePrompt.java
@@ -129,7 +129,8 @@
     }
 
     /**
-     * Displays the details in case an existing address to be updated.
+     * Displays the details in case an existing address to be updated. If oldDetails are empty, only
+     * newDetails are shown.
      *
      * @param subtitle the text to display below the title.
      * @param oldDetails details in the existing profile that differ.
@@ -137,8 +138,8 @@
      */
     @CalledByNative
     private void setUpdateDetails(String subtitle, String oldDetails, String newDetails) {
-        // TODO(crbug.com/1167061): Properly handle the case when oldDetails is empty.
         ((TextView) mDialogView.findViewById(R.id.subtitle)).setText(subtitle);
+        showHeaders(!TextUtils.isEmpty(oldDetails));
         showTextIfNotEmpty(mDialogView.findViewById(R.id.details_old), oldDetails);
         showTextIfNotEmpty(mDialogView.findViewById(R.id.details_new), newDetails);
     }
@@ -181,4 +182,11 @@
             textView.setText(text);
         }
     }
+
+    private void showHeaders(boolean show) {
+        mDialogView.findViewById(R.id.header_new).setVisibility(show ? View.VISIBLE : View.GONE);
+        mDialogView.findViewById(R.id.header_old).setVisibility(show ? View.VISIBLE : View.GONE);
+        mDialogView.findViewById(R.id.no_header_space)
+                .setVisibility(show ? View.GONE : View.VISIBLE);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
index fa1f293b..270e63c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
@@ -140,12 +140,9 @@
             assert mSheetContent == null;
             createWebContents(profile);
             mSheetObserver = new EmptyBottomSheetObserver() {
-                private int mCloseReason;
-
                 @Override
                 public void onSheetContentChanged(BottomSheetContent newContent) {
                     if (newContent != mSheetContent) {
-                        mMetrics.recordMetricsForClosed(mCloseReason);
                         mPeeked = false;
                         destroyWebContents();
                     }
@@ -179,13 +176,6 @@
                 }
 
                 @Override
-                public void onSheetClosed(int reason) {
-                    // "Closed" actually means "Peek" for bottom sheet. Save the reason to
-                    // log when the sheet goes to hidden state. See http://crbug.com/986310.
-                    mCloseReason = reason;
-                }
-
-                @Override
                 public void onSheetOffsetChanged(float heightFraction, float offsetPx) {
                     if (mSheetContent == null) return;
                     if (mCanPromoteToNewTab) mSheetContent.showOpenInNewTabButton(heightFraction);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabMetrics.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabMetrics.java
index 66fc0cb1..d0beb5863 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabMetrics.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabMetrics.java
@@ -7,7 +7,6 @@
 import org.chromium.base.TimeUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
-import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 
 /**
  * Metrics util class for ephemeral tab.
@@ -53,21 +52,19 @@
     }
 
     /** Records metrics when the panel has been closed. */
-    public void recordMetricsForClosed(@StateChangeReason int stateChangeReason) {
+    private void recordMetricsForClosed() {
         if (!mIsVisible) return;
 
         finishPeekTimer();
         finishOpenTimer();
         RecordHistogram.recordBooleanHistogram("EphemeralTab.CtrPeek", mIsViewed);
         RecordHistogram.recordBooleanHistogram("EphemeralTab.Ctr", mDidRecordFirstOpen);
-        RecordHistogram.recordEnumeratedHistogram("EphemeralTab.BottomSheet.CloseReason",
-                stateChangeReason, StateChangeReason.MAX_VALUE + 1);
         reset();
     }
 
     /** Records a user action that promotes the ephemeral tab to a full tab. */
     public void recordOpenInNewTab() {
-        recordMetricsForClosed(StateChangeReason.PROMOTE_TAB);
+        recordMetricsForClosed();
         RecordUserAction.record("EphemeralTab.OpenInNewTab");
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java
index 57eecb45..0db0e7e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheet.java
@@ -21,10 +21,11 @@
      */
     interface Delegate {
         /**
-         * @param {@code true} if the requested history is of forward navigation.
+         * @param forward {@code true} if the requested history is of forward navigation.
+         * @param isOffTheRecord {@code true} if the history is called from incognito mode.
          * @return {@link NavigationHistory} object.
          */
-        NavigationHistory getHistory(boolean forward);
+        NavigationHistory getHistory(boolean forward, boolean isOffTheRecord);
 
         /**
          * Navigates to the page associated with the given index.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
index b3d1a56..df85d13b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetCoordinator.java
@@ -114,6 +114,8 @@
     // no peek/half state.
     private boolean mFullyExpand;
 
+    private Profile mProfile;
+
     /**
      * Construct a new NavigationSheet.
      */
@@ -123,6 +125,7 @@
         mBottomSheetController = bottomSheetController;
         mLayoutInflater = LayoutInflater.from(context);
         mToolbarView = mLayoutInflater.inflate(R.layout.navigation_sheet_toolbar, null);
+        mProfile = profile;
         mMediator = new NavigationSheetMediator(context, mModelList, profile, (position, index) -> {
             mDelegate.navigateToIndex(index);
             close(false);
@@ -162,7 +165,10 @@
                 (NavigationSheetView) mLayoutInflater.inflate(R.layout.navigation_sheet, null);
         ListView listview = (ListView) mContentView.findViewById(R.id.navigation_entries);
         listview.setAdapter(mModelAdapter);
-        NavigationHistory history = mDelegate.getHistory(mForward);
+        NavigationHistory history = mDelegate.getHistory(mForward, mProfile.isOffTheRecord());
+        // If there is no entry, the sheet should not be opened. This is the case when in a fresh
+        // Incognito NTP.
+        if (history.getEntryCount() == 0) return false;
         mMediator.populateEntries(history);
         if (!mBottomSheetController.get().requestShowContent(this, true)) {
             close(false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
index 5658c58e..a6a2fd6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationSheetMediator.java
@@ -14,6 +14,7 @@
 import android.view.View;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconUtils;
@@ -37,6 +38,8 @@
  * Mediator class for navigation sheet.
  */
 class NavigationSheetMediator {
+    private static final String INCOGNITO_HISTORY_ENTRIES_FLAG =
+            ChromeFeatureList.UPDATE_HISTORY_ENTRY_POINTS_IN_INCOGNITO;
     private final ClickListener mClickListener;
     private final FaviconHelper mFaviconHelper;
     private final RoundedIconGenerator mIconGenerator;
@@ -44,7 +47,9 @@
     private final ModelList mModelList;
     private final Drawable mHistoryIcon;
     private final Drawable mDefaultIcon;
+    private final Drawable mIncognitoIcon;
     private final String mNewTabText;
+    private final String mNewIncognitoTabText;
     private final Profile mProfile;
 
     private NavigationHistory mHistory;
@@ -88,7 +93,10 @@
                 context, R.drawable.ic_history_googblue_24dp, R.color.default_icon_color);
         mDefaultIcon = TintedDrawable.constructTintedDrawable(
                 context, R.drawable.ic_chrome, R.color.default_icon_color);
+        mIncognitoIcon = TintedDrawable.constructTintedDrawable(
+                context, R.drawable.incognito_small, R.color.default_icon_color);
         mNewTabText = context.getResources().getString(R.string.menu_new_tab);
+        mNewIncognitoTabText = context.getResources().getString(R.string.menu_new_incognito_tab);
     }
 
     /**
@@ -143,7 +151,7 @@
                 Drawable drawable;
                 if (favicon == null) {
                     drawable = UrlUtilities.isNTPUrl(pageUrl)
-                            ? mDefaultIcon
+                            ? getNTPIcon()
                             : new BitmapDrawable(mIconGenerator.generateIconForUrl(pageUrl));
                 } else {
                     drawable = new BitmapDrawable(favicon);
@@ -155,9 +163,23 @@
 
     private String getEntryText(NavigationEntry entry) {
         String entryText = entry.getTitle();
-        if (UrlUtilities.isNTPUrl(entry.getUrl())) entryText = mNewTabText;
+        if (UrlUtilities.isNTPUrl(entry.getUrl())) entryText = getNTPText();
         if (TextUtils.isEmpty(entryText)) entryText = entry.getVirtualUrl().getSpec();
         if (TextUtils.isEmpty(entryText)) entryText = entry.getUrl().getSpec();
         return entryText;
     }
+
+    private Drawable getNTPIcon() {
+        return mProfile.isOffTheRecord()
+                        && ChromeFeatureList.isEnabled(INCOGNITO_HISTORY_ENTRIES_FLAG)
+                ? mIncognitoIcon
+                : mDefaultIcon;
+    }
+
+    private String getNTPText() {
+        return mProfile.isOffTheRecord()
+                        && ChromeFeatureList.isEnabled(INCOGNITO_HISTORY_ENTRIES_FLAG)
+                ? mNewIncognitoTabText
+                : mNewTabText;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java
index 3c107de0..e265a444 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/TabbedSheetDelegate.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.gesturenav;
 
 import org.chromium.base.Consumer;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.NavigationEntry;
@@ -18,6 +19,8 @@
 public class TabbedSheetDelegate implements NavigationSheet.Delegate {
     private static final int MAXIMUM_HISTORY_ITEMS = 8;
     private static final int FULL_HISTORY_ENTRY_INDEX = -1;
+    private static final String INCOGNITO_HISTORY_ENTRIES_FLAG =
+            ChromeFeatureList.UPDATE_HISTORY_ENTRY_POINTS_IN_INCOGNITO;
 
     private final Tab mTab;
     private final Consumer<Tab> mShowHistoryManager;
@@ -30,13 +33,15 @@
     }
 
     @Override
-    public NavigationHistory getHistory(boolean forward) {
+    public NavigationHistory getHistory(boolean forward, boolean isOffTheRecord) {
         NavigationHistory history =
                 mTab.getWebContents().getNavigationController().getDirectedNavigationHistory(
                         forward, MAXIMUM_HISTORY_ITEMS);
-        history.addEntry(new NavigationEntry(FULL_HISTORY_ENTRY_INDEX,
-                new GURL(UrlConstants.HISTORY_URL), GURL.emptyGURL(), GURL.emptyGURL(),
-                GURL.emptyGURL(), mFullHistoryMenu, null, 0, 0));
+        if (!isOffTheRecord || !ChromeFeatureList.isEnabled(INCOGNITO_HISTORY_ENTRIES_FLAG)) {
+            history.addEntry(new NavigationEntry(FULL_HISTORY_ENTRY_INDEX,
+                    new GURL(UrlConstants.HISTORY_URL), GURL.emptyGURL(), GURL.emptyGURL(),
+                    GURL.emptyGURL(), mFullHistoryMenu, null, 0, 0));
+        }
         return history;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 93e0e62..ecc7f4c0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -135,7 +135,6 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
-import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.content_public.browser.test.util.ClickUtils;
 import org.chromium.content_public.browser.test.util.DOMUtils;
 import org.chromium.content_public.browser.test.util.JavaScriptUtils;
@@ -174,30 +173,6 @@
     private static final String FRAGMENT_TEST_PAGE = "/chrome/test/data/android/fragment.html";
     private static final String TARGET_BLANK_TEST_PAGE =
             "/chrome/test/data/android/cct_target_blank.html";
-    private static final String JS_MESSAGE = "from_js";
-    private static final String TITLE_FROM_POSTMESSAGE_TO_CHANNEL =
-            "<!DOCTYPE html><html><body>"
-            + "    <script>"
-            + "        var received = '';"
-            + "        onmessage = function (e) {"
-            + "            var myport = e.ports[0];"
-            + "            myport.onmessage = function (f) {"
-            + "                received += f.data;"
-            + "                document.title = received;"
-            + "            }"
-            + "        }"
-            + "   </script>"
-            + "</body></html>";
-    private static final String MESSAGE_FROM_PAGE_TO_CHANNEL =
-            "<!DOCTYPE html><html><body>"
-            + "    <script>"
-            + "        onmessage = function (e) {"
-            + "            if (e.ports != null && e.ports.length > 0) {"
-            + "               e.ports[0].postMessage(\"" + JS_MESSAGE + "\");"
-            + "            }"
-            + "        }"
-            + "   </script>"
-            + "</body></html>";
     private static final String ONLOAD_TITLE_CHANGE = "<!DOCTYPE html><html><body>"
             + "    <script>"
             + "        window.onload = function () {"
@@ -1143,326 +1118,6 @@
     }
 
     /**
-     * Tests that basic postMessage functionality works through sending a single postMessage
-     * request.
-     */
-    @Test
-    @SmallTest
-    public void testPostMessageBasic() throws Exception {
-        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
-        Context context = InstrumentationRegistry.getTargetContext();
-        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
-        final CustomTabsSessionToken token =
-                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
-        Assert.assertTrue(connection.newSession(token));
-        Assert.assertTrue(connection.requestPostMessageChannel(token, null));
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CriteriaHelper.pollInstrumentationThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
-        });
-        Assert.assertTrue(
-                connection.postMessage(token, "Message", null) == CustomTabsService.RESULT_SUCCESS);
-        TestThreadUtils.runOnUiThreadBlocking(
-                (Runnable) () -> mCustomTabActivityTestRule.getActivity().getActivityTab().loadUrl(
-                                new LoadUrlParams(mTestPage2)));
-        CriteriaHelper.pollUiThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            return ChromeTabUtils.isLoadingAndRenderingDone(currentTab);
-        });
-        Assert.assertTrue(connection.postMessage(token, "Message", null)
-                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
-    }
-
-    /**
-     * Tests that postMessage channel is not functioning after web contents get destroyed and also
-     * not breaking things.
-     */
-    @Test
-    @SmallTest
-    public void testPostMessageWebContentsDestroyed() throws Exception {
-        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
-        Context context = InstrumentationRegistry.getTargetContext();
-        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
-        final CustomTabsSessionToken token =
-                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
-        Assert.assertTrue(connection.newSession(token));
-        Assert.assertTrue(connection.requestPostMessageChannel(token, null));
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CriteriaHelper.pollInstrumentationThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
-        });
-        Assert.assertTrue(
-                connection.postMessage(token, "Message", null) == CustomTabsService.RESULT_SUCCESS);
-
-        final CallbackHelper renderProcessCallback = new CallbackHelper();
-        new WebContentsObserver(mCustomTabActivityTestRule.getWebContents()) {
-            @Override
-            public void renderProcessGone(boolean wasOomProtected) {
-                renderProcessCallback.notifyCalled();
-            }
-        };
-        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
-            WebContentsUtils.simulateRendererKilled(
-                    mCustomTabActivityTestRule.getActivity().getActivityTab().getWebContents(),
-                    false);
-        });
-        renderProcessCallback.waitForCallback(0);
-        Assert.assertTrue(connection.postMessage(token, "Message", null)
-                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
-    }
-
-    /**
-     * Tests whether validatePostMessageOrigin is necessary for making successful postMessage
-     * requests.
-     */
-    @Test
-    @SmallTest
-    public void testPostMessageRequiresValidation() throws Exception {
-        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
-        Context context = InstrumentationRegistry.getTargetContext();
-        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
-        final CustomTabsSessionToken token =
-                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
-        Assert.assertTrue(connection.newSession(token));
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CriteriaHelper.pollInstrumentationThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
-        });
-        Assert.assertTrue(connection.postMessage(token, "Message", null)
-                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
-    }
-
-    /**
-     * Tests the sent postMessage requests not only return success, but is also received by page.
-     */
-    @Test
-    @SmallTest
-    public void testPostMessageReceivedInPage() throws Exception {
-        final String url =
-                mWebServer.setResponse("/test.html", TITLE_FROM_POSTMESSAGE_TO_CHANNEL, null);
-        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
-        Context context = InstrumentationRegistry.getTargetContext();
-        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, url);
-        final CustomTabsSessionToken token =
-                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
-        Assert.assertTrue(connection.newSession(token));
-        Assert.assertTrue(connection.requestPostMessageChannel(token, null));
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CriteriaHelper.pollInstrumentationThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
-        });
-        Assert.assertTrue(connection.postMessage(token, "New title", null)
-                == CustomTabsService.RESULT_SUCCESS);
-        waitForTitle("New title");
-    }
-
-    /**
-     * Tests the postMessage requests sent from the page is received on the client side.
-     */
-    @Test
-    @SmallTest
-    public void testPostMessageReceivedFromPage() throws Exception {
-        final CallbackHelper messageChannelHelper = new CallbackHelper();
-        final CallbackHelper onPostMessageHelper = new CallbackHelper();
-        final String url = mWebServer.setResponse("/test.html", MESSAGE_FROM_PAGE_TO_CHANNEL, null);
-        CustomTabsTestUtils.warmUpAndWait();
-        final CustomTabsSession session =
-                CustomTabsTestUtils.bindWithCallback(new CustomTabsCallback() {
-                    @Override
-                    public void onMessageChannelReady(Bundle extras) {
-                        messageChannelHelper.notifyCalled();
-                    }
-
-                    @Override
-                    public void onPostMessage(String message, Bundle extras) {
-                        onPostMessageHelper.notifyCalled();
-                    }
-                }).session;
-        session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
-        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
-        intent.setData(Uri.parse(url));
-        intent.setComponent(new ComponentName(
-                InstrumentationRegistry.getTargetContext(), ChromeLauncherActivity.class));
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        Assert.assertTrue(session.postMessage("Message", null)
-                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
-
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        messageChannelHelper.waitForCallback(0);
-        onPostMessageHelper.waitForCallback(0);
-    }
-
-    /**
-     * Tests the postMessage requests sent from the page is received on the client side even though
-     * the request is sent after the page is created.
-     */
-    @Test
-    @SmallTest
-    @DisabledTest(message = "https://crbug.com/692025")
-    public void testPostMessageReceivedFromPageWithLateRequest() throws Exception {
-        final CallbackHelper messageChannelHelper = new CallbackHelper();
-        final CallbackHelper onPostMessageHelper = new CallbackHelper();
-        final String url = mWebServer.setResponse("/test.html", MESSAGE_FROM_PAGE_TO_CHANNEL, null);
-        CustomTabsTestUtils.warmUpAndWait();
-        final CustomTabsSession session =
-                CustomTabsTestUtils.bindWithCallback(new CustomTabsCallback() {
-                    @Override
-                    public void onMessageChannelReady(Bundle extras) {
-                        messageChannelHelper.notifyCalled();
-                    }
-
-                    @Override
-                    public void onPostMessage(String message, Bundle extras) {
-                        onPostMessageHelper.notifyCalled();
-                    }
-                }).session;
-
-        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
-        intent.setData(Uri.parse(url));
-        intent.setComponent(new ComponentName(
-                InstrumentationRegistry.getTargetContext(), ChromeLauncherActivity.class));
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        CriteriaHelper.pollInstrumentationThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
-        });
-
-        session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
-
-        messageChannelHelper.waitForCallback(0);
-        onPostMessageHelper.waitForCallback(0);
-
-        Assert.assertTrue(session.postMessage("Message", null) == CustomTabsService.RESULT_SUCCESS);
-    }
-
-    private static final int BEFORE_MAY_LAUNCH_URL = 0;
-    private static final int BEFORE_INTENT = 1;
-    private static final int AFTER_INTENT = 2;
-
-    /**
-     * Tests a postMessage request chain can start while loading a hidden tab and continue
-     * afterwards. Request sent before the hidden tab start.
-     */
-    @Test
-    @SmallTest
-    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
-    public void testPostMessageThroughHiddenTabWithRequestBeforeMayLaunchUrl() throws Exception {
-        sendPostMessageDuringHiddenTabTransition(BEFORE_MAY_LAUNCH_URL);
-    }
-
-    /**
-     * Tests a postMessage request chain can start while loading a hidden tab and continue
-     * afterwards. Request sent after the hidden tab start and before intent launched.
-     */
-    @Test
-    @SmallTest
-    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
-    public void testPostMessageThroughHiddenTabWithRequestBeforeIntent() throws Exception {
-        sendPostMessageDuringHiddenTabTransition(BEFORE_INTENT);
-    }
-
-    /**
-     * Tests a postMessage request chain can start while loading a hidden tab and continue
-     * afterwards. Request sent after intent received.
-     */
-    @Test
-    @SmallTest
-    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
-    public void testPostMessageThroughHiddenTabWithRequestAfterIntent() throws Exception {
-        sendPostMessageDuringHiddenTabTransition(AFTER_INTENT);
-    }
-
-    private void sendPostMessageDuringHiddenTabTransition(int requestTime) throws Exception {
-        final CallbackHelper messageChannelHelper = new CallbackHelper();
-        final String url =
-                mWebServer.setResponse("/test.html", TITLE_FROM_POSTMESSAGE_TO_CHANNEL, null);
-        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
-
-        final CustomTabsSession session =
-                CustomTabsTestUtils.bindWithCallback(new CustomTabsCallback() {
-                    @Override
-                    public void onMessageChannelReady(Bundle extras) {
-                        messageChannelHelper.notifyCalled();
-                    }
-                }).session;
-
-        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
-        intent.setData(Uri.parse(url));
-        intent.setComponent(new ComponentName(
-                InstrumentationRegistry.getTargetContext(), ChromeLauncherActivity.class));
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
-
-        boolean channelRequested = false;
-        String titleString = "";
-
-        if (requestTime == BEFORE_MAY_LAUNCH_URL) {
-            channelRequested =
-                    session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
-            Assert.assertTrue(channelRequested);
-        }
-
-        setCanUseHiddenTabForSession(connection, token, true);
-        session.mayLaunchUrl(Uri.parse(url), null, null);
-        ensureCompletedSpeculationForUrl(connection, url);
-
-        if (requestTime == BEFORE_INTENT) {
-            channelRequested =
-                    session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
-            Assert.assertTrue(channelRequested);
-        }
-
-        if (channelRequested) {
-            messageChannelHelper.waitForCallback(0);
-            String currentMessage = "Prerendering ";
-            // Initial title update during prerender.
-            assertEquals(
-                    CustomTabsService.RESULT_SUCCESS, session.postMessage(currentMessage, null));
-            titleString = currentMessage;
-        }
-
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-
-        CriteriaHelper.pollInstrumentationThread(() -> {
-            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
-        });
-
-        if (requestTime == AFTER_INTENT) {
-            channelRequested =
-                    session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
-            Assert.assertTrue(channelRequested);
-            messageChannelHelper.waitForCallback(0);
-        }
-
-        String currentMessage = "and loading ";
-        // Update title again and verify both updates went through with the channel still intact.
-        assertEquals(
-                CustomTabsService.RESULT_SUCCESS, session.postMessage(currentMessage, null));
-        titleString += currentMessage;
-
-        // Request a new channel, verify it was created.
-        session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
-        messageChannelHelper.waitForCallback(1);
-
-        String newMessage = "and refreshing";
-        // Update title again and verify both updates went through with the channel still intact.
-        assertEquals(
-                CustomTabsService.RESULT_SUCCESS, session.postMessage(newMessage, null));
-        titleString += newMessage;
-
-        final String title = titleString;
-        waitForTitle(title);
-    }
-
-    /**
      * Tests that when we use a pre-created renderer, the page loaded is the
      * only one in the navigation history.
      */
@@ -2779,11 +2434,6 @@
         return historyObserver.getHistoryQueryResults();
     }
 
-    private void waitForTitle(String newTitle) {
-        Tab currentTab = getActivity().getActivityTab();
-        ChromeTabUtils.waitForTitle(currentTab, newTitle);
-    }
-
     private SessionDataHolder getSessionDataHolder() {
         return ChromeApplicationImpl.getComponent().resolveSessionDataHolder();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabPostMessageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabPostMessageTest.java
new file mode 100644
index 0000000..f5e2e92e
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabPostMessageTest.java
@@ -0,0 +1,456 @@
+// 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.customtabs;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+
+import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
+import static org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule.LONG_TIMEOUT_MS;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+
+import androidx.browser.customtabs.CustomTabsCallback;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsService;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.browser.customtabs.CustomTabsSessionToken;
+import androidx.test.filters.SmallTest;
+
+import org.hamcrest.Matchers;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.task.PostTask;
+import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Criteria;
+import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.Restriction;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
+import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.content_public.browser.test.util.TestThreadUtils;
+import org.chromium.content_public.browser.test.util.WebContentsUtils;
+import org.chromium.net.test.util.TestWebServer;
+
+/**
+ * Integration tests for the Custom Tab post message support.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class CustomTabPostMessageTest {
+    private static final int BEFORE_MAY_LAUNCH_URL = 0;
+    private static final int BEFORE_INTENT = 1;
+    private static final int AFTER_INTENT = 2;
+
+    private static final String JS_MESSAGE = "from_js";
+    private static final String TEST_PAGE = "/chrome/test/data/android/google.html";
+    private static final String TEST_PAGE_2 = "/chrome/test/data/android/test.html";
+
+    private static final String TITLE_FROM_POSTMESSAGE_TO_CHANNEL = "<!DOCTYPE html><html><body>"
+            + "    <script>"
+            + "        var received = '';"
+            + "        onmessage = function (e) {"
+            + "            var myport = e.ports[0];"
+            + "            myport.onmessage = function (f) {"
+            + "                received += f.data;"
+            + "                document.title = received;"
+            + "            }"
+            + "        }"
+            + "   </script>"
+            + "</body></html>";
+    private static final String MESSAGE_FROM_PAGE_TO_CHANNEL = "<!DOCTYPE html><html><body>"
+            + "    <script>"
+            + "        onmessage = function (e) {"
+            + "            if (e.ports != null && e.ports.length > 0) {"
+            + "               e.ports[0].postMessage(\"" + JS_MESSAGE + "\");"
+            + "            }"
+            + "        }"
+            + "   </script>"
+            + "</body></html>";
+
+    @Rule
+    public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
+
+    private String mTestPage;
+    private String mTestPage2;
+    private CustomTabsConnection mConnectionToCleanup;
+    private TestWebServer mWebServer;
+
+    @Before
+    public void setUp() throws Exception {
+        mTestPage = mCustomTabActivityTestRule.getTestServer().getURL(TEST_PAGE);
+        mTestPage2 = mCustomTabActivityTestRule.getTestServer().getURL(TEST_PAGE_2);
+        LibraryLoader.getInstance().ensureInitialized();
+        mWebServer = TestWebServer.start();
+    }
+
+    @After
+    public void tearDown() {
+        if (mConnectionToCleanup != null) CustomTabsTestUtils.cleanupSessions(mConnectionToCleanup);
+        if (mWebServer != null) mWebServer.shutdown();
+    }
+
+    private void waitForTitle(String newTitle) {
+        Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+        ChromeTabUtils.waitForTitle(currentTab, newTitle);
+    }
+
+    private void setCanUseHiddenTabForSession(
+            CustomTabsConnection connection, CustomTabsSessionToken token, boolean useHiddenTab) {
+        assert mConnectionToCleanup == null || mConnectionToCleanup == connection;
+        // Save the connection. In case the hidden tab is not consumed by the test, ensure that it
+        // is properly cleaned up after the test.
+        mConnectionToCleanup = connection;
+        connection.setCanUseHiddenTabForSession(token, useHiddenTab);
+    }
+
+    private static void ensureCompletedSpeculationForUrl(
+            final CustomTabsConnection connection, final String url) {
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat("Tab was not created", connection.getSpeculationParamsForTesting(),
+                    Matchers.notNullValue());
+        }, LONG_TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
+        ChromeTabUtils.waitForTabPageLoaded(connection.getSpeculationParamsForTesting().tab, url);
+    }
+
+    /**
+     * Tests that basic postMessage functionality works through sending a single postMessage
+     * request.
+     */
+    @Test
+    @SmallTest
+    public void testPostMessageBasic() throws Exception {
+        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
+        final CustomTabsSessionToken token =
+                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+        Assert.assertTrue(connection.newSession(token));
+        Assert.assertTrue(connection.requestPostMessageChannel(token, null));
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+        });
+        Assert.assertTrue(
+                connection.postMessage(token, "Message", null) == CustomTabsService.RESULT_SUCCESS);
+        TestThreadUtils.runOnUiThreadBlocking(
+                (Runnable) ()
+                        -> mCustomTabActivityTestRule.getActivity().getActivityTab().loadUrl(
+                                new LoadUrlParams(mTestPage2)));
+        CriteriaHelper.pollUiThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            return ChromeTabUtils.isLoadingAndRenderingDone(currentTab);
+        });
+        Assert.assertTrue(connection.postMessage(token, "Message", null)
+                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
+    }
+
+    /**
+     * Tests that postMessage channel is not functioning after web contents get destroyed and also
+     * not breaking things.
+     */
+    @Test
+    @SmallTest
+    public void testPostMessageWebContentsDestroyed() throws Exception {
+        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
+        final CustomTabsSessionToken token =
+                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+        Assert.assertTrue(connection.newSession(token));
+        Assert.assertTrue(connection.requestPostMessageChannel(token, null));
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+        });
+        Assert.assertTrue(
+                connection.postMessage(token, "Message", null) == CustomTabsService.RESULT_SUCCESS);
+
+        final CallbackHelper renderProcessCallback = new CallbackHelper();
+        new WebContentsObserver(mCustomTabActivityTestRule.getWebContents()) {
+            @Override
+            public void renderProcessGone(boolean wasOomProtected) {
+                renderProcessCallback.notifyCalled();
+            }
+        };
+        PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
+            WebContentsUtils.simulateRendererKilled(
+                    mCustomTabActivityTestRule.getActivity().getActivityTab().getWebContents(),
+                    false);
+        });
+        renderProcessCallback.waitForCallback(0);
+        Assert.assertTrue(connection.postMessage(token, "Message", null)
+                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
+    }
+
+    /**
+     * Tests whether validatePostMessageOrigin is necessary for making successful postMessage
+     * requests.
+     */
+    @Test
+    @SmallTest
+    public void testPostMessageRequiresValidation() throws Exception {
+        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
+        final CustomTabsSessionToken token =
+                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+        Assert.assertTrue(connection.newSession(token));
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(mTestPage));
+        });
+        Assert.assertTrue(connection.postMessage(token, "Message", null)
+                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
+    }
+
+    /**
+     * Tests the sent postMessage requests not only return success, but is also received by page.
+     */
+    @Test
+    @SmallTest
+    public void testPostMessageReceivedInPage() throws Exception {
+        final String url =
+                mWebServer.setResponse("/test.html", TITLE_FROM_POSTMESSAGE_TO_CHANNEL, null);
+        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
+        Context context = InstrumentationRegistry.getTargetContext();
+        Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, url);
+        final CustomTabsSessionToken token =
+                CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+        Assert.assertTrue(connection.newSession(token));
+        Assert.assertTrue(connection.requestPostMessageChannel(token, null));
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+        });
+        Assert.assertTrue(connection.postMessage(token, "New title", null)
+                == CustomTabsService.RESULT_SUCCESS);
+        waitForTitle("New title");
+    }
+
+    /**
+     * Tests the postMessage requests sent from the page is received on the client side.
+     */
+    @Test
+    @SmallTest
+    public void testPostMessageReceivedFromPage() throws Exception {
+        final CallbackHelper messageChannelHelper = new CallbackHelper();
+        final CallbackHelper onPostMessageHelper = new CallbackHelper();
+        final String url = mWebServer.setResponse("/test.html", MESSAGE_FROM_PAGE_TO_CHANNEL, null);
+        CustomTabsTestUtils.warmUpAndWait();
+        final CustomTabsSession session =
+                CustomTabsTestUtils
+                        .bindWithCallback(new CustomTabsCallback() {
+                            @Override
+                            public void onMessageChannelReady(Bundle extras) {
+                                messageChannelHelper.notifyCalled();
+                            }
+
+                            @Override
+                            public void onPostMessage(String message, Bundle extras) {
+                                onPostMessageHelper.notifyCalled();
+                            }
+                        })
+                        .session;
+        session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
+        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
+        intent.setData(Uri.parse(url));
+        intent.setComponent(new ComponentName(
+                InstrumentationRegistry.getTargetContext(), ChromeLauncherActivity.class));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        Assert.assertTrue(session.postMessage("Message", null)
+                == CustomTabsService.RESULT_FAILURE_MESSAGING_ERROR);
+
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        messageChannelHelper.waitForCallback(0);
+        onPostMessageHelper.waitForCallback(0);
+    }
+
+    /**
+     * Tests the postMessage requests sent from the page is received on the client side even though
+     * the request is sent after the page is created.
+     */
+    @Test
+    @SmallTest
+    @DisabledTest(message = "https://crbug.com/692025")
+    public void testPostMessageReceivedFromPageWithLateRequest() throws Exception {
+        final CallbackHelper messageChannelHelper = new CallbackHelper();
+        final CallbackHelper onPostMessageHelper = new CallbackHelper();
+        final String url = mWebServer.setResponse("/test.html", MESSAGE_FROM_PAGE_TO_CHANNEL, null);
+        CustomTabsTestUtils.warmUpAndWait();
+        final CustomTabsSession session =
+                CustomTabsTestUtils
+                        .bindWithCallback(new CustomTabsCallback() {
+                            @Override
+                            public void onMessageChannelReady(Bundle extras) {
+                                messageChannelHelper.notifyCalled();
+                            }
+
+                            @Override
+                            public void onPostMessage(String message, Bundle extras) {
+                                onPostMessageHelper.notifyCalled();
+                            }
+                        })
+                        .session;
+
+        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
+        intent.setData(Uri.parse(url));
+        intent.setComponent(new ComponentName(
+                InstrumentationRegistry.getTargetContext(), ChromeLauncherActivity.class));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+        });
+
+        session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
+
+        messageChannelHelper.waitForCallback(0);
+        onPostMessageHelper.waitForCallback(0);
+
+        Assert.assertTrue(session.postMessage("Message", null) == CustomTabsService.RESULT_SUCCESS);
+    }
+
+    /**
+     * Tests a postMessage request chain can start while loading a hidden tab and continue
+     * afterwards. Request sent before the hidden tab start.
+     */
+    @Test
+    @SmallTest
+    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
+    public void testPostMessageThroughHiddenTabWithRequestBeforeMayLaunchUrl() throws Exception {
+        sendPostMessageDuringHiddenTabTransition(BEFORE_MAY_LAUNCH_URL);
+    }
+
+    /**
+     * Tests a postMessage request chain can start while loading a hidden tab and continue
+     * afterwards. Request sent after the hidden tab start and before intent launched.
+     */
+    @Test
+    @SmallTest
+    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
+    public void testPostMessageThroughHiddenTabWithRequestBeforeIntent() throws Exception {
+        sendPostMessageDuringHiddenTabTransition(BEFORE_INTENT);
+    }
+
+    /**
+     * Tests a postMessage request chain can start while loading a hidden tab and continue
+     * afterwards. Request sent after intent received.
+     */
+    @Test
+    @SmallTest
+    @Restriction(RESTRICTION_TYPE_NON_LOW_END_DEVICE)
+    public void testPostMessageThroughHiddenTabWithRequestAfterIntent() throws Exception {
+        sendPostMessageDuringHiddenTabTransition(AFTER_INTENT);
+    }
+
+    private void sendPostMessageDuringHiddenTabTransition(int requestTime) throws Exception {
+        final CallbackHelper messageChannelHelper = new CallbackHelper();
+        final String url =
+                mWebServer.setResponse("/test.html", TITLE_FROM_POSTMESSAGE_TO_CHANNEL, null);
+        final CustomTabsConnection connection = CustomTabsTestUtils.warmUpAndWait();
+
+        final CustomTabsSession session =
+                CustomTabsTestUtils
+                        .bindWithCallback(new CustomTabsCallback() {
+                            @Override
+                            public void onMessageChannelReady(Bundle extras) {
+                                messageChannelHelper.notifyCalled();
+                            }
+                        })
+                        .session;
+
+        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
+        intent.setData(Uri.parse(url));
+        intent.setComponent(new ComponentName(
+                InstrumentationRegistry.getTargetContext(), ChromeLauncherActivity.class));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
+
+        boolean channelRequested = false;
+        String titleString = "";
+
+        if (requestTime == BEFORE_MAY_LAUNCH_URL) {
+            channelRequested =
+                    session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
+            Assert.assertTrue(channelRequested);
+        }
+
+        setCanUseHiddenTabForSession(connection, token, true);
+        session.mayLaunchUrl(Uri.parse(url), null, null);
+        ensureCompletedSpeculationForUrl(connection, url);
+
+        if (requestTime == BEFORE_INTENT) {
+            channelRequested =
+                    session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
+            Assert.assertTrue(channelRequested);
+        }
+
+        if (channelRequested) {
+            messageChannelHelper.waitForCallback(0);
+            String currentMessage = "Prerendering ";
+            // Initial title update during prerender.
+            assertEquals(
+                    CustomTabsService.RESULT_SUCCESS, session.postMessage(currentMessage, null));
+            titleString = currentMessage;
+        }
+
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            final Tab currentTab = mCustomTabActivityTestRule.getActivity().getActivityTab();
+            Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(currentTab), is(url));
+        });
+
+        if (requestTime == AFTER_INTENT) {
+            channelRequested =
+                    session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
+            Assert.assertTrue(channelRequested);
+            messageChannelHelper.waitForCallback(0);
+        }
+
+        String currentMessage = "and loading ";
+        // Update title again and verify both updates went through with the channel still intact.
+        assertEquals(CustomTabsService.RESULT_SUCCESS, session.postMessage(currentMessage, null));
+        titleString += currentMessage;
+
+        // Request a new channel, verify it was created.
+        session.requestPostMessageChannel(Uri.parse("https://www.example.com/"));
+        messageChannelHelper.waitForCallback(1);
+
+        String newMessage = "and refreshing";
+        // Update title again and verify both updates went through with the channel still intact.
+        assertEquals(CustomTabsService.RESULT_SUCCESS, session.postMessage(newMessage, null));
+        titleString += newMessage;
+
+        final String title = titleString;
+        waitForTitle(title);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java
index 2237d3967..673568a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationSheetTest.java
@@ -26,6 +26,7 @@
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.gesturenav.NavigationSheetMediator.ItemProperties;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -34,7 +35,9 @@
 import org.chromium.chrome.browser.ui.RootUiCoordinator;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.NavigationHistory;
@@ -58,6 +61,8 @@
     private static final int INVALID_NAVIGATION_INDEX = -1;
     private static final int NAVIGATION_INDEX_1 = 1;
     private static final int NAVIGATION_INDEX_2 = 5;
+    private static final int NAVIGATION_INDEX_3 = 9;
+    private static final int FULL_HISTORY_ENTRY_INDEX = 13;
 
     private BottomSheetController mBottomSheetController;
 
@@ -88,6 +93,9 @@
             mHistory.addEntry(new TestNavigationEntry(NAVIGATION_INDEX_2,
                     new GURL(UrlUtils.encodeHtmlDataUri("<html>1</html>")), GURL.emptyGURL(),
                     GURL.emptyGURL(), null, null, 0, 0));
+            mHistory.addEntry(
+                    new TestNavigationEntry(NAVIGATION_INDEX_3, new GURL(UrlConstants.NTP_URL),
+                            GURL.emptyGURL(), GURL.emptyGURL(), null, null, 0, 0));
         }
 
         @Override
@@ -111,9 +119,18 @@
         }
 
         @Override
-        public NavigationHistory getHistory(boolean forward) {
-            return mNavigationController.getDirectedNavigationHistory(
+        public NavigationHistory getHistory(boolean forward, boolean isOffTheRecord) {
+            NavigationHistory history = mNavigationController.getDirectedNavigationHistory(
                     forward, MAXIMUM_HISTORY_ITEMS);
+            if (!isOffTheRecord) {
+                history.addEntry(new NavigationEntry(FULL_HISTORY_ENTRY_INDEX,
+                        new GURL(UrlConstants.HISTORY_URL), GURL.emptyGURL(), GURL.emptyGURL(),
+                        GURL.emptyGURL(),
+                        mActivityTestRule.getActivity().getResources().getString(
+                                R.string.show_full_history),
+                        null, 0, 0));
+            }
+            return history;
         }
 
         @Override
@@ -132,7 +149,8 @@
     @MediumTest
     public void testFaviconFetching() throws ExecutionException {
         TestNavigationController controller = new TestNavigationController();
-        NavigationSheetCoordinator sheet = (NavigationSheetCoordinator) showPopup(controller);
+        NavigationSheetCoordinator sheet =
+                (NavigationSheetCoordinator) showPopup(controller, false);
         ListView listview = sheet.getContentView().findViewById(R.id.navigation_entries);
 
         CriteriaHelper.pollUiThread(() -> {
@@ -148,7 +166,8 @@
     @SmallTest
     public void testItemSelection() throws ExecutionException {
         TestNavigationController controller = new TestNavigationController();
-        NavigationSheetCoordinator sheet = (NavigationSheetCoordinator) showPopup(controller);
+        NavigationSheetCoordinator sheet =
+                (NavigationSheetCoordinator) showPopup(controller, false);
         ListView listview = sheet.getContentView().findViewById(R.id.navigation_entries);
 
         CriteriaHelper.pollUiThread(() -> listview.getChildCount() >= 2);
@@ -196,12 +215,75 @@
         Assert.assertNull(TestThreadUtils.runOnUiThreadBlocking(this::getNavigationSheet));
     }
 
-    private NavigationSheet showPopup(NavigationController controller) throws ExecutionException {
+    @Test
+    @MediumTest
+    @EnableFeatures({ChromeFeatureList.UPDATE_HISTORY_ENTRY_POINTS_IN_INCOGNITO})
+    public void testFieldsForOffTheRecordProfile() throws ExecutionException {
+        TestNavigationController controller = new TestNavigationController();
+        NavigationSheetCoordinator sheet = (NavigationSheetCoordinator) showPopup(controller, true);
+        ListView listview = sheet.getContentView().findViewById(R.id.navigation_entries);
+
+        CriteriaHelper.pollUiThread(() -> {
+            boolean doesNewIncognitoTabItemPresent = false;
+            boolean doesShowFullHistoryItemPresent = false;
+            for (int i = 0; i < controller.mHistory.getEntryCount(); i++) {
+                ListItem item = (ListItem) listview.getAdapter().getItem(i);
+                String label = item.model.get(ItemProperties.LABEL);
+                String incognitoNTPText = mActivityTestRule.getActivity().getResources().getString(
+                        R.string.menu_new_incognito_tab);
+                String fullHistoryText = mActivityTestRule.getActivity().getResources().getString(
+                        R.string.show_full_history);
+                if (label.equals(incognitoNTPText)) {
+                    doesNewIncognitoTabItemPresent = true;
+                } else if (label.equals(fullHistoryText)) {
+                    doesShowFullHistoryItemPresent = true;
+                }
+            }
+            Assert.assertTrue(doesNewIncognitoTabItemPresent);
+            Assert.assertFalse(doesShowFullHistoryItemPresent);
+        });
+    }
+
+    @Test
+    @MediumTest
+    @EnableFeatures({ChromeFeatureList.UPDATE_HISTORY_ENTRY_POINTS_IN_INCOGNITO})
+    public void testFieldsForRegularProfile() throws ExecutionException {
+        TestNavigationController controller = new TestNavigationController();
+        NavigationSheetCoordinator sheet =
+                (NavigationSheetCoordinator) showPopup(controller, false);
+        ListView listview = sheet.getContentView().findViewById(R.id.navigation_entries);
+
+        CriteriaHelper.pollUiThread(() -> {
+            boolean doesNewTabItemPresent = false;
+            boolean doesShowFullHisotryItemPresent = false;
+            for (int i = 0; i < controller.mHistory.getEntryCount(); i++) {
+                ListItem item = (ListItem) listview.getAdapter().getItem(i);
+                String label = item.model.get(ItemProperties.LABEL);
+                String regularNTPText = mActivityTestRule.getActivity().getResources().getString(
+                        R.string.menu_new_tab);
+                String fullHistoryText = mActivityTestRule.getActivity().getResources().getString(
+                        R.string.show_full_history);
+                if (label.equals(regularNTPText)) {
+                    doesNewTabItemPresent = true;
+                } else if (label.equals(fullHistoryText)) {
+                    doesShowFullHisotryItemPresent = true;
+                }
+            }
+            Assert.assertTrue(doesNewTabItemPresent);
+            Assert.assertTrue(doesShowFullHisotryItemPresent);
+        });
+    }
+
+    private NavigationSheet showPopup(NavigationController controller, boolean isOffTheRecord)
+            throws ExecutionException {
         return TestThreadUtils.runOnUiThreadBlocking(() -> {
             Tab tab = mActivityTestRule.getActivity().getActivityTabProvider().get();
-            NavigationSheet navigationSheet =
-                    NavigationSheet.create(tab.getContentView(), mActivityTestRule.getActivity(),
-                            () -> mBottomSheetController, Profile.getLastUsedRegularProfile());
+            Profile profile = Profile.getLastUsedRegularProfile();
+            if (isOffTheRecord) {
+                profile = profile.getPrimaryOTRProfile(true);
+            }
+            NavigationSheet navigationSheet = NavigationSheet.create(tab.getContentView(),
+                    mActivityTestRule.getActivity(), () -> mBottomSheetController, profile);
             navigationSheet.setDelegate(new TestSheetDelegate(controller));
             navigationSheet.startAndExpand(false, false);
             return navigationSheet;
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c235983..7aada7d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2842,6 +2842,7 @@
       "android/seccomp_support_detector.h",
       "android/send_tab_to_self/android_notification_handler.cc",
       "android/send_tab_to_self/android_notification_handler.h",
+      "android/send_tab_to_self/metrics_recorder.cc",
       "android/send_tab_to_self/send_tab_to_self_android_bridge.cc",
       "android/send_tab_to_self/send_tab_to_self_entry_bridge.cc",
       "android/send_tab_to_self/send_tab_to_self_entry_bridge.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index ae453d7..283aafb9 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -6757,12 +6757,6 @@
      flag_descriptions::kSyncAutofillWalletOfferDataDescription, kOsAll,
      FEATURE_VALUE_TYPE(switches::kSyncAutofillWalletOfferData)},
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    {"enable-holding-space", flag_descriptions::kHoldingSpaceName,
-     flag_descriptions::kHoldingSpaceDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kTemporaryHoldingSpace)},
-#endif
-
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
     defined(OS_CHROMEOS)
     {"enable-oop-print-drivers", flag_descriptions::kEnableOopPrintDriversName,
diff --git a/chrome/browser/android/autofill_assistant/starter_android.cc b/chrome/browser/android/autofill_assistant/starter_android.cc
index 8f826b3..2803029 100644
--- a/chrome/browser/android/autofill_assistant/starter_android.cc
+++ b/chrome/browser/android/autofill_assistant/starter_android.cc
@@ -73,7 +73,7 @@
   jlong jtest_service_request_sender_to_inject =
       Java_AutofillAssistantServiceInjector_getServiceRequestSenderToInject(
           base::android::AttachCurrentThread());
-  std::unique_ptr<ServiceRequestSender> test_service_request_sender = nullptr;
+  std::unique_ptr<ServiceRequestSender> test_service_request_sender;
   if (jtest_service_request_sender_to_inject) {
     test_service_request_sender.reset(static_cast<ServiceRequestSender*>(
         reinterpret_cast<void*>(jtest_service_request_sender_to_inject)));
@@ -117,13 +117,11 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
     jboolean is_interactable) {
-  if (!is_interactable || !starter_) {
+  if (!starter_) {
     return;
   }
 
-  // The tab has become interactable again. Users may have adjusted their
-  // settings, so we need to check them again.
-  starter_->CheckSettings();
+  starter_->OnTabInteractabilityChanged(is_interactable);
 }
 
 void StarterAndroid::OnActivityAttachmentChanged(
@@ -248,20 +246,7 @@
       /* onboarding_shown = */ false, /* is_direct_action = */ false,
       jinitial_url);
 
-  starter_->Start(std::move(trigger_context),
-                  base::BindOnce(&StarterAndroid::OnStarterDone,
-                                 weak_ptr_factory_.GetWeakPtr()));
-}
-
-void StarterAndroid::OnStarterDone(
-    bool start_regular_script,
-    GURL url,
-    std::unique_ptr<TriggerContext> trigger_context,
-    const base::Optional<TriggerScriptProto>& trigger_script) {
-  if (!start_regular_script) {
-    return;
-  }
-  StartRegularScript(url, std::move(trigger_context), trigger_script);
+  starter_->Start(std::move(trigger_context));
 }
 
 void StarterAndroid::StartRegularScript(
diff --git a/chrome/browser/android/autofill_assistant/starter_android.h b/chrome/browser/android/autofill_assistant/starter_android.h
index 5fa0375..47f3c6a 100644
--- a/chrome/browser/android/autofill_assistant/starter_android.h
+++ b/chrome/browser/android/autofill_assistant/starter_android.h
@@ -48,6 +48,10 @@
   CreateTriggerScriptUiDelegate() override;
   std::unique_ptr<ServiceRequestSender> GetTriggerScriptRequestSenderToInject()
       override;
+  void StartRegularScript(
+      GURL url,
+      std::unique_ptr<TriggerContext> trigger_context,
+      const base::Optional<TriggerScriptProto>& trigger_script) override;
   WebsiteLoginManager* GetWebsiteLoginManager() const override;
   version_info::Channel GetChannel() const override;
   bool GetFeatureModuleInstalled() const override;
@@ -117,19 +121,6 @@
 
   void CreateJavaDependenciesIfNecessary();
 
-  void OnStarterDone(bool start_regular_script,
-                     GURL url,
-                     std::unique_ptr<TriggerContext> trigger_context,
-                     const base::Optional<TriggerScriptProto>& trigger_script);
-
-  // Start autofill-assistant on |url| using |trigger_context|. This will
-  // create/reuse a ClientAndroid instance which is tied to the WebContents and
-  // thus independent of this starter.
-  void StartRegularScript(
-      GURL url,
-      std::unique_ptr<TriggerContext> trigger_context,
-      const base::Optional<TriggerScriptProto>& trigger_script);
-
   WEB_CONTENTS_USER_DATA_KEY_DECL();
   content::WebContents* web_contents_;
   std::unique_ptr<Starter> starter_;
diff --git a/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.cc b/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.cc
index 6df93c7e..97b506d 100644
--- a/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.cc
+++ b/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.cc
@@ -74,16 +74,6 @@
   return trigger_script_coordinator_->OnBackButtonPressed();
 }
 
-void TriggerScriptBridgeAndroid::OnTabInteractabilityChanged(
-    JNIEnv* env,
-    const base::android::JavaParamRef<jobject>& jcaller,
-    jboolean jinteractable) {
-  if (!trigger_script_coordinator_) {
-    return;
-  }
-  trigger_script_coordinator_->OnTabInteractabilityChanged(jinteractable);
-}
-
 void TriggerScriptBridgeAndroid::OnKeyboardVisibilityChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h b/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h
index 2105530..e04dd05 100644
--- a/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h
+++ b/chrome/browser/android/autofill_assistant/trigger_script_bridge_android.h
@@ -47,12 +47,6 @@
   bool OnBackButtonPressed(JNIEnv* env,
                            const base::android::JavaParamRef<jobject>& jcaller);
 
-  // Called by the UI when the tab's interactability has changed.
-  void OnTabInteractabilityChanged(
-      JNIEnv* env,
-      const base::android::JavaParamRef<jobject>& jcaller,
-      jboolean jinteractable);
-
   // Called by the UI when the keyboard was shown or hidden.
   void OnKeyboardVisibilityChanged(
       JNIEnv* env,
diff --git a/chrome/browser/android/send_tab_to_self/metrics_recorder.cc b/chrome/browser/android/send_tab_to_self/metrics_recorder.cc
new file mode 100644
index 0000000..1cd10fc
--- /dev/null
+++ b/chrome/browser/android/send_tab_to_self/metrics_recorder.cc
@@ -0,0 +1,17 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
+#include "chrome/browser/share/android/jni_headers/MetricsRecorder_jni.h"
+#include "components/send_tab_to_self/metrics_util.h"
+
+namespace send_tab_to_self {
+
+// Static free function declared and called directly from java.
+static void JNI_MetricsRecorder_RecordDeviceClickedInShareSheet(JNIEnv* env) {
+  RecordDeviceClicked(ShareEntryPoint::kShareSheet);
+}
+
+}  // namespace send_tab_to_self
diff --git a/chrome/browser/apps/app_service/publishers/borealis_apps.cc b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
index e3708ea..254d28ac 100644
--- a/chrome/browser/apps/app_service/publishers/borealis_apps.cc
+++ b/chrome/browser/apps/app_service/publishers/borealis_apps.cc
@@ -190,9 +190,7 @@
                                 GetMenuModelCallback callback) {
   apps::mojom::MenuItemsPtr menu_items = apps::mojom::MenuItems::New();
 
-  // TODO(b/171353248): Uninstall for individual apps (not just the parent one).
-  if (app_id == borealis::kBorealisAppId &&
-      borealis::BorealisService::GetForProfile(profile_)
+  if (borealis::BorealisService::GetForProfile(profile_)
           ->Features()
           .IsEnabled()) {
     AddCommandItem(ash::UNINSTALL, IDS_APP_LIST_UNINSTALL_ITEM, &menu_items);
diff --git a/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api.cc b/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api.cc
index 62dc426..82e7346 100644
--- a/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api.cc
+++ b/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api.cc
@@ -108,8 +108,8 @@
       BindOnce(
           &storage::FileSystemContext::DeleteFileSystem, file_system_context,
           url::Origin::Create(source_url().GetOrigin()), file_system_url.type(),
-          Bind(&SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem,
-               this)));
+          BindOnce(&SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem,
+                   this)));
   return RespondLater();
 }
 
@@ -212,7 +212,7 @@
 
   sync_file_system_service->GetFileSyncStatus(
       file_system_url,
-      Bind(&SyncFileSystemGetFileStatusFunction::DidGetFileStatus, this));
+      BindOnce(&SyncFileSystemGetFileStatusFunction::DidGetFileStatus, this));
   return RespondLater();
 }
 
@@ -264,8 +264,8 @@
 
     sync_file_system_service->GetFileSyncStatus(
         file_system_url,
-        Bind(&SyncFileSystemGetFileStatusesFunction::DidGetFileStatus, this,
-             file_system_url));
+        BindOnce(&SyncFileSystemGetFileStatusesFunction::DidGetFileStatus, this,
+                 file_system_url));
   }
 
   return RespondLater();
@@ -339,8 +339,8 @@
           &storage::QuotaManager::GetUsageAndQuotaForWebApps, quota_manager,
           url::Origin::Create(source_url()),
           storage::FileSystemTypeToQuotaStorageType(file_system_url.type()),
-          Bind(&SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota,
-               this)));
+          BindOnce(&SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota,
+                   this)));
 
   return RespondLater();
 }
diff --git a/chrome/browser/ash/borealis/borealis_app_uninstaller.cc b/chrome/browser/ash/borealis/borealis_app_uninstaller.cc
index 35aec43..e1e3370 100644
--- a/chrome/browser/ash/borealis/borealis_app_uninstaller.cc
+++ b/chrome/browser/ash/borealis/borealis_app_uninstaller.cc
@@ -4,23 +4,85 @@
 
 #include "chrome/browser/ash/borealis/borealis_app_uninstaller.h"
 
+#include "base/base64.h"
 #include "base/logging.h"
+#include "chrome/browser/ash/borealis/borealis_app_launcher.h"
 #include "chrome/browser/ash/borealis/borealis_installer.h"
 #include "chrome/browser/ash/borealis/borealis_service.h"
 #include "chrome/browser/ash/borealis/borealis_util.h"
+#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
+#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
 
 namespace borealis {
 
+const char kBorealisUninstallPrefix[] = "Oi8vdW5pbnN0YWxsLw==";
+
 BorealisAppUninstaller::BorealisAppUninstaller(Profile* profile)
     : profile_(profile) {}
 
 void BorealisAppUninstaller::Uninstall(std::string app_id,
                                        OnUninstalledCallback callback) {
-  // TODO(b/171353248): Allow uninstalling other apps
-  DCHECK(app_id == kBorealisAppId);
+  if (app_id == kBorealisAppId || app_id == kBorealisMainAppId) {
+    BorealisService::GetForProfile(profile_)->Installer().Uninstall(
+        base::BindOnce(
+            [](OnUninstalledCallback callback, BorealisUninstallResult result) {
+              if (result != BorealisUninstallResult::kSuccess) {
+                LOG(ERROR) << "Failed to uninstall borealis";
+                std::move(callback).Run(UninstallResult::kError);
+                return;
+              }
+              std::move(callback).Run(UninstallResult::kSuccess);
+            },
+            std::move(callback)));
+    return;
+  }
 
-  BorealisService::GetForProfile(profile_)->Installer().Uninstall(
-      base::DoNothing());
+  base::Optional<guest_os::GuestOsRegistryService::Registration> registration =
+      guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile_)
+          ->GetRegistration(app_id);
+  if (!registration.has_value()) {
+    LOG(ERROR) << "Tried to uninstall an application that does not exist in "
+                  "the registry";
+    std::move(callback).Run(UninstallResult::kError);
+    return;
+  }
+  base::Optional<int> uninstall_app_id = GetBorealisAppId(registration->Exec());
+  if (!uninstall_app_id.has_value()) {
+    LOG(ERROR) << "Couldn't retrieve the borealis app id from the exec "
+                  "information provided";
+    std::move(callback).Run(UninstallResult::kError);
+    return;
+  }
+  // TODO(174282035): Changeup string usage and finish tests.
+  base::Optional<guest_os::GuestOsRegistryService::Registration> main_app =
+      guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile_)
+          ->GetRegistration(kBorealisMainAppId);
+  if (!main_app.has_value()) {
+    LOG(ERROR) << "Failed to retrieve a registration for the Borealis main app";
+    std::move(callback).Run(UninstallResult::kError);
+    return;
+  }
+  std::string prefix;
+  if (!base::Base64Decode(kBorealisUninstallPrefix, &prefix)) {
+    LOG(ERROR) << "Couldn't decode the Borealis uninstall prefix";
+    std::move(callback).Run(UninstallResult::kError);
+    return;
+  }
+  std::string uninstall_string = main_app->DesktopFileId() + prefix +
+                                 base::NumberToString(*uninstall_app_id);
+  borealis::BorealisService::GetForProfile(profile_)->AppLauncher().Launch(
+      kBorealisMainAppId, {uninstall_string},
+      base::BindOnce(
+          [](OnUninstalledCallback callback,
+             BorealisAppLauncher::LaunchResult result) {
+            if (result != BorealisAppLauncher::LaunchResult::kSuccess) {
+              LOG(ERROR) << "Failed to uninstall a borealis application";
+              std::move(callback).Run(UninstallResult::kError);
+              return;
+            }
+            std::move(callback).Run(UninstallResult::kSuccess);
+          },
+          std::move(callback)));
 }
 
 }  // namespace borealis
diff --git a/chrome/browser/ash/borealis/borealis_app_uninstaller_unittest.cc b/chrome/browser/ash/borealis/borealis_app_uninstaller_unittest.cc
new file mode 100644
index 0000000..ea156231
--- /dev/null
+++ b/chrome/browser/ash/borealis/borealis_app_uninstaller_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/borealis/borealis_app_uninstaller.h"
+
+#include <memory>
+
+#include "base/bind.h"
+#include "base/test/bind.h"
+#include "chrome/browser/ash/borealis/borealis_installer.h"
+#include "chrome/browser/ash/borealis/borealis_service_fake.h"
+#include "chrome/browser/ash/borealis/borealis_util.h"
+#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
+#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace borealis {
+namespace {
+
+class CallbackFactory : public testing::StrictMock<testing::MockFunction<void(
+                            BorealisAppUninstaller::UninstallResult)>> {
+ public:
+  base::OnceCallback<void(BorealisAppUninstaller::UninstallResult)> BindOnce() {
+    return base::BindOnce(&CallbackFactory::Call, base::Unretained(this));
+  }
+};
+
+class BorealisInstallerMock : public BorealisInstaller {
+ public:
+  BorealisInstallerMock() = default;
+  ~BorealisInstallerMock() = default;
+  MOCK_METHOD(bool, IsProcessing, (), ());
+  MOCK_METHOD(void, Start, (), ());
+  MOCK_METHOD(void, Cancel, (), ());
+  MOCK_METHOD(void,
+              Uninstall,
+              (base::OnceCallback<void(BorealisUninstallResult)>),
+              ());
+  MOCK_METHOD(void, AddObserver, (Observer * observer), ());
+  MOCK_METHOD(void, RemoveObserver, (Observer * observer), ());
+};
+
+class BorealisAppUninstallerTest : public testing::Test {
+ public:
+  BorealisAppUninstallerTest() = default;
+
+ protected:
+  void SetUp() override {
+    CreateProfile();
+    mock_installer_ =
+        std::make_unique<testing::StrictMock<BorealisInstallerMock>>();
+    BorealisServiceFake* fake_service =
+        BorealisServiceFake::UseFakeForTesting(profile_.get());
+    fake_service->SetInstallerForTesting(mock_installer_.get());
+  }
+
+  void TearDown() override {
+    profile_.reset();
+    mock_installer_.reset();
+  }
+
+  // Sets up the registry with a single app. Returns its app id.
+  std::string SetDummyApp(const std::string& desktop_file_id,
+                          std::string exec) {
+    vm_tools::apps::ApplicationList list;
+    list.set_vm_name("test_vm_name");
+    list.set_container_name("test_container_name");
+    vm_tools::apps::App* app = list.add_apps();
+    app->set_desktop_file_id(desktop_file_id);
+    vm_tools::apps::App::LocaleString::Entry* entry =
+        app->mutable_name()->add_values();
+    entry->set_locale(std::string());
+    entry->set_value(desktop_file_id);
+    app->set_no_display(false);
+    guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile_.get())
+        ->UpdateApplicationList(list);
+    return guest_os::GuestOsRegistryService::GenerateAppId(
+        desktop_file_id, list.vm_name(), list.container_name());
+  }
+
+  std::unique_ptr<TestingProfile> profile_;
+  std::unique_ptr<testing::StrictMock<BorealisInstallerMock>> mock_installer_;
+  content::BrowserTaskEnvironment task_environment_;
+
+ private:
+  void CreateProfile() {
+    TestingProfile::Builder profile_builder;
+    profile_builder.SetProfileName("defaultprofile");
+    profile_ = profile_builder.Build();
+  }
+};
+
+TEST_F(BorealisAppUninstallerTest, BorealisAppUninstallsBorealis) {
+  CallbackFactory callback_check;
+  EXPECT_CALL(callback_check,
+              Call(BorealisAppUninstaller::UninstallResult::kSuccess));
+  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
+  EXPECT_CALL(*mock_installer_, Uninstall(testing::_))
+      .WillOnce(testing::Invoke(
+          [](base::OnceCallback<void(BorealisUninstallResult)> callback) {
+            std::move(callback).Run(BorealisUninstallResult::kSuccess);
+          }));
+  uninstaller.Uninstall(kBorealisAppId, callback_check.BindOnce());
+}
+
+TEST_F(BorealisAppUninstallerTest, BorealisMainAppUninstallsBorealis) {
+  CallbackFactory callback_check;
+  EXPECT_CALL(callback_check,
+              Call(BorealisAppUninstaller::UninstallResult::kSuccess));
+  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
+  EXPECT_CALL(*mock_installer_, Uninstall(testing::_))
+      .WillOnce(testing::Invoke(
+          [](base::OnceCallback<void(BorealisUninstallResult)> callback) {
+            std::move(callback).Run(BorealisUninstallResult::kSuccess);
+          }));
+  uninstaller.Uninstall(kBorealisMainAppId, callback_check.BindOnce());
+}
+
+TEST_F(BorealisAppUninstallerTest, NonExistentAppFails) {
+  CallbackFactory callback_check;
+  EXPECT_CALL(callback_check,
+              Call(BorealisAppUninstaller::UninstallResult::kError));
+  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
+  uninstaller.Uninstall("IdontExist", callback_check.BindOnce());
+}
+
+TEST_F(BorealisAppUninstallerTest, AppWithEmptyExecFails) {
+  std::string baz_id = SetDummyApp("baz.desktop", "");
+  CallbackFactory callback_check;
+  EXPECT_CALL(callback_check,
+              Call(BorealisAppUninstaller::UninstallResult::kError));
+  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
+  uninstaller.Uninstall(baz_id, callback_check.BindOnce());
+}
+
+TEST_F(BorealisAppUninstallerTest, AppWithInvalidExecFails) {
+  std::string baz_id = SetDummyApp("test.desktop", "desktopname with no id");
+  CallbackFactory callback_check;
+  EXPECT_CALL(callback_check,
+              Call(BorealisAppUninstaller::UninstallResult::kError));
+  BorealisAppUninstaller uninstaller = BorealisAppUninstaller(profile_.get());
+  uninstaller.Uninstall(baz_id, callback_check.BindOnce());
+}
+// TODO(174282035): Add additional tests when strings are changed.
+
+}  // namespace
+}  // namespace borealis
diff --git a/chrome/browser/ash/borealis/borealis_util.cc b/chrome/browser/ash/borealis/borealis_util.cc
index 968b9ca5..8375c447 100644
--- a/chrome/browser/ash/borealis/borealis_util.cc
+++ b/chrome/browser/ash/borealis/borealis_util.cc
@@ -15,8 +15,13 @@
 // are updated.
 const char kBorealisAppIdRegex[] = "([^/]+\\d+)";
 
-bool GetBorealisAppId(std::string exec, int& app_id) {
-  return RE2::PartialMatch(exec, kBorealisAppIdRegex, &app_id);
+base::Optional<int> GetBorealisAppId(std::string exec) {
+  int app_id;
+  if (RE2::PartialMatch(exec, kBorealisAppIdRegex, &app_id)) {
+    return app_id;
+  } else {
+    return base::nullopt_t(0);
+  }
 }
 
 }  // namespace borealis
diff --git a/chrome/browser/ash/borealis/borealis_util.h b/chrome/browser/ash/borealis/borealis_util.h
index 361be39..5dddd2fc 100644
--- a/chrome/browser/ash/borealis/borealis_util.h
+++ b/chrome/browser/ash/borealis/borealis_util.h
@@ -30,7 +30,7 @@
 // returns true if successful.
 // TODO(b/173547790): This should probably be moved when we've decided
 // the details of how/where it will be used.
-bool GetBorealisAppId(std::string exec, int& app_id);
+base::Optional<int> GetBorealisAppId(std::string exec);
 
 // Shows the splash screen (borealis_splash_screen_view).
 void ShowBorealisSplashScreenView(Profile* profile);
diff --git a/chrome/browser/ash/login/app_mode/auto_launched_kiosk_browsertest.cc b/chrome/browser/ash/login/app_mode/auto_launched_kiosk_browsertest.cc
index 59dbd61..5340028e 100644
--- a/chrome/browser/ash/login/app_mode/auto_launched_kiosk_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/auto_launched_kiosk_browsertest.cc
@@ -252,6 +252,9 @@
   std::unique_ptr<ExtensionTestMessageListener> app_window_loaded_listener_;
   std::unique_ptr<TerminationObserver> termination_observer_;
 
+  DeviceStateMixin device_state_{
+      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
+
  private:
   FakeCWS fake_cws_;
   extensions::SandboxedUnpacker::ScopedVerifierFormatOverrideForTest
@@ -262,9 +265,6 @@
       &mixin_host_, embedded_test_server()};
   LoginManagerMixin login_manager_{&mixin_host_, {}};
 
-  DeviceStateMixin device_state_{
-      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
-
   DISALLOW_COPY_AND_ASSIGN(AutoLaunchedKioskTest);
 };
 
@@ -305,6 +305,38 @@
   ASSERT_TRUE(CloseAppWindow(KioskAppsMixin::kKioskAppId));
 }
 
+class AutoLaunchedKioskEphemeralUsersTest : public AutoLaunchedKioskTest {
+ public:
+  AutoLaunchedKioskEphemeralUsersTest() = default;
+  ~AutoLaunchedKioskEphemeralUsersTest() override = default;
+
+  // AutoLaunchedKioskTest:
+  void SetUpInProcessBrowserTestFixture() override {
+    AutoLaunchedKioskTest::SetUpInProcessBrowserTestFixture();
+    std::unique_ptr<chromeos::ScopedDevicePolicyUpdate> device_policy_update =
+        device_state_.RequestDevicePolicyUpdate();
+    device_policy_update->policy_payload()
+        ->mutable_ephemeral_users_enabled()
+        ->set_ephemeral_users_enabled(true);
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(AutoLaunchedKioskEphemeralUsersTest, Launches) {
+  // Set up default network connections, so tests think the device is online.
+  DBusThreadManager::Get()
+      ->GetShillManagerClient()
+      ->GetTestInterface()
+      ->SetupDefaultEnvironment();
+
+  // Check that policy flags have not been lost.
+  ExpectCommandLineHasDefaultPolicySwitches(
+      *base::CommandLine::ForCurrentProcess());
+
+  EXPECT_TRUE(app_window_loaded_listener_->WaitUntilSatisfied());
+
+  EXPECT_TRUE(IsKioskAppAutoLaunched(KioskAppsMixin::kKioskAppId));
+}
+
 // Used to test app auto-launch flow when the launched app is not kiosk enabled.
 class AutoLaunchedNonKioskEnabledAppTest : public AutoLaunchedKioskTest {
  public:
diff --git a/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc b/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc
index 7f47f0a1..0bba6ff 100644
--- a/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc
+++ b/chrome/browser/ash/login/quick_unlock/quick_unlock_utils.cc
@@ -43,8 +43,11 @@
 bool HasPolicyValue(const PrefService* pref_service, const char* value) {
   const base::ListValue* quick_unlock_allowlist =
       pref_service->GetList(prefs::kQuickUnlockModeAllowlist);
-  return quick_unlock_allowlist->Find(base::Value(value)) !=
-         quick_unlock_allowlist->end();
+  // TODO(crbug.com/1187106): Use base::Contains once |quick_unlock_allowlist|
+  // is not a ListValue.
+  return std::find(quick_unlock_allowlist->begin(),
+                   quick_unlock_allowlist->end(),
+                   base::Value(value)) != quick_unlock_allowlist->end();
 }
 
 }  // namespace
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index d711164..e13a16f 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -1228,8 +1228,10 @@
     SetFirstLoginPrefs(profile, user_context.GetPublicSessionLocale(),
                        user_context.GetPublicSessionInputMethod());
 
+    // Kiosks do not have onboarding.
     if (user_manager->GetPrimaryUser() == user &&
-        !user_manager->IsUserNonCryptohomeDataEphemeral(user->GetAccountId())) {
+        !user_manager->IsUserNonCryptohomeDataEphemeral(user->GetAccountId()) &&
+        !user->IsKioskType()) {
       LoginDisplayHost::default_host()
           ->GetSigninUI()
           ->SetAuthSessionForOnboarding(user_context);
diff --git a/chrome/browser/ash/login/ui/login_display_host_common.cc b/chrome/browser/ash/login/ui/login_display_host_common.cc
index ffcceb8..a0e7298 100644
--- a/chrome/browser/ash/login/ui/login_display_host_common.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_common.cc
@@ -418,7 +418,9 @@
     const UserContext& user_context) {
   if (PinSetupScreen::ShouldSkipBecauseOfPolicy())
     return;
-  GetWizardController()->SetAuthSessionForOnboarding(user_context);
+  // WizardController may not be initialized in the WebUI login display host.
+  if (GetWizardController())
+    GetWizardController()->SetAuthSessionForOnboarding(user_context);
 }
 
 void LoginDisplayHostCommon::StartEncryptionMigration(
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
index de70cf4..d60c076 100644
--- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
+++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -1267,7 +1267,10 @@
   const base::ListValue& reporting_users =
       *(GetLocalState()->GetList(::prefs::kReportingUsers));
   base::Value user_id_value(FullyCanonicalize(user_id));
-  return !(reporting_users.Find(user_id_value) == reporting_users.end());
+  // TODO(crbug.com/1187106): Use base::Contains once |reporting_users| is not a
+  // ListValue.
+  return !(std::find(reporting_users.begin(), reporting_users.end(),
+                     user_id_value) == reporting_users.end());
 }
 
 bool ChromeUserManagerImpl::IsManagedSessionEnabledForUser(
diff --git a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
index 1066d81..f9344e1 100644
--- a/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_app_integration_browsertest.cc
@@ -219,7 +219,7 @@
   // test doesn't provide coverage for that.
   // Note: If "object-src" is not set in the CSP, the `<embed>` element fails to
   // load and times out.
-  constexpr char loadPdf[] = R"(
+  constexpr char kLoadPdf[] = R"(
       (() => {
         const embedBlob =  document.createElement('embed');
         embedBlob.type ='application/pdf';
@@ -235,7 +235,7 @@
       })();
   )";
 
-  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, loadPdf));
+  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, kLoadPdf));
 }
 
 // These tests try to load files bundled in our CIPD package. The CIPD package
@@ -257,7 +257,7 @@
   // button and is calculated from a hash of the label ("Annotate"). This id is
   // used since cl/366443893 because the UI toolkit has loose guarantees about
   // where the actual label appears in the shadow DOM.
-  constexpr char clickAnnotate[] = R"(
+  constexpr char kClickAnnotate[] = R"(
     (async () => {
       const annotateButton = await waitForNode(
           '#icon-button-3709949292', ['backlight-app-bar', 'backlight-app']);
@@ -265,13 +265,13 @@
       return true;
     })();
   )";
-  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, clickAnnotate));
+  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, kClickAnnotate));
 
   // Checks ink is loaded for images by ensuring the ink engine canvas has a non
   // zero width and height attributes (checking <canvas.width/height is
   // insufficient since it has a default width of 300 and height of 150).
   // Note: The loading of ink engine elements can be async.
-  constexpr char checkInkLoaded[] = R"(
+  constexpr char kCheckInkLoaded[] = R"(
     (async () => {
       const inkEngineCanvas = await waitForNode(
           'canvas#ink-engine[width]', ['backlight-image-handler']);
@@ -284,7 +284,8 @@
   )";
   // TODO(b/175840855): Consider checking `inkEngineCanvas` size, it is
   // currently different to image size.
-  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, checkInkLoaded));
+  EXPECT_EQ(true,
+            MediaAppUiBrowserTest::EvalJsInAppFrame(app, kCheckInkLoaded));
 }
 
 // Tests that clicking on the 'Info' button in the app bar opens the information
@@ -300,7 +301,7 @@
   EXPECT_EQ("640x480", WaitForImageAlt(app, kFileJpeg640x480));
 
   // Expect info panel to not be open on first load.
-  constexpr char hasInfoPanelOpen[] = R"(
+  constexpr char kHasInfoPanelOpen[] = R"(
     (async () => {
       const metadataPanel = await getNode(
           'backlight-metadata-panel', ['backlight-image-handler']);
@@ -308,14 +309,14 @@
     })();
   )";
   EXPECT_EQ(false,
-            MediaAppUiBrowserTest::EvalJsInAppFrame(app, hasInfoPanelOpen));
+            MediaAppUiBrowserTest::EvalJsInAppFrame(app, kHasInfoPanelOpen));
 
   // Click info button.
   // Note the button id (icon-button-2283726) corresponds to the info panel
   // button and is calculated from a hash of the label ("Info"). This id is
   // used because the UI toolkit has loose guarantees about where the actual
   // label appears in the shadow DOM.
-  constexpr char clickInfo[] = R"(
+  constexpr char kClickInfo[] = R"(
     (async () => {
       const infoButton = await getNode(
           '#icon-button-2283726', ['backlight-app-bar', 'backlight-app']);
@@ -323,11 +324,11 @@
       return true;
     })();
   )";
-  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, clickInfo));
+  EXPECT_EQ(true, MediaAppUiBrowserTest::EvalJsInAppFrame(app, kClickInfo));
 
   // Expect info panel to be open after clicking info button.
   EXPECT_EQ(true,
-            MediaAppUiBrowserTest::EvalJsInAppFrame(app, hasInfoPanelOpen));
+            MediaAppUiBrowserTest::EvalJsInAppFrame(app, kHasInfoPanelOpen));
 }
 #endif  // BUILDFLAG(ENABLE_CROS_MEDIA_APP)
 
diff --git a/chrome/browser/autofill/autofill_provider_browsertest.cc b/chrome/browser/autofill/autofill_provider_browsertest.cc
index 1760ea43..a227f45 100644
--- a/chrome/browser/autofill/autofill_provider_browsertest.cc
+++ b/chrome/browser/autofill/autofill_provider_browsertest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "base/base_switches.h"
-#include "base/bind.h"
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/browser.h"
@@ -13,7 +12,6 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
-#include "components/autofill/core/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_provider.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -121,8 +119,18 @@
     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
         web_contents, autofill_client_.get(), "en-US",
         BrowserAutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER,
-        base::BindRepeating(&AndroidAutofillManager::Create,
-                            autofill_provider_.get()));
+        autofill_provider_.get());
+  }
+
+  void ReplaceAutofillDriver() {
+    content::WebContents* web_contents = WebContents();
+    // Set AutofillProvider for current WebContents.
+    ContentAutofillDriverFactory* factory =
+        ContentAutofillDriverFactory::FromWebContents(web_contents);
+    ContentAutofillDriver* driver =
+        factory->DriverForFrame(web_contents->GetMainFrame());
+    driver->SetAutofillProviderForTesting(autofill_provider_.get(),
+                                          autofill_client_.get());
   }
 
   void TearDownOnMainThread() override {
@@ -158,6 +166,7 @@
   }
 
   void SetLabelChangeExpectationAndTriggerQuery() {
+    ReplaceAutofillDriver();
     // One query for the single click, and a second query when the typing is
     // simulated.
     base::RunLoop run_loop;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index b5e1015..75335bcc 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -224,6 +224,7 @@
     "//chromeos/disks",
     "//chromeos/geolocation",
     "//chromeos/ime:gencode",
+    "//chromeos/language/language_packs",
     "//chromeos/login/auth",
     "//chromeos/login/login_state",
     "//chromeos/login/session",
@@ -3682,6 +3683,7 @@
     "../ash/base/file_flusher_unittest.cc",
     "../ash/bluetooth/debug_logs_manager_unittest.cc",
     "../ash/borealis/borealis_app_launcher_unittest.cc",
+    "../ash/borealis/borealis_app_uninstaller_unittest.cc",
     "../ash/borealis/borealis_context_manager_unittest.cc",
     "../ash/borealis/borealis_context_unittest.cc",
     "../ash/borealis/borealis_features_unittest.cc",
diff --git a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
index a95ee00..14f3e2b 100644
--- a/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/file_manager_private_apitest.cc
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
-#include "ash/public/cpp/ash_features.h"
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/path_service.h"
@@ -355,25 +354,6 @@
   file_manager::EventRouter* event_router_ = nullptr;
 };
 
-// Parameterize by whether holding space feature is enabled.
-class FileManagerPrivateHoldingSpaceApiTest
-    : public FileManagerPrivateApiTest,
-      public testing::WithParamInterface<bool> {
- public:
-  FileManagerPrivateHoldingSpaceApiTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        ash::features::kTemporaryHoldingSpace, GetParam());
-  }
-  ~FileManagerPrivateHoldingSpaceApiTest() override = default;
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-INSTANTIATE_TEST_SUITE_P(HoldingSpaceEnabled,
-                         FileManagerPrivateHoldingSpaceApiTest,
-                         testing::Bool());
-
 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, Mount) {
   using chromeos::file_system_provider::IconSet;
   profile()->GetPrefs()->SetBoolean(drive::prefs::kDisableDrive, true);
@@ -618,7 +598,7 @@
   EXPECT_TRUE(response_helper.GetResponse());
 }
 
-IN_PROC_BROWSER_TEST_P(FileManagerPrivateHoldingSpaceApiTest, HoldingSpace) {
+IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, HoldingSpace) {
   const base::FilePath test_dir = temp_dir_.GetPath();
   AddLocalFileSystem(browser()->profile(), test_dir);
 
@@ -635,14 +615,8 @@
     ASSERT_TRUE(video_file.IsValid());
   }
 
-  if (GetParam()) {
-    EXPECT_TRUE(RunExtensionTest({.name = "file_browser/holding_space"},
-                                 {.load_as_component = true}));
-  } else {
-    EXPECT_TRUE(
-        RunExtensionTest({.name = "file_browser/holding_space_disabled"},
-                         {.load_as_component = true}));
-  }
+  EXPECT_TRUE(RunExtensionTest({.name = "file_browser/holding_space"},
+                               {.load_as_component = true}));
 }
 
 IN_PROC_BROWSER_TEST_F(FileManagerPrivateApiTest, GetVolumeRoot) {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 71ef7ce..406414b 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -143,11 +143,6 @@
     return *this;
   }
 
-  TestCase& EnableHoldingSpace(bool enable) {
-    options.enable_holding_space = enable;
-    return *this;
-  }
-
   TestCase& DisableJsModules() {
     options.enable_js_modules = false;
     return *this;
@@ -705,21 +700,13 @@
     HoldingSpace, /* holding_space.js */
     FilesAppBrowserTest,
     ::testing::Values(
-        TestCase("holdingSpaceWelcomeBannerWithFeatureDisabled")
-            .EnableHoldingSpace(false),
-        TestCase("holdingSpaceWelcomeBannerWithFeatureEnabled")
-            .EnableHoldingSpace(true),
-        TestCase("holdingSpaceWelcomeBannerWontShowAfterBeingDismissed")
-            .EnableHoldingSpace(true),
-        TestCase("holdingSpaceWelcomeBannerWontShowAfterReachingLimit")
-            .EnableHoldingSpace(true),
+        TestCase("holdingSpaceWelcomeBanner"),
+        TestCase("holdingSpaceWelcomeBannerWontShowAfterBeingDismissed"),
+        TestCase("holdingSpaceWelcomeBannerWontShowAfterReachingLimit"),
         TestCase("holdingSpaceWelcomeBannerWontShowForModalDialogs")
-            .EnableHoldingSpace(true)
             .WithBrowser(),
-        TestCase("holdingSpaceWelcomeBannerWontShowOnDrive")
-            .EnableHoldingSpace(true),
-        TestCase("holdingSpaceWelcomeBannerOnTabletModeChanged")
-            .EnableHoldingSpace(true)));
+        TestCase("holdingSpaceWelcomeBannerWontShowOnDrive"),
+        TestCase("holdingSpaceWelcomeBannerOnTabletModeChanged")));
 
 WRAPPED_INSTANTIATE_TEST_SUITE_P(
     Transfer, /* transfer.js */
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 8dbb430..223802a0 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1717,12 +1717,6 @@
     disabled_features.push_back(chromeos::features::kFilesTrash);
   }
 
-  if (options.enable_holding_space) {
-    enabled_features.push_back(ash::features::kTemporaryHoldingSpace);
-  } else {
-    disabled_features.push_back(ash::features::kTemporaryHoldingSpace);
-  }
-
   if (options.enable_js_modules) {
     enabled_features.push_back(chromeos::features::kFilesJsModules);
   } else {
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
index b817dff..a6b984e 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -109,9 +109,6 @@
     // Whether test should enable trash.
     bool enable_trash = false;
 
-    // Whether test should enable holding space.
-    bool enable_holding_space = false;
-
     // Whether test should run Files app UI as JS modules.
     bool enable_js_modules = true;
   };
diff --git a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
index 3abc2eb8..7a1e68e 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_string_util.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/chromeos/file_manager/file_manager_string_util.h"
 
 #include "ash/constants/ash_features.h"
-#include "ash/public/cpp/ash_features.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
@@ -1078,8 +1077,6 @@
   dict->SetBoolean(
       "FILTERS_IN_RECENTS_ENABLED",
       base::FeatureList::IsEnabled(chromeos::features::kFiltersInRecents));
-  dict->SetBoolean("HOLDING_SPACE_ENABLED",
-                   ash::features::IsTemporaryHoldingSpaceEnabled());
   dict->SetBoolean("FILES_SINGLE_PARTITION_FORMAT_ENABLED",
                    base::FeatureList::IsEnabled(
                        chromeos::features::kFilesSinglePartitionFormat));
diff --git a/chrome/browser/chromeos/fileapi/recent_model.cc b/chrome/browser/chromeos/fileapi/recent_model.cc
index 87dd00fb..32c7e27 100644
--- a/chrome/browser/chromeos/fileapi/recent_model.cc
+++ b/chrome/browser/chromeos/fileapi/recent_model.cc
@@ -49,7 +49,7 @@
   // Downloads / MyFiles.
   sources.emplace_back(std::make_unique<RecentDiskSource>(
       file_manager::util::GetDownloadsMountPointName(profile),
-      false /* ignore_dotfiles */, 0 /* max_depth unlimited */,
+      true /* ignore_dotfiles */, 0 /* max_depth unlimited */,
       "FileBrowser.Recent.LoadDownloads"));
   sources.emplace_back(std::make_unique<RecentDriveSource>(profile));
   return sources;
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine_browsertest.cc b/chrome/browser/chromeos/input_method/native_input_method_engine_browsertest.cc
index 55a75e3..a948c752 100644
--- a/chrome/browser/chromeos/input_method/native_input_method_engine_browsertest.cc
+++ b/chrome/browser/chromeos/input_method/native_input_method_engine_browsertest.cc
@@ -357,7 +357,8 @@
 
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfileIfExists(profile_);
-  signin::SetPrimaryAccount(identity_manager, "johnwayne@me.xyz");
+  signin::SetPrimaryAccount(identity_manager, "johnwayne@me.xyz",
+                            signin::ConsentLevel::kSync);
 
   engine_->Enable(kEngineIdUs);
 
@@ -399,7 +400,8 @@
 
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfileIfExists(profile_);
-  signin::SetPrimaryAccount(identity_manager, "johnwayne@me.xyz");
+  signin::SetPrimaryAccount(identity_manager, "johnwayne@me.xyz",
+                            signin::ConsentLevel::kSync);
 
   engine_->Enable(kEngineIdUs);
 
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
index 3483eeeb..f060f2a 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
@@ -285,4 +285,10 @@
   }
 }
 
+void DeviceCloudPolicyStoreChromeOS::UpdateFirstPoliciesLoaded() {
+  CloudPolicyStore::UpdateFirstPoliciesLoaded();
+  // Mark policies as loaded if we don't expect any policies to be loaded.
+  first_policies_loaded_ |= !install_attributes_->IsEnterpriseManaged();
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h
index 9bcf4164..64bd4aea 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h
@@ -60,6 +60,9 @@
   void DeviceSettingsUpdated() override;
   void OnDeviceSettingsServiceShutdown() override;
 
+  // CloudPolicyStore:
+  void UpdateFirstPoliciesLoaded() override;
+
  private:
   // Create a validator for |policy| with basic device policy configuration and
   // OnPolicyStored() as the completion callback.
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc
index c10452b0..c341995 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos_unittest.cc
@@ -85,6 +85,8 @@
   void ExpectFailure(CloudPolicyStore::Status expected_status) {
     EXPECT_EQ(expected_status, store_->status());
     EXPECT_TRUE(store_->is_initialized());
+    EXPECT_EQ(!install_attributes_->IsEnterpriseManaged(),
+              store_->first_policies_loaded());
     EXPECT_FALSE(store_->has_policy());
     EXPECT_FALSE(store_->is_managed());
     EXPECT_EQ(std::string(), store_->policy_signature_public_key());
@@ -93,6 +95,7 @@
   void ExpectSuccess() {
     EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
     EXPECT_TRUE(store_->is_initialized());
+    EXPECT_TRUE(store_->first_policies_loaded());
     EXPECT_TRUE(store_->has_policy());
     EXPECT_TRUE(store_->is_managed());
     EXPECT_TRUE(store_->policy());
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index c5f813af..838a512 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -1140,30 +1140,35 @@
 void DevToolsUIBindings::DevicesDiscoveryConfigUpdated() {
   base::DictionaryValue config;
   config.Set(kConfigDiscoverUsbDevices,
-             profile_->GetPrefs()
-                 ->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled)
-                 ->GetValue()
-                 ->CreateDeepCopy());
+             base::Value::ToUniquePtrValue(
+                 profile_->GetPrefs()
+                     ->FindPreference(prefs::kDevToolsDiscoverUsbDevicesEnabled)
+                     ->GetValue()
+                     ->Clone()));
   config.Set(kConfigPortForwardingEnabled,
-             profile_->GetPrefs()
-                 ->FindPreference(prefs::kDevToolsPortForwardingEnabled)
-                 ->GetValue()
-                 ->CreateDeepCopy());
+             base::Value::ToUniquePtrValue(
+                 profile_->GetPrefs()
+                     ->FindPreference(prefs::kDevToolsPortForwardingEnabled)
+                     ->GetValue()
+                     ->Clone()));
   config.Set(kConfigPortForwardingConfig,
-             profile_->GetPrefs()
-                 ->FindPreference(prefs::kDevToolsPortForwardingConfig)
-                 ->GetValue()
-                 ->CreateDeepCopy());
+             base::Value::ToUniquePtrValue(
+                 profile_->GetPrefs()
+                     ->FindPreference(prefs::kDevToolsPortForwardingConfig)
+                     ->GetValue()
+                     ->Clone()));
   config.Set(kConfigNetworkDiscoveryEnabled,
-             profile_->GetPrefs()
-                 ->FindPreference(prefs::kDevToolsDiscoverTCPTargetsEnabled)
-                 ->GetValue()
-                 ->CreateDeepCopy());
+             base::Value::ToUniquePtrValue(
+                 profile_->GetPrefs()
+                     ->FindPreference(prefs::kDevToolsDiscoverTCPTargetsEnabled)
+                     ->GetValue()
+                     ->Clone()));
   config.Set(kConfigNetworkDiscoveryConfig,
-             profile_->GetPrefs()
-                 ->FindPreference(prefs::kDevToolsTCPDiscoveryConfig)
-                 ->GetValue()
-                 ->CreateDeepCopy());
+             base::Value::ToUniquePtrValue(
+                 profile_->GetPrefs()
+                     ->FindPreference(prefs::kDevToolsTCPDiscoveryConfig)
+                     ->GetValue()
+                     ->Clone()));
   CallClientMethod("DevToolsAPI", "devicesDiscoveryConfigChanged",
                    std::move(config));
 }
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index 7f76a77..b8921a4 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -463,7 +463,8 @@
         ASSERT_FALSE(
             identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
         signin::MakeAccountAvailable(identity_manager, "primary@example.com");
-        signin::SetPrimaryAccount(identity_manager, "primary@example.com");
+        signin::SetPrimaryAccount(identity_manager, "primary@example.com",
+                                  signin::ConsentLevel::kSync);
       } else {
         FixOrAddSecondaryAccount();
       }
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 5b932100..ca7e90b 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -370,8 +370,8 @@
         extension->FindStringPath(kWebAppMigrationFlag);
     bool is_migrating_to_web_app =
         web_app_migration_flag &&
-        web_app::IsPreinstalledAppInstallFeatureEnabled(
-            *web_app_migration_flag);
+        web_app::IsPreinstalledAppInstallFeatureEnabled(*web_app_migration_flag,
+                                                        *profile_);
     bool keep_if_present =
         extension->FindBoolPath(kKeepIfPresent).value_or(false);
     if (keep_if_present || is_migrating_to_web_app) {
diff --git a/chrome/browser/extensions/preinstalled_apps.cc b/chrome/browser/extensions/preinstalled_apps.cc
index 4e03dc8f..3294fe5 100644
--- a/chrome/browser/extensions/preinstalled_apps.cc
+++ b/chrome/browser/extensions/preinstalled_apps.cc
@@ -188,7 +188,8 @@
           pref.FindStringPath(kWebAppMigrationFlag);
       if (!web_app_flag)
         return false;  // Isn't migrating.
-      if (web_app::IsPreinstalledAppInstallFeatureEnabled(*web_app_flag)) {
+      if (web_app::IsPreinstalledAppInstallFeatureEnabled(*web_app_flag,
+                                                          *profile)) {
         // The feature is still enabled; it's responsible for the behavior.
         return false;
       }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 9d9e672..154c0a6 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1919,16 +1919,6 @@
     "expiry_milestone": 76
   },
   {
-    "name": "enable-holding-space",
-    "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
-    "expiry_milestone": 90
-  },
-  {
-    "name": "enable-holding-space-previews",
-    "owners": [ "//ash/public/cpp/holding_space/OWNERS" ],
-    "expiry_milestone": 90
-  },
-  {
     "name": "enable-hosted-app-quit-notification",
     "owners": [ "ccameron" ],
     "expiry_milestone": 77
@@ -2098,12 +2088,12 @@
   {
     "name": "enable-migrate-default-chrome-app-to-web-apps-gsuite",
     "owners": [ "alancutter", "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 91
+    "expiry_milestone": 94
   },
   {
     "name": "enable-migrate-default-chrome-app-to-web-apps-non-gsuite",
     "owners": [ "alancutter", "desktop-pwas-team@google.com" ],
-    "expiry_milestone": 91
+    "expiry_milestone": 94
   },
   {
     "name": "enable-nacl",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index e6c98a1..94ac969b7 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4535,14 +4535,6 @@
     "Hides media notifications for ARC apps. Requires "
     "#enable-media-session-notifications to be enabled.";
 
-const char kHoldingSpaceName[] =
-    "Quick access to screenshots, downloads, and files";
-const char kHoldingSpaceDescription[] =
-    "Enables quick access to screenshots, downloads, and important files which "
-    "aims to increase productivity by saving time. When enabled, access recent "
-    "screenshots and downloads from the shelf. Pin important files with the "
-    "Files App context menu to keep them one click away.";
-
 const char kImeAssistAutocorrectName[] = "Enable assistive autocorrect";
 const char kImeAssistAutocorrectDescription[] =
     "Enable assistive auto-correct features for native IME";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index f4cee371..c49f135 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2639,12 +2639,6 @@
 extern const char kHideArcMediaNotificationsName[];
 extern const char kHideArcMediaNotificationsDescription[];
 
-extern const char kHoldingSpaceName[];
-extern const char kHoldingSpaceDescription[];
-
-extern const char kHoldingSpacePreviewsName[];
-extern const char kHoldingSpacePreviewsDescription[];
-
 extern const char kImeAssistAutocorrectName[];
 extern const char kImeAssistAutocorrectDescription[];
 
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index bc9d3aa..7e79456f 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -258,6 +258,7 @@
     &kTrustedWebActivityQualityEnforcementWarning,
     &kStartSurfaceAndroid,
     &kUmaBackgroundSessions,
+    &kUpdateHistoryEntryPointsInIncognito,
     &kUpdateNotificationSchedulingIntegration,
     &kUpdateNotificationScheduleServiceImmediateShowOption,
     &kVoiceSearchAudioCapturePolicy,
@@ -309,6 +310,7 @@
     &signin::kMobileIdentityConsistencyFRE,
     &switches::kDeprecateMenagerieAPI,
     &switches::kDecoupleSyncFromAndroidMasterSync,
+    &switches::kMinorModeSupport,
     &switches::kSyncUseSessionsUnregisterDelay,
     &subresource_filter::kSafeBrowsingSubresourceFilter,
     &video_tutorials::features::kVideoTutorials,
@@ -722,6 +724,9 @@
 const base::Feature kUmaBackgroundSessions{"UMABackgroundSessions",
                                            base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kUpdateHistoryEntryPointsInIncognito{
+    "UpdateHistoryEntryPointsInIncognito", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kUpdateNotificationSchedulingIntegration{
     "UpdateNotificationSchedulingIntegration",
     base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index ec7a8f76..7fbce753 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -149,6 +149,7 @@
 extern const base::Feature kTrustedWebActivityQualityEnforcementWarning;
 extern const base::Feature kStartSurfaceAndroid;
 extern const base::Feature kUmaBackgroundSessions;
+extern const base::Feature kUpdateHistoryEntryPointsInIncognito;
 extern const base::Feature kUpdateNotificationSchedulingIntegration;
 extern const base::Feature
     kUpdateNotificationScheduleServiceImmediateShowOption;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 5a977e6..c5b52ab 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -376,6 +376,7 @@
     public static final String MOBILE_IDENTITY_CONSISTENCY_M2 = "MobileIdentityConsistencyFRE";
     public static final String MODAL_PERMISSION_DIALOG_VIEW = "ModalPermissionDialogView";
     public static final String METRICS_SETTINGS_ANDROID = "MetricsSettingsAndroid";
+    public static final String MINOR_MODE_SUPPORT = "MinorModeSupport";
     public static final String NOTIFICATION_SUSPENDER = "NotificationSuspender";
     public static final String OFFLINE_INDICATOR = "OfflineIndicator";
     public static final String OFFLINE_INDICATOR_V2 = "OfflineIndicatorV2";
@@ -490,6 +491,8 @@
             "UpdateNotificationSchedulingIntegration";
     public static final String UPDATE_NOTIFICATION_IMMEDIATE_SHOW_OPTION =
             "UpdateNotificationScheduleServiceImmediateShowOption";
+    public static final String UPDATE_HISTORY_ENTRY_POINTS_IN_INCOGNITO =
+            "UpdateHistoryEntryPointsInIncognito";
     public static final String USE_CHIME_ANDROID_SDK = "UseChimeAndroidSdk";
     public static final String USE_NOTIFICATION_COMPAT_BUILDER = "UseNotificationCompatBuilder";
     public static final String VOICE_SEARCH_AUDIO_CAPTURE_POLICY = "VoiceSearchAudioCapturePolicy";
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager.cc b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
index 57e6bbdf..2a2d4a0f 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "ash/public/cpp/ash_features.h"
 #include "base/callback_helpers.h"
 #include "base/files/file_util.h"
 #include "base/notreached.h"
@@ -883,15 +882,12 @@
       NotificationHandler::Type::NEARBY_SHARE, notification,
       /*metadata=*/nullptr);
 
-  if (ash::features::IsTemporaryHoldingSpaceEnabled()) {
-    ash::HoldingSpaceKeyedService* holding_space_keyed_service =
-        ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(
-            profile_);
-    if (holding_space_keyed_service) {
-      for (const auto& file : share_target.file_attachments) {
-        if (file.file_path().has_value())
-          holding_space_keyed_service->AddNearbyShare(file.file_path().value());
-      }
+  ash::HoldingSpaceKeyedService* holding_space_keyed_service =
+      ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(profile_);
+  if (holding_space_keyed_service) {
+    for (const auto& file : share_target.file_attachments) {
+      if (file.file_path().has_value())
+        holding_space_keyed_service->AddNearbyShare(file.file_path().value());
     }
   }
 }
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
index 571a9494..1f7dbd0 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 #include <vector>
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
 #include "ash/public/cpp/holding_space/holding_space_model.h"
@@ -1324,8 +1323,7 @@
   NearbyFilesHoldingSpaceTest()
       : session_controller_(std::make_unique<TestSessionController>()),
         user_manager_(new ash::FakeChromeUserManager) {
-    scoped_feature_list_.InitWithFeatures(
-        {features::kNearbySharing, ash::features::kTemporaryHoldingSpace}, {});
+    scoped_feature_list_.InitAndEnableFeature(features::kNearbySharing);
 
     holding_space_controller_ = std::make_unique<ash::HoldingSpaceController>();
     profile_manager_ = CreateTestingProfileManager();
diff --git a/chrome/browser/notifications/alert_dispatcher_mojo.mm b/chrome/browser/notifications/alert_dispatcher_mojo.mm
index feb1ee40b..b32798d 100644
--- a/chrome/browser/notifications/alert_dispatcher_mojo.mm
+++ b/chrome/browser/notifications/alert_dispatcher_mojo.mm
@@ -64,7 +64,8 @@
 }
 
 - (void)closeAllNotifications {
-  [[self serviceProxy] closeAllNotifications];
+  if (_mojoService)
+    [[self serviceProxy] closeAllNotifications];
   // We know that there are no more notifications after this.
   [self onServiceDisconnectedGracefully:YES];
 }
diff --git a/chrome/browser/notifications/alert_dispatcher_mojo_unittest.mm b/chrome/browser/notifications/alert_dispatcher_mojo_unittest.mm
index b4cdb74c..cf7dab0 100644
--- a/chrome/browser/notifications/alert_dispatcher_mojo_unittest.mm
+++ b/chrome/browser/notifications/alert_dispatcher_mojo_unittest.mm
@@ -178,15 +178,6 @@
   FakeMacNotificationProviderFactory* provider_factory_ = nullptr;
 };
 
-TEST_F(AlertDispatcherMojoTest, CloseAllNotifications) {
-  base::RunLoop run_loop;
-  // Expect that we disconnect after closing all notifications.
-  ExpectDisconnect(run_loop.QuitClosure());
-  EXPECT_CALL(service(), CloseAllNotifications);
-  [alert_dispatcher_ closeAllNotifications];
-  run_loop.Run();
-}
-
 TEST_F(AlertDispatcherMojoTest, CloseNotificationAndDisconnect) {
   base::RunLoop run_loop;
   // Expect that we disconnect after closing the last notification.
@@ -233,6 +224,13 @@
 
   run_loop.Run();
   ExpectKeepConnected();
+
+  base::RunLoop run_loop2;
+  // Expect that we disconnect after closing all notifications.
+  ExpectDisconnect(run_loop2.QuitClosure());
+  EXPECT_CALL(service(), CloseAllNotifications);
+  [alert_dispatcher_ closeAllNotifications];
+  run_loop2.Run();
 }
 
 TEST_F(AlertDispatcherMojoTest, CloseProfileNotificationsAndDisconnect) {
diff --git a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
index 9f093b7..7bcb119 100644
--- a/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_browsertest.cc
@@ -226,7 +226,8 @@
     auto* identity_manager =
         IdentityManagerFactory::GetForProfile(browser()->profile());
     ASSERT_TRUE(identity_manager);
-    signin::SetPrimaryAccount(identity_manager, GetTestUser());
+    signin::SetPrimaryAccount(identity_manager, GetTestUser(),
+                              signin::ConsentLevel::kSync);
 
     UserCloudPolicyManager* policy_manager =
         browser()->profile()->GetUserCloudPolicyManager();
diff --git a/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc b/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc
index cf0069d..cfa59cf5 100644
--- a/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc
+++ b/chrome/browser/policy/cloud/cloud_policy_manager_browsertest.cc
@@ -177,7 +177,8 @@
     // the username to the UserCloudPolicyValidator.
     auto* identity_manager =
         IdentityManagerFactory::GetForProfile(browser()->profile());
-    signin::SetPrimaryAccount(identity_manager, "user@example.com");
+    signin::SetPrimaryAccount(identity_manager, "user@example.com",
+                              signin::ConsentLevel::kSync);
 
     ASSERT_TRUE(policy_manager());
     policy_manager()->Connect(
diff --git a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
index 3d0e85e..74f0229 100644
--- a/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
+++ b/chrome/browser/policy/cloud/component_cloud_policy_browsertest.cc
@@ -186,7 +186,7 @@
     // the account id to the UserCloudPolicyValidator.
     signin::SetPrimaryAccount(
         IdentityManagerFactory::GetForProfile(browser()->profile()),
-        PolicyBuilder::kFakeUsername);
+        PolicyBuilder::kFakeUsername, signin::ConsentLevel::kSync);
 
     UserCloudPolicyManager* policy_manager =
         browser()->profile()->GetUserCloudPolicyManager();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 8e5fd2a..72b02e0 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -117,6 +117,7 @@
 #include "components/search_engines/search_engines_pref_names.h"
 #include "components/search_engines/template_url.h"
 #include "components/search_engines/template_url_service.h"
+#include "components/send_tab_to_self/metrics_util.h"
 #include "components/spellcheck/browser/pref_names.h"
 #include "components/spellcheck/browser/spellcheck_host_metrics.h"
 #include "components/spellcheck/common/spellcheck_common.h"
@@ -2392,16 +2393,16 @@
     case IDC_SEND_TAB_TO_SELF_SINGLE_TARGET:
       send_tab_to_self::ShareToSingleTarget(
           GetBrowser()->tab_strip_model()->GetActiveWebContents());
-      send_tab_to_self::RecordSendTabToSelfClickResult(
-          send_tab_to_self::kContentMenu, SendTabToSelfClickResult::kClickItem);
+      send_tab_to_self::RecordDeviceClicked(
+          send_tab_to_self::ShareEntryPoint::kContentMenu);
       break;
 
     case IDC_CONTENT_LINK_SEND_TAB_TO_SELF_SINGLE_TARGET:
       send_tab_to_self::ShareToSingleTarget(
           GetBrowser()->tab_strip_model()->GetActiveWebContents(),
           params_.link_url);
-      send_tab_to_self::RecordSendTabToSelfClickResult(
-          send_tab_to_self::kLinkMenu, SendTabToSelfClickResult::kClickItem);
+      send_tab_to_self::RecordDeviceClicked(
+          send_tab_to_self::ShareEntryPoint::kLinkMenu);
       break;
 
     case IDC_CONTENT_CONTEXT_GENERATE_QR_CODE: {
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
index a2eb61d..09bc60a 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
@@ -87,8 +87,8 @@
     aria-label="[[_label(emoji, variants)]]">
   [[emoji]]
 </button>
-<paper-tooltip id="tooltip" for="button" fit-to-visible-bounds part="tooltip"
-  offset="8">
+<paper-tooltip id="tooltip" for="emoji-button" fit-to-visible-bounds
+  part="tooltip" offset="8">
   [[tooltip]]
 </paper-tooltip>
 <template is="dom-if" if="[[variantsVisible]]">
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
index 670a25d..65f5667e 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_group.html
@@ -63,6 +63,10 @@
     border: 2px solid var(--cr-toggle-color);
   }
 
+  #fake-focus-target {
+    position: absolute;
+  }
+
 </style>
 
 <div id="heading" role="heading" aria-level="2" tabindex="0">
@@ -79,6 +83,7 @@
   </button>
   </template>
 <div id="emoji">
+  <div id="fake-focus-target" tabindex="-1"></div>
   <template is="dom-repeat" items="[[data.emoji]]">
     <emoji-button emoji="[[getDisplayEmojiForEmoji(item.base.string)]]"
       variants="[[item.alternates]]" tooltip="[[getTooltipForEmoji(item.base)]]"
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
index a2e9ef48..9e67d04 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.html
@@ -86,6 +86,7 @@
 
   .chevron {
     background-color: var(--cr-card-background-color);
+    border-radius: 0;
     height: var(--emoji-group-button-size);
     margin: 0;
     padding: 0;
@@ -98,6 +99,10 @@
     left: calc((var(--emoji-picker-width) -
       2 * var(--emoji-picker-side-padding)) / 9 * 8
       + var(--emoji-picker-side-padding));
+    /* Icons may become visible to the right of this during scrolling without
+     * the additional padding.
+     */
+    padding-inline-end: 5px;
   }
 
   #left-chevron {
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index 88f89ce..816c085d 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -259,8 +259,8 @@
     const group =
         this.shadowRoot.querySelector(`div[data-group="${newGroup}"]`);
     group.querySelector('emoji-group')
-        .shadowRoot.querySelector('emoji-button')
-        .focusButton();
+        .shadowRoot.querySelector('#fake-focus-target')
+        .focus();
     group.scrollIntoView();
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.html b/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.html
index 1d7e40eb..84793a4 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.html
@@ -25,7 +25,6 @@
 <dom-module id="cellular-networks-list">
   <template>
     <style include="cr-shared-style os-settings-icons settings-shared iron-flex">
-
       :host > div {
         /* network-list is padded to the right to allow space for a ripple */
         padding-inline-end: calc(var(--cr-section-padding) -
@@ -39,14 +38,13 @@
       }
 
       .cellular-network-list-header {
-        padding-bottom: 8px;
+        align-items: center;
+        display: flex;
+        height: 72px;
+        padding-bottom: 16px;
         padding-top: 16px;
       }
 
-      .esim-list-header {
-        padding-top: 6px;
-      }
-
       .esim-list-title {
         align-self: center;
       }
@@ -70,46 +68,78 @@
         flex-direction: column;
       }
 
-      .add-button {
-        margin-inline-end: 2px;
+      #eidPopupButton {
+        --cr-icon-button-size: 18px;
+        cursor: pointer;
+        margin-inline-start: 4px;
+      }
+
+      #alignEnd {
+        align-items: center;
+        display: flex;
+        margin-inline-end: 12px;
         margin-inline-start: auto;
       }
 
-      #eidPopupButton {
-        cursor: pointer;
-        margin-inline-start: 0;
+      #inhibitedSubtext {
+        color: var(--cr-secondary-text-color);
+      }
+
+      paper-spinner-lite {
+        height: 20px;
+        width: 20px;
       }
     </style>
     <template is="dom-if" if="[[shouldShowEsimSection_(euicc_,
         cellularDeviceState, cellularDeviceState.*)]]" restamp>
       <div class="cellular-network-list-separator"></div>
-      <div class="cellular-network-list-header esim-list-header flex">
-        <div class="esim-list-title">$i18n{cellularNetworkEsimLabel}</div>
-        <div class="flex-column">
-          <cr-icon-button
-              id="eidPopupButton"
-              iron-icon="cr:info-outline"
-              title="$i18n{showEidPopupButtonLabel}"
-              aria-label="$i18n{showEidPopupButtonLabel}"
-              on-click="toggleEidPopup_">
-          </cr-icon-button>
-          <template is="dom-if" if="[[shouldShowEidPopup_]]" restamp>
-            <cellular-eid-popup class="eid-popup" euicc="[[euicc_]]">
-            </cellular-eid-popup>
-          </template>
+        <div class="cellular-network-list-header esim-list-header
+            flex settings-box-text">
+          <div class="flex-column">
+            <div class="flex header-row">
+              <div class="esim-list-title">
+                $i18n{cellularNetworkEsimLabel}
+              </div>
+              <div class="flex-column">
+                <cr-icon-button
+                    id="eidPopupButton"
+                    iron-icon="cr:info-outline"
+                    title="$i18n{showEidPopupButtonLabel}"
+                    aria-label="$i18n{showEidPopupButtonLabel}"
+                    on-click="toggleEidPopup_">
+                </cr-icon-button>
+                <template is="dom-if" if="[[shouldShowEidPopup_]]" restamp>
+                  <cellular-eid-popup class="eid-popup" euicc="[[euicc_]]">
+                  </cellular-eid-popup>
+                </template>
+              </div>
+            </div>
+            <div id="inhibitedSubtext" class="header-row secondary-box-text"
+                hidden="[[!isDeviceInhibited_(cellularDeviceState,
+                    cellularDeviceState.inhibitReason)]]">
+              [[getInhibitedSubtextMessage_(cellularDeviceState,
+                  cellularDeviceState.inhibitReason)]]
+            </div>
+          </div>
+          <div id="alignEnd">
+            <paper-spinner-lite id="inhibitedSpinner"
+                active="[[isDeviceInhibited_(cellularDeviceState,
+                    cellularDeviceState.inhibitReason)]]">
+            </paper-spinner-lite>
+            <template is="dom-if" if="[[showAddESimButton_(cellularDeviceState,
+                globalPolicy)]]" restamp>
+              <cr-icon-button class="icon-add-cellular add-button"
+                  aria-label="$i18n{internetAddCellular}" id="addESimButton"
+                  disabled="[[isDeviceInhibited_(cellularDeviceState,
+                    cellularDeviceState.inhibitReason)]]"
+                  on-click="onAddEsimButtonTap_">
+              </cr-icon-button>
+            </template>
+          </div>
         </div>
-        <template is="dom-if"
-            if="[[showAddESimButton_(cellularDeviceState, globalPolicy)]]" restamp>
-          <cr-icon-button class="icon-add-cellular add-button"
-              aria-label="$i18n{internetAddCellular}" id="addESimButton"
-              disabled="[[isDeviceInhibited_(cellularDeviceState,
-                cellularDeviceState.inhibitReason)]]"
-              on-click="onAddEsimButtonTap_">
-          </cr-icon-button>
-        </template>
       </div>
-      <template is="dom-if"
-          if="[[shouldShowNetworkSublist_(eSimNetworks_, eSimPendingProfileItems_)]]" restamp>
+      <template is="dom-if" if="[[shouldShowNetworkSublist_(eSimNetworks_,
+          eSimPendingProfileItems_)]]" restamp>
         <div class="cellular-network-content">
           <network-list id="esimNetworkList" show-buttons
               show-technology-badge="[[showTechnologyBadge]]"
@@ -119,9 +149,8 @@
           </network-list>
         </div>
       </template>
-      <template
-          is="dom-if"
-          if="[[!shouldShowNetworkSublist_(eSimNetworks_, eSimPendingProfileItems_)]]" restamp>
+      <template is="dom-if" if="[[!shouldShowNetworkSublist_(eSimNetworks_,
+          eSimPendingProfileItems_)]]" restamp>
         <div id="eSimNoNetworkFound"
             class="cellular-network-content cellular-not-setup">
           <settings-localized-link
@@ -137,7 +166,7 @@
         if="[[shouldShowPSimSection_(cellularDeviceState,
           cellularDeviceState.*)]]" restamp>
       <div class="cellular-network-list-separator"></div>
-      <div class="cellular-network-list-header">
+      <div class="cellular-network-list-header settings-box-text">
         $i18n{cellularNetworkPsimLabel}
       </div>
       <template
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js b/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js
index 394719b..bb19d2290 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/cellular_networks_list.js
@@ -488,4 +488,32 @@
   getAddEsimButton() {
     return /** @type {?CrIconButtonElement} */ (this.$$('#addESimButton'));
   },
+
+  /**
+   * @return {string} Inhibited subtext message.
+   * @private
+   */
+  getInhibitedSubtextMessage_() {
+    if (!this.cellularDeviceState) {
+      return '';
+    }
+
+    const mojom = chromeos.networkConfig.mojom.InhibitReason;
+    const inhibitReason = this.cellularDeviceState.inhibitReason;
+
+    switch (inhibitReason) {
+      case mojom.kInstallingProfile:
+        return this.i18n('cellularNetworkInstallingProfile');
+      case mojom.kRenamingProfile:
+        return this.i18n('cellularNetworkRenamingProfile');
+      case mojom.kRemovingProfile:
+        return this.i18n('cellularNetworkRemovingProfile');
+      case mojom.kConnectingToProfile:
+        return this.i18n('cellularNetworkConnectingToProfile');
+      case mojom.kRefreshingProfileList:
+        return this.i18n('cellularNetworRefreshingProfileListProfile');
+    }
+
+    return '';
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html
index a328a20..f5051c5 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.html
@@ -82,9 +82,6 @@
         border-top: none;
       }
     </style>
-    <template is="dom-if" if="[[disabled_]]" restamp>
-      <cellular-banner device-state="[[deviceState_]]"></cellular-banner>
-    </template>
     <!-- Title section: Icon + name + connection state. -->
     <div id="titleDiv" class="settings-box first">
       <div class="start layout horizontal center">
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html
index f0225532..9ab9db0 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.html
@@ -35,6 +35,11 @@
         min-height: var(--settings-row-min-height);
       }
 
+      #cellularNetworkList {
+        /* No extra margin-top when displaying the cellular network list. */
+        margin-top: calc(-1*var(--cr-section-vertical-margin));
+      }
+
       /* Set padding on children instead of the container itself to ensure that
          separator lines can fill the entire width of the page. */
       #networkListDiv > * {
@@ -97,10 +102,6 @@
         padding-inline-start: 0;
       }
     </style>
-    <template is="dom-if" restamp
-        if="[[isDeviceInhibited_(deviceState, deviceState.inhibitReason)]]">
-      <cellular-banner device-state="[[deviceState]]"></cellular-banner>
-    </template>
     <template is="dom-if" if="[[enableToggleIsVisible_(deviceState)]]">
       <div class="settings-box first">
         <div id="onOff" class="start" on$="[[deviceIsEnabled_(deviceState)]]"
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
index 9570e7c2..b614cdd 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_subpage.js
@@ -311,7 +311,14 @@
       this.hasCompletedScanSinceLastEnabled_ = this.showSpinner &&
           !this.deviceState.scanning &&
           this.deviceState.deviceState === mojom.DeviceStateType.kEnabled;
-      this.showSpinner = !!this.deviceState.scanning;
+
+      // If the cellular network list is showing and currently inhibited, there
+      // is a separate spinner that shows in the CellularNetworkList.
+      if (this.shouldShowCellularNetworkList_() && this.isDeviceInhibited_()) {
+        this.showSpinner = false;
+      } else {
+        this.showSpinner = !!this.deviceState.scanning;
+      }
     }
 
     // Scans should only be triggered by the "networks" subpage.
diff --git a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
index 9cb24589..27dcb4fb 100644
--- a/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
+++ b/chrome/browser/resources/settings/clear_browsing_data_dialog/clear_browsing_data_dialog.html
@@ -100,6 +100,22 @@
         clip: rect(0, 0, 0, 0);
         position: fixed;
       }
+
+      #searchHistoryTextBox {
+        background-color: var(--google-grey-50);
+        border: 1px solid var(--google-grey-200);
+        border-radius: 4px;
+        margin-top: 12px;
+        padding: 12px;
+      }
+
+      /* dark mode */
+      @media (prefers-color-scheme: dark) {
+        #searchHistoryTextBox {
+          background-color: rgba(0, 0, 0, .3);
+          border-color: transparent;
+        }
+      }
     </style>
 
     <cr-dialog id="clearBrowsingDataDialog"
@@ -164,7 +180,7 @@
             </settings-checkbox>
             <div id="searchHistoryTextBox"
                 hidden="[[!shouldShowSearchHistoryLabel_]]">
-                $i18nRaw{clearSearchHistorySummarySignedIn}
+              $i18nRaw{clearSearchHistorySummarySignedIn}
             </div>
           </div>
           <div id="advanced-tab">
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc
index 7fe293c1..aea823fb 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.cc
@@ -76,12 +76,6 @@
                  link_url);
 }
 
-void RecordSendTabToSelfClickResult(const std::string& entry_point,
-                                    SendTabToSelfClickResult state) {
-  base::UmaHistogramEnumeration("SendTabToSelf." + entry_point + ".ClickResult",
-                                state);
-}
-
 size_t GetValidDeviceCount(Profile* profile) {
   SendTabToSelfSyncService* service =
       SendTabToSelfSyncServiceFactory::GetForProfile(profile);
diff --git a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h
index faf553b0..b9bd40d 100644
--- a/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h
+++ b/chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h
@@ -16,24 +16,8 @@
 class WebContents;
 }
 
-// State of the send tab to self option in the context menu.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class SendTabToSelfClickResult {
-  kShowItem = 0,
-  kClickItem = 1,
-  kShowDeviceList = 2,
-  kMaxValue = kShowDeviceList,
-};
-
 namespace send_tab_to_self {
 
-const char kOmniboxIcon[] = "OmniboxIcon";
-const char kContentMenu[] = "ContentMenu";
-const char kLinkMenu[] = "LinkMenu";
-const char kOmniboxMenu[] = "OmniboxMenu";
-const char kTabMenu[] = "TabMenu";
-
 enum SendTabToSelfMenuType { kTab, kOmnibox, kContent, kLink };
 
 // Adds a new entry to SendTabToSelfModel when user clicks a target device. Will
@@ -48,11 +32,6 @@
 void ShareToSingleTarget(content::WebContents* tab,
                          const GURL& link_url = GURL());
 
-// Records whether the user click to send a tab or link when send tab to self
-// entry point is shown.
-void RecordSendTabToSelfClickResult(const std::string& entry_point,
-                                    SendTabToSelfClickResult state);
-
 // Gets the count of valid device number.
 size_t GetValidDeviceCount(Profile* profile);
 
diff --git a/chrome/browser/sessions/better_session_restore_browsertest.cc b/chrome/browser/sessions/better_session_restore_browsertest.cc
index 0f6b3fb..19c20eb 100644
--- a/chrome/browser/sessions/better_session_restore_browsertest.cc
+++ b/chrome/browser/sessions/better_session_restore_browsertest.cc
@@ -527,10 +527,13 @@
   // ... but not if the content setting is set to clear on exit.
   CookieSettingsFactory::GetForProfile(new_browser->profile())
       ->SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY);
-  // ... unless background mode is active.
+
   EnableBackgroundMode();
   new_browser = QuitBrowserAndRestore(new_browser, false);
-  CheckReloadedPageRestored(new_browser);
+  if (browser_defaults::kBrowserAliveWithNoWindows)
+    CheckReloadedPageRestored(new_browser);
+  else
+    CheckReloadedPageNotRestored(new_browser);
 
   DisableBackgroundMode();
   new_browser = QuitBrowserAndRestore(new_browser, false);
@@ -798,10 +801,13 @@
   // ... but not if the content setting is set to clear on exit.
   CookieSettingsFactory::GetForProfile(new_browser->profile())
       ->SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY);
-  // ... unless background mode is active.
+
   EnableBackgroundMode();
   new_browser = QuitBrowserAndRestore(new_browser, false);
-  NavigateAndCheckStoredData(new_browser, "cookies.html");
+  if (browser_defaults::kBrowserAliveWithNoWindows)
+    NavigateAndCheckStoredData(new_browser, "cookies.html");
+  else
+    StoreDataWithPage(new_browser, "cookies.html");
   DisableBackgroundMode();
   new_browser = QuitBrowserAndRestore(new_browser, false);
   if (browser_defaults::kBrowserAliveWithNoWindows)
@@ -846,7 +852,10 @@
   StoreDataWithPage("session_cookies.html");
   EnableBackgroundMode();
   Browser* new_browser = QuitBrowserAndRestore(browser(), false);
-  NavigateAndCheckStoredData(new_browser, "session_cookies.html");
+  if (browser_defaults::kBrowserAliveWithNoWindows)
+    NavigateAndCheckStoredData(new_browser, "session_cookies.html");
+  else
+    StoreDataWithPage(new_browser, "session_cookies.html");
   DisableBackgroundMode();
   new_browser = QuitBrowserAndRestore(new_browser, false);
   if (browser_defaults::kBrowserAliveWithNoWindows)
diff --git a/chrome/browser/sessions/session_data_service.cc b/chrome/browser/sessions/session_data_service.cc
index 6d3d4b12..010da88 100644
--- a/chrome/browser/sessions/session_data_service.cc
+++ b/chrome/browser/sessions/session_data_service.cc
@@ -5,8 +5,6 @@
 #include "chrome/browser/sessions/session_data_service.h"
 
 #include "base/bind.h"
-#include "chrome/browser/background/background_mode_manager.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sessions/session_data_deleter.h"
@@ -45,12 +43,9 @@
     return;
 
   // Clear session data if the last window for a profile has been closed and
-  // closing the last window would normally close Chrome, unless background mode
-  // is active.  Tests don't have a background_mode_manager.
-  if (browser_defaults::kBrowserAliveWithNoWindows ||
-      g_browser_process->background_mode_manager()->IsBackgroundModeActive()) {
+  // closing the last window would normally close Chrome.
+  if (browser_defaults::kBrowserAliveWithNoWindows)
     return;
-  }
 
   // Check for any open windows for the current profile that we aren't tracking.
   for (auto* browser : *BrowserList::GetInstance()) {
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index ad43a3d3f2..0cb1e123 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -16,12 +16,13 @@
 #include "base/metrics/histogram_functions.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
-#include "chrome/browser/background/background_mode_manager.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/buildflags.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/sessions/session_common_utils.h"
 #include "chrome/browser/sessions/session_data_deleter.h"
diff --git a/chrome/browser/share/android/BUILD.gn b/chrome/browser/share/android/BUILD.gn
index 38ee4ff..70dafd8 100644
--- a/chrome/browser/share/android/BUILD.gn
+++ b/chrome/browser/share/android/BUILD.gn
@@ -41,6 +41,7 @@
     "java/src/org/chromium/chrome/browser/share/long_screenshots/bitmap_generation/LongScreenshotsTabServiceFactory.java",
     "java/src/org/chromium/chrome/browser/share/qrcode/QRCodeGenerationRequest.java",
     "java/src/org/chromium/chrome/browser/share/screenshot/EditorScreenshotTask.java",
+    "java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java",
     "java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java",
     "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java",
     "java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java",
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
index 3b49c22..1b3de8e 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java
@@ -18,7 +18,6 @@
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.share.send_tab_to_self.SendTabToSelfMetrics.SendTabToSelfShareClickResult;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
@@ -175,8 +174,7 @@
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        SendTabToSelfShareClickResult.recordClickResult(
-                SendTabToSelfShareClickResult.ClickType.CLICK_ITEM);
+        MetricsRecorder.recordDeviceClickedInShareSheet();
         TargetDeviceInfo targetDeviceInfo = mAdapter.getItem(position);
 
         SendTabToSelfAndroidBridge.addEntry(
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java
new file mode 100644
index 0000000..5d5dbb6
--- /dev/null
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.share.send_tab_to_self;
+
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+
+/**
+ * Class that captures all the metrics needed for Send Tab To Self on Android.
+ */
+@JNINamespace("send_tab_to_self")
+class MetricsRecorder {
+    public static void recordDeviceClickedInShareSheet() {
+        MetricsRecorderJni.get().recordDeviceClickedInShareSheet();
+    }
+
+    @NativeMethods
+    interface Natives {
+        void recordDeviceClickedInShareSheet();
+    }
+}
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java
deleted file mode 100644
index e528b069..0000000
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.share.send_tab_to_self;
-
-import androidx.annotation.IntDef;
-
-import org.chromium.base.metrics.RecordHistogram;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Class that captures all the metrics needed for Send Tab To Self on Android.
- */
-class SendTabToSelfMetrics {
-    /**
-     * Metrics captured when a user initiates and completes the sending flow.
-     */
-    static class SendTabToSelfShareClickResult {
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef({ClickType.SHOW_ITEM, ClickType.CLICK_ITEM, ClickType.SHOW_DEVICE_LIST})
-        @interface ClickType {
-            // These values are used for UMA. Don't reuse or reorder values.
-            // If you add something, update NUM_ENTRIES.This must be kept in sync with
-            // send_tab_to_self_desktop_util.h
-            int SHOW_ITEM = 0;
-            int CLICK_ITEM = 1;
-            int SHOW_DEVICE_LIST = 2;
-            int NUM_ENTRIES = 3;
-        }
-
-        static void recordClickResult(@ClickType int result) {
-            RecordHistogram.recordEnumeratedHistogram(
-                    "SendTabToSelf.AndroidShareSheet.ClickResult", result, ClickType.NUM_ENTRIES);
-        }
-    }
-}
diff --git a/chrome/browser/share/android/java_sources.gni b/chrome/browser/share/android/java_sources.gni
index f32f301..78fd3513 100644
--- a/chrome/browser/share/android/java_sources.gni
+++ b/chrome/browser/share/android/java_sources.gni
@@ -53,6 +53,7 @@
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/screenshot/ScreenshotShareSheetViewProperties.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetAdapter.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/DevicePickerBottomSheetContent.java",
+  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/MetricsRecorder.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationManager.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/NotificationSharedPrefManager.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfAndroidBridge.java",
@@ -60,7 +61,6 @@
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfEntry.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBar.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfInfoBarController.java",
-  "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfMetrics.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/SendTabToSelfModelObserverBridge.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/send_tab_to_self/TargetDeviceInfo.java",
   "//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ChromeProvidedSharingOptionsProvider.java",
diff --git a/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninCheckerTest.java b/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninCheckerTest.java
index 9985f5a8..2a51a9a 100644
--- a/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninCheckerTest.java
+++ b/chrome/browser/signin/ui/android/java/src/org/chromium/chrome/browser/signin/ui/SigninCheckerTest.java
@@ -74,7 +74,7 @@
         final CoreAccountInfo expectedPrimaryAccount =
                 mAccountManagerTestRule.addAccount(newAccountEmail);
 
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(oldAccount.getEmail());
+        mAccountManagerTestRule.removeAccount(oldAccount.getEmail());
 
         CriteriaHelper.pollUiThread(() -> {
             return expectedPrimaryAccount.equals(
@@ -92,7 +92,7 @@
         when(mAccountRenameCheckerDelegateMock.getNewNameOfRenamedAccount(oldAccount.getEmail()))
                 .thenReturn(newAccountEmail);
 
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(oldAccount.getEmail());
+        mAccountManagerTestRule.removeAccount(oldAccount.getEmail());
 
         CriteriaHelper.pollUiThread(() -> {
             return !IdentityServicesProvider.get()
@@ -109,7 +109,7 @@
         final CoreAccountInfo oldAccount =
                 mAccountManagerTestRule.addTestAccountThenSigninAndEnableSync();
 
-        mAccountManagerTestRule.removeAccountAndWaitForSeeding(oldAccount.getEmail());
+        mAccountManagerTestRule.removeAccount(oldAccount.getEmail());
 
         CriteriaHelper.pollUiThread(() -> {
             return !IdentityServicesProvider.get()
diff --git a/chrome/browser/speech/chrome_speech_recognition_service.cc b/chrome/browser/speech/chrome_speech_recognition_service.cc
index 5476a4d..d513ec6 100644
--- a/chrome/browser/speech/chrome_speech_recognition_service.cc
+++ b/chrome/browser/speech/chrome_speech_recognition_service.cc
@@ -24,9 +24,8 @@
 
 ChromeSpeechRecognitionService::ChromeSpeechRecognitionService(
     content::BrowserContext* context)
-    : context_(context),
-      enable_soda_(
-          base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)) {}
+    : enable_soda_(base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)),
+      context_(context) {}
 
 ChromeSpeechRecognitionService::~ChromeSpeechRecognitionService() = default;
 
@@ -69,12 +68,15 @@
   DCHECK(profile_prefs);
   DCHECK(global_prefs);
 
-  auto binary_path = global_prefs->GetFilePath(prefs::kSodaBinaryPath);
-  auto config_path =
-      ChromeSpeechRecognitionService::GetSodaConfigPath(profile_prefs);
-  if (enable_soda_ && (binary_path.empty() || config_path.empty())) {
-    LOG(ERROR) << "Unable to find SODA files on the device.";
-    return;
+  base::FilePath binary_path, config_path;
+  if (enable_soda_) {
+    binary_path = global_prefs->GetFilePath(prefs::kSodaBinaryPath);
+    config_path =
+        ChromeSpeechRecognitionService::GetSodaConfigPath(profile_prefs);
+    if (binary_path.empty() || config_path.empty()) {
+      LOG(ERROR) << "Unable to find SODA files on the device.";
+      return;
+    }
   }
 
   content::ServiceProcessHost::Launch(
diff --git a/chrome/browser/speech/chrome_speech_recognition_service.h b/chrome/browser/speech/chrome_speech_recognition_service.h
index d90f23b..b7bb770 100644
--- a/chrome/browser/speech/chrome_speech_recognition_service.h
+++ b/chrome/browser/speech/chrome_speech_recognition_service.h
@@ -39,6 +39,11 @@
   // media::mojom::SpeechRecognitionServiceClient
   void OnNetworkServiceDisconnect() override;
 
+ protected:
+  // A flag indicating whether to use the Speech On-Device API (SODA) for speech
+  // recognition.
+  const bool enable_soda_;
+
  private:
   // Launches the speech recognition service in a sandboxed utility process.
   void LaunchIfNotRunning();
@@ -49,10 +54,6 @@
   // The browser context associated with the keyed service.
   content::BrowserContext* const context_;
 
-  // A flag indicating whether to use the Speech On-Device API (SODA) for speech
-  // recognition.
-  const bool enable_soda_;
-
   // The remote to the speech recognition service. The browser will not launch a
   // new speech recognition service process if this remote is already bound.
   mojo::Remote<media::mojom::SpeechRecognitionService>
diff --git a/chrome/browser/speech/cros_speech_recognition_service.cc b/chrome/browser/speech/cros_speech_recognition_service.cc
index c783fd26..19d2f972 100644
--- a/chrome/browser/speech/cros_speech_recognition_service.cc
+++ b/chrome/browser/speech/cros_speech_recognition_service.cc
@@ -16,9 +16,7 @@
 
 CrosSpeechRecognitionService::CrosSpeechRecognitionService(
     content::BrowserContext* context)
-    : ChromeSpeechRecognitionService(context),
-      enable_soda_(
-          base::FeatureList::IsEnabled(media::kUseSodaForLiveCaption)) {}
+    : ChromeSpeechRecognitionService(context) {}
 
 CrosSpeechRecognitionService::~CrosSpeechRecognitionService() {}
 
diff --git a/chrome/browser/speech/cros_speech_recognition_service.h b/chrome/browser/speech/cros_speech_recognition_service.h
index 97dfee5..948061df 100644
--- a/chrome/browser/speech/cros_speech_recognition_service.h
+++ b/chrome/browser/speech/cros_speech_recognition_service.h
@@ -50,7 +50,6 @@
                          base::FilePath& languagepack_path);
   mojo::ReceiverSet<media::mojom::SpeechRecognitionContext>
       speech_recognition_contexts_;
-  const bool enable_soda_;
 };
 
 }  // namespace speech
diff --git a/chrome/browser/subresource_filter/chrome_content_subresource_filter_throttle_manager_factory.cc b/chrome/browser/subresource_filter/chrome_content_subresource_filter_throttle_manager_factory.cc
index e7e33ee..0d5541d 100644
--- a/chrome/browser/subresource_filter/chrome_content_subresource_filter_throttle_manager_factory.cc
+++ b/chrome/browser/subresource_filter/chrome_content_subresource_filter_throttle_manager_factory.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/subresource_filter/subresource_filter_profile_context_factory.h"
-#include "components/infobars/content/content_infobar_manager.h"
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h"
 #include "components/subresource_filter/content/browser/ruleset_service.h"
@@ -38,6 +37,5 @@
           web_contents,
           SubresourceFilterProfileContextFactory::GetForProfile(
               Profile::FromBrowserContext(web_contents->GetBrowserContext())),
-          infobars::ContentInfoBarManager::FromWebContents(web_contents),
           GetDatabaseManagerFromSafeBrowsingService(), dealer);
 }
diff --git a/chrome/browser/sync/test/integration/preferences_helper.cc b/chrome/browser/sync/test/integration/preferences_helper.cc
index 4c748a3..c7b48b7 100644
--- a/chrome/browser/sync/test/integration/preferences_helper.cc
+++ b/chrome/browser/sync/test/integration/preferences_helper.cc
@@ -61,7 +61,7 @@
   ListPrefUpdate update(GetPrefs(index), pref_name);
   base::ListValue* list = update.Get();
   for (const auto& it : new_value) {
-    list->Append(it.CreateDeepCopy());
+    list->Append(it.Clone());
   }
 }
 
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
index f6c9fdef..ff0adad 100644
--- a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
@@ -10,7 +10,6 @@
 #include <utility>
 #include <vector>
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/notification_utils.h"
 #include "ash/shell.h"
 #include "base/base64.h"
@@ -545,7 +544,7 @@
       ->Display(NotificationHandler::Type::TRANSIENT, *notification,
                 /*metadata=*/nullptr);
 
-  if (success && ash::features::IsTemporaryHoldingSpaceEnabled()) {
+  if (success) {
     ash::HoldingSpaceKeyedServiceFactory::GetInstance()
         ->GetService(GetProfile())
         ->AddScreenshot(screenshot_path);
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber_browsertest.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber_browsertest.cc
index 19d98a7e..cb4c181 100644
--- a/chrome/browser/ui/ash/chrome_screenshot_grabber_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber_browsertest.cc
@@ -32,17 +32,12 @@
 using testing::_;
 using testing::Return;
 
-// Parameterized by TemporaryHoldingSpace feature state.
 class ChromeScreenshotGrabberBrowserTest
     : public InProcessBrowserTest,
-      public testing::WithParamInterface<bool>,
       public ChromeScreenshotGrabberTestObserver,
       public ui::ClipboardObserver {
  public:
-  ChromeScreenshotGrabberBrowserTest() {
-    scoped_feature_list_.InitWithFeatureState(
-        ash::features::kTemporaryHoldingSpace, GetParam());
-  }
+  ChromeScreenshotGrabberBrowserTest() = default;
   ~ChromeScreenshotGrabberBrowserTest() override = default;
 
   void SetUpOnMainThread() override {
@@ -90,8 +85,6 @@
         ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr);
   }
 
-  bool TemporaryHoldingSpaceEnabled() const { return GetParam(); }
-
   ash::HoldingSpaceModel* GetHoldingSpaceModel() const {
     ash::HoldingSpaceController* const controller =
         ash::HoldingSpaceController::Get();
@@ -108,16 +101,10 @@
   policy::MockDlpContentManager mock_dlp_content_manager_;
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(ChromeScreenshotGrabberBrowserTest);
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         ChromeScreenshotGrabberBrowserTest,
-                         testing::Bool());
-
-IN_PROC_BROWSER_TEST_P(ChromeScreenshotGrabberBrowserTest, TakeScreenshot) {
+IN_PROC_BROWSER_TEST_F(ChromeScreenshotGrabberBrowserTest, TakeScreenshot) {
   ChromeScreenshotGrabber* chrome_screenshot_grabber =
       ChromeScreenshotGrabber::Get();
   SetTestObserver(chrome_screenshot_grabber, this);
@@ -149,19 +136,15 @@
     EXPECT_TRUE(base::PathExists(screenshot_path_));
   }
 
-  if (TemporaryHoldingSpaceEnabled()) {
-    ash::HoldingSpaceModel* holding_space_model = GetHoldingSpaceModel();
-    ASSERT_TRUE(holding_space_model);
-    ASSERT_EQ(1u, holding_space_model->items().size());
+  ash::HoldingSpaceModel* holding_space_model = GetHoldingSpaceModel();
+  ASSERT_TRUE(holding_space_model);
+  ASSERT_EQ(1u, holding_space_model->items().size());
 
-    ash::HoldingSpaceItem* holding_space_item =
-        holding_space_model->items()[0].get();
-    EXPECT_EQ(ash::HoldingSpaceItem::Type::kScreenshot,
-              holding_space_item->type());
-    EXPECT_EQ(screenshot_path_, holding_space_item->file_path());
-  } else {
-    EXPECT_FALSE(GetHoldingSpaceModel());
-  }
+  ash::HoldingSpaceItem* holding_space_item =
+      holding_space_model->items()[0].get();
+  EXPECT_EQ(ash::HoldingSpaceItem::Type::kScreenshot,
+            holding_space_item->type());
+  EXPECT_EQ(screenshot_path_, holding_space_item->file_path());
 
   EXPECT_FALSE(IsImageClipboardAvailable());
   ui::ClipboardMonitor::GetInstance()->AddObserver(this);
@@ -177,7 +160,7 @@
   EXPECT_TRUE(IsImageClipboardAvailable());
 }
 
-IN_PROC_BROWSER_TEST_P(ChromeScreenshotGrabberBrowserTest,
+IN_PROC_BROWSER_TEST_F(ChromeScreenshotGrabberBrowserTest,
                        ScreenshotsDisallowed) {
   ChromeScreenshotGrabber* chrome_screenshot_grabber =
       ChromeScreenshotGrabber::Get();
@@ -196,16 +179,12 @@
             notification->system_notification_warning_level());
   EXPECT_EQ(ui::ScreenshotResult::DISABLED, screenshot_result_);
 
-  if (TemporaryHoldingSpaceEnabled()) {
-    ash::HoldingSpaceModel* holding_space_model = GetHoldingSpaceModel();
-    ASSERT_TRUE(holding_space_model);
-    EXPECT_TRUE(holding_space_model->items().empty());
-  } else {
-    EXPECT_FALSE(GetHoldingSpaceModel());
-  }
+  ash::HoldingSpaceModel* holding_space_model = GetHoldingSpaceModel();
+  ASSERT_TRUE(holding_space_model);
+  EXPECT_TRUE(holding_space_model->items().empty());
 }
 
-IN_PROC_BROWSER_TEST_P(ChromeScreenshotGrabberBrowserTest,
+IN_PROC_BROWSER_TEST_F(ChromeScreenshotGrabberBrowserTest,
                        ScreenshotsRestricted) {
   ChromeScreenshotGrabber* chrome_screenshot_grabber =
       ChromeScreenshotGrabber::Get();
@@ -228,11 +207,7 @@
             notification->system_notification_warning_level());
   EXPECT_EQ(ui::ScreenshotResult::DISABLED_BY_DLP, screenshot_result_);
 
-  if (TemporaryHoldingSpaceEnabled()) {
-    ash::HoldingSpaceModel* holding_space_model = GetHoldingSpaceModel();
-    ASSERT_TRUE(holding_space_model);
-    EXPECT_TRUE(holding_space_model->items().empty());
-  } else {
-    EXPECT_FALSE(GetHoldingSpaceModel());
-  }
+  ash::HoldingSpaceModel* holding_space_model = GetHoldingSpaceModel();
+  ASSERT_TRUE(holding_space_model);
+  EXPECT_TRUE(holding_space_model->items().empty());
 }
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc b/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc
index f993d5b9..26acd607 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_browsertest_base.cc
@@ -6,7 +6,6 @@
 
 #include <string>
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
 #include "ash/public/cpp/holding_space/holding_space_image.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
@@ -126,9 +125,7 @@
 // HoldingSpaceBrowserTestBase -------------------------------------------------
 
 HoldingSpaceBrowserTestBase::HoldingSpaceBrowserTestBase()
-    : web_app::SystemWebAppBrowserTestBase(false) {
-  scoped_feature_list_.InitAndEnableFeature(features::kTemporaryHoldingSpace);
-}
+    : web_app::SystemWebAppBrowserTestBase(false) {}
 
 HoldingSpaceBrowserTestBase::~HoldingSpaceBrowserTestBase() = default;
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
index 3fa2987..55b68cd5 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_downloads_delegate.cc
@@ -38,7 +38,7 @@
 
 void HoldingSpaceDownloadsDelegate::Init() {
   // ARC downloads.
-  if (features::IsTemporaryHoldingSpaceArcIntegrationEnabled()) {
+  if (features::IsHoldingSpaceArcIntegrationEnabled()) {
     // NOTE: The `arc_intent_helper_bridge` may be `nullptr` if the `profile()`
     // is not allowed to use ARC, e.g. if the `profile()` is OTR.
     auto* const arc_intent_helper_bridge =
@@ -67,7 +67,7 @@
 void HoldingSpaceDownloadsDelegate::OnArcDownloadAdded(
     const base::FilePath& relative_path,
     const std::string& owner_package_name) {
-  DCHECK(features::IsTemporaryHoldingSpaceArcIntegrationEnabled());
+  DCHECK(features::IsHoldingSpaceArcIntegrationEnabled());
   if (is_restoring_persistence())
     return;
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc
index 80c71b38..a56185c 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_browsertest.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "ash/public/cpp/ash_features.h"
 #include "ash/public/cpp/holding_space/holding_space_controller.h"
 #include "ash/public/cpp/holding_space/holding_space_image.h"
 #include "ash/public/cpp/holding_space/holding_space_item.h"
@@ -21,7 +20,6 @@
 #include "base/scoped_observation.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_path_override.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/unguessable_token.h"
@@ -287,10 +285,7 @@
     : public InProcessBrowserTest,
       public ::testing::WithParamInterface<FileSystemType> {
  public:
-  HoldingSpaceKeyedServiceBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        ash::features::kTemporaryHoldingSpace);
-  }
+  HoldingSpaceKeyedServiceBrowserTest() = default;
 
   // InProcessBrowserTest:
   bool SetUpUserDataDirectory() override {
@@ -377,8 +372,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   // List of files paths that are created by default by the test suite.
   std::vector<base::FilePath> predefined_test_files_;
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc
index 49e0240..59b94eb 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc
@@ -29,7 +29,7 @@
     : BrowserContextKeyedServiceFactory(
           "HoldingSpaceService",
           BrowserContextDependencyManager::GetInstance()) {
-  if (features::IsTemporaryHoldingSpaceArcIntegrationEnabled())
+  if (features::IsHoldingSpaceArcIntegrationEnabled())
     DependsOn(arc::ArcIntentHelperBridge::GetFactory());
   DependsOn(chromeos::FileChangeServiceFactory::GetInstance());
   DependsOn(drive::DriveIntegrationServiceFactory::GetInstance());
@@ -57,9 +57,6 @@
 
 KeyedService* HoldingSpaceKeyedServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  if (!features::IsTemporaryHoldingSpaceEnabled())
-    return nullptr;
-
   Profile* const profile = Profile::FromBrowserContext(context);
   DCHECK_EQ(profile->IsGuestSession(), profile->IsOffTheRecord());
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
index 345e82a..051419f 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -285,7 +285,6 @@
   HoldingSpaceKeyedServiceTest()
       : fake_user_manager_(new FakeChromeUserManager),
         user_manager_enabler_(base::WrapUnique(fake_user_manager_)) {
-    scoped_feature_list_.InitAndEnableFeature(features::kTemporaryHoldingSpace);
     HoldingSpaceImage::SetUseZeroInvalidationDelayForTesting(true);
   }
 
@@ -461,8 +460,6 @@
   user_manager::ScopedUserManager user_manager_enabler_;
   testing::NiceMock<MockDownloadManager> download_manager_;
   arc::ArcServiceManager arc_service_manager_;
-
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(HoldingSpaceKeyedServiceTest, GuestUserProfile) {
@@ -1692,7 +1689,7 @@
  public:
   HoldingSpaceKeyedServiceArcIntegrationTest() {
     scoped_feature_list_.InitAndEnableFeature(
-        features::kTemporaryHoldingSpaceArcIntegration);
+        features::kHoldingSpaceArcIntegration);
   }
 
  private:
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
index cd7576a..c984ed1 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -21,7 +21,6 @@
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/autofill_external_delegate.h"
-#include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -78,12 +77,11 @@
 class MockAutofillDriver : public ContentAutofillDriver {
  public:
   MockAutofillDriver(content::RenderFrameHost* rfh, MockAutofillClient* client)
-      : ContentAutofillDriver(
-            rfh,
-            client,
-            kAppLocale,
-            kDownloadState,
-            AutofillManager::AutofillManagerFactoryCallback()) {}
+      : ContentAutofillDriver(rfh,
+                              client,
+                              kAppLocale,
+                              kDownloadState,
+                              nullptr) {}
 
   ~MockAutofillDriver() override = default;
   MOCK_CONST_METHOD0(GetAxTreeId, ui::AXTreeID());
diff --git a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
index fbeffe5..be40799 100644
--- a/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_blocker_browsertest.cc
@@ -76,7 +76,7 @@
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/keycodes/dom/keycode_converter.h"
 
-#if defined(OS_CHROMEOS) || defined(OS_LINUX)
+#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MAC)
 #include "third_party/blink/public/common/switches.h"
 #endif
 
@@ -114,9 +114,9 @@
     ASSERT_TRUE(embedded_test_server()->Start());
   }
 
-#if defined(OS_CHROMEOS) || defined(OS_LINUX)
-  // ChromeOS testing via linux, chromeos and maybe others, is flaky
-  // due to slower loading interacting with deferred commits.
+#if defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MAC)
+  // Testing on some platforms is flaky due to slower loading interacting with
+  // deferred commits.
   void SetUpCommandLine(base::CommandLine* command_line) override {
     InProcessBrowserTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
@@ -735,13 +735,7 @@
 
 // Tests that the tapping gesture with cntl/cmd key on a link open the
 // backgournd tab.
-// crbug.com/1192343: flaky on Mac
-#if defined(OS_MAC)
-#define MAYBE_TapGestureWithCtrlKey DISABLED_TapGestureWithCtrlKey
-#else
-#define MAYBE_TapGestureWithCtrlKey TapGestureWithCtrlKey
-#endif
-IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, MAYBE_TapGestureWithCtrlKey) {
+IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, TapGestureWithCtrlKey) {
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
 
   GURL url(embedded_test_server()->GetURL(
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc
index a786f2906..41a0089b3 100644
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc
+++ b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/pref_registry/pref_registry_syncable.h"
+#include "components/send_tab_to_self/metrics_util.h"
 #include "components/send_tab_to_self/pref_names.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
@@ -78,8 +79,7 @@
 void SendTabToSelfBubbleController::OnDeviceSelected(
     const std::string& target_device_name,
     const std::string& target_device_guid) {
-  RecordSendTabToSelfClickResult(kOmniboxIcon,
-                                 SendTabToSelfClickResult::kClickItem);
+  send_tab_to_self::RecordDeviceClicked(ShareEntryPoint::kOmniboxIcon);
   CreateNewEntry(web_contents_, target_device_name, target_device_guid, GURL());
 }
 
diff --git a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
index d886ed7b..e464bef 100644
--- a/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
+++ b/chrome/browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_desktop_util.h"
 #include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/send_tab_to_self/metrics_util.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/send_tab_to_self/send_tab_to_self_sync_service.h"
 #include "components/send_tab_to_self/target_device_info.h"
@@ -61,18 +62,16 @@
 }
 
 // Converts menu type to string.
-const std::string MenuTypeToString(SendTabToSelfMenuType menu_type) {
+ShareEntryPoint MenuTypeToEntryPoint(SendTabToSelfMenuType menu_type) {
   switch (menu_type) {
     case SendTabToSelfMenuType::kTab:
-      return kTabMenu;
+      return ShareEntryPoint::kTabMenu;
     case SendTabToSelfMenuType::kContent:
-      return kContentMenu;
+      return ShareEntryPoint::kContentMenu;
     case SendTabToSelfMenuType::kOmnibox:
-      return kOmniboxMenu;
+      return ShareEntryPoint::kOmniboxMenu;
     case SendTabToSelfMenuType::kLink:
-      return kLinkMenu;
-    default:
-      NOTREACHED();
+      return ShareEntryPoint::kLinkMenu;
   }
 }
 
@@ -125,8 +124,7 @@
     CreateNewEntry(tab_, item.device_name, item.cache_guid);
   }
 
-  RecordSendTabToSelfClickResult(MenuTypeToString(menu_type_),
-                                 SendTabToSelfClickResult::kClickItem);
+  send_tab_to_self::RecordDeviceClicked(MenuTypeToEntryPoint(menu_type_));
   return;
 }
 
diff --git a/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc b/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
index 92c5b99..3e665fe 100644
--- a/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
+++ b/chrome/browser/ui/signin_reauth_view_controller_browsertest.cc
@@ -173,9 +173,10 @@
             https_server(), kChallengePath);
     https_server()->StartAcceptingConnections();
 
-    account_id_ = signin::SetUnconsentedPrimaryAccount(identity_manager(),
-                                                       "alice@gmail.com")
-                      .account_id;
+    account_id_ =
+        signin::SetPrimaryAccount(identity_manager(), "alice@gmail.com",
+                                  signin::ConsentLevel::kSignin)
+            .account_id;
 
     reauth_result_loop_ = std::make_unique<base::RunLoop>();
     InProcessBrowserTest::SetUpOnMainThread();
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 673aa1a9..e5d570e9 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -47,6 +47,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/reading_list/core/reading_list_model.h"
+#include "components/send_tab_to_self/metrics_util.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
@@ -1405,8 +1406,8 @@
 
     case CommandSendTabToSelfSingleTarget: {
       send_tab_to_self::ShareToSingleTarget(GetWebContentsAt(context_index));
-      send_tab_to_self::RecordSendTabToSelfClickResult(
-          send_tab_to_self::kTabMenu, SendTabToSelfClickResult::kClickItem);
+      send_tab_to_self::RecordDeviceClicked(
+          send_tab_to_self::ShareEntryPoint::kTabMenu);
       break;
     }
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index af6ab3eb..521e0c5 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -52,6 +52,7 @@
 #include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/prefs/pref_service.h"
 #include "components/security_state/core/security_state.h"
+#include "components/send_tab_to_self/metrics_util.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/url_formatter/url_fixer.h"
@@ -805,8 +806,8 @@
     case IDC_SEND_TAB_TO_SELF_SINGLE_TARGET:
       send_tab_to_self::ShareToSingleTarget(
           location_bar_view_->GetWebContents());
-      send_tab_to_self::RecordSendTabToSelfClickResult(
-          send_tab_to_self::kOmniboxMenu, SendTabToSelfClickResult::kClickItem);
+      send_tab_to_self::RecordDeviceClicked(
+          send_tab_to_self::ShareEntryPoint::kOmniboxMenu);
       return;
 
     // These commands do invoke the popup.
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 a737261..55d4261b 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -522,47 +522,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
-                       CreateSignedInProfileWithSyncDisabled) {
-  ASSERT_EQ(1u, BrowserList::GetInstance()->size());
-  Profile* profile_being_created = StartSigninFlow();
-
-  // Disable sync by setting the device as managed in prefs.
-  syncer::SyncPrefs prefs(profile_being_created->GetPrefs());
-  prefs.SetManagedForTest(true);
-  syncer::SyncService* sync_service =
-      ProfileSyncServiceFactory::GetForProfile(profile_being_created);
-
-  // Simulate a successful Gaia sign-in.
-  SignIn(profile_being_created, "joe.consumer@gmail.com", "Joe");
-
-  // Wait for the sign-in to propagate to the flow, resulting in new browser
-  // getting opened.
-  Browser* new_browser = BrowserAddedWaiter(2u).Wait();
-  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
-                    GURL("chrome://newtab/"));
-
-  WaitForPickerClosed();
-
-  // Now the sync consent screen is shown, simulate closing the UI with "Stay
-  // signed in"
-  LoginUIServiceFactory::GetForProfile(profile_being_created)
-      ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
-
-  // Check expectations when the profile creation flow is done.
-  ProfileAttributesEntry* entry =
-      g_browser_process->profile_manager()
-          ->GetProfileAttributesStorage()
-          .GetProfileAttributesWithPath(profile_being_created->GetPath());
-  ASSERT_NE(entry, nullptr);
-  EXPECT_FALSE(entry->IsEphemeral());
-  EXPECT_EQ(entry->GetLocalProfileName(), u"Joe");
-  EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created)
-                ->GetAutogeneratedThemeColor(),
-            kProfileColor);
-  EXPECT_FALSE(sync_service->GetUserSettings()->IsSyncRequested());
-}
-
-IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest,
                        CreateSignedInProfileSettings) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   Profile* profile_being_created = StartSigninFlow();
@@ -903,7 +862,10 @@
 class ProfilePickerEnterpriseCreationFlowBrowserTest
     : public ProfilePickerCreationFlowBrowserTest {
  public:
-  ProfilePickerEnterpriseCreationFlowBrowserTest() = default;
+  explicit ProfilePickerEnterpriseCreationFlowBrowserTest(bool enable_feature) {
+    feature_list_.InitWithFeatureState(
+        features::kSignInProfileCreationEnterprise, enable_feature);
+  }
 
   void OnWillCreateBrowserContextServices(
       content::BrowserContext* context) override {
@@ -911,9 +873,20 @@
         context,
         base::BindRepeating(&FakeUserPolicySigninService::BuildForEnterprise));
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest,
+class ProfilePickerSeparateEnterpriseCreationFlowBrowserTest
+    : public ProfilePickerEnterpriseCreationFlowBrowserTest {
+ public:
+  ProfilePickerSeparateEnterpriseCreationFlowBrowserTest()
+      : ProfilePickerEnterpriseCreationFlowBrowserTest(
+            /*enable_feature=*/false) {}
+};
+
+IN_PROC_BROWSER_TEST_F(ProfilePickerSeparateEnterpriseCreationFlowBrowserTest,
                        CreateSignedInProfile) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   Profile* profile_being_created = StartSigninFlow();
@@ -961,9 +934,55 @@
                    ->UsingAutogeneratedTheme());
 }
 
+IN_PROC_BROWSER_TEST_F(ProfilePickerSeparateEnterpriseCreationFlowBrowserTest,
+                       CreateSignedInProfileWithSyncDisabledBefore) {
+  ASSERT_EQ(1u, BrowserList::GetInstance()->size());
+  Profile* profile_being_created = StartSigninFlow();
+
+  // Disable sync by setting the device as managed in prefs.
+  syncer::SyncPrefs prefs(profile_being_created->GetPrefs());
+  prefs.SetManagedForTest(true);
+  syncer::SyncService* sync_service =
+      ProfileSyncServiceFactory::GetForProfile(profile_being_created);
+
+  // Consumer-looking gmail address avoids code that forces the sync service to
+  // actually start which would add overhead in mocking further stuff.
+  // Enterprise domain needed for this profile being detected as Work.
+  SignIn(profile_being_created, "joe.enterprise@gmail.com", "Joe",
+         "enterprise.com");
+
+  // Wait for the sign-in to propagate to the flow, resulting in new browser
+  // getting opened.
+  Browser* new_browser = BrowserAddedWaiter(2u).Wait();
+  WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(),
+                    GURL("chrome://newtab/"));
+
+  WaitForPickerClosed();
+
+  // Now the sync disabled screen is shown, simulate closing the UI with "Stay
+  // signed in"
+  LoginUIServiceFactory::GetForProfile(profile_being_created)
+      ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
+
+  // Check expectations when the profile creation flow is done.
+  ProfileAttributesEntry* entry =
+      g_browser_process->profile_manager()
+          ->GetProfileAttributesStorage()
+          .GetProfileAttributesWithPath(profile_being_created->GetPath());
+  ASSERT_NE(entry, nullptr);
+  EXPECT_FALSE(entry->IsEphemeral());
+  EXPECT_EQ(entry->GetLocalProfileName(), u"enterprise.com");
+
+  // The color is not applied for enterprise users.
+  EXPECT_FALSE(ThemeServiceFactory::GetForProfile(profile_being_created)
+                   ->UsingAutogeneratedTheme());
+  // Sync is disabled.
+  EXPECT_FALSE(sync_service->GetUserSettings()->IsSyncRequested());
+}
+
 // Regression test for https://crbug.com/1184746.
-IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest,
-                       CreateSignedInProfileWithSyncDisabled) {
+IN_PROC_BROWSER_TEST_F(ProfilePickerSeparateEnterpriseCreationFlowBrowserTest,
+                       CreateSignedInProfileWithSyncDisabledAfter) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   Profile* profile_being_created = StartSigninFlow();
 
@@ -1000,7 +1019,7 @@
                     GURL("chrome://newtab/"));
   WaitForPickerClosed();
 
-  // Now the sync consent screen is shown, simulate closing the UI with "Stay
+  // Now the sync disabled screen is shown, simulate closing the UI with "Stay
   // signed in"
   LoginUIServiceFactory::GetForProfile(profile_being_created)
       ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS);
@@ -1021,7 +1040,7 @@
   EXPECT_FALSE(sync_service->GetUserSettings()->IsSyncRequested());
 }
 
-IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest,
+IN_PROC_BROWSER_TEST_F(ProfilePickerSeparateEnterpriseCreationFlowBrowserTest,
                        CreateSignedInProfileSettings) {
   ASSERT_EQ(1u, BrowserList::GetInstance()->size());
   Profile* profile_being_created = StartSigninFlow();
@@ -1068,10 +1087,9 @@
 class ProfilePickerIntegratedEnterpriseCreationFlowBrowserTest
     : public ProfilePickerEnterpriseCreationFlowBrowserTest {
  public:
-  ProfilePickerIntegratedEnterpriseCreationFlowBrowserTest() {
-    feature_list_.InitAndEnableFeature(
-        features::kSignInProfileCreationEnterprise);
-  }
+  ProfilePickerIntegratedEnterpriseCreationFlowBrowserTest()
+      : ProfilePickerEnterpriseCreationFlowBrowserTest(
+            /*enable_feature=*/true) {}
 
   void ExpectEnterpriseScreenTypeAndProceed(
       EnterpriseProfileWelcomeUI::ScreenType expected_type,
@@ -1087,9 +1105,6 @@
     // Simulate clicking on the next button.
     handler->CallProceedCallbackForTesting(proceed);
   }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(ProfilePickerIntegratedEnterpriseCreationFlowBrowserTest,
diff --git a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
index e3a72a5e..10f943fe 100644
--- a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits>
 #include <string>
 #include <vector>
 
@@ -10,13 +11,17 @@
 #include "base/files/file_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/bind.h"
 #include "base/threading/thread_restrictions.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
+#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
+#include "chrome/browser/chromeos/fileapi/recent_file.h"
+#include "chrome/browser/chromeos/fileapi/recent_model.h"
 #include "chrome/browser/sharesheet/sharesheet_service.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -26,10 +31,12 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/services/app_service/public/cpp/share_target.h"
+#include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/network/public/cpp/resource_request_body.h"
+#include "storage/browser/file_system/file_system_context.h"
 #include "ui/display/types/display_constants.h"
 #include "url/gurl.h"
 
@@ -126,6 +133,28 @@
     EXPECT_TRUE(content::WaitForLoadStop(contents));
     return contents;
   }
+
+  unsigned NumRecentFiles(content::WebContents* contents) {
+    unsigned result = std::numeric_limits<unsigned>::max();
+    base::RunLoop run_loop;
+
+    const scoped_refptr<storage::FileSystemContext> file_system_context =
+        file_manager::util::GetFileSystemContextForRenderFrameHost(
+            profile(), contents->GetMainFrame());
+    chromeos::RecentModel::GetForProfile(profile())->GetRecentFiles(
+        file_system_context.get(),
+        /*origin=*/GURL(),
+        /*file_type=*/chromeos::RecentModel::FileType::kAll,
+        base::BindLambdaForTesting(
+            [&result,
+             &run_loop](const std::vector<chromeos::RecentFile>& files) {
+              result = files.size();
+              run_loop.Quit();
+            }));
+
+    run_loop.Run();
+    return result;
+  }
 };
 
 IN_PROC_BROWSER_TEST_F(WebShareTargetBrowserTest, ShareTextFiles) {
@@ -151,6 +180,7 @@
   content::WebContents* const web_contents =
       LaunchAppWithIntent(app_id, std::move(intent), share_target_url());
   EXPECT_EQ("1,2,3,4,5 6,7,8,9,0", ReadTextContent(web_contents, "records"));
+  EXPECT_EQ(NumRecentFiles(web_contents), 0U);
 
   RemoveWebShareDirectory(directory);
 }
@@ -182,6 +212,7 @@
   EXPECT_EQ("Elements", ReadTextContent(web_contents, "headline"));
   EXPECT_EQ("Euclid", ReadTextContent(web_contents, "author"));
   EXPECT_EQ("https://example.org/", ReadTextContent(web_contents, "link"));
+  EXPECT_EQ(NumRecentFiles(web_contents), 0U);
 
   RemoveWebShareDirectory(directory);
 }
@@ -214,6 +245,7 @@
   content::WebContents* const web_contents =
       LaunchAppWithIntent(app_id, std::move(intent), share_target_url());
   EXPECT_EQ("a b c", ReadTextContent(web_contents, "notes"));
+  EXPECT_EQ(NumRecentFiles(web_contents), 0U);
 
   RemoveWebShareDirectory(directory);
 }
@@ -230,7 +262,6 @@
 
   content::WebContents* const web_contents =
       LaunchAppWithIntent(app_id, std::move(intent), share_target_url());
-
   // Poster web app's service worker detects omitted values.
   EXPECT_EQ("N/A", ReadTextContent(web_contents, "headline"));
   EXPECT_EQ("N/A", ReadTextContent(web_contents, "author"));
@@ -336,6 +367,7 @@
 
   web_contents = ShareToTarget("share_url()");
   EXPECT_EQ("https://example.com/", ReadTextContent(web_contents, "link"));
+  EXPECT_EQ(NumRecentFiles(web_contents), 0U);
 }
 
 IN_PROC_BROWSER_TEST_F(WebShareTargetBrowserTest, ShareToPartialWild) {
@@ -352,6 +384,7 @@
 
   content::WebContents* web_contents = ShareToTarget("share_single_file()");
   EXPECT_EQ("************", ReadTextContent(web_contents, "graphs"));
+  EXPECT_EQ(NumRecentFiles(web_contents), 0U);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.cc b/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.cc
index 3eef888c1..868fd64 100644
--- a/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.cc
@@ -84,6 +84,12 @@
   return dialog;
 }
 
+ui::ModalType InlineLoginDialogChromeOSOnboarding::GetDialogModalType() const {
+  // Override the default system-modal behavior of the dialog so that the
+  // shelf can be accessed during onboarding.
+  return ui::ModalType::MODAL_TYPE_WINDOW;
+}
+
 InlineLoginDialogChromeOSOnboarding::InlineLoginDialogChromeOSOnboarding(
     const gfx::Size& size,
     base::OnceCallback<void(void)> dialog_closed_callback)
diff --git a/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.h b/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.h
index 1a69897..c559229 100644
--- a/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.h
+++ b/chrome/browser/ui/webui/signin/inline_login_dialog_chromeos_onboarding.h
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h"
 
 #include "base/callback.h"
+#include "ui/base/ui_base_types.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/native_widget_types.h"
 #include "ui/views/widget/widget_observer.h"
@@ -55,6 +56,10 @@
       gfx::NativeWindow window,
       base::OnceCallback<void(void)> dialog_closed_callback);
 
+ protected:
+  // ui::WebDialogDelegate overrides
+  ui::ModalType GetDialogModalType() const override;
+
  private:
   InlineLoginDialogChromeOSOnboarding(
       const gfx::Size& bounds,
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index fa475a9..d701097 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -224,6 +224,7 @@
     "file_handler_manager_unittest.cc",
     "install_finalizer_unittest.cc",
     "os_integration_manager_unittest.cc",
+    "preinstalled_app_install_features_unittest.cc",
     "protocol_handler_manager_unittest.cc",
     "web_app_constants_unittest.cc",
     "web_app_data_retriever_unittest.cc",
diff --git a/chrome/browser/web_applications/components/preinstalled_app_install_features.cc b/chrome/browser/web_applications/components/preinstalled_app_install_features.cc
index ec7a27f..072e4a47 100644
--- a/chrome/browser/web_applications/components/preinstalled_app_install_features.cc
+++ b/chrome/browser/web_applications/components/preinstalled_app_install_features.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/web_applications/components/preinstalled_app_install_features.h"
 
 #include "base/feature_list.h"
+#include "chrome/browser/policy/profile_policy_connector.h"
+#include "chrome/browser/profiles/profile.h"
 
 namespace web_app {
 
@@ -24,6 +26,13 @@
 
 bool g_always_enabled_for_testing = false;
 
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+bool IsMigrationFeature(const base::Feature& feature) {
+  return &feature == &kMigrateDefaultChromeAppToWebAppsGSuite ||
+         &feature == &kMigrateDefaultChromeAppToWebAppsNonGSuite;
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+
 }  // namespace
 
 // Enables migration of default installed GSuite apps over to their replacement
@@ -41,6 +50,20 @@
 };
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+// Whether to allow the MigrateDefaultChromeAppToWebAppsGSuite and
+// MigrateDefaultChromeAppToWebAppsNonGSuite flags for managed users.
+// Without this flag enabled managed users will not undergo the default web app
+// migration.
+//
+// Why have a separate flag?
+// Field trials are not able to accurately distinguish managed Chrome OS users.
+// Because admin installed Chrome apps conflict with the default web app
+// migration we need to maintain separate control over the rollout for mananged
+// users.
+const base::Feature kAllowDefaultWebAppMigrationForChromeOsManagedUsers{
+    "AllowDefaultWebAppMigrationForChromeOsManagedUsers",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables default installing the Chat web app.
 const base::Feature kDefaultChatWebApp{"DefaultChatWebApp",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
@@ -50,11 +73,22 @@
                                        base::FEATURE_DISABLED_BY_DEFAULT};
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 
-bool IsPreinstalledAppInstallFeatureEnabled(base::StringPiece feature_name) {
+bool IsPreinstalledAppInstallFeatureEnabled(base::StringPiece feature_name,
+                                            const Profile& profile) {
   if (g_always_enabled_for_testing)
     return true;
 
   for (const base::Feature* feature : kPreinstalledAppInstallFeatures) {
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+    // See |kAllowDefaultWebAppMigrationForChromeOsManagedUsers| comment above.
+    if (base::FeatureList::IsEnabled(*feature) &&
+        feature->name == feature_name && IsMigrationFeature(*feature) &&
+        profile.GetProfilePolicyConnector()->IsManaged()) {
+      return base::FeatureList::IsEnabled(
+          kAllowDefaultWebAppMigrationForChromeOsManagedUsers);
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+
     if (feature->name == feature_name)
       return base::FeatureList::IsEnabled(*feature);
   }
diff --git a/chrome/browser/web_applications/components/preinstalled_app_install_features.h b/chrome/browser/web_applications/components/preinstalled_app_install_features.h
index 4365272..77de463a 100644
--- a/chrome/browser/web_applications/components/preinstalled_app_install_features.h
+++ b/chrome/browser/web_applications/components/preinstalled_app_install_features.h
@@ -9,6 +9,8 @@
 #include "base/feature_list.h"
 #include "base/strings/string_piece_forward.h"
 
+class Profile;
+
 namespace web_app {
 
 extern const base::Feature kMigrateDefaultChromeAppToWebAppsGSuite;
@@ -16,6 +18,8 @@
 extern const base::Feature kMigrateDefaultChromeAppToWebAppsNonGSuite;
 
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+extern const base::Feature kAllowDefaultWebAppMigrationForChromeOsManagedUsers;
+
 extern const base::Feature kDefaultChatWebApp;
 
 extern const base::Feature kDefaultMeetWebApp;
@@ -24,7 +28,8 @@
 // Returns the base::Feature in |kPreinstalledAppInstallFeatures| that
 // corresponds to |feature_name|. Used by external app install configs to gate
 // installation on features listed in |kPreinstalledAppInstallFeatures|.
-bool IsPreinstalledAppInstallFeatureEnabled(base::StringPiece feature_name);
+bool IsPreinstalledAppInstallFeatureEnabled(base::StringPiece feature_name,
+                                            const Profile& profile);
 
 base::AutoReset<bool> SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
 
diff --git a/chrome/browser/web_applications/components/preinstalled_app_install_features_unittest.cc b/chrome/browser/web_applications/components/preinstalled_app_install_features_unittest.cc
new file mode 100644
index 0000000..bd41f6f
--- /dev/null
+++ b/chrome/browser/web_applications/components/preinstalled_app_install_features_unittest.cc
@@ -0,0 +1,123 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/components/preinstalled_app_install_features.h"
+
+#include "base/strings/strcat.h"
+#include "base/system/sys_info.h"
+#include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace web_app {
+
+struct Migrate {
+  bool base;
+  bool managed;
+};
+
+class PreinstalledWebAppInstallFeaturesTest
+    : public testing::Test,
+      public ::testing::WithParamInterface<Migrate> {
+ public:
+  static std::string ParamToString(
+      const ::testing::TestParamInfo<Migrate> param_info) {
+    std::string result = "Migrate";
+    if (param_info.param.base)
+      result += "Base";
+    if (param_info.param.managed)
+      result += "Managed";
+    if (result == "Migrate")
+      result += "None";
+    return result;
+  }
+
+  PreinstalledWebAppInstallFeaturesTest() {
+    if (MigrateBase()) {
+      base_migration_.InitWithFeatures(
+          {kMigrateDefaultChromeAppToWebAppsGSuite,
+           kMigrateDefaultChromeAppToWebAppsNonGSuite},
+          {});
+    } else {
+      base_migration_.InitWithFeatures(
+          {}, {kMigrateDefaultChromeAppToWebAppsGSuite,
+               kMigrateDefaultChromeAppToWebAppsNonGSuite});
+    }
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+    if (MigrateManaged()) {
+      managed_migration_.InitAndEnableFeature(
+          kAllowDefaultWebAppMigrationForChromeOsManagedUsers);
+    } else {
+      managed_migration_.InitAndDisableFeature(
+          kAllowDefaultWebAppMigrationForChromeOsManagedUsers);
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  }
+
+  ~PreinstalledWebAppInstallFeaturesTest() override = default;
+
+  bool MigrateBase() const { return GetParam().base; }
+  bool MigrateManaged() const { return GetParam().managed; }
+
+  void ExpectMigrationEnabled(bool unmanaged_expectation,
+                              bool managed_expectation) {
+    {
+      TestingProfile::Builder builder;
+      builder.OverridePolicyConnectorIsManagedForTesting(false);
+      std::unique_ptr<TestingProfile> unmanaged_profile = builder.Build();
+      EXPECT_EQ(
+          IsPreinstalledAppInstallFeatureEnabled(
+              kMigrateDefaultChromeAppToWebAppsGSuite.name, *unmanaged_profile),
+          unmanaged_expectation);
+      EXPECT_EQ(IsPreinstalledAppInstallFeatureEnabled(
+                    kMigrateDefaultChromeAppToWebAppsNonGSuite.name,
+                    *unmanaged_profile),
+                unmanaged_expectation);
+    }
+
+    {
+      TestingProfile::Builder builder;
+      builder.OverridePolicyConnectorIsManagedForTesting(true);
+      std::unique_ptr<TestingProfile> managed_profile = builder.Build();
+      EXPECT_EQ(
+          IsPreinstalledAppInstallFeatureEnabled(
+              kMigrateDefaultChromeAppToWebAppsGSuite.name, *managed_profile),
+          managed_expectation);
+      EXPECT_EQ(IsPreinstalledAppInstallFeatureEnabled(
+                    kMigrateDefaultChromeAppToWebAppsNonGSuite.name,
+                    *managed_profile),
+                managed_expectation);
+    }
+  }
+
+ protected:
+  base::test::ScopedFeatureList base_migration_;
+  base::test::ScopedFeatureList managed_migration_;
+
+  content::BrowserTaskEnvironment task_environment_;
+};
+
+TEST_P(PreinstalledWebAppInstallFeaturesTest, MigrationEnabled) {
+  ExpectMigrationEnabled(
+      /*unmanaged_expectation=*/MigrateBase(),
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+      /*managed_expectation=*/MigrateBase() && MigrateManaged()
+#else
+      /*managed_expectation=*/MigrateBase()
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  );
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    PreinstalledWebAppInstallFeaturesTest,
+    testing::Values(Migrate{.base = false, .managed = false},
+                    Migrate{.base = false, .managed = true},
+                    Migrate{.base = true, .managed = false},
+                    Migrate{.base = true, .managed = true}),
+    &PreinstalledWebAppInstallFeaturesTest::ParamToString);
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.cc b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
index 62739da9..69f6397 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
@@ -164,8 +164,8 @@
   }
 
   // Remove if gated on a disabled feature.
-  if (options.gate_on_feature &&
-      !IsPreinstalledAppInstallFeatureEnabled(*options.gate_on_feature)) {
+  if (options.gate_on_feature && !IsPreinstalledAppInstallFeatureEnabled(
+                                     *options.gate_on_feature, *profile)) {
     return options.install_url.spec() +
            " disabled because feature is disabled: " + *options.gate_on_feature;
   }
@@ -541,10 +541,11 @@
 
   SetMigrationRun(profile_, kMigrateDefaultChromeAppToWebAppsGSuite.name,
                   IsPreinstalledAppInstallFeatureEnabled(
-                      kMigrateDefaultChromeAppToWebAppsGSuite.name));
-  SetMigrationRun(profile_, kMigrateDefaultChromeAppToWebAppsNonGSuite.name,
-                  IsPreinstalledAppInstallFeatureEnabled(
-                      kMigrateDefaultChromeAppToWebAppsNonGSuite.name));
+                      kMigrateDefaultChromeAppToWebAppsGSuite.name, *profile_));
+  SetMigrationRun(
+      profile_, kMigrateDefaultChromeAppToWebAppsNonGSuite.name,
+      IsPreinstalledAppInstallFeatureEnabled(
+          kMigrateDefaultChromeAppToWebAppsNonGSuite.name, *profile_));
 
   if (callback) {
     std::move(callback).Run(std::move(install_results),
diff --git a/chrome/browser/web_applications/preinstalled_web_app_migration_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_app_migration_browsertest.cc
index 95b8076..66e359f 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_migration_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_migration_browsertest.cc
@@ -249,7 +249,8 @@
 
   // Set up pre-migration state.
   {
-    ASSERT_FALSE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_FALSE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     SyncExternalWebApps(/*expect_install=*/false, /*expect_uninstall=*/false);
@@ -274,7 +275,8 @@
   {
     base::AutoReset<bool> testing_scope =
         SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
-    ASSERT_TRUE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_TRUE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     // Extension sticks around to be uninstalled by the replacement web app.
@@ -317,7 +319,8 @@
 
   // Revert migration.
   {
-    ASSERT_FALSE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_FALSE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     SyncExternalWebApps(/*expect_install=*/false, /*expect_uninstall=*/true);
@@ -340,7 +343,8 @@
     base::HistogramTester histograms;
     base::AutoReset<bool> testing_scope =
         SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
-    ASSERT_TRUE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_TRUE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     extensions::TestExtensionRegistryObserver uninstall_observer(
         extensions::ExtensionRegistry::Get(profile()));
@@ -388,7 +392,8 @@
 
   // Set up pre-migration state.
   {
-    ASSERT_FALSE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_FALSE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     SyncExternalWebApps(/*expect_install=*/false, /*expect_uninstall=*/false);
@@ -422,7 +427,8 @@
   {
     base::AutoReset<bool> testing_scope =
         SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
-    ASSERT_TRUE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_TRUE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     // Extension sticks around to be uninstalled by the replacement web app.
@@ -482,7 +488,8 @@
                        UserUninstalledExtensionApp) {
   // Set up pre-migration state.
   {
-    ASSERT_FALSE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_FALSE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     SyncExternalWebApps(/*expect_install=*/false, /*expect_uninstall=*/false);
@@ -507,7 +514,8 @@
   {
     base::AutoReset<bool> testing_scope =
         SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
-    ASSERT_TRUE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_TRUE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     EXPECT_FALSE(IsExtensionAppInstalled());
@@ -540,7 +548,8 @@
   {
     base::HistogramTester histograms;
 
-    ASSERT_FALSE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_FALSE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     SyncExternalWebApps(/*expect_install=*/false, /*expect_uninstall=*/false,
@@ -561,7 +570,8 @@
   {
     base::AutoReset<bool> testing_scope =
         SetPreinstalledAppInstallFeatureAlwaysEnabledForTesting();
-    ASSERT_TRUE(IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag));
+    ASSERT_TRUE(
+        IsPreinstalledAppInstallFeatureEnabled(kMigrationFlag, *profile()));
 
     SyncExternalExtensions();
     // Extension sticks around to be uninstalled by the replacement web app.
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/BUILD.gn b/chrome/browser/web_applications/preinstalled_web_apps/BUILD.gn
index 77d24831..053e3cf 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps/BUILD.gn
+++ b/chrome/browser/web_applications/preinstalled_web_apps/BUILD.gn
@@ -44,6 +44,8 @@
       sources += [
         "google_calendar.cc",
         "google_calendar.h",
+        "google_chat.cc",
+        "google_chat.h",
       ]
     }
 
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/google_chat.cc b/chrome/browser/web_applications/preinstalled_web_apps/google_chat.cc
new file mode 100644
index 0000000..78ec1b67
--- /dev/null
+++ b/chrome/browser/web_applications/preinstalled_web_apps/google_chat.cc
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/preinstalled_web_apps/google_chat.h"
+
+#include "chrome/browser/web_applications/components/preinstalled_app_install_features.h"
+
+namespace web_app {
+
+ExternalInstallOptions GetConfigForGoogleChat() {
+  ExternalInstallOptions options(
+      /*install_url=*/GURL(
+          "https://mail.google.com/chat/download?usp=chrome_default"),
+      /*user_display_mode=*/DisplayMode::kStandalone,
+      /*install_source=*/ExternalInstallSource::kExternalDefault);
+
+  options.user_type_allowlist = {"unmanaged", "managed", "child"};
+  options.gate_on_feature = kDefaultChatWebApp.name;
+  options.add_to_quick_launch_bar = false;
+  options.add_to_desktop = false;
+  options.only_for_new_users = true;
+
+  return options;
+}
+
+}  // namespace web_app
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/google_chat.h b/chrome/browser/web_applications/preinstalled_web_apps/google_chat.h
new file mode 100644
index 0000000..d2d0dc2
--- /dev/null
+++ b/chrome/browser/web_applications/preinstalled_web_apps/google_chat.h
@@ -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.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_GOOGLE_CHAT_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_GOOGLE_CHAT_H_
+
+#include "chrome/browser/web_applications/components/external_install_options.h"
+
+namespace web_app {
+
+ExternalInstallOptions GetConfigForGoogleChat();
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_PREINSTALLED_WEB_APPS_GOOGLE_CHAT_H_
diff --git a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
index fcae4089..d8c5e6e 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
+++ b/chrome/browser/web_applications/preinstalled_web_apps/preinstalled_web_apps.cc
@@ -23,6 +23,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chrome/browser/web_applications/preinstalled_web_apps/google_calendar.h"
+#include "chrome/browser/web_applications/preinstalled_web_apps/google_chat.h"
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
@@ -67,6 +68,7 @@
       GetConfigForYouTube(),
 #if BUILDFLAG(IS_CHROMEOS_ASH)
       GetConfigForGoogleCalendar(),
+      GetConfigForGoogleChat(),
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
       // clang-format on
   };
diff --git a/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
index bfb4a23..fd0dd3b 100644
--- a/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_apps_browsertest.cc
@@ -41,11 +41,11 @@
 
   auto& provider = *WebAppProvider::Get(browser()->profile());
 
-  struct Expectation {
+  struct OfflineOnlyExpectation {
     const char* app_id;
     const char* install_url;
     const char* launch_url;
-  } kExpectations[] = {
+  } kOfflineOnlyExpectations[] = {
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
     {
@@ -86,7 +86,22 @@
     },
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
   };
-  size_t kExpectedCount = sizeof(kExpectations) / sizeof(kExpectations[0]);
+  size_t kOfflineOnlyExpectedCount =
+      sizeof(kOfflineOnlyExpectations) / sizeof(kOfflineOnlyExpectations[0]);
+
+  struct OnlineOnlyExpectation {
+    const char* install_url;
+  } kOnlineOnlyExpectations[] = {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+    {
+        "https://mail.google.com/chat/download?usp=chrome_default",
+    },
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  };
+  size_t kOnlineOnlyExpectedCount =
+      sizeof(kOnlineOnlyExpectations) / sizeof(kOnlineOnlyExpectations[0]);
 
   base::RunLoop run_loop;
   provider.preinstalled_web_app_manager().LoadAndSynchronizeForTesting(
@@ -94,20 +109,26 @@
           [&](std::map<GURL, ExternallyManagedAppManager::InstallResult>
                   install_results,
               std::map<GURL, bool> uninstall_results) {
-            EXPECT_EQ(install_results.size(), kExpectedCount);
+            EXPECT_EQ(install_results.size(),
+                      kOfflineOnlyExpectedCount + kOnlineOnlyExpectedCount);
 
-            for (const Expectation& expectation : kExpectations) {
+            for (const auto& expectation : kOfflineOnlyExpectations) {
               EXPECT_EQ(install_results[GURL(expectation.install_url)].code,
                         InstallResultCode::kSuccessOfflineOnlyInstall);
             }
 
+            for (const auto& expectation : kOnlineOnlyExpectations) {
+              EXPECT_EQ(install_results[GURL(expectation.install_url)].code,
+                        InstallResultCode::kInstallURLLoadFailed);
+            }
+
             EXPECT_EQ(uninstall_results.size(), 0u);
 
             run_loop.Quit();
           }));
   run_loop.Run();
 
-  for (const Expectation& expectation : kExpectations) {
+  for (const auto& expectation : kOfflineOnlyExpectations) {
     EXPECT_EQ(provider.registrar().GetAppLaunchUrl(expectation.app_id),
               GURL(expectation.launch_url));
   }
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 687fad9c..591975d 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1620172529-2e7042d3e0d503f2fbf4b5265e801bd87c4ffdbc.profdata
+chrome-win32-master-1620193489-ecb74f092b3d0ff9732042da0ab458ae29c4bd49.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index a6fea34..6ea73e6 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1620172529-8f86fc13ad1836b1082b9990ba65808c278ad45e.profdata
+chrome-win64-master-1620205001-59a405d5d3cc1e81f6386200200564e6437ec397.profdata
diff --git a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
index df1b3e1..feba8c4 100644
--- a/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
+++ b/chrome/renderer/cart/commerce_hint_agent_browsertest.cc
@@ -162,7 +162,8 @@
     service_ = CartServiceFactory::GetForProfile(profile);
     auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
     ASSERT_TRUE(identity_manager);
-    signin::SetPrimaryAccount(identity_manager, "user@gmail.com");
+    signin::SetPrimaryAccount(identity_manager, "user@gmail.com",
+                              signin::ConsentLevel::kSync);
 
     // This is necessary to test non-localhost domains. See |NavigateToURL|.
     host_resolver()->AddRule("*", "127.0.0.1");
@@ -451,7 +452,8 @@
   SendXHR("/add-to-cart", "product: 123");
   WaitForCartCount(kEmptyExpected);
 
-  signin::SetPrimaryAccount(identity_manager, "user@gmail.com");
+  signin::SetPrimaryAccount(identity_manager, "user@gmail.com",
+                            signin::ConsentLevel::kSync);
   NavigateToURL("https://www.guitarcenter.com/");
   SendXHR("/add-to-cart", "product: 123");
   WaitForCartCount(kExpectedExampleFallbackCart);
diff --git a/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/manifest.json b/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/manifest.json
deleted file mode 100644
index 31bb8d1..0000000
--- a/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/manifest.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  // chrome-extension://pkplfbidichfdicaijlchgnapepdginl
-  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtDfX9dHNh948bt00YhZBm3P6E5QLaOt+v8kXVtibQfiPtOD2FTScB/f0wX/EQWVO7BkaSOsRkTPcPIgocyMPYr2FLgqGLFlYT9nQpKJZUFNF5oJ5rG6Nv7ppf4zEB3j6da1IBRTz2yOZ+6O1TMZxol/V62/QcqrJeggsHTEPGLdr9Ua4b1Ka0xKJnJngZljsbw93FI1o+P9dAh5BS6wTPiZI/vmJVjvMTkSTnaZ3n9Go2t7A0XLcSxLcVyuLAd2mAvSN0mIviOukdM66wr7llif71nKuUt+4qvlr/r9HfwzN6pA4jkwhtS1UD+3CmB+wsHwsnohNcuu4FIQ6rgq/7QIDAQAB",
-  "name": "chrome.fileManagerPrivate tests",
-  "version": "0.1",
-  "manifest_version": 2,
-  "description": "Tests of chrome.fileManagerPrivate holding space methods",
-  "app": {
-    "background": {
-      "scripts": ["test.js"]
-    }
-  },
-  "permissions": [
-    "fileManagerPrivate",
-    {
-      "fileSystem": ["requestFileSystem"]
-    }
-  ]
-}
diff --git a/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/test.js b/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/test.js
deleted file mode 100644
index 456461a..0000000
--- a/chrome/test/data/extensions/api_test/file_browser/holding_space_disabled/test.js
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * Gets an external file entry from a path.
- * @param {string} volumeType volume type for entry.
- * @param {string} path path of entry.
- * @return {!Promise<Entry>} specified entry.
- */
-function getFileEntry(volumeType, path) {
-  return new Promise(resolve => {
-    chrome.fileManagerPrivate.getVolumeMetadataList(list => {
-      const volume = list.find(v => v.volumeType === volumeType);
-      chrome.fileSystem.requestFileSystem({volumeId: volume.volumeId}, fs => {
-        fs.root.getFile(path, {}, entry => {
-          chrome.fileManagerPrivate.resolveIsolatedEntries(
-              [entry], externalEntries => {
-                resolve(externalEntries[0]);
-              });
-        });
-      });
-    });
-  });
-}
-
-// Run the tests.
-chrome.test.runTests([
-  function testGetHoldingSpaceFails() {
-    chrome.fileManagerPrivate.getHoldingSpaceState(
-        chrome.test.callbackFail('Not enabled'));
-  },
-
-  function testAddSingleEntry() {
-    getFileEntry('testing', 'test_dir/test_file.txt')
-        .then(chrome.test.callbackPass(entry => {
-          chrome.fileManagerPrivate.toggleAddedToHoldingSpace(
-              [entry], true, chrome.test.callbackFail('Not enabled'));
-        }));
-  },
-]);
diff --git a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js
index a2c76525..d121845 100644
--- a/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js
+++ b/chrome/test/data/webui/chromeos/emoji_picker/emoji_picker_test.js
@@ -147,7 +147,7 @@
     const emojiButton = (await waitForCondition(
                              () => findInEmojiPicker(
                                  '[data-group="0"] > emoji-group',
-                                 'emoji-button:nth-child(2)')))
+                                 'emoji-button:nth-child(3)')))
                             .shadowRoot.querySelector('button');
     emojiButton.click();
 
@@ -171,7 +171,7 @@
         const emojiButton = (await waitForCondition(
                                  () => findInEmojiPicker(
                                      '[data-group="0"] > emoji-group',
-                                     'emoji-button:nth-child(1)')))
+                                     'emoji-button:nth-child(2)')))
                                 .shadowRoot.querySelector('button');
         emojiButton.click();
 
@@ -221,7 +221,7 @@
       firstEmojiButton = (await waitForCondition(
                               () => findInEmojiPicker(
                                   '[data-group="0"] > emoji-group',
-                                  'emoji-button:nth-child(2)')))
+                                  'emoji-button:nth-child(3)')))
                              .shadowRoot;
 
 
@@ -257,7 +257,7 @@
       const emojiButton2 = await waitForCondition(
           () =>
               findInEmojiPicker(
-                  '[data-group="0"] > emoji-group', 'emoji-button:nth-child(3)')
+                  '[data-group="0"] > emoji-group', 'emoji-button:nth-child(4)')
                   .shadowRoot);
 
       // right click on second emoji button
diff --git a/chrome/test/data/webui/ntp4_browsertest.cc b/chrome/test/data/webui/ntp4_browsertest.cc
index d244ccc..7837107e 100644
--- a/chrome/test/data/webui/ntp4_browsertest.cc
+++ b/chrome/test/data/webui/ntp4_browsertest.cc
@@ -16,5 +16,6 @@
 void NTP4LoggedInWebUITest::SetLoginName(const std::string& name) {
   auto* identity_manager =
       IdentityManagerFactory::GetForProfile(browser()->profile());
-  signin::SetPrimaryAccount(identity_manager, name);
+  signin::SetPrimaryAccount(identity_manager, name,
+                            signin::ConsentLevel::kSync);
 }
diff --git a/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js b/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js
index 1ef37ad..c649067 100644
--- a/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js
+++ b/chrome/test/data/webui/settings/chromeos/cellular_networks_list_test.js
@@ -407,4 +407,32 @@
     await flushAsync();
     assertTrue(esimLocalizedLink.linkDisabled);
   });
+
+  test('Show inhibited subtext and spinner when inhibited', async () => {
+    eSimManagerRemote.addEuiccForTest(0);
+    init();
+    cellularNetworkList.deviceState = {
+      type: mojom.NetworkType.kCellular,
+      deviceState: mojom.DeviceStateType.kEnabled,
+      inhibitReason: mojom.InhibitReason.kNotInhibited
+    };
+    addESimSlot();
+
+    await flushAsync();
+
+    const inhibitedSubtext = cellularNetworkList.$$('#inhibitedSubtext');
+    const inhibitedSpinner = cellularNetworkList.$$('#inhibitedSpinner');
+    assertTrue(inhibitedSubtext.hidden);
+    assertFalse(inhibitedSpinner.active);
+
+    cellularNetworkList.cellularDeviceState = {
+      type: mojom.NetworkType.kCellular,
+      deviceState: mojom.DeviceStateType.kEnabled,
+      inhibitReason: mojom.InhibitReason.kInstallingProfile
+    };
+    addESimSlot();
+    await flushAsync();
+    assertFalse(inhibitedSubtext.hidden);
+    assertTrue(inhibitedSpinner.active);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/internet_detail_page_tests.js b/chrome/test/data/webui/settings/chromeos/internet_detail_page_tests.js
index df87ebe2..11dc259 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_detail_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_detail_page_tests.js
@@ -832,7 +832,6 @@
       assertFalse(networkIpConfig.disabled);
       assertFalse(networkNameservers.disabled);
       assertFalse(networkProxySection.disabled);
-      assertFalse(!!internetDetailPage.$$('cellular-banner'));
 
       // Mock device being inhibited.
       mojoApi_.setDeviceStateForTest({
@@ -857,7 +856,6 @@
       assertTrue(networkIpConfig.disabled);
       assertTrue(networkNameservers.disabled);
       assertTrue(networkProxySection.disabled);
-      assertTrue(!!internetDetailPage.$$('cellular-banner'));
 
       // Uninhibit.
       mojoApi_.setDeviceStateForTest({
@@ -882,7 +880,6 @@
       assertFalse(networkIpConfig.disabled);
       assertFalse(networkNameservers.disabled);
       assertFalse(networkProxySection.disabled);
-      assertFalse(!!internetDetailPage.$$('cellular-banner'));
     });
   });
 
diff --git a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
index 1342d6c3..9c4556e 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/internet_subpage_tests.js
@@ -231,34 +231,6 @@
           deepLinkElement, getDeepActiveElement(),
           'Add cellular button should be focused for settingId=26.');
     });
-    test(
-        'Device inhibited disables toggle and shows banner', async () => {
-          initSubpage(true);
-          const mojom = chromeos.networkConfig.mojom;
-          mojoApi_.setNetworkTypeEnabledState(mojom.NetworkType.kCellular);
-          setNetworksForTest(mojom.NetworkType.kCellular, [
-            OncMojo.getDefaultNetworkState(
-                mojom.NetworkType.kCellular, 'cellular1'),
-          ]);
-          internetSubpage.deviceState = {
-            type: mojom.NetworkType.kCellular,
-            deviceState: mojom.DeviceStateType.kEnabled,
-            inhibitReason: mojom.InhibitReason.kInstallingProfile
-          };
-          internetSubpage.globalPolicy = {
-            allowOnlyPolicyNetworksToConnect: false,
-          };
-
-          await flushAsync();
-
-          const deviceEnabledButton =
-              internetSubpage.$$('#deviceEnabledButton');
-          assertTrue(!!deviceEnabledButton);
-          assertTrue(deviceEnabledButton.disabled);
-
-          const cellularBanner = internetSubpage.$$('cellular-banner');
-          assertTrue(!!cellularBanner);
-        });
 
     test(
         'Tether plus Cellular with updatedCellularActivationUi false',
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index a5ac324..d986730 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -185,6 +185,7 @@
       "//chromeos/dbus/services:unit_tests",
       "//chromeos/disks:unit_tests",
       "//chromeos/geolocation:unit_tests",
+      "//chromeos/language/language_packs:unit_tests",
       "//chromeos/login/auth:unit_tests",
       "//chromeos/login/login_state:unit_tests",
       "//chromeos/login/session:unit_tests",
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 7983f80..18cc3ef3 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13929.0.0
\ No newline at end of file
+13951.0.0
\ No newline at end of file
diff --git a/chromeos/components/camera_app_ui/resources/css/ptz_panel.css b/chromeos/components/camera_app_ui/resources/css/ptz_panel.css
index b898ed6..ec63c1a5 100644
--- a/chromeos/components/camera_app_ui/resources/css/ptz_panel.css
+++ b/chromeos/components/camera_app_ui/resources/css/ptz_panel.css
@@ -3,6 +3,9 @@
  * found in the LICENSE file. */
 
 #panel-container {
+  --divider-color: rgba(95, 99, 104, 0.38);
+  --divider-width: 1px;
+
   backdrop-filter: blur(10px);
   background: rgba(32, 33, 36, 0.9);
   border-radius: 4px;
@@ -37,33 +40,33 @@
 }
 
 #pan-left {
-  --svg-width: 8px;
-  --svg-height: 10px;
+  --svg-width: 20px;
+  --svg-height: 20px;
 }
 
 #pan-right {
-  --svg-width: 8px;
-  --svg-height: 10px;
+  --svg-width: 20px;
+  --svg-height: 20px;
 }
 
 #tilt-up {
-  --svg-width: 12px;
-  --svg-height: 8px;
+  --svg-width: 20px;
+  --svg-height: 20px;
 }
 
 #tilt-down {
-  --svg-width: 12px;
-  --svg-height: 8px;
+  --svg-width: 20px;
+  --svg-height: 20px;
 }
 
 #zoom-in {
-  --svg-width: 16px;
-  --svg-height: 16px;
+  --svg-width: 20px;
+  --svg-height: 20px;
 }
 
 #zoom-out {
-  --svg-width: 16px;
-  --svg-height: 16px;
+  --svg-width: 20px;
+  --svg-height: 20px;
 }
 
 /* Icons. */
@@ -310,9 +313,9 @@
 }
 
 .ptz-divider {
-  background: rgba(95, 99, 104, 0.38);
+  background: var(--divider-color);
   position: absolute;
-  width: 1px;
+  width: var(--divider-width);
 }
 
 #ptz-divider1 {
@@ -353,3 +356,15 @@
 body.has-pan-support.has-tilt-support.has-zoom-support .ptz-divider {
   height: 98px;
 }
+
+body.has-pan-support.has-tilt-support.has-zoom-support :is(#zoom-in, #zoom-out) {
+  border: var(--divider-width) solid var(--divider-color);
+}
+
+body.has-pan-support.has-tilt-support.has-zoom-support #zoom-in {
+  border-left-width: 0;  /* csschecker-disable-line left-right */
+}
+
+body.has-pan-support.has-tilt-support.has-zoom-support #zoom-out {
+  border-right-width: 0;  /* csschecker-disable-line left-right */
+}
diff --git a/chromeos/components/camera_app_ui/resources/images/ptz_pan_left.svg b/chromeos/components/camera_app_ui/resources/images/ptz_pan_left.svg
index 0764f78..884ea3b 100644
--- a/chromeos/components/camera_app_ui/resources/images/ptz_pan_left.svg
+++ b/chromeos/components/camera_app_ui/resources/images/ptz_pan_left.svg
@@ -1,3 +1,3 @@
-<svg width="8" height="10" viewBox="0 0 8 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M7.33667 8.825L3.21611 5L7.33667 1.175L6.06811 0L0.670003 5L6.06811 10L7.33667 8.825Z" fill="#E8EAED"/>
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14 14.59L9.05533 10L14 5.41L12.4777 4L6 10L12.4777 16L14 14.59Z" fill="#EEEEEE"/>
 </svg>
diff --git a/chromeos/components/camera_app_ui/resources/images/ptz_pan_right.svg b/chromeos/components/camera_app_ui/resources/images/ptz_pan_right.svg
index a7d8343f..0f3c59ad 100644
--- a/chromeos/components/camera_app_ui/resources/images/ptz_pan_right.svg
+++ b/chromeos/components/camera_app_ui/resources/images/ptz_pan_right.svg
@@ -1,3 +1,3 @@
-<svg width="8" height="10" viewBox="0 0 8 10" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666687 8.825L4.78724 5L0.666687 1.175L1.93524 0L7.33335 5L1.93524 10L0.666687 8.825Z" fill="#E8EAED"/>
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6 5.41L10.9447 10L6 14.59L7.52227 16L14 10L7.52227 4L6 5.41Z" fill="#EEEEEE"/>
 </svg>
diff --git a/chromeos/components/camera_app_ui/resources/images/ptz_tilt_down.svg b/chromeos/components/camera_app_ui/resources/images/ptz_tilt_down.svg
index c24aeff..0a80017 100644
--- a/chromeos/components/camera_app_ui/resources/images/ptz_tilt_down.svg
+++ b/chromeos/components/camera_app_ui/resources/images/ptz_tilt_down.svg
@@ -1,3 +1,3 @@
-<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M1.41 0L6 4.94467L10.59 0L12 1.52227L6 8L0 1.52227L1.41 0Z" fill="#E8EAED"/>
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.41 6L10 10.9447L14.59 6L16 7.52227L10 14L4 7.52227L5.41 6Z" fill="#EEEEEE"/>
 </svg>
diff --git a/chromeos/components/camera_app_ui/resources/images/ptz_tilt_up.svg b/chromeos/components/camera_app_ui/resources/images/ptz_tilt_up.svg
index 58ae20a..7856c56 100644
--- a/chromeos/components/camera_app_ui/resources/images/ptz_tilt_up.svg
+++ b/chromeos/components/camera_app_ui/resources/images/ptz_tilt_up.svg
@@ -1,3 +1,3 @@
-<svg width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M1.41 8L6 3.05533L10.59 8L12 6.47773L6 0L0 6.47773L1.41 8Z" fill="#E8EAED"/>
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M5.41 14L10 9.05533L14.59 14L16 12.4777L10 6L4 12.4777L5.41 14Z" fill="#EEEEEE"/>
 </svg>
diff --git a/chromeos/components/camera_app_ui/resources/images/ptz_zoom_in.svg b/chromeos/components/camera_app_ui/resources/images/ptz_zoom_in.svg
index 9386682..3f4dc71 100644
--- a/chromeos/components/camera_app_ui/resources/images/ptz_zoom_in.svg
+++ b/chromeos/components/camera_app_ui/resources/images/ptz_zoom_in.svg
@@ -1,3 +1,5 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M15.49 14L11.76 10.27C12.53 9.2 13 7.91 13 6.5C13 2.91 10.09 0 6.5 0C2.91 0 0 2.91 0 6.5C0 10.09 2.91 13 6.5 13C7.91 13 9.2 12.53 10.27 11.76L14 15.49L15.49 14ZM6.5 11C4.01 11 2 8.99 2 6.5C2 4.01 4.01 2 6.5 2C8.99 2 11 4.01 11 6.5C11 8.99 8.99 11 6.5 11ZM7.5 5.5H9.5V7.5H7.5V9.5H5.5V7.5H3.5V5.5H5.5V3.5H7.5V5.5Z" fill="#E8EAED"/>
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="zoom-in">
+<path id="Shape 1" fill-rule="evenodd" clip-rule="evenodd" d="M17.49 16L13.76 12.27C14.53 11.2 15 9.91 15 8.5C15 4.91 12.09 2 8.5 2C4.91 2 2 4.91 2 8.5C2 12.09 4.91 15 8.5 15C9.91 15 11.2 14.53 12.27 13.76L16 17.49L17.49 16ZM8.5 13C6.01 13 4 10.99 4 8.5C4 6.01 6.01 4 8.5 4C10.99 4 13 6.01 13 8.5C13 10.99 10.99 13 8.5 13ZM9.5 7.5H11.5V9.5H9.5V11.5H7.5V9.5H5.5V7.5H7.5V5.5H9.5V7.5Z" fill="#EEEEEE"/>
+</g>
 </svg>
diff --git a/chromeos/components/camera_app_ui/resources/images/ptz_zoom_out.svg b/chromeos/components/camera_app_ui/resources/images/ptz_zoom_out.svg
index 3bc6b24..a6d5c5e 100644
--- a/chromeos/components/camera_app_ui/resources/images/ptz_zoom_out.svg
+++ b/chromeos/components/camera_app_ui/resources/images/ptz_zoom_out.svg
@@ -1,3 +1,5 @@
-<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path fill-rule="evenodd" clip-rule="evenodd" d="M15.49 14L11.76 10.27C12.53 9.2 13 7.91 13 6.5C13 2.91 10.09 0 6.5 0C2.91 0 0 2.91 0 6.5C0 10.09 2.91 13 6.5 13C7.91 13 9.2 12.53 10.27 11.76L14 15.49L15.49 14ZM6.5 11C4.01 11 2 8.99 2 6.5C2 4.01 4.01 2 6.5 2C8.99 2 11 4.01 11 6.5C11 8.99 8.99 11 6.5 11ZM9.5 5.5V7.5H3.5V5.5H9.5Z" fill="#E8EAED"/>
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="zoom-out">
+<path id="Shape 1" fill-rule="evenodd" clip-rule="evenodd" d="M17.49 16L13.76 12.27C14.53 11.2 15 9.91 15 8.5C15 4.91 12.09 2 8.5 2C4.91 2 2 4.91 2 8.5C2 12.09 4.91 15 8.5 15C9.91 15 11.2 14.53 12.27 13.76L16 17.49L17.49 16ZM8.5 13C6.01 13 4 10.99 4 8.5C4 6.01 6.01 4 8.5 4C10.99 4 13 6.01 13 8.5C13 10.99 10.99 13 8.5 13ZM11.5 7.5V9.5H5.5V7.5H11.5Z" fill="#EEEEEE"/>
+</g>
 </svg>
diff --git a/chromeos/dbus/dlcservice/fake_dlcservice_client.h b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
index f9eb553..2dcba28 100644
--- a/chromeos/dbus/dlcservice/fake_dlcservice_client.h
+++ b/chromeos/dbus/dlcservice/fake_dlcservice_client.h
@@ -55,8 +55,7 @@
       const dlcservice::DlcsWithContent& dlcs_with_content) {
     dlcs_with_content_ = dlcs_with_content;
   }
-  void set_dlc_state(
-      const dlcservice::DlcState& dlc_state) {
+  void set_dlc_state(const dlcservice::DlcState& dlc_state) {
     dlc_state_ = dlc_state;
   }
 
diff --git a/chromeos/language/DIR_METADATA b/chromeos/language/DIR_METADATA
new file mode 100644
index 0000000..fd396f22
--- /dev/null
+++ b/chromeos/language/DIR_METADATA
@@ -0,0 +1,7 @@
+buganizer {
+  component_id: 934840
+}
+monorail {
+  component: "OS>Software>MarketExpansion"
+}
+team_email: "cros-borders-eng@google.com"
\ No newline at end of file
diff --git a/chromeos/language/OWNERS b/chromeos/language/OWNERS
new file mode 100644
index 0000000..9aaf0f7e
--- /dev/null
+++ b/chromeos/language/OWNERS
@@ -0,0 +1,3 @@
+claudiomagni@chromium.org
+dvallet@chromium.org
+mlcui@google.com
\ No newline at end of file
diff --git a/chromeos/language/language_packs/BUILD.gn b/chromeos/language/language_packs/BUILD.gn
new file mode 100644
index 0000000..ed5b870e
--- /dev/null
+++ b/chromeos/language/language_packs/BUILD.gn
@@ -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.
+
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+static_library("language_packs") {
+  sources = [
+    "language_pack_manager.cc",
+    "language_pack_manager.h",
+  ]
+  deps = [
+    "//base",
+    "//chromeos/dbus/dlcservice:dlcservice",
+    "//chromeos/dbus/dlcservice:dlcservice_proto",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "language_pack_manager_unittest.cc" ]
+  deps = [
+    ":language_packs",
+    "//base",
+    "//base/test:test_support",
+    "//chromeos/dbus/dlcservice:dlcservice",
+    "//chromeos/dbus/dlcservice:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromeos/language/language_packs/language_pack_manager.cc b/chromeos/language/language_packs/language_pack_manager.cc
new file mode 100644
index 0000000..55fbe63
--- /dev/null
+++ b/chromeos/language/language_packs/language_pack_manager.cc
@@ -0,0 +1,185 @@
+// 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 "chromeos/language/language_packs/language_pack_manager.h"
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/containers/contains.h"
+#include "base/containers/flat_map.h"
+#include "base/logging.h"
+#include "chromeos/dbus/dlcservice/dlcservice.pb.h"
+#include "chromeos/dbus/dlcservice/dlcservice_client.h"
+
+namespace chromeos {
+namespace language_packs {
+namespace {
+
+const base::flat_map<PackSpecPair, std::string>& GetAllDlcIds() {
+  // Create the map of all DLCs and corresponding IDs.
+  // Whenever a new DLC is created, it needs to be added here.
+  // Clients of Language Packs don't need to know the IDs.
+  static const base::NoDestructor<base::flat_map<PackSpecPair, std::string>>
+      all_dlc_ids({{{kHandwritingFeatureId, "en"}, "handwriting-en-dlc"}});
+
+  return *all_dlc_ids;
+}
+
+void OnInstallDlcComplete(
+    OnInstallCompleteCallback callback,
+    const chromeos::DlcserviceClient::InstallResult& dlc_result) {
+  PackResult result;
+  result.operation_error = dlc_result.error;
+
+  if (dlc_result.error == dlcservice::kErrorNone) {
+    result.pack_state = PackResult::INSTALLED;
+    result.path = dlc_result.root_path;
+  } else {
+    result.pack_state = PackResult::UNKNOWN;
+  }
+
+  std::move(callback).Run(result);
+}
+
+void OnGetDlcState(GetPackStateCallback callback,
+                   const std::string& err,
+                   const dlcservice::DlcState& dlc_state) {
+  PackResult result;
+  result.operation_error = err;
+
+  if (err == dlcservice::kErrorNone) {
+    switch (dlc_state.state()) {
+      case dlcservice::DlcState_State_INSTALLED:
+        result.pack_state = PackResult::INSTALLED;
+        result.path = dlc_state.root_path();
+        break;
+      case dlcservice::DlcState_State_INSTALLING:
+        result.pack_state = PackResult::IN_PROGRESS;
+        break;
+      case dlcservice::DlcState_State_NOT_INSTALLED:
+        result.pack_state = PackResult::NOT_INSTALLED;
+        break;
+      default:
+        result.pack_state = PackResult::UNKNOWN;
+        break;
+    }
+
+  } else {
+    result.pack_state = PackResult::UNKNOWN;
+  }
+
+  std::move(callback).Run(result);
+}
+
+void OnUninstallDlcComplete(OnUninstallCompleteCallback callback,
+                            const std::string& err) {
+  PackResult result;
+  result.operation_error = err;
+
+  if (err == dlcservice::kErrorNone) {
+    result.pack_state = PackResult::NOT_INSTALLED;
+  } else {
+    result.pack_state = PackResult::UNKNOWN;
+  }
+
+  std::move(callback).Run(result);
+}
+
+}  // namespace
+
+bool LanguagePackManager::IsPackAvailable(const std::string& pack_id,
+                                          const std::string& locale) {
+  // We search in the static list for the given Pack spec.
+  const PackSpecPair spec(pack_id, locale);
+  return base::Contains(GetAllDlcIds(), spec);
+}
+
+bool LanguagePackManager::GetDlcId(const std::string& pack_id,
+                                   const std::string& locale,
+                                   std::string* const dlc_id) {
+  // We search in the static list for the given Pack spec.
+  const PackSpecPair spec(pack_id, locale);
+  const auto it = GetAllDlcIds().find(spec);
+
+  if (it == GetAllDlcIds().end()) {
+    return false;
+  }
+
+  *dlc_id = it->second;
+  return true;
+}
+
+void LanguagePackManager::InstallPack(const std::string& pack_id,
+                                      const std::string& locale,
+                                      OnInstallCompleteCallback callback) {
+  std::string dlc_id;
+  const bool found = GetDlcId(pack_id, locale, &dlc_id);
+
+  // If the given Language Pack doesn't exist, run callback and don't reach the
+  // DLC Service.
+  if (!found) {
+    PackResult result;
+    result.operation_error = dlcservice::kErrorInvalidDlc;
+    result.pack_state = PackResult::WRONG_ID;
+    std::move(callback).Run(result);
+    return;
+  }
+
+  chromeos::DlcserviceClient::Get()->Install(
+      dlc_id, base::BindOnce(&OnInstallDlcComplete, std::move(callback)),
+      base::DoNothing());
+}
+
+void LanguagePackManager::GetPackState(const std::string& pack_id,
+                                       const std::string& locale,
+                                       GetPackStateCallback callback) {
+  std::string dlc_id;
+  const bool found = GetDlcId(pack_id, locale, &dlc_id);
+
+  // If the given Language Pack doesn't exist, run callback and don't reach the
+  // DLC Service.
+  if (!found) {
+    PackResult result;
+    result.operation_error = dlcservice::kErrorInvalidDlc;
+    result.pack_state = PackResult::WRONG_ID;
+    std::move(callback).Run(result);
+    return;
+  }
+
+  chromeos::DlcserviceClient::Get()->GetDlcState(
+      dlc_id, base::BindOnce(&OnGetDlcState, std::move(callback)));
+}
+
+void LanguagePackManager::RemovePack(const std::string& pack_id,
+                                     const std::string& locale,
+                                     OnUninstallCompleteCallback callback) {
+  std::string dlc_id;
+  const bool found = GetDlcId(pack_id, locale, &dlc_id);
+
+  // If the given Language Pack doesn't exist, run callback and don't reach the
+  // DLC Service.
+  if (!found) {
+    PackResult result;
+    result.operation_error = dlcservice::kErrorInvalidDlc;
+    result.pack_state = PackResult::WRONG_ID;
+    std::move(callback).Run(result);
+    return;
+  }
+
+  chromeos::DlcserviceClient::Get()->Uninstall(
+      dlc_id, base::BindOnce(&OnUninstallDlcComplete, std::move(callback)));
+}
+
+LanguagePackManager::LanguagePackManager() = default;
+LanguagePackManager::~LanguagePackManager() = default;
+
+// static
+LanguagePackManager* LanguagePackManager::GetInstance() {
+  static base::NoDestructor<LanguagePackManager> instance;
+  return instance.get();
+}
+
+}  // namespace language_packs
+}  // namespace chromeos
diff --git a/chromeos/language/language_packs/language_pack_manager.h b/chromeos/language/language_packs/language_pack_manager.h
new file mode 100644
index 0000000..1fd0506
--- /dev/null
+++ b/chromeos/language/language_packs/language_pack_manager.h
@@ -0,0 +1,141 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_LANGUAGE_LANGUAGE_PACKS_LANGUAGE_PACK_MANAGER_H_
+#define CHROMEOS_LANGUAGE_LANGUAGE_PACKS_LANGUAGE_PACK_MANAGER_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/no_destructor.h"
+#include "base/strings/strcat.h"
+
+namespace chromeos {
+namespace language_packs {
+
+// All Language Pack IDs are listed here.
+constexpr char kHandwritingFeatureId[] = "LP_ID_HANDWRITING";
+
+// Status contains information about the status of an operation.
+struct PackResult {
+  // This string contains the error returns by DLC Service.
+  std::string operation_error;
+
+  enum StatusCode {
+    UNKNOWN = 0,
+    WRONG_ID,
+    NOT_INSTALLED,
+    IN_PROGRESS,
+    INSTALLED
+  };
+
+  // The code that indicates the current state of the Pack.
+  // INSTALLED means that the Pack is ready to be used.
+  StatusCode pack_state;
+
+  // The path where the Pack is available for users to use.
+  std::string path;
+};
+
+// We define an internal type to identify a Language Pack.
+// It's a pair of featured_id and locale that is hashable.
+struct PackSpecPair {
+  std::string feature_id;
+  std::string locale;
+
+  PackSpecPair(const std::string& feature_id, const std::string& locale)
+      : feature_id(feature_id), locale(locale) {}
+
+  bool operator==(const PackSpecPair& other) const {
+    return (feature_id == other.feature_id && locale == other.locale);
+  }
+
+  bool operator!=(const PackSpecPair& other) const { return !(*this == other); }
+
+  // Allows PackSpecPair to be used as a key in STL containers, like flat_map.
+  bool operator<(const PackSpecPair& other) const {
+    if (feature_id == other.feature_id)
+      return locale < other.locale;
+
+    return feature_id < other.feature_id;
+  }
+
+  // Simple hash function: XOR the string hash.
+  struct HashFunction {
+    size_t operator()(const PackSpecPair& obj) const {
+      size_t first_hash = std::hash<std::string>()(obj.feature_id);
+      size_t second_hash = std::hash<std::string>()(obj.locale) << 1;
+      return first_hash ^ second_hash;
+    }
+  };
+};
+
+using OnInstallCompleteCallback =
+    base::OnceCallback<void(const PackResult& pack_result)>;
+using GetPackStateCallback =
+    base::OnceCallback<void(const PackResult& pack_result)>;
+using OnUninstallCompleteCallback =
+    base::OnceCallback<void(const PackResult& pack_result)>;
+
+// This class manages all Language Packs on the device.
+// This is a Singleton and needs to be accessed via Get().
+class LanguagePackManager {
+ public:
+  // Returns true if the given Language Pack exists and can be installed on
+  // this device.
+  // TODO(claudiomagni): Check per board.
+  bool IsPackAvailable(const std::string& pack_id, const std::string& locale);
+
+  // Installs the Language Pack.
+  // It takes a callback that will be triggered once the operation is done.
+  // A state is passed to the callback.
+  void InstallPack(const std::string& pack_id,
+                   const std::string& locale,
+                   OnInstallCompleteCallback callback);
+
+  // Checks the state of a Language Pack.
+  // It takes a callback that will be triggered once the operation is done.
+  // A state is passed to the callback.
+  // If the state marks the Language Pack as ready, then there's no need to
+  // call Install(), otherwise the client should call Install() and not call
+  // this method a second time.
+  void GetPackState(const std::string& pack_id,
+                    const std::string& locale,
+                    GetPackStateCallback callback);
+
+  // Features should call this method to indicate that they do not intend to
+  // use the Pack again, until they will call |InstallPack()|.
+  // The Language Pack will be removed from disk, but no guarantee is given on
+  // when that will happen.
+  // TODO(claudiomagni): Allow callers to force immediate removal. Useful to
+  //                     clear space on disk for another language.
+  void RemovePack(const std::string& pack_id,
+                  const std::string& locale,
+                  OnUninstallCompleteCallback callback);
+
+  // Returns the global instance..
+  static LanguagePackManager* GetInstance();
+
+ private:
+  friend base::NoDestructor<LanguagePackManager>;
+
+  // This class should be accessed only via GetInstance();
+  LanguagePackManager();
+  ~LanguagePackManager();
+
+  // Disallow copy and assign.
+  LanguagePackManager(const LanguagePackManager&) = delete;
+  LanguagePackManager& operator=(const LanguagePackManager&) = delete;
+
+  // Finds the ID of the DLC corresponding to the given spec.
+  // Returns true if the DLC exists or false otherwise.
+  bool GetDlcId(const std::string& pack_id,
+                const std::string& locale,
+                std::string* dlc_id);
+};
+
+}  // namespace language_packs
+}  // namespace chromeos
+
+#endif  // CHROMEOS_LANGUAGE_LANGUAGE_PACKS_LANGUAGE_PACK_MANAGER_H_
diff --git a/chromeos/language/language_packs/language_pack_manager_unittest.cc b/chromeos/language/language_packs/language_pack_manager_unittest.cc
new file mode 100644
index 0000000..4dc7fd2
--- /dev/null
+++ b/chromeos/language/language_packs/language_pack_manager_unittest.cc
@@ -0,0 +1,267 @@
+// 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 "chromeos/language/language_packs/language_pack_manager.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "chromeos/dbus/dlcservice/dlcservice_client.h"
+#include "chromeos/dbus/dlcservice/fake_dlcservice_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::chromeos::language_packs::LanguagePackManager;
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+
+namespace chromeos {
+namespace language_packs {
+
+namespace {
+
+constexpr char kFakeDlcId[] = "FakeDlc";
+
+// We need a mock callback so that we can check that it gets called.
+class CallbackForTesting {
+ public:
+  OnInstallCompleteCallback GetInstallCallback() {
+    return base::BindOnce(&CallbackForTesting::Callback,
+                          base::Unretained(this));
+  }
+
+  GetPackStateCallback GetPackStateCallback() {
+    return base::BindOnce(&CallbackForTesting::Callback,
+                          base::Unretained(this));
+  }
+
+  OnUninstallCompleteCallback GetRemoveCallback() {
+    return base::BindOnce(&CallbackForTesting::Callback,
+                          base::Unretained(this));
+  }
+
+  MOCK_METHOD(void, Callback, (const PackResult&), ());
+};
+
+}  // namespace
+
+class LanguagePackManagerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    manager_ = LanguagePackManager::GetInstance();
+
+    DlcserviceClient::InitializeFake();
+    dlcservice_client_ =
+        static_cast<FakeDlcserviceClient*>(DlcserviceClient::Get());
+
+    ResetPackResult();
+
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void TearDown() override { DlcserviceClient::Shutdown(); }
+
+  void InstallTestCallback(const PackResult& pack_result) {
+    pack_result_ = pack_result;
+  }
+
+  void GetPackStateTestCallback(const PackResult& pack_result) {
+    pack_result_ = pack_result;
+  }
+
+  void RemoveTestCallback(const PackResult& pack_result) {
+    pack_result_ = pack_result;
+  }
+
+ protected:
+  LanguagePackManager* manager_;
+  PackResult pack_result_;
+  FakeDlcserviceClient* dlcservice_client_;
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+
+  void ResetPackResult() {
+    PackResult temp = PackResult();
+    pack_result_ = temp;
+  }
+};
+
+TEST_F(LanguagePackManagerTest, IsPackAvailableTrueTest) {
+  const bool available = manager_->IsPackAvailable(kHandwritingFeatureId, "en");
+  EXPECT_TRUE(available);
+}
+
+TEST_F(LanguagePackManagerTest, IsPackAvailableFalseTest) {
+  // Correct ID, wrong language.
+  bool available = manager_->IsPackAvailable(kHandwritingFeatureId, "fr");
+  EXPECT_FALSE(available);
+
+  // ID doesn't exists.
+  available = manager_->IsPackAvailable("foo", "fr");
+  EXPECT_FALSE(available);
+}
+
+TEST_F(LanguagePackManagerTest, InstallSuccessTest) {
+  dlcservice_client_->set_install_error(dlcservice::kErrorNone);
+  dlcservice_client_->set_install_root_path("/path");
+
+  // We need to use an existing Pack ID, so that we do get a result back.
+  manager_->InstallPack(
+      kHandwritingFeatureId, "en",
+      base::BindOnce(&LanguagePackManagerTest::InstallTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorNone);
+  EXPECT_EQ(pack_result_.pack_state, PackResult::INSTALLED);
+  EXPECT_EQ(pack_result_.path, "/path");
+}
+
+TEST_F(LanguagePackManagerTest, InstallFailureTest) {
+  dlcservice_client_->set_install_error(dlcservice::kErrorInternal);
+
+  // We need to use an existing Pack ID, so that we do get a result back.
+  manager_->InstallPack(
+      kHandwritingFeatureId, "en",
+      base::BindOnce(&LanguagePackManagerTest::InstallTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorInternal);
+  EXPECT_NE(pack_result_.pack_state, PackResult::INSTALLED);
+}
+
+TEST_F(LanguagePackManagerTest, InstallWrongIdTest) {
+  manager_->InstallPack(
+      kFakeDlcId, "en",
+      base::BindOnce(&LanguagePackManagerTest::InstallTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorInvalidDlc);
+  EXPECT_EQ(pack_result_.pack_state, PackResult::WRONG_ID);
+}
+
+// Check that the callback is actually called.
+TEST_F(LanguagePackManagerTest, InstallCallbackTest) {
+  dlcservice_client_->set_install_error(dlcservice::kErrorNone);
+  dlcservice_client_->set_install_root_path("/path");
+
+  testing::StrictMock<CallbackForTesting> callback;
+  EXPECT_CALL(callback, Callback(_));
+
+  manager_->InstallPack(kFakeDlcId, "en", callback.GetInstallCallback());
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(LanguagePackManagerTest, GetPackStateSuccessTest) {
+  dlcservice_client_->set_get_dlc_state_error(dlcservice::kErrorNone);
+  dlcservice::DlcState dlc_state;
+  dlc_state.set_state(dlcservice::DlcState_State_INSTALLED);
+  dlc_state.set_is_verified(true);
+  dlc_state.set_root_path("/path");
+  dlcservice_client_->set_dlc_state(dlc_state);
+
+  // We need to use an existing Pack ID, so that we do get a result back.
+  manager_->GetPackState(
+      kHandwritingFeatureId, "en",
+      base::BindOnce(&LanguagePackManagerTest::GetPackStateTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorNone);
+  EXPECT_EQ(pack_result_.pack_state, PackResult::INSTALLED);
+  EXPECT_EQ(pack_result_.path, "/path");
+}
+
+TEST_F(LanguagePackManagerTest, GetPackStateFailureTest) {
+  dlcservice_client_->set_get_dlc_state_error(dlcservice::kErrorInternal);
+
+  // We need to use an existing Pack ID, so that we do get a result back.
+  manager_->GetPackState(
+      kHandwritingFeatureId, "en",
+      base::BindOnce(&LanguagePackManagerTest::GetPackStateTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorInternal);
+  EXPECT_NE(pack_result_.pack_state, PackResult::INSTALLED);
+}
+
+TEST_F(LanguagePackManagerTest, GetPackStateWrongIdTest) {
+  manager_->GetPackState(
+      kFakeDlcId, "en",
+      base::BindOnce(&LanguagePackManagerTest::GetPackStateTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorInvalidDlc);
+  EXPECT_EQ(pack_result_.pack_state, PackResult::WRONG_ID);
+}
+
+// Check that the callback is actually called.
+TEST_F(LanguagePackManagerTest, GetPackStateCallbackTest) {
+  dlcservice_client_->set_get_dlc_state_error(dlcservice::kErrorNone);
+
+  testing::StrictMock<CallbackForTesting> callback;
+  EXPECT_CALL(callback, Callback(_));
+
+  manager_->GetPackState(kFakeDlcId, "en", callback.GetPackStateCallback());
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(LanguagePackManagerTest, RemovePackSuccessTest) {
+  dlcservice_client_->set_uninstall_error(dlcservice::kErrorNone);
+
+  // We need to use an existing Pack ID, so that we do get a result back.
+  manager_->RemovePack(
+      kHandwritingFeatureId, "en",
+      base::BindOnce(&LanguagePackManagerTest::RemoveTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorNone);
+  EXPECT_EQ(pack_result_.pack_state, PackResult::NOT_INSTALLED);
+}
+
+TEST_F(LanguagePackManagerTest, RemovePackFailureTest) {
+  dlcservice_client_->set_uninstall_error(dlcservice::kErrorInternal);
+
+  // We need to use an existing Pack ID, so that we do get a result back.
+  manager_->RemovePack(
+      kHandwritingFeatureId, "en",
+      base::BindOnce(&LanguagePackManagerTest::RemoveTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorInternal);
+}
+
+TEST_F(LanguagePackManagerTest, RemovePackWrongIdTest) {
+  manager_->RemovePack(
+      kFakeDlcId, "en",
+      base::BindOnce(&LanguagePackManagerTest::RemoveTestCallback,
+                     base::Unretained(this)));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(pack_result_.operation_error, dlcservice::kErrorInvalidDlc);
+  EXPECT_EQ(pack_result_.pack_state, PackResult::WRONG_ID);
+}
+
+// Check that the callback is actually called.
+TEST_F(LanguagePackManagerTest, RemovePackCallbackTest) {
+  dlcservice_client_->set_uninstall_error(dlcservice::kErrorNone);
+
+  testing::StrictMock<CallbackForTesting> callback;
+  EXPECT_CALL(callback, Callback(_));
+
+  manager_->RemovePack(kFakeDlcId, "en", callback.GetRemoveCallback());
+  base::RunLoop().RunUntilIdle();
+}
+
+}  // namespace language_packs
+}  // namespace chromeos
diff --git a/chromeos/services/libassistant/chromium_api_delegate.h b/chromeos/services/libassistant/chromium_api_delegate.h
index 2990b59..bb70ee3d 100644
--- a/chromeos/services/libassistant/chromium_api_delegate.h
+++ b/chromeos/services/libassistant/chromium_api_delegate.h
@@ -23,7 +23,7 @@
 
 class ChromiumHttpConnectionFactory;
 
-class ChromiumApiDelegate : public assistant_client::ChromeOSApiDelegate {
+class ChromiumApiDelegate : public assistant_client::FuchsiaApiDelegate {
  public:
   explicit ChromiumApiDelegate(
       std::unique_ptr<network::PendingSharedURLLoaderFactory>
diff --git a/chromeos/services/libassistant/service_controller.cc b/chromeos/services/libassistant/service_controller.cc
index e452385..1ca11ba 100644
--- a/chromeos/services/libassistant/service_controller.cc
+++ b/chromeos/services/libassistant/service_controller.cc
@@ -327,7 +327,7 @@
 
   assistant_manager_internal()
       ->GetFuchsiaApiHelperOrDie()
-      ->SetChromeOSApiDelegate(chromium_api_delegate_.get());
+      ->SetFuchsiaApiDelegate(chromium_api_delegate_.get());
 }
 
 void ServiceController::CreateChromiumApiDelegate(
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 7525ba7..48e42cfa 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -4,7 +4,6 @@
 
 #include "components/autofill/content/browser/content_autofill_driver.h"
 
-#include <memory>
 #include <tuple>
 #include <utility>
 #include <vector>
@@ -13,6 +12,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "build/build_config.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
+#include "components/autofill/core/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
@@ -64,21 +64,15 @@
     AutofillClient* client,
     const std::string& app_locale,
     AutofillManager::AutofillDownloadManagerState enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback)
+    AutofillProvider* provider)
     : render_frame_host_(render_frame_host),
       browser_autofill_manager_(nullptr),
       key_press_handler_manager_(this),
       log_manager_(client->GetLogManager()) {
-  // AutofillManager isn't used if provider is valid, Autofill provider is
-  // currently used by Android WebView only.
-  if (autofill_manager_factory_callback) {
-    autofill_manager_ = autofill_manager_factory_callback.Run(
-        this, client, app_locale, enable_download_manager);
-    GetAutofillAgent()->SetUserGestureRequired(false);
-    GetAutofillAgent()->SetSecureContextRequired(true);
-    GetAutofillAgent()->SetFocusRequiresScroll(false);
-    GetAutofillAgent()->SetQueryPasswordSuggestion(true);
+  // BrowserAutofillManager isn't used if provider is valid, Autofill provider
+  // is currently used by Android WebView only.
+  if (provider) {
+    SetAutofillProvider(provider, client, enable_download_manager);
   } else {
     SetBrowserAutofillManager(std::make_unique<BrowserAutofillManager>(
         this, client, app_locale, enable_download_manager));
@@ -177,11 +171,11 @@
 
 void ContentAutofillDriver::PropagateAutofillPredictions(
     const std::vector<FormStructure*>& forms) {
-  AutofillManager* manager = browser_autofill_manager_
+  AutofillManager* handler = browser_autofill_manager_
                                  ? browser_autofill_manager_
                                  : autofill_manager_.get();
-  DCHECK(manager);
-  manager->PropagateAutofillPredictions(render_frame_host_, forms);
+  DCHECK(handler);
+  handler->PropagateAutofillPredictions(render_frame_host_, forms);
 }
 
 void ContentAutofillDriver::HandleParsedForms(
@@ -377,7 +371,7 @@
   ShowOfferNotificationIfApplicable(navigation_handle);
 
   // When IsServedFromBackForwardCache, the form data is not parsed
-  // again. So, we should keep and use the autofill manager's
+  // again. So, we should keep and use the autofill handler's
   // form_structures from BFCache for form submit.
   if (navigation_handle->IsServedFromBackForwardCache())
     return;
@@ -435,6 +429,18 @@
   view->GetRenderWidgetHost()->RemoveKeyPressEventCallback(handler);
 }
 
+void ContentAutofillDriver::SetAutofillProvider(
+    AutofillProvider* provider,
+    AutofillClient* client,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+  autofill_manager_ = std::make_unique<AndroidAutofillManager>(
+      this, client, provider, enable_download_manager);
+  GetAutofillAgent()->SetUserGestureRequired(false);
+  GetAutofillAgent()->SetSecureContextRequired(true);
+  GetAutofillAgent()->SetFocusRequiresScroll(false);
+  GetAutofillAgent()->SetQueryPasswordSuggestion(true);
+}
+
 bool ContentAutofillDriver::DocumentUsedWebOTP() const {
   return render_frame_host_->DocumentUsedWebOTP();
 }
@@ -475,6 +481,16 @@
       static_cast<PhoneCollectionMetricState>(phone_collection_metric_state_));
 }
 
+void ContentAutofillDriver::SetAutofillProviderForTesting(
+    AutofillProvider* provider,
+    AutofillClient* client) {
+  SetAutofillProvider(provider, client,
+                      AutofillManager::AutofillDownloadManagerState::
+                          DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
+  // BrowserAutofillManager isn't used if provider is valid.
+  browser_autofill_manager_ = nullptr;
+}
+
 void ContentAutofillDriver::ShowOfferNotificationIfApplicable(
     content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->IsInMainFrame())
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index ca0f934..7c96eb16 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -29,6 +29,7 @@
 namespace autofill {
 
 class AutofillClient;
+class AutofillProvider;
 class LogManager;
 
 // Use <Phone><WebOTP><OTC> as the bit pattern to identify the metrics state.
@@ -62,8 +63,7 @@
       AutofillClient* client,
       const std::string& app_locale,
       AutofillManager::AutofillDownloadManagerState enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback
-          autofill_manager_factory_callback);
+      AutofillProvider* provider);
   ~ContentAutofillDriver() override;
 
   // Gets the driver for |render_frame_host|.
@@ -159,6 +159,9 @@
       const content::RenderWidgetHost::KeyPressEventCallback& handler);
   void RemoveKeyPressHandler();
 
+  void SetAutofillProviderForTesting(AutofillProvider* provider,
+                                     AutofillClient* client);
+
   // Sets the manager to |manager|. Takes ownership of |manager|.
   void SetBrowserAutofillManager(
       std::unique_ptr<BrowserAutofillManager> manager);
@@ -185,6 +188,11 @@
   void RemoveHandler(
       const content::RenderWidgetHost::KeyPressEventCallback& handler) override;
 
+  void SetAutofillProvider(
+      AutofillProvider* provider,
+      AutofillClient* client,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+
   // Returns whether navigator.credentials.get({otp: {transport:"sms"}}) has
   // been used.
   bool DocumentUsedWebOTP() const;
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.cc b/components/autofill/content/browser/content_autofill_driver_factory.cc
index 34d4ea9..780119b 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.cc
+++ b/components/autofill/content/browser/content_autofill_driver_factory.cc
@@ -30,11 +30,9 @@
     const std::string& app_locale,
     BrowserAutofillManager::AutofillDownloadManagerState
         enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback) {
+    AutofillProvider* provider) {
   return std::make_unique<ContentAutofillDriver>(
-      render_frame_host, client, app_locale, enable_download_manager,
-      std::move(autofill_manager_factory_callback));
+      render_frame_host, client, app_locale, enable_download_manager, provider);
 }
 
 }  // namespace
@@ -52,9 +50,8 @@
     const std::string& app_locale,
     BrowserAutofillManager::AutofillDownloadManagerState
         enable_download_manager) {
-  CreateForWebContentsAndDelegate(
-      contents, client, app_locale, enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback());
+  CreateForWebContentsAndDelegate(contents, client, app_locale,
+                                  enable_download_manager, nullptr);
 }
 
 void ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
@@ -63,16 +60,14 @@
     const std::string& app_locale,
     BrowserAutofillManager::AutofillDownloadManagerState
         enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback) {
+    AutofillProvider* provider) {
   if (FromWebContents(contents))
     return;
 
   contents->SetUserData(
       kContentAutofillDriverFactoryWebContentsUserDataKey,
       std::make_unique<ContentAutofillDriverFactory>(
-          contents, client, app_locale, enable_download_manager,
-          std::move(autofill_manager_factory_callback)));
+          contents, client, app_locale, enable_download_manager, provider));
 }
 
 // static
@@ -112,14 +107,12 @@
     const std::string& app_locale,
     BrowserAutofillManager::AutofillDownloadManagerState
         enable_download_manager,
-    AutofillManager::AutofillManagerFactoryCallback
-        autofill_manager_factory_callback)
+    AutofillProvider* provider)
     : AutofillDriverFactory(client),
       content::WebContentsObserver(web_contents),
       app_locale_(app_locale),
       enable_download_manager_(enable_download_manager),
-      autofill_manager_factory_callback_(
-          std::move(autofill_manager_factory_callback)) {}
+      provider_(provider) {}
 
 ContentAutofillDriver* ContentAutofillDriverFactory::DriverForFrame(
     content::RenderFrameHost* render_frame_host) {
@@ -127,10 +120,10 @@
 
   // ContentAutofillDriver are created on demand here.
   if (!driver) {
-    AddForKey(render_frame_host,
-              base::BindRepeating(CreateDriver, render_frame_host, client(),
-                                  app_locale_, enable_download_manager_,
-                                  autofill_manager_factory_callback_));
+    AddForKey(
+        render_frame_host,
+        base::BindRepeating(CreateDriver, render_frame_host, client(),
+                            app_locale_, enable_download_manager_, provider_));
     driver = DriverForKey(render_frame_host);
   }
 
diff --git a/components/autofill/content/browser/content_autofill_driver_factory.h b/components/autofill/content/browser/content_autofill_driver_factory.h
index 58c3da4..186879e 100644
--- a/components/autofill/content/browser/content_autofill_driver_factory.h
+++ b/components/autofill/content/browser/content_autofill_driver_factory.h
@@ -10,7 +10,6 @@
 #include "base/supports_user_data.h"
 #include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
 #include "components/autofill/core/browser/autofill_driver_factory.h"
-#include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -22,6 +21,7 @@
 namespace autofill {
 
 class ContentAutofillDriver;
+class AutofillProvider;
 
 // Manages lifetime of ContentAutofillDriver. One Factory per WebContents
 // creates one Driver per RenderFrame.
@@ -37,8 +37,7 @@
       const std::string& app_locale,
       BrowserAutofillManager::AutofillDownloadManagerState
           enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback
-          autofill_manager_factory_callback);
+      AutofillProvider* provider);
 
   ~ContentAutofillDriverFactory() override;
 
@@ -55,8 +54,7 @@
       const std::string& app_locale,
       BrowserAutofillManager::AutofillDownloadManagerState
           enable_download_manager,
-      AutofillManager::AutofillManagerFactoryCallback
-          autofill_manager_factory_callback);
+      AutofillProvider* provider);
 
   static ContentAutofillDriverFactory* FromWebContents(
       content::WebContents* contents);
@@ -82,8 +80,7 @@
  private:
   std::string app_locale_;
   BrowserAutofillManager::AutofillDownloadManagerState enable_download_manager_;
-  AutofillManager::AutofillManagerFactoryCallback
-      autofill_manager_factory_callback_;
+  AutofillProvider* provider_;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc
index ddeafa755..94bb308 100644
--- a/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -284,12 +284,11 @@
  public:
   TestContentAutofillDriver(content::RenderFrameHost* rfh,
                             AutofillClient* client)
-      : ContentAutofillDriver(
-            rfh,
-            client,
-            kAppLocale,
-            kDownloadState,
-            AutofillManager::AutofillManagerFactoryCallback()) {
+      : ContentAutofillDriver(rfh,
+                              client,
+                              kAppLocale,
+                              kDownloadState,
+                              nullptr) {
     std::unique_ptr<BrowserAutofillManager> autofill_manager(
         new MockBrowserAutofillManager(this, client));
     SetBrowserAutofillManager(std::move(autofill_manager));
diff --git a/components/autofill/core/browser/android_autofill_manager.cc b/components/autofill/core/browser/android_autofill_manager.cc
index 43a0106..5710567 100644
--- a/components/autofill/core/browser/android_autofill_manager.cc
+++ b/components/autofill/core/browser/android_autofill_manager.cc
@@ -4,24 +4,12 @@
 
 #include "components/autofill/core/browser/android_autofill_manager.h"
 
-#include "base/memory/ptr_util.h"
 #include "components/autofill/core/browser/autofill_provider.h"
 
 namespace autofill {
 
 using base::TimeTicks;
 
-// static
-std::unique_ptr<AutofillManager> AndroidAutofillManager::Create(
-    AutofillProvider* provider,
-    AutofillDriver* driver,
-    AutofillClient* client,
-    const std::string& /*app_locale*/,
-    AutofillManager::AutofillDownloadManagerState enable_download_manager) {
-  return base::WrapUnique(new AndroidAutofillManager(driver, client, provider,
-                                                     enable_download_manager));
-}
-
 AndroidAutofillManager::AndroidAutofillManager(
     AutofillDriver* driver,
     AutofillClient* client,
@@ -33,7 +21,7 @@
                       version_info::Channel::UNKNOWN),
       provider_(provider) {}
 
-AndroidAutofillManager::~AndroidAutofillManager() = default;
+AndroidAutofillManager::~AndroidAutofillManager() {}
 
 void AndroidAutofillManager::OnFormSubmittedImpl(
     const FormData& form,
diff --git a/components/autofill/core/browser/android_autofill_manager.h b/components/autofill/core/browser/android_autofill_manager.h
index 17c7ef8..71b0c4d 100644
--- a/components/autofill/core/browser/android_autofill_manager.h
+++ b/components/autofill/core/browser/android_autofill_manager.h
@@ -16,13 +16,11 @@
 // This class forwards AutofillManager calls to AutofillProvider.
 class AndroidAutofillManager : public AutofillManager {
  public:
-  static std::unique_ptr<AutofillManager> Create(
-      AutofillProvider* provider,
+  AndroidAutofillManager(
       AutofillDriver* driver,
       AutofillClient* client,
-      const std::string& app_locale,
+      AutofillProvider* provider,
       AutofillManager::AutofillDownloadManagerState enable_download_manager);
-
   ~AndroidAutofillManager() override;
 
   void OnFocusNoLongerOnForm(bool had_interacted_form) override;
@@ -44,12 +42,6 @@
   bool has_server_prediction() const { return has_server_prediction_; }
 
  protected:
-  AndroidAutofillManager(
-      AutofillDriver* driver,
-      AutofillClient* client,
-      AutofillProvider* provider,
-      AutofillManager::AutofillDownloadManagerState enable_download_manager);
-
   void OnFormSubmittedImpl(const FormData& form,
                            bool known_success,
                            mojom::SubmissionSource source) override;
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h
index 963b907..81553596 100644
--- a/components/autofill/core/browser/autofill_manager.h
+++ b/components/autofill/core/browser/autofill_manager.h
@@ -57,15 +57,6 @@
     virtual void OnFormParsed() = 0;
   };
 
-  // The factory method for the embedder to create the subclass of
-  // AutofillManager in ContentAutofillDriver.
-  using AutofillManagerFactoryCallback =
-      base::RepeatingCallback<std::unique_ptr<AutofillManager>(
-          AutofillDriver*,
-          AutofillClient*,
-          const std::string& app_locale,
-          AutofillManager::AutofillDownloadManagerState)>;
-
   // Raw metadata uploading enabled iff this Chrome instance is on Canary or Dev
   // channel.
   static bool IsRawMetadataUploadingEnabled(version_info::Channel channel);
diff --git a/components/autofill/core/browser/payments/strike_database.cc b/components/autofill/core/browser/payments/strike_database.cc
index a4acdfc..69e80fe 100644
--- a/components/autofill/core/browser/payments/strike_database.cc
+++ b/components/autofill/core/browser/payments/strike_database.cc
@@ -46,7 +46,7 @@
                                 weak_ptr_factory_.GetWeakPtr()));
 }
 
-StrikeDatabase::~StrikeDatabase() {}
+StrikeDatabase::~StrikeDatabase() = default;
 
 int StrikeDatabase::AddStrikes(int strikes_increase, const std::string& key) {
   DCHECK(strikes_increase > 0);
@@ -76,16 +76,28 @@
   ClearAllProtoStrikesForKey(key, base::DoNothing());
 }
 
-void StrikeDatabase::ClearAllStrikesForProject(
+std::vector<std::string> StrikeDatabase::GetAllStrikeKeysForProject(
     const std::string& project_prefix) {
-  std::vector<std::string> keys_to_delete;
+  std::vector<std::string> project_keys;
   for (std::pair<std::string, StrikeData> entry : strike_map_cache_) {
     if (entry.first.find(project_prefix) == 0) {
-      keys_to_delete.push_back(entry.first);
+      project_keys.push_back(entry.first);
     }
   }
-  for (std::string key : keys_to_delete)
-    ClearStrikes(key);
+  return project_keys;
+}
+
+void StrikeDatabase::ClearAllStrikesForProject(
+    const std::string& project_prefix) {
+  ClearStrikesForKeys(GetAllStrikeKeysForProject(project_prefix));
+}
+
+void StrikeDatabase::ClearStrikesForKeys(
+    const std::vector<std::string>& keys_to_remove) {
+  for (const auto& key : keys_to_remove) {
+    strike_map_cache_.erase(key);
+  }
+  ClearAllProtoStrikesForKeys(keys_to_remove, base::DoNothing());
 }
 
 void StrikeDatabase::ClearAllStrikes() {
@@ -161,8 +173,8 @@
       outer_callback);
 }
 
-void StrikeDatabase::ClearAllProtoStrikesForKey(
-    const std::string& key,
+void StrikeDatabase::ClearAllProtoStrikesForKeys(
+    const std::vector<std::string>& keys,
     const ClearStrikesCallback& outer_callback) {
   if (!database_initialized_) {
     outer_callback.Run(false);
@@ -170,13 +182,20 @@
   }
   std::unique_ptr<std::vector<std::string>> keys_to_remove(
       new std::vector<std::string>());
-  keys_to_remove->push_back(key);
+  *keys_to_remove = keys;
   db_->UpdateEntries(
       /*entries_to_save=*/std::make_unique<
           leveldb_proto::ProtoDatabase<StrikeData>::KeyEntryVector>(),
       /*keys_to_remove=*/std::move(keys_to_remove), outer_callback);
 }
 
+void StrikeDatabase::ClearAllProtoStrikesForKey(
+    const std::string& key,
+    const ClearStrikesCallback& outer_callback) {
+  std::vector<std::string> keys_to_delete({key});
+  ClearAllProtoStrikesForKeys(keys_to_delete, outer_callback);
+}
+
 void StrikeDatabase::GetProtoStrikeData(const std::string& key,
                                         const GetValueCallback& callback) {
   if (!database_initialized_) {
diff --git a/components/autofill/core/browser/payments/strike_database.h b/components/autofill/core/browser/payments/strike_database.h
index 0a2dd30..d28f29b5 100644
--- a/components/autofill/core/browser/payments/strike_database.h
+++ b/components/autofill/core/browser/payments/strike_database.h
@@ -72,6 +72,15 @@
   // ProtoDatabase.
   void ClearStrikes(const std::string& key);
 
+  // Returns all strike keys for |project_prefix|.
+  // The returned keys still contain the |project_prefix|.
+  std::vector<std::string> GetAllStrikeKeysForProject(
+      const std::string& project_prefix);
+
+  // Removes database entry for keys in |keys_to_remove| from in-memory cache
+  // and the underlying ProtoDatabase.
+  void ClearStrikesForKeys(const std::vector<std::string>& keys_to_remove);
+
   // Removes all database entries from in-memory cache and underlying
   // ProtoDatabase for the whole project.
   void ClearAllStrikesForProject(const std::string& project_prefix);
@@ -138,6 +147,11 @@
       const std::string& key,
       const ClearStrikesCallback& outer_callback);
 
+  // Same as |ClearAllProtoStrikesForKey()| but for a vector of |keys|.
+  virtual void ClearAllProtoStrikesForKeys(
+      const std::vector<std::string>& keys,
+      const ClearStrikesCallback& outer_callback);
+
   // Passes success status and StrikeData entry for |key| to |inner_callback|.
   void GetProtoStrikeData(const std::string& key,
                           const GetValueCallback& inner_callback);
diff --git a/components/autofill/core/browser/payments/strike_database_unittest.cc b/components/autofill/core/browser/payments/strike_database_unittest.cc
index 1fa2338..ddbda726 100644
--- a/components/autofill/core/browser/payments/strike_database_unittest.cc
+++ b/components/autofill/core/browser/payments/strike_database_unittest.cc
@@ -57,7 +57,7 @@
 // ProtoDatabase.
 class StrikeDatabaseTest : public ::testing::Test {
  public:
-  StrikeDatabaseTest() {}
+  StrikeDatabaseTest() = default;
 
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
@@ -114,6 +114,20 @@
     run_loop.Run();
   }
 
+  void OnClearAllProtoStrikesForKeys(base::RepeatingClosure run_loop_closure,
+                                     bool success) {
+    run_loop_closure.Run();
+  }
+
+  void ClearAllProtoStrikesForKeys(const std::vector<std::string>& keys) {
+    base::RunLoop run_loop;
+    strike_database_->ClearAllProtoStrikesForKeys(
+        keys,
+        base::BindRepeating(&StrikeDatabaseTest::OnClearAllProtoStrikesForKeys,
+                            base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
   void OnClearAllProtoStrikes(base::RepeatingClosure run_loop_closure,
                               bool success) {
     run_loop_closure.Run();
@@ -152,7 +166,7 @@
   std::vector<std::pair<std::string, StrikeData>> entries;
   StrikeData data;
   data.set_num_strikes(3);
-  entries.push_back(std::make_pair(key, data));
+  entries.emplace_back(key, data);
   AddProtoEntries(entries);
 
   int strikes = GetProtoStrikes(key);
@@ -172,7 +186,7 @@
   std::vector<std::pair<std::string, StrikeData>> entries;
   StrikeData data;
   data.set_num_strikes(3);
-  entries.push_back(std::make_pair(key, data));
+  entries.emplace_back(key, data);
   AddProtoEntries(entries);
 
   int strikes = GetProtoStrikes(key);
@@ -182,6 +196,30 @@
   EXPECT_EQ(0, strikes);
 }
 
+TEST_F(StrikeDatabaseTest, ClearStrikesForMultipleNonZeroStrikesTest) {
+  // Set up database with 3 pre-existing strikes for three different keys.
+  const std::string key1 = "12345";
+  const std::string key2 = "67890";
+  const std::string key3 = "99000";
+  std::vector<std::pair<std::string, StrikeData>> entries;
+  StrikeData data;
+  data.set_num_strikes(3);
+  entries.emplace_back(key1, data);
+  entries.emplace_back(key2, data);
+  entries.emplace_back(key3, data);
+  AddProtoEntries(entries);
+
+  EXPECT_EQ(3, GetProtoStrikes(key1));
+  EXPECT_EQ(3, GetProtoStrikes(key2));
+  EXPECT_EQ(3, GetProtoStrikes(key3));
+  std::vector<std::string> keys_to_clear({key1, key2});
+  ClearAllProtoStrikesForKeys(keys_to_clear);
+  EXPECT_EQ(0, GetProtoStrikes(key1));
+  EXPECT_EQ(0, GetProtoStrikes(key2));
+  // The strikes for the third key should not have been reset.
+  EXPECT_EQ(3, GetProtoStrikes(key3));
+}
+
 TEST_F(StrikeDatabaseTest, ClearStrikesForMultipleNonZeroStrikesEntriesTest) {
   // Set up database with 3 pre-existing strikes at |key1|, and 5 pre-existing
   // strikes at |key2|.
@@ -190,10 +228,10 @@
   std::vector<std::pair<std::string, StrikeData>> entries;
   StrikeData data1;
   data1.set_num_strikes(3);
-  entries.push_back(std::make_pair(key1, data1));
+  entries.emplace_back(key1, data1);
   StrikeData data2;
   data2.set_num_strikes(5);
-  entries.push_back(std::make_pair(key2, data2));
+  entries.emplace_back(key2, data2);
   AddProtoEntries(entries);
 
   int strikes = GetProtoStrikes(key1);
@@ -228,4 +266,37 @@
   EXPECT_EQ(0, GetProtoStrikes(key2));
 }
 
+TEST_F(StrikeDatabaseTest, GetAllStrikeKeysForProject) {
+  const std::string key1 = "project_12345";
+  const std::string key2 = "project_13579";
+  const std::string key3 = "otherproject_13579";
+  strike_database_->AddStrikes(1, key1);
+  strike_database_->AddStrikes(2, key2);
+  strike_database_->AddStrikes(2, key3);
+  std::vector<std::string> expected_keys({key1, key2});
+  EXPECT_EQ(strike_database_->GetAllStrikeKeysForProject("project"),
+            expected_keys);
+  expected_keys = {key3};
+  EXPECT_EQ(strike_database_->GetAllStrikeKeysForProject("otherproject"),
+            expected_keys);
+  ClearAllProtoStrikes();
+}
+
+TEST_F(StrikeDatabaseTest, ClearStrikesForKeys) {
+  const std::string key1 = "project_12345";
+  const std::string key2 = "project_13579";
+  const std::string key3 = "otherproject_13579";
+  strike_database_->AddStrikes(1, key1);
+  strike_database_->AddStrikes(2, key2);
+  strike_database_->AddStrikes(2, key3);
+  strike_database_->ClearStrikesForKeys(std::vector<std::string>({key1, key2}));
+  std::vector<std::string> expected_keys({});
+  EXPECT_EQ(strike_database_->GetAllStrikeKeysForProject("project"),
+            expected_keys);
+  expected_keys.emplace_back(key3);
+  EXPECT_EQ(strike_database_->GetAllStrikeKeysForProject("otherproject"),
+            expected_keys);
+  ClearAllProtoStrikes();
+}
+
 }  // namespace autofill
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 9fae369..cc682e2f 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -198,6 +198,8 @@
     "service/simple_url_loader_factory.h",
     "starter.cc",
     "starter.h",
+    "starter_heuristic.cc",
+    "starter_heuristic.h",
     "starter_platform_delegate.h",
     "startup_util.cc",
     "startup_util.h",
@@ -405,6 +407,7 @@
     "service/service_impl_unittest.cc",
     "service/service_request_sender_impl_unittest.cc",
     "service/service_request_sender_local_impl_unittest.cc",
+    "starter_heuristic_unittest.cc",
     "starter_unittest.cc",
     "startup_util_unittest.cc",
     "string_conversions_util_unittest.cc",
diff --git a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
index c801ae1..e57bc649 100644
--- a/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/collect_user_data_action_unittest.cc
@@ -20,6 +20,8 @@
 #include "components/autofill_assistant/browser/test_util.h"
 #include "components/autofill_assistant/browser/user_model.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -54,10 +56,13 @@
 using ::testing::StrEq;
 using ::testing::UnorderedElementsAre;
 
-class CollectUserDataActionTest : public content::RenderViewHostTestHarness {
+class CollectUserDataActionTest : public testing::Test {
  public:
   void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
+    web_contents_ = content::WebContentsTester::CreateTestWebContents(
+        &browser_context_, nullptr);
+    content::WebContentsTester::For(web_contents_.get())
+        ->SetLastCommittedURL(GURL(kFakeUrl));
 
     ON_CALL(mock_action_delegate_, GetPersonalDataManager)
         .WillByDefault(Return(&mock_personal_data_manager_));
@@ -78,21 +83,21 @@
               std::move(collect_user_data_options->confirm_callback)
                   .Run(&user_data_, &user_model_);
             }));
-
     ON_CALL(mock_website_login_manager_, OnGetLoginsForUrl(_, _))
         .WillByDefault(
             RunOnceCallback<1>(std::vector<WebsiteLoginManager::Login>{
                 WebsiteLoginManager::Login(GURL(kFakeUrl), kFakeUsername)}));
     ON_CALL(mock_website_login_manager_, OnGetPasswordForLogin(_, _))
         .WillByDefault(RunOnceCallback<1>(true, kFakePassword));
-
-    content::WebContentsTester::For(web_contents())
-        ->SetLastCommittedURL(GURL(kFakeUrl));
     ON_CALL(mock_action_delegate_, GetWebContents())
-        .WillByDefault(Return(web_contents()));
+        .WillByDefault(Return(web_contents_.get()));
   }
 
  protected:
+  content::BrowserTaskEnvironment task_environment_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+  content::TestBrowserContext browser_context_;
+  std::unique_ptr<content::WebContents> web_contents_;
   base::MockCallback<Action::ProcessActionCallback> callback_;
   MockPersonalDataManager mock_personal_data_manager_;
   MockWebsiteLoginManager mock_website_login_manager_;
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
index 74c693f..1368c06e 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler.cc
@@ -336,7 +336,7 @@
 
     action_delegate_->GetWebController()->SelectOption(
         re2, /* case_sensitive= */ false, option_comparison_attribute,
-        *element_ptr, std::move(on_set_field_value));
+        /* strict= */ true, *element_ptr, std::move(on_set_field_value));
     return;
   }
 
diff --git a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
index fcc8f10c..a39f0616 100644
--- a/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
+++ b/components/autofill_assistant/browser/actions/fallback_handler/required_fields_fallback_handler_unittest.cc
@@ -490,9 +490,9 @@
               GetElementTag(EqualsElement(expected_element), _))
       .WillOnce(RunOnceCallback<1>(OkClientStatus(), "SELECT"));
   EXPECT_CALL(mock_web_controller_,
-              SelectOption("^2050", false, SelectOptionProto::LABEL,
+              SelectOption("^2050", false, SelectOptionProto::LABEL, true,
                            EqualsElement(expected_element), _))
-      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+      .WillOnce(RunOnceCallback<5>(OkClientStatus()));
 
   // Second validation succeeds.
   EXPECT_CALL(mock_web_controller_,
diff --git a/components/autofill_assistant/browser/actions/select_option_action.cc b/components/autofill_assistant/browser/actions/select_option_action.cc
index da352dda..e194a08f 100644
--- a/components/autofill_assistant/browser/actions/select_option_action.cc
+++ b/components/autofill_assistant/browser/actions/select_option_action.cc
@@ -92,7 +92,8 @@
       base::BindOnce(&WebController::SelectOption,
                      delegate_->GetWebController()->GetWeakPtr(), value_,
                      case_sensitive_,
-                     proto_.select_option().option_comparison_attribute()),
+                     proto_.select_option().option_comparison_attribute(),
+                     proto_.select_option().strict()),
       base::BindOnce(&SelectOptionAction::EndAction,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/components/autofill_assistant/browser/actions/select_option_action_unittest.cc b/components/autofill_assistant/browser/actions/select_option_action_unittest.cc
index 81100d4..a8a2a4f 100644
--- a/components/autofill_assistant/browser/actions/select_option_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/select_option_action_unittest.cc
@@ -111,6 +111,7 @@
   Selector selector({"#select"});
   *proto_.mutable_element() = selector.proto;
   proto_.mutable_text_filter_value()->set_re2("option");
+  proto_.set_strict(true);
 
   Selector expected_selector = selector;
   EXPECT_CALL(mock_action_delegate_,
@@ -120,9 +121,9 @@
   auto expected_element =
       test_util::MockFindElement(mock_action_delegate_, expected_selector);
   EXPECT_CALL(mock_web_controller_,
-              SelectOption("option", false, SelectOptionProto::VALUE,
+              SelectOption("option", false, SelectOptionProto::VALUE, true,
                            EqualsElement(expected_element), _))
-      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+      .WillOnce(RunOnceCallback<5>(OkClientStatus()));
 
   EXPECT_CALL(
       callback_,
@@ -191,11 +192,11 @@
       .WillOnce(RunOnceCallback<1>(OkClientStatus(),
                                    base::TimeDelta::FromSeconds(0)));
   EXPECT_CALL(mock_web_controller_,
-              SelectOption("John", false, SelectOptionProto::VALUE,
+              SelectOption("John", false, SelectOptionProto::VALUE, false,
                            EqualsElement(test_util::MockFindElement(
                                mock_action_delegate_, expected_selector)),
                            _))
-      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+      .WillOnce(RunOnceCallback<5>(OkClientStatus()));
 
   EXPECT_CALL(
       callback_,
@@ -219,9 +220,9 @@
   auto expected_element =
       test_util::MockFindElement(mock_action_delegate_, expected_selector);
   EXPECT_CALL(mock_web_controller_,
-              SelectOption("^option$", true, SelectOptionProto::VALUE,
+              SelectOption("^option$", true, SelectOptionProto::VALUE, false,
                            EqualsElement(expected_element), _))
-      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+      .WillOnce(RunOnceCallback<5>(OkClientStatus()));
 
   EXPECT_CALL(
       callback_,
@@ -255,12 +256,13 @@
               OnShortWaitForElement(expected_selector, _))
       .WillOnce(RunOnceCallback<1>(OkClientStatus(),
                                    base::TimeDelta::FromSeconds(0)));
-  EXPECT_CALL(mock_web_controller_,
-              SelectOption("^\\+41791234567$", true, SelectOptionProto::VALUE,
-                           EqualsElement(test_util::MockFindElement(
-                               mock_action_delegate_, expected_selector)),
-                           _))
-      .WillOnce(RunOnceCallback<4>(OkClientStatus()));
+  EXPECT_CALL(
+      mock_web_controller_,
+      SelectOption("^\\+41791234567$", true, SelectOptionProto::VALUE, false,
+                   EqualsElement(test_util::MockFindElement(
+                       mock_action_delegate_, expected_selector)),
+                   _))
+      .WillOnce(RunOnceCallback<5>(OkClientStatus()));
 
   EXPECT_CALL(
       callback_,
diff --git a/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc b/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
index 4ada277..d701343 100644
--- a/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
@@ -14,6 +14,8 @@
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/user_model.h"
 #include "components/autofill_assistant/browser/value_util.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -36,12 +38,13 @@
 using ::testing::UnorderedElementsAre;
 using ::testing::UnorderedElementsAreArray;
 
-class ShowGenericUiActionTest : public content::RenderViewHostTestHarness {
+class ShowGenericUiActionTest : public testing::Test {
  public:
-  ShowGenericUiActionTest() {}
-
   void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
+    web_contents_ = content::WebContentsTester::CreateTestWebContents(
+        &browser_context_, nullptr);
+    content::WebContentsTester::For(web_contents_.get())
+        ->SetLastCommittedURL(GURL(kFakeUrl));
 
     ON_CALL(mock_action_delegate_, SetGenericUi(_, _, _))
         .WillByDefault(
@@ -67,10 +70,8 @@
         .WillByDefault(
             RunOnceCallback<1>(std::vector<WebsiteLoginManager::Login>{
                 WebsiteLoginManager::Login(GURL(kFakeUrl), kFakeUsername)}));
-    content::WebContentsTester::For(web_contents())
-        ->SetLastCommittedURL(GURL(kFakeUrl));
     ON_CALL(mock_action_delegate_, GetWebContents())
-        .WillByDefault(Return(web_contents()));
+        .WillByDefault(Return(web_contents_.get()));
   }
 
  protected:
@@ -85,6 +86,10 @@
     return action;
   }
 
+  content::BrowserTaskEnvironment task_environment_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+  content::TestBrowserContext browser_context_;
+  std::unique_ptr<content::WebContents> web_contents_;
   UserData user_data_;
   UserModel user_model_;
   MockPersonalDataManager mock_personal_data_manager_;
diff --git a/components/autofill_assistant/browser/client_status.cc b/components/autofill_assistant/browser/client_status.cc
index 6e833a16..4ff1de094 100644
--- a/components/autofill_assistant/browser/client_status.cc
+++ b/components/autofill_assistant/browser/client_status.cc
@@ -142,6 +142,12 @@
     case ProcessedActionStatusProto::PASSWORD_ORIGIN_MISMATCH:
       out << "PASSWORD_ORIGIN_MISMATCH";
       break;
+    case ProcessedActionStatusProto::TOO_MANY_OPTION_VALUES_FOUND:
+      out << "TOO_MANY_OPTION_VALUES_FOUND";
+      break;
+    case ProcessedActionStatusProto::INVALID_TARGET:
+      out << "INVALID_TARGET";
+      break;
 
       // Intentionally no default case to make compilation fail if a new value
       // was added to the enum but not to this list.
diff --git a/components/autofill_assistant/browser/devtools/value_conversions.h b/components/autofill_assistant/browser/devtools/value_conversions.h
index 18d08347f..078d799 100644
--- a/components/autofill_assistant/browser/devtools/value_conversions.h
+++ b/components/autofill_assistant/browser/devtools/value_conversions.h
@@ -11,6 +11,7 @@
 
 #include <memory>
 
+#include "base/values.h"
 #include "components/autofill_assistant/browser/devtools/error_reporter.h"
 
 namespace autofill_assistant {
@@ -135,7 +136,7 @@
 struct FromValue<base::Value> {
   static std::unique_ptr<base::Value> Parse(const base::Value& value,
                                             ErrorReporter* errors) {
-    return value.CreateDeepCopy();
+    return base::Value::ToUniquePtrValue(value.Clone());
   }
 };
 
diff --git a/components/autofill_assistant/browser/fake_starter_platform_delegate.cc b/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
index cec341f..3ae3df5a 100644
--- a/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
+++ b/components/autofill_assistant/browser/fake_starter_platform_delegate.cc
@@ -19,6 +19,16 @@
   return std::move(trigger_script_request_sender_for_test_);
 }
 
+void FakeStarterPlatformDelegate::StartRegularScript(
+    GURL url,
+    std::unique_ptr<TriggerContext> trigger_context,
+    const base::Optional<TriggerScriptProto>& trigger_script) {
+  if (start_regular_script_callback_) {
+    std::move(start_regular_script_callback_)
+        .Run(url, std::move(trigger_context), trigger_script);
+  }
+}
+
 WebsiteLoginManager* FakeStarterPlatformDelegate::GetWebsiteLoginManager()
     const {
   return website_login_manager_;
diff --git a/components/autofill_assistant/browser/fake_starter_platform_delegate.h b/components/autofill_assistant/browser/fake_starter_platform_delegate.h
index c1658fd..472f03e 100644
--- a/components/autofill_assistant/browser/fake_starter_platform_delegate.h
+++ b/components/autofill_assistant/browser/fake_starter_platform_delegate.h
@@ -22,6 +22,10 @@
   CreateTriggerScriptUiDelegate() override;
   std::unique_ptr<ServiceRequestSender> GetTriggerScriptRequestSenderToInject()
       override;
+  void StartRegularScript(
+      GURL url,
+      std::unique_ptr<TriggerContext> trigger_context,
+      const base::Optional<TriggerScriptProto>& trigger_script) override;
   WebsiteLoginManager* GetWebsiteLoginManager() const override;
   version_info::Channel GetChannel() const override;
   bool GetFeatureModuleInstalled() const override;
@@ -63,6 +67,11 @@
   bool proactive_help_enabled_ = true;
   bool msbb_enabled_ = true;
   bool is_custom_tab_ = true;
+  base::OnceCallback<void(
+      GURL url,
+      std::unique_ptr<TriggerContext> trigger_context,
+      const base::Optional<TriggerScriptProto>& trigger_script)>
+      start_regular_script_callback_;
 
   int num_install_feature_module_called_ = 0;
   int num_show_onboarding_called_ = 0;
diff --git a/components/autofill_assistant/browser/metrics.h b/components/autofill_assistant/browser/metrics.h
index 5cef460a..ab4dbc5 100644
--- a/components/autofill_assistant/browser/metrics.h
+++ b/components/autofill_assistant/browser/metrics.h
@@ -277,6 +277,11 @@
     LITE_SCRIPT_BASE64_DECODING_ERROR = 24,
     // The user rejected the bottom sheet onboarding
     LITE_SCRIPT_BOTTOMSHEET_ONBOARDING_REJECTED = 25,
+    // Transitioning from CCT to regular tab is currently not supported.
+    LITE_SCRIPT_CCT_TO_TAB_NOT_SUPPORTED = 26,
+    // The current trigger script was canceled. This typically happens when a
+    // new startup request takes precedence.
+    LITE_SCRIPT_CANCELED = 27,
 
     // NOTE: All values in this block are DEPRECATED and will only be sent by
     // Chrome M-86 and M-87.
@@ -307,7 +312,7 @@
     // Since Chrome M-88. The bottom sheet was swipe-dismissed by the user.
     LITE_SCRIPT_PROMPT_SWIPE_DISMISSED = 16,
 
-    kMaxValue = LITE_SCRIPT_BOTTOMSHEET_ONBOARDING_REJECTED
+    kMaxValue = LITE_SCRIPT_CANCELED
   };
 
   // The different ways a user who has successfully completed a light script may
@@ -584,6 +589,13 @@
         break;
       case LiteScriptFinishedState::LITE_SCRIPT_PROMPT_SWIPE_DISMISSED:
         out << "LITE_SCRIPT_PROMPT_SWIPE_DISMISSED";
+        break;
+      case LiteScriptFinishedState::LITE_SCRIPT_CCT_TO_TAB_NOT_SUPPORTED:
+        out << "LITE_SCRIPT_CCT_TO_TAB_NOT_SUPPORTED";
+        break;
+      case LiteScriptFinishedState::LITE_SCRIPT_CANCELED:
+        out << "LITE_SCRIPT_CANCELED";
+        break;
         // Do not add default case to force compilation error for new values.
     }
     return out;
diff --git a/components/autofill_assistant/browser/model.proto b/components/autofill_assistant/browser/model.proto
index 8f5ea7c..dc107d2b 100644
--- a/components/autofill_assistant/browser/model.proto
+++ b/components/autofill_assistant/browser/model.proto
@@ -210,6 +210,13 @@
   // password's origin.
   PASSWORD_ORIGIN_MISMATCH = 29;
 
+  // Selecting an option failed because more than one option matched.
+  TOO_MANY_OPTION_VALUES_FOUND = 30;
+
+  // The action's target did not fit the action. E.g. a |SelectOption| being
+  // called on any element other than <select>.
+  INVALID_TARGET = 31;
+
   reserved 15, 23, 25;
 }
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 7b91504..757d5b25 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1393,6 +1393,11 @@
   }
   optional OptionComparisonAttribute option_comparison_attribute = 6;
 
+  // If |strict|, only one match is allowed. Multiple matches will return a
+  // |TOO_MANY_OPTION_VALUES_FOUND| error. If not |strict| and multiple matches
+  // are found, the first one is selected.
+  optional bool strict = 9;
+
   reserved 1, 3 to 5;
 }
 
diff --git a/components/autofill_assistant/browser/starter.cc b/components/autofill_assistant/browser/starter.cc
index cbc4f7ae..5fccde24 100644
--- a/components/autofill_assistant/browser/starter.cc
+++ b/components/autofill_assistant/browser/starter.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill_assistant/browser/starter.h"
 
+#include <map>
+
 #include "base/base64url.h"
 #include "base/bind.h"
 #include "base/callback.h"
@@ -11,7 +13,9 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/metrics/field_trial.h"
+#include "base/no_destructor.h"
 #include "components/autofill_assistant/browser/features.h"
+#include "components/autofill_assistant/browser/intent_strings.h"
 #include "components/autofill_assistant/browser/service/api_key_fetcher.h"
 #include "components/autofill_assistant/browser/service/server_url_fetcher.h"
 #include "components/autofill_assistant/browser/service/service_request_sender.h"
@@ -21,6 +25,9 @@
 #include "components/autofill_assistant/browser/switches.h"
 #include "components/autofill_assistant/browser/trigger_scripts/dynamic_trigger_conditions.h"
 #include "components/autofill_assistant/browser/trigger_scripts/static_trigger_conditions.h"
+#include "components/autofill_assistant/browser/url_utils.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 
 namespace autofill_assistant {
 
@@ -62,6 +69,15 @@
       /* disable_auth_if_no_access_token = */ true);
 }
 
+// The heuristic is shared across all instances and initialized on first use. As
+// such, we do not support updating the heuristic while Chrome is running.
+const scoped_refptr<StarterHeuristic> GetOrCreateStarterHeuristic() {
+  static const base::NoDestructor<scoped_refptr<StarterHeuristic>>
+      starter_heuristic(
+          [] { return base::MakeRefCounted<StarterHeuristic>(); }());
+  return *starter_heuristic;
+}
+
 }  // namespace
 
 Starter::Starter(content::WebContents* web_contents,
@@ -71,7 +87,8 @@
     : content::WebContentsObserver(web_contents),
       platform_delegate_(platform_delegate),
       ukm_recorder_(ukm_recorder),
-      runtime_manager_(runtime_manager) {
+      runtime_manager_(runtime_manager),
+      starter_heuristic_(GetOrCreateStarterHeuristic()) {
   CheckSettings();
 }
 
@@ -82,7 +99,7 @@
   // User-initiated navigations during non-trigger-script startups will cancel
   // the startup. This is mostly intended for navigations while the onboarding
   // is being shown.
-  if (pending_callback_ && !navigation_handle->WasServerRedirect() &&
+  if (IsStartupPending() && !navigation_handle->WasServerRedirect() &&
       !trigger_script_coordinator_ &&
       navigation_handle->GetURL() !=
           StartupUtil().ChooseStartupUrlForIntent(*pending_trigger_context_)) {
@@ -91,14 +108,61 @@
                                 : Metrics::DropOutReason::NAVIGATION,
         pending_trigger_context_->GetScriptParameters().GetIntent().value_or(
             std::string()));
-    CancelPendingStartup();
+    CancelPendingStartup(
+        Metrics::LiteScriptFinishedState::LITE_SCRIPT_CANCELED);
   }
 
-  if (!fetch_trigger_scripts_on_navigation_) {
+  if (!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage() ||
+      navigation_handle->WasServerRedirect()) {
     return;
   }
 
-  // TODO(arbesser): fetch trigger scripts when appropriate.
+  MaybeStartImplicitlyForUrl(navigation_handle->GetURL());
+}
+
+void Starter::MaybeStartImplicitlyForUrl(const GURL& url) {
+  if (!fetch_trigger_scripts_on_navigation_ || IsStartupPending() ||
+      !url.is_valid()) {
+    return;
+  }
+
+  // Run the heuristic in a separate task.
+  starter_heuristic_->RunHeuristicAsync(
+      url, base::BindOnce(&Starter::OnHeuristicMatch,
+                          weak_ptr_factory_.GetWeakPtr(), url));
+}
+
+void Starter::OnHeuristicMatch(const GURL& url, bool result) {
+  if (!result || IsStartupPending() || !fetch_trigger_scripts_on_navigation_) {
+    return;
+  }
+
+  // TODO(arbesser): add new command line switches to allow adding debug script
+  // parameters, like DEBUG_SOCKET_ID
+  Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(std::map<std::string, std::string>{
+          {"ENABLED", "true"},
+          {"START_IMMEDIATELY", "false"},
+          {"REQUEST_TRIGGER_SCRIPT", "true"},
+          {"ORIGINAL_DEEPLINK", url.spec()},
+          {"INTENT", kShoppingAssistedCheckout}}),
+      TriggerContext::Options{/* experiment_ids = */ std::string(),
+                              /* is_cct = */ is_custom_tab_,
+                              /* onboarding_shown = */ false,
+                              /* is_direct_action = */ false,
+                              /* initial_url = */ std::string()}));
+}
+
+bool Starter::IsStartupPending() const {
+  return pending_trigger_context_ != nullptr ||
+         trigger_script_coordinator_ != nullptr;
+}
+
+void Starter::OnTabInteractabilityChanged(bool is_interactable) {
+  CheckSettings();
+  if (trigger_script_coordinator_) {
+    trigger_script_coordinator_->OnTabInteractabilityChanged(is_interactable);
+  }
 }
 
 void Starter::CheckSettings() {
@@ -111,6 +175,8 @@
       platform_delegate_->GetMakeSearchesAndBrowsingBetterEnabled();
   bool feature_module_installed =
       platform_delegate_->GetFeatureModuleInstalled();
+  bool prev_fetch_trigger_scripts_on_navigation =
+      fetch_trigger_scripts_on_navigation_;
   fetch_trigger_scripts_on_navigation_ =
       base::FeatureList::IsEnabled(
           features::kAutofillAssistantInChromeTriggering) &&
@@ -118,7 +184,7 @@
 
   // If there is a pending startup, re-check that the settings are still
   // allowing the startup to proceed. If not, cancel the startup.
-  if (pending_callback_) {
+  if (IsStartupPending()) {
     StartupMode startup_mode = StartupUtil().ChooseStartupModeForIntent(
         trigger_script_coordinator_ != nullptr
             ? trigger_script_coordinator_->GetTriggerContext()
@@ -135,22 +201,25 @@
         }
         // Trigger scripts are not allowed to persist when transitioning from
         // CCT to regular tab.
-        CancelPendingStartup();
+        CancelPendingStartup(Metrics::LiteScriptFinishedState::
+                                 LITE_SCRIPT_CCT_TO_TAB_NOT_SUPPORTED);
         return;
       default:
-        CancelPendingStartup();
+        CancelPendingStartup(Metrics::LiteScriptFinishedState::
+                                 LITE_SCRIPT_DISABLED_PROACTIVE_HELP_SETTING);
         return;
     }
+  } else if (!prev_fetch_trigger_scripts_on_navigation &&
+             fetch_trigger_scripts_on_navigation_) {
+    MaybeStartImplicitlyForUrl(web_contents()->GetLastCommittedURL());
   }
 }
 
-void Starter::Start(std::unique_ptr<TriggerContext> trigger_context,
-                    StarterResultCallback callback) {
+void Starter::Start(std::unique_ptr<TriggerContext> trigger_context) {
   DCHECK(trigger_context);
   DCHECK(!trigger_context->GetDirectAction());
-  CancelPendingStartup();
+  CancelPendingStartup(Metrics::LiteScriptFinishedState::LITE_SCRIPT_CANCELED);
   pending_trigger_context_ = std::move(trigger_context);
-  pending_callback_ = std::move(callback);
 
   if (base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           switches::kAutofillAssistantForceOnboarding) == "true") {
@@ -184,7 +253,7 @@
     case StartupMode::MANDATORY_PARAMETERS_MISSING:
     case StartupMode::SETTING_DISABLED:
     case StartupMode::NO_INITIAL_URL:
-      RunCallback(/* start_regular_script = */ false);
+      OnStartDone(/* start_regular_script = */ false);
       return;
     case StartupMode::START_BASE64_TRIGGER_SCRIPT:
     case StartupMode::START_RPC_TRIGGER_SCRIPT:
@@ -194,8 +263,8 @@
   }
 }
 
-void Starter::CancelPendingStartup() {
-  if (!pending_callback_) {
+void Starter::CancelPendingStartup(Metrics::LiteScriptFinishedState state) {
+  if (!IsStartupPending()) {
     return;
   }
   platform_delegate_->HideOnboarding();
@@ -204,7 +273,10 @@
     Metrics::RecordOnboardingResult(Metrics::OnBoarding::OB_SHOWN);
     waiting_for_onboarding_ = false;
   }
-  RunCallback(/* start_regular_script = */ false);
+  OnStartDone(/* start_regular_script = */ false);
+  if (trigger_script_coordinator_) {
+    trigger_script_coordinator_->Stop(state);
+  }
   trigger_script_coordinator_.reset();
   pending_trigger_context_.reset();
 }
@@ -234,7 +306,7 @@
         Metrics::DropOutReason::DFM_INSTALL_FAILED,
         pending_trigger_context_->GetScriptParameters().GetIntent().value_or(
             std::string()));
-    RunCallback(/* start_regular_script = */ false);
+    OnStartDone(/* start_regular_script = */ false);
     return;
   }
 
@@ -248,7 +320,7 @@
       return;
     default:
       DCHECK(false);
-      RunCallback(/* start_regular_script = */ false);
+      OnStartDone(/* start_regular_script = */ false);
       return;
   }
 }
@@ -284,7 +356,7 @@
     } else {
       // Should never happen.
       DCHECK(false);
-      RunCallback(false);
+      OnStartDone(/* start_regular_script = */ false);
       return;
     }
   }
@@ -315,8 +387,14 @@
     Metrics::LiteScriptFinishedState state,
     std::unique_ptr<TriggerContext> trigger_context,
     base::Optional<TriggerScriptProto> trigger_script) {
+  // Delete the coordinator asynchronously, to give this notification time to
+  // end gracefully.
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&Starter::DeleteTriggerScriptCoordinator,
+                                weak_ptr_factory_.GetWeakPtr()));
+
   if (state != Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_SUCCEEDED) {
-    RunCallback(/* start_regular_script = */ false);
+    OnStartDone(/* start_regular_script = */ false);
     return;
   }
 
@@ -327,7 +405,7 @@
   // different metric for the result. We need to be careful to only run the
   // regular onboarding if necessary to avoid logging metrics more than once.
   if (platform_delegate_->GetOnboardingAccepted()) {
-    RunCallback(/* start_regular_script = */ true, trigger_script);
+    OnStartDone(/* start_regular_script = */ true, trigger_script);
     return;
   } else {
     MaybeShowOnboarding(trigger_script);
@@ -385,37 +463,35 @@
 
   if (result != OnboardingResult::ACCEPTED) {
     runtime_manager_->SetUIState(UIState::kNotShown);
-    RunCallback(/* start_regular_script = */ false);
+    OnStartDone(/* start_regular_script = */ false);
     return;
   }
 
   // Onboarding is the last step before regular startup.
   platform_delegate_->SetOnboardingAccepted(true);
   pending_trigger_context_->SetOnboardingShown(shown);
-  RunCallback(/* start_regular_script = */ true, trigger_script);
+  OnStartDone(/* start_regular_script = */ true, trigger_script);
 }
 
-void Starter::RunCallback(bool start_regular_script,
+void Starter::OnStartDone(bool start_regular_script,
                           base::Optional<TriggerScriptProto> trigger_script) {
-  DCHECK(pending_callback_);
   if (!start_regular_script) {
     // Catch-all to ensure that after a failed startup attempt we no longer
     // register as visible to runtime observers.
     runtime_manager_->SetUIState(UIState::kNotShown);
-
-    pending_trigger_context_ = nullptr;
-    std::move(pending_callback_)
-        .Run(/* start_regular_script = */ false, GURL(), nullptr,
-             base::nullopt);
+    pending_trigger_context_.reset();
     return;
   }
 
   auto startup_url =
       StartupUtil().ChooseStartupUrlForIntent(*pending_trigger_context_);
   DCHECK(startup_url.has_value());
-  std::move(pending_callback_)
-      .Run(/* start_regular_script = */ true, *startup_url,
-           std::move(pending_trigger_context_), trigger_script);
+  platform_delegate_->StartRegularScript(
+      *startup_url, std::move(pending_trigger_context_), trigger_script);
 }
 
-}  // namespace autofill_assistant
\ No newline at end of file
+void Starter::DeleteTriggerScriptCoordinator() {
+  trigger_script_coordinator_.reset();
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter.h b/components/autofill_assistant/browser/starter.h
index 3aff25a..ba51a96 100644
--- a/components/autofill_assistant/browser/starter.h
+++ b/components/autofill_assistant/browser/starter.h
@@ -7,12 +7,13 @@
 
 #include <memory>
 
-#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "components/autofill_assistant/browser/controller.h"
 #include "components/autofill_assistant/browser/metrics.h"
 #include "components/autofill_assistant/browser/public/runtime_manager_impl.h"
+#include "components/autofill_assistant/browser/starter_heuristic.h"
 #include "components/autofill_assistant/browser/starter_platform_delegate.h"
 #include "components/autofill_assistant/browser/startup_util.h"
 #include "components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h"
@@ -25,14 +26,6 @@
 // access platform-dependent features.
 class Starter : public content::WebContentsObserver {
  public:
-  // Note: parameters are only valid and not null if |start_regular_script| is
-  // true.
-  using StarterResultCallback = base::OnceCallback<void(
-      bool start_regular_script,
-      GURL url,
-      std::unique_ptr<TriggerContext> trigger_context,
-      const base::Optional<TriggerScriptProto>& trigger_script)>;
-
   explicit Starter(content::WebContents* web_contents,
                    StarterPlatformDelegate* platform_delegate,
                    ukm::UkmRecorder* ukm_recorder,
@@ -47,27 +40,36 @@
   //  - Install feature module if necessary
   //  - Run and wait for trigger script to finish if necessary
   //  - Show onboarding if necessary
-  //  - Invoke |callback| with the result. On success, the caller should start
-  // the regular script. TODO(mcarlen): client startup should also be in
-  // handled here, rather than in the caller.
+  //  - Request the platform_delegate to start the regular script.
+  // TODO(mcarlen): client startup should also be handled here, rather than in
+  // the platform_delegate.
   //
   // Only one call to |Start| can be processed at any time. If this method is
   // called before the previous call has finished, the previous call is
   // cancelled.
-  void Start(std::unique_ptr<TriggerContext> trigger_context,
-             StarterResultCallback callback);
+  void Start(std::unique_ptr<TriggerContext> trigger_context);
 
   // content::WebContentsObserver:
   void DidFinishNavigation(
       content::NavigationHandle* navigation_handle) override;
 
+  // Invoked when the tab interactability has changed.
+  void OnTabInteractabilityChanged(bool is_interactable);
+
   // Re-check settings. This may cancel ongoing startup requests if the required
   // settings are no longer enabled.
   void CheckSettings();
 
  private:
-  // Cancels the currently pending startup request, if any.
-  void CancelPendingStartup();
+  // Starts a flow for |url| if possible. Will fail (do nothing) if the feature
+  // is disabled or if there is already a pending startup.
+  void MaybeStartImplicitlyForUrl(const GURL& url);
+
+  // Cancels the currently pending startup request, if any. If a trigger script
+  // is currently running, this will record |state| as the reason for stopping.
+  // This will also hide any currently shown UI (such as a trigger script or the
+  // onboarding).
+  void CancelPendingStartup(Metrics::LiteScriptFinishedState state);
 
   // Installs the feature module if necessary, otherwise directly invokes
   // |OnFeatureModuleInstalled|.
@@ -100,11 +102,19 @@
                             bool shown,
                             OnboardingResult result);
 
-  // Internal helper to invoke the pending callback.
-  void RunCallback(
+  // Called at the end of each |Start| invocation.
+  void OnStartDone(
       bool start_regular_script,
       base::Optional<TriggerScriptProto> trigger_script = base::nullopt);
 
+  // Called when the heuristic result for |url| is available.
+  void OnHeuristicMatch(const GURL& url, bool result);
+
+  // Returns whether there is a currently pending call to |Start| or not.
+  bool IsStartupPending() const;
+
+  void DeleteTriggerScriptCoordinator();
+
   bool waiting_for_onboarding_ = false;
   bool is_custom_tab_ = false;
   StarterPlatformDelegate* platform_delegate_ = nullptr;
@@ -112,8 +122,8 @@
   base::WeakPtr<RuntimeManagerImpl> runtime_manager_;
   bool fetch_trigger_scripts_on_navigation_ = false;
   std::unique_ptr<TriggerContext> pending_trigger_context_;
-  StarterResultCallback pending_callback_;
   std::unique_ptr<TriggerScriptCoordinator> trigger_script_coordinator_;
+  const scoped_refptr<StarterHeuristic> starter_heuristic_;
   base::WeakPtrFactory<Starter> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill_assistant/browser/starter_heuristic.cc b/components/autofill_assistant/browser/starter_heuristic.cc
new file mode 100644
index 0000000..ec80770
--- /dev/null
+++ b/components/autofill_assistant/browser/starter_heuristic.cc
@@ -0,0 +1,35 @@
+// 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 "components/autofill_assistant/browser/starter_heuristic.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "third_party/re2/src/re2/re2.h"
+
+namespace autofill_assistant {
+
+const char kStubCartHeuristic[] = "cart";
+
+StarterHeuristic::StarterHeuristic() = default;
+StarterHeuristic::~StarterHeuristic() = default;
+
+bool StarterHeuristic::IsHeuristicMatch(const GURL& url) const {
+  const re2::RE2 re(kStubCartHeuristic);
+  // TODO(arbesser): implement the heuristic.
+  return re2::RE2::PartialMatch(url.spec(), re);
+}
+
+void StarterHeuristic::RunHeuristicAsync(
+    const GURL& url,
+    base::OnceCallback<void(bool result)> callback) const {
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+      base::BindOnce(&StarterHeuristic::IsHeuristicMatch,
+                     base::RetainedRef(this), url),
+      std::move(callback));
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_heuristic.h b/components/autofill_assistant/browser/starter_heuristic.h
new file mode 100644
index 0000000..6d8ec03
--- /dev/null
+++ b/components/autofill_assistant/browser/starter_heuristic.h
@@ -0,0 +1,42 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_H_
+
+#include "base/callback_forward.h"
+#include "base/memory/ref_counted.h"
+#include "url/gurl.h"
+
+namespace autofill_assistant {
+
+// Utility that implements a heuristic for autofill-assistant URLs.
+//
+// This class inherits from RefCountedThreadSafe to allow safe evaluation on
+// worker threads.
+class StarterHeuristic : public base::RefCountedThreadSafe<StarterHeuristic> {
+ public:
+  StarterHeuristic();
+  StarterHeuristic(const StarterHeuristic&) = delete;
+  StarterHeuristic& operator=(const StarterHeuristic&) = delete;
+
+  // Runs the heuristic against |url|. Returns true if a trigger script might be
+  // available for |url|. This method runs on a worker thread and notifies the
+  // caller via |callback| when done.
+  void RunHeuristicAsync(const GURL& url,
+                         base::OnceCallback<void(bool result)> callback) const;
+
+ private:
+  friend class base::RefCountedThreadSafe<StarterHeuristic>;
+  friend class StarterHeuristicTest;
+  ~StarterHeuristic();
+
+  // Runs the heuristic against |url|. Returns true if a trigger script might be
+  // available for |url|.
+  bool IsHeuristicMatch(const GURL& url) const;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_STARTER_HEURISTIC_H_
diff --git a/components/autofill_assistant/browser/starter_heuristic_unittest.cc b/components/autofill_assistant/browser/starter_heuristic_unittest.cc
new file mode 100644
index 0000000..0e1ac255
--- /dev/null
+++ b/components/autofill_assistant/browser/starter_heuristic_unittest.cc
@@ -0,0 +1,43 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/starter_heuristic.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+class StarterHeuristicTest : public testing::Test {
+ public:
+  StarterHeuristicTest()
+      : starter_heuristic_(base::MakeRefCounted<StarterHeuristic>()) {}
+
+  // Synchronous evaluation of the heuristic for easier testing.
+  bool IsHeuristicMatchForTest(const GURL& url) {
+    return starter_heuristic_->IsHeuristicMatch(url);
+  }
+
+ protected:
+  scoped_refptr<StarterHeuristic> starter_heuristic_;
+};
+
+TEST_F(StarterHeuristicTest, SmokeTest) {
+  EXPECT_TRUE(IsHeuristicMatchForTest(GURL("https://www.example.com/cart")));
+  EXPECT_FALSE(IsHeuristicMatchForTest(GURL("https://www.example.com")));
+}
+
+TEST_F(StarterHeuristicTest, RunHeuristicAsync) {
+  base::test::TaskEnvironment task_environment;
+  base::MockCallback<base::OnceCallback<void(bool result)>> callback;
+  EXPECT_CALL(callback, Run(true));
+  starter_heuristic_->RunHeuristicAsync(GURL("https://www.example.com/cart"),
+                                        callback.Get());
+  task_environment.RunUntilIdle();
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/starter_platform_delegate.h b/components/autofill_assistant/browser/starter_platform_delegate.h
index aff4d97b..d5003ac7 100644
--- a/components/autofill_assistant/browser/starter_platform_delegate.h
+++ b/components/autofill_assistant/browser/starter_platform_delegate.h
@@ -31,6 +31,12 @@
   virtual std::unique_ptr<ServiceRequestSender>
   GetTriggerScriptRequestSenderToInject() = 0;
 
+  // Requests the platform delegate to start the regular script.
+  virtual void StartRegularScript(
+      GURL url,
+      std::unique_ptr<TriggerContext> trigger_context,
+      const base::Optional<TriggerScriptProto>& trigger_script) = 0;
+
   // Access to the login manager.
   virtual WebsiteLoginManager* GetWebsiteLoginManager() const = 0;
   // Returns the channel for the installation (canary, dev, beta, stable).
diff --git a/components/autofill_assistant/browser/starter_unittest.cc b/components/autofill_assistant/browser/starter_unittest.cc
index fe26ae0..01509019 100644
--- a/components/autofill_assistant/browser/starter_unittest.cc
+++ b/components/autofill_assistant/browser/starter_unittest.cc
@@ -47,7 +47,7 @@
 using ::testing::WithArg;
 using ::testing::WithArgs;
 
-const char kExampleUrl[] = "https://www.example.com";
+const char kExampleDeeplink[] = "https://www.example.com";
 
 class StarterTest : public content::RenderViewHostTestHarness {
  public:
@@ -55,27 +55,9 @@
     RenderViewHostTestHarness::SetUp();
     ukm::InitializeSourceUrlRecorderForWebContents(web_contents());
     content::WebContentsTester::For(web_contents())
-        ->NavigateAndCommit(GURL(kExampleUrl));
-    auto mock_trigger_script_ui_delegate =
-        std::make_unique<NiceMock<MockTriggerScriptUiDelegate>>();
-    mock_trigger_script_ui_delegate_ = mock_trigger_script_ui_delegate.get();
-    fake_platform_delegate_.trigger_script_ui_delegate_ =
-        std::move(mock_trigger_script_ui_delegate);
-
-    ON_CALL(*mock_trigger_script_ui_delegate_, Attach)
-        .WillByDefault(WithArg<0>(
-            [&](TriggerScriptCoordinator* trigger_script_coordinator) {
-              trigger_script_coordinator_ = trigger_script_coordinator;
-            }));
-    ON_CALL(*mock_trigger_script_ui_delegate_, Detach).WillByDefault([&]() {
-      trigger_script_coordinator_ = nullptr;
-    });
-    auto mock_trigger_script_service_request_sender =
-        std::make_unique<NiceMock<MockServiceRequestSender>>();
-    mock_trigger_script_service_request_sender_ =
-        mock_trigger_script_service_request_sender.get();
-    fake_platform_delegate_.trigger_script_request_sender_for_test_ =
-        std::move(mock_trigger_script_service_request_sender);
+        ->NavigateAndCommit(GURL(kExampleDeeplink));
+    PrepareTriggerScriptUiDelegate();
+    PrepareTriggerScriptRequestSender();
     fake_platform_delegate_.website_login_manager_ =
         &mock_website_login_manager_;
     ON_CALL(mock_website_login_manager_, OnGetLoginsForUrl)
@@ -112,15 +94,11 @@
     fake_platform_delegate_.onboarding_accepted_ = true;
   }
 
-  // Returns a base64-encoded GetTriggerScriptsResponseProto containing a single
-  // trigger script without any trigger conditions. As such, it will be shown
-  // immediately upon startup.
+  // Returns a base64-encoded trigger script response, as created by
+  // |CreateTriggerScriptResponseForTest|.
   std::string CreateBase64TriggerScriptResponseForTest() {
-    GetTriggerScriptsResponseProto get_trigger_scripts_response;
-    get_trigger_scripts_response.add_trigger_scripts();
-    std::string serialized_get_trigger_scripts_response;
-    get_trigger_scripts_response.SerializeToString(
-        &serialized_get_trigger_scripts_response);
+    std::string serialized_get_trigger_scripts_response =
+        CreateTriggerScriptResponseForTest();
     std::string base64_get_trigger_scripts_response;
     base::Base64UrlEncode(serialized_get_trigger_scripts_response,
                           base::Base64UrlEncodePolicy::INCLUDE_PADDING,
@@ -128,17 +106,29 @@
     return base64_get_trigger_scripts_response;
   }
 
+  // Returns a serialized GetTriggerScriptsResponseProto containing a single
+  // trigger script without any trigger conditions. As such, it will be shown
+  // immediately upon startup.
+  std::string CreateTriggerScriptResponseForTest() {
+    GetTriggerScriptsResponseProto get_trigger_scripts_response;
+    get_trigger_scripts_response.add_trigger_scripts();
+    std::string serialized_get_trigger_scripts_response;
+    get_trigger_scripts_response.SerializeToString(
+        &serialized_get_trigger_scripts_response);
+    return serialized_get_trigger_scripts_response;
+  }
+
   // Returns true if a specific |state| was recorded for |entry_name| and
   // |metric_name|.
   bool RecordedUkmMetric(base::StringPiece entry_name,
                          base::StringPiece metric_name,
-                         int64_t expected_state) {
+                         int64_t expected_state,
+                         const GURL& source_url) {
     auto entries = ukm_recorder_.GetEntriesByName(entry_name);
     if (entries.size() != 1) {
       return false;
     }
-    ukm_recorder_.ExpectEntrySourceHasUrl(
-        entries[0], web_contents()->GetLastCommittedURL());
+    ukm_recorder_.ExpectEntrySourceHasUrl(entries[0], source_url);
     const int64_t* actual_state =
         ukm_recorder_.GetEntryMetric(entries[0], metric_name);
     return actual_state != nullptr && *actual_state == expected_state;
@@ -149,20 +139,26 @@
     return !ukm_recorder_.GetEntriesByName(entry_name).empty();
   }
 
-  bool UkmLiteScriptStarted(Metrics::LiteScriptStarted state) {
+  bool UkmLiteScriptStarted(Metrics::LiteScriptStarted state,
+                            const GURL& source_url = GURL(kExampleDeeplink)) {
     return RecordedUkmMetric("AutofillAssistant.LiteScriptStarted",
-                             "LiteScriptStarted", static_cast<int64_t>(state));
+                             "LiteScriptStarted", static_cast<int64_t>(state),
+                             source_url);
   }
 
-  bool UkmLiteScriptFinished(Metrics::LiteScriptFinishedState state) {
+  bool UkmLiteScriptFinished(Metrics::LiteScriptFinishedState state,
+                             const GURL& source_url = GURL(kExampleDeeplink)) {
     return RecordedUkmMetric("AutofillAssistant.LiteScriptFinished",
-                             "LiteScriptFinished", static_cast<int64_t>(state));
+                             "LiteScriptFinished", static_cast<int64_t>(state),
+                             source_url);
   }
 
-  bool UkmLiteScriptOnboarding(Metrics::LiteScriptOnboarding result) {
+  bool UkmLiteScriptOnboarding(
+      Metrics::LiteScriptOnboarding result,
+      const GURL& source_url = GURL(kExampleDeeplink)) {
     return RecordedUkmMetric("AutofillAssistant.LiteScriptOnboarding",
                              "LiteScriptOnboarding",
-                             static_cast<int64_t>(result));
+                             static_cast<int64_t>(result), source_url);
   }
 
   bool UkmLiteScriptStarted() {
@@ -192,6 +188,38 @@
     simulator->Commit();
   }
 
+  // Each request sender is only good for one trigger script. This call will
+  // create a new mock and prepare it to be used in the next call.
+  void PrepareTriggerScriptRequestSender() {
+    auto mock_trigger_script_service_request_sender =
+        std::make_unique<NiceMock<MockServiceRequestSender>>();
+    mock_trigger_script_service_request_sender_ =
+        mock_trigger_script_service_request_sender.get();
+    fake_platform_delegate_.trigger_script_request_sender_for_test_ =
+        std::move(mock_trigger_script_service_request_sender);
+  }
+
+  // Each trigger script UI delegate is only good for one trigger script and
+  // must be prepared anew if a test needs to show more than one trigger script.
+  void PrepareTriggerScriptUiDelegate() {
+    auto mock_trigger_script_ui_delegate =
+        std::make_unique<NiceMock<MockTriggerScriptUiDelegate>>();
+    mock_trigger_script_ui_delegate_ = mock_trigger_script_ui_delegate.get();
+    fake_platform_delegate_.trigger_script_ui_delegate_ =
+        std::move(mock_trigger_script_ui_delegate);
+    fake_platform_delegate_.start_regular_script_callback_ =
+        mock_start_regular_script_callback_.Get();
+
+    ON_CALL(*mock_trigger_script_ui_delegate_, Attach)
+        .WillByDefault(WithArg<0>(
+            [&](TriggerScriptCoordinator* trigger_script_coordinator) {
+              trigger_script_coordinator_ = trigger_script_coordinator;
+            }));
+    ON_CALL(*mock_trigger_script_ui_delegate_, Detach).WillByDefault([&]() {
+      trigger_script_coordinator_ = nullptr;
+    });
+  }
+
   NiceMock<MockTriggerScriptUiDelegate>* mock_trigger_script_ui_delegate_ =
       nullptr;
   NiceMock<MockServiceRequestSender>*
@@ -204,18 +232,21 @@
   MockRuntimeManager mock_runtime_manager_;
   std::unique_ptr<Starter> starter_;
   base::HistogramTester histogram_tester_;
-  base::MockCallback<Starter::StarterResultCallback> mock_callback_;
+  base::MockCallback<base::OnceCallback<void(
+      GURL url,
+      std::unique_ptr<TriggerContext> trigger_context,
+      const base::Optional<TriggerScriptProto>& trigger_script)>>
+      mock_start_regular_script_callback_;
 };
 
 TEST_F(StarterTest, RegularScriptFailsWithoutInitialUrl) {
   std::map<std::string, std::string> params = {{"ENABLED", "true"},
                                                {"START_IMMEDIATELY", "true"}};
   TriggerContext::Options options;
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(params), options),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(params), options));
 
   EXPECT_FALSE(UkmLiteScriptStarted());
   EXPECT_FALSE(UkmLiteScriptFinished());
@@ -232,11 +263,10 @@
       {"START_IMMEDIATELY", "false"},
       {"TRIGGER_SCRIPTS_BASE64", "abc"}};
   TriggerContext::Options options;
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(params), options),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(params), options));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_NO_INITIAL_URL));
@@ -253,12 +283,11 @@
   std::map<std::string, std::string> params = {
       {"START_IMMEDIATELY", "false"}, {"TRIGGER_SCRIPTS_BASE64", "abc"}};
   TriggerContext::Options options;
-  options.initial_url = "https://www.example.com";
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  options.initial_url = kExampleDeeplink;
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(params), options),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(params), options));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_MANDATORY_PARAMETER_MISSING));
@@ -280,11 +309,10 @@
   auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
   scoped_feature_list->InitAndDisableFeature(
       features::kAutofillAssistantProactiveHelp);
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(params), options),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(params), options));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_FEATURE_DISABLED));
@@ -304,16 +332,14 @@
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
   TriggerContext::Options options;
   options.initial_url = "https://redirect.com/to/www/example/com";
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ true,
-                                  GURL("https://www.example.com"), _, _))
-      .WillOnce(WithArg<2>([](std::unique_ptr<TriggerContext> trigger_context) {
+  EXPECT_CALL(mock_start_regular_script_callback_,
+              Run(GURL(kExampleDeeplink), _, _))
+      .WillOnce(WithArg<1>([](std::unique_ptr<TriggerContext> trigger_context) {
         EXPECT_THAT(trigger_context->GetOnboardingShown(), Eq(false));
       }));
 
-  starter_->Start(
-      std::make_unique<TriggerContext>(
-          std::make_unique<ScriptParameters>(script_parameters), options),
-      mock_callback_.Get());
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters), options));
 
   EXPECT_THAT(fake_platform_delegate_.num_install_feature_module_called_,
               Eq(0));
@@ -339,16 +365,14 @@
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
   TriggerContext::Options options;
   options.initial_url = "https://redirect.com/to/www/example/com";
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ true,
-                                  GURL("https://www.example.com"), _, _))
-      .WillOnce(WithArg<2>([](std::unique_ptr<TriggerContext> trigger_context) {
+  EXPECT_CALL(mock_start_regular_script_callback_,
+              Run(GURL(kExampleDeeplink), _, _))
+      .WillOnce(WithArg<1>([](std::unique_ptr<TriggerContext> trigger_context) {
         EXPECT_THAT(trigger_context->GetOnboardingShown(), Eq(true));
       }));
 
-  starter_->Start(
-      std::make_unique<TriggerContext>(
-          std::make_unique<ScriptParameters>(script_parameters), options),
-      mock_callback_.Get());
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters), options));
 
   EXPECT_THAT(fake_platform_delegate_.num_install_feature_module_called_,
               Eq(1));
@@ -369,37 +393,22 @@
 
 TEST_F(StarterTest, ForceOnboardingFlagForReturningUsersSucceeds) {
   SetupPlatformDelegateForReturningUser();
-  fake_platform_delegate_.show_onboarding_result_shown_ = true;
-  fake_platform_delegate_.show_onboarding_result_ = OnboardingResult::ACCEPTED;
+  base::MockCallback<base::OnceCallback<void(
+      base::OnceCallback<void(bool, OnboardingResult)>)>>
+      mock_onboarding_callback;
+  fake_platform_delegate_.on_show_onboarding_callback_ =
+      mock_onboarding_callback.Get();
+
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       switches::kAutofillAssistantForceOnboarding, "true");
   std::map<std::string, std::string> script_parameters = {
       {"ENABLED", "true"},
       {"START_IMMEDIATELY", "true"},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
-  EXPECT_CALL(
-      mock_callback_,
-      Run(/* start_regular_script = */ true, GURL("https://www.example.com"),
-          Pointee(Property(&TriggerContext::GetOnboardingShown, true)), _));
-
+  EXPECT_CALL(mock_onboarding_callback, Run);
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options()),
-                  mock_callback_.Get());
-
-  EXPECT_THAT(fake_platform_delegate_.num_install_feature_module_called_,
-              Eq(0));
-  EXPECT_THAT(fake_platform_delegate_.num_show_onboarding_called_, Eq(1));
-  EXPECT_FALSE(UkmLiteScriptStarted());
-  EXPECT_FALSE(UkmLiteScriptFinished());
-  EXPECT_FALSE(UkmLiteScriptOnboarding());
-  histogram_tester_.ExpectUniqueSample(
-      "Android.AutofillAssistant.FeatureModuleInstallation",
-      Metrics::FeatureModuleInstallation::DFM_ALREADY_INSTALLED, 1u);
-  histogram_tester_.ExpectBucketCount("Android.AutofillAssistant.OnBoarding",
-                                      Metrics::OnBoarding::OB_ACCEPTED, 1u);
-  histogram_tester_.ExpectBucketCount("Android.AutofillAssistant.OnBoarding",
-                                      Metrics::OnBoarding::OB_SHOWN, 1u);
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options()));
 }
 
 TEST_F(StarterTest, ForceFirstTimeUserExperienceForReturningUser) {
@@ -440,9 +449,8 @@
   EXPECT_CALL(*mock_trigger_script_ui_delegate_,
               ShowTriggerScript(first_time_user_script->user_interface()));
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options()),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options()));
 }
 
 TEST_F(StarterTest, RegularStartupFailsIfDfmInstallationFails) {
@@ -455,12 +463,10 @@
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
   TriggerContext::Options options;
   options.initial_url = "https://redirect.com/to/www/example/com";
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
-  starter_->Start(
-      std::make_unique<TriggerContext>(
-          std::make_unique<ScriptParameters>(script_parameters), options),
-      mock_callback_.Get());
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters), options));
 
   EXPECT_THAT(fake_platform_delegate_.num_install_feature_module_called_,
               Eq(1));
@@ -488,12 +494,10 @@
       {"INTENT", "SHOPPING_ASSISTED_CHECKOUT"}};
   TriggerContext::Options options;
   options.initial_url = "https://redirect.com/to/www/example/com";
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
-  starter_->Start(
-      std::make_unique<TriggerContext>(
-          std::make_unique<ScriptParameters>(script_parameters), options),
-      mock_callback_.Get());
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters), options));
 
   EXPECT_THAT(fake_platform_delegate_.GetOnboardingAccepted(), Eq(false));
   EXPECT_FALSE(UkmLiteScriptStarted());
@@ -519,12 +523,11 @@
   EXPECT_CALL(*mock_trigger_script_ui_delegate_, Attach).Times(0);
   EXPECT_CALL(*mock_trigger_script_service_request_sender_, OnSendRequest)
       .Times(0);
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options()),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options()));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_PROACTIVE_TRIGGERING_DISABLED));
@@ -547,12 +550,11 @@
   EXPECT_CALL(*mock_trigger_script_ui_delegate_, Attach).Times(0);
   EXPECT_CALL(*mock_trigger_script_service_request_sender_, OnSendRequest)
       .Times(0);
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options()),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options()));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_PROACTIVE_TRIGGERING_DISABLED));
@@ -565,15 +567,6 @@
 }
 
 TEST_F(StarterTest, RpcTriggerScriptSucceeds) {
-  // A trigger script without trigger conditions will be shown immediately.
-  GetTriggerScriptsResponseProto get_trigger_scripts_response;
-  get_trigger_scripts_response.add_trigger_scripts()
-      ->mutable_user_interface()
-      ->set_status_message("trigger script");
-  std::string serialized_get_trigger_scripts_response;
-  get_trigger_scripts_response.SerializeToString(
-      &serialized_get_trigger_scripts_response);
-
   SetupPlatformDelegateForFirstTimeUser();
   fake_platform_delegate_.feature_module_installed_ = true;
   std::map<std::string, std::string> script_parameters = {
@@ -602,18 +595,18 @@
             ASSERT_TRUE(request.ParseFromString(request_body));
             EXPECT_THAT(request.url(), Eq(GURL("https://www.example.com")));
             std::move(callback).Run(net::HTTP_OK,
-                                    serialized_get_trigger_scripts_response);
+                                    CreateTriggerScriptResponseForTest());
           }));
-  EXPECT_CALL(
-      mock_callback_,
-      Run(/* start_regular_script = */ true, GURL("https://www.example.com"),
-          Pointee(Property(&TriggerContext::GetOnboardingShown, true)),
-          Optional(get_trigger_scripts_response.trigger_scripts(0))));
+  GetTriggerScriptsResponseProto get_trigger_scripts_response;
+  get_trigger_scripts_response.ParseFromString(
+      CreateTriggerScriptResponseForTest());
+  EXPECT_CALL(mock_start_regular_script_callback_,
+              Run(GURL(kExampleDeeplink),
+                  Pointee(Property(&TriggerContext::GetOnboardingShown, true)),
+                  Optional(get_trigger_scripts_response.trigger_scripts(0))));
 
-  starter_->Start(
-      std::make_unique<TriggerContext>(
-          std::make_unique<ScriptParameters>(script_parameters), options),
-      mock_callback_.Get());
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters), options));
 
   EXPECT_THAT(fake_platform_delegate_.num_show_onboarding_called_, Eq(1));
   EXPECT_TRUE(UkmLiteScriptStarted(
@@ -640,12 +633,11 @@
       {"TRIGGER_SCRIPTS_BASE64", "#invalid_hashtag"},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
   EXPECT_CALL(*mock_trigger_script_ui_delegate_, Attach).Times(0);
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options()),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options()));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_RETURNING_USER));
@@ -671,12 +663,11 @@
       {"TRIGGER_SCRIPTS_BASE64", CreateBase64TriggerScriptResponseForTest()},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
   EXPECT_CALL(*mock_trigger_script_ui_delegate_, Attach).Times(0);
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
 
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options()),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options()));
 
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_PROACTIVE_TRIGGERING_DISABLED));
@@ -712,16 +703,13 @@
         trigger_script_coordinator_->PerformTriggerScriptAction(
             TriggerScriptProto::ACCEPT);
       });
-  EXPECT_CALL(
-      mock_callback_,
-      Run(/* start_regular_script = */ true, GURL("https://www.example.com"),
-          Pointee(Property(&TriggerContext::GetOnboardingShown, true)),
-          testing::Ne(base::nullopt)));
+  EXPECT_CALL(mock_start_regular_script_callback_,
+              Run(GURL(kExampleDeeplink),
+                  Pointee(Property(&TriggerContext::GetOnboardingShown, true)),
+                  testing::Ne(base::nullopt)));
 
-  starter_->Start(
-      std::make_unique<TriggerContext>(
-          std::make_unique<ScriptParameters>(script_parameters), options),
-      mock_callback_.Get());
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters), options));
 
   EXPECT_THAT(fake_platform_delegate_.num_show_onboarding_called_, Eq(1));
   EXPECT_TRUE(UkmLiteScriptStarted(
@@ -749,15 +737,46 @@
       {"TRIGGER_SCRIPTS_BASE64", CreateBase64TriggerScriptResponseForTest()},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
 
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
   EXPECT_CALL(*mock_trigger_script_ui_delegate_, ShowTriggerScript);
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options{}),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
 
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  EXPECT_CALL(*mock_trigger_script_ui_delegate_, HideTriggerScript);
   fake_platform_delegate_.is_custom_tab_ = false;
   starter_->CheckSettings();
+  EXPECT_TRUE(UkmLiteScriptStarted(
+      Metrics::LiteScriptStarted::LITE_SCRIPT_RETURNING_USER));
+  EXPECT_TRUE(UkmLiteScriptFinished(
+      Metrics::LiteScriptFinishedState::LITE_SCRIPT_CCT_TO_TAB_NOT_SUPPORTED));
+  EXPECT_FALSE(UkmLiteScriptOnboarding());
+}
+
+TEST_F(StarterTest, CancelPendingTriggerScriptWhenHandlingNewStartupRequest) {
+  SetupPlatformDelegateForReturningUser();
+  fake_platform_delegate_.trigger_script_request_sender_for_test_ = nullptr;
+  mock_trigger_script_service_request_sender_ = nullptr;
+
+  std::map<std::string, std::string> script_parameters = {
+      {"ENABLED", "true"},
+      {"START_IMMEDIATELY", "false"},
+      {"TRIGGER_SCRIPTS_BASE64", CreateBase64TriggerScriptResponseForTest()},
+      {"ORIGINAL_DEEPLINK", kExampleDeeplink}};
+
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
+  EXPECT_CALL(*mock_trigger_script_ui_delegate_, ShowTriggerScript);
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
+
+  EXPECT_CALL(*mock_trigger_script_ui_delegate_, HideTriggerScript);
+  PrepareTriggerScriptUiDelegate();
+  starter_->Start(std::make_unique<TriggerContext>(
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
+  EXPECT_TRUE(UkmLiteScriptFinished(
+      Metrics::LiteScriptFinishedState::LITE_SCRIPT_CANCELED));
 }
 
 TEST_F(StarterTest, RegularStartupFailsIfNavigationDuringOnboarding) {
@@ -767,19 +786,17 @@
   fake_platform_delegate_.on_show_onboarding_callback_ =
       base::DoNothing::Once<base::OnceCallback<void(bool, OnboardingResult)>>();
 
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
   std::map<std::string, std::string> script_parameters = {
       {"ENABLED", "true"},
       {"START_IMMEDIATELY", "true"},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options{}),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
 
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
   content::WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.different.com"));
-
   EXPECT_FALSE(UkmLiteScriptStarted());
   EXPECT_FALSE(UkmLiteScriptFinished());
   EXPECT_FALSE(UkmLiteScriptOnboarding());
@@ -812,12 +829,14 @@
         trigger_script_coordinator_->PerformTriggerScriptAction(
             TriggerScriptProto::ACCEPT);
       });
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options{}),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
 
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL("https://www.different.com"));
+
   EXPECT_TRUE(UkmLiteScriptStarted(
       Metrics::LiteScriptStarted::LITE_SCRIPT_FIRST_TIME_USER));
 
@@ -826,10 +845,12 @@
   // TODO(b/185476714): Fix lite script metrics to record for ORIGINAL_DEEPLINK
   // instead of the current URL.
   EXPECT_TRUE(UkmLiteScriptFinished(
-      Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_FAILED_NAVIGATE));
+      Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_FAILED_NAVIGATE,
+      GURL("https://www.different.com")));
   EXPECT_TRUE(UkmLiteScriptOnboarding(
       Metrics::LiteScriptOnboarding::
-          LITE_SCRIPT_ONBOARDING_SEEN_AND_INTERRUPTED_BY_NAVIGATION));
+          LITE_SCRIPT_ONBOARDING_SEEN_AND_INTERRUPTED_BY_NAVIGATION,
+      GURL("https://www.different.com")));
   histogram_tester_.ExpectUniqueSample(
       "Android.AutofillAssistant.FeatureModuleInstallation",
       Metrics::FeatureModuleInstallation::DFM_ALREADY_INSTALLED, 1u);
@@ -848,21 +869,19 @@
       {"ENABLED", "true"},
       {"START_IMMEDIATELY", "true"},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options{}),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
 
   // Expect that the onboarding is not interrupted by a redirect to the
   // ORIGINAL_DEEPLINK.
-  EXPECT_CALL(mock_callback_, Run).Times(0);
   SimulateRedirectToUrl(GURL("https://www.example.com"),
                         GURL("http://redirect.example.com"));
   histogram_tester_.ExpectTotalCount("Android.AutofillAssistant.OnBoarding",
                                      0u);
 
   // Redirecting to a different URL will cancel the onboarding.
-  EXPECT_CALL(mock_callback_, Run(/* start_regular_script = */ false, _, _, _));
   SimulateRedirectToUrl(GURL("https://www.different.com"),
                         GURL("http://redirect.example.com"));
 
@@ -894,11 +913,10 @@
       {"ENABLED", "true"},
       {"START_IMMEDIATELY", "true"},
       {"ORIGINAL_DEEPLINK", "https://www.example.com"}};
-  EXPECT_CALL(mock_callback_, Run).Times(0);
+  EXPECT_CALL(mock_start_regular_script_callback_, Run).Times(0);
   starter_->Start(std::make_unique<TriggerContext>(
-                      std::make_unique<ScriptParameters>(script_parameters),
-                      TriggerContext::Options{}),
-                  mock_callback_.Get());
+      std::make_unique<ScriptParameters>(script_parameters),
+      TriggerContext::Options{}));
 
   EXPECT_FALSE(UkmLiteScriptStarted());
   EXPECT_FALSE(UkmLiteScriptFinished());
@@ -910,5 +928,154 @@
                                      0u);
 }
 
+TEST_F(StarterTest, ImplicitStartupOnSupportedDomain) {
+  SetupPlatformDelegateForReturningUser();
+  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list->InitAndEnableFeature(
+      features::kAutofillAssistantInChromeTriggering);
+  starter_->CheckSettings();
+
+  EXPECT_CALL(*mock_trigger_script_service_request_sender_,
+              OnSendRequest(
+                  GURL("https://automate-pa.googleapis.com/v1/triggers"), _, _))
+      .WillOnce(
+          WithArgs<1, 2>([&](const std::string& request_body,
+                             ServiceRequestSender::ResponseCallback& callback) {
+            GetTriggerScriptsRequestProto request;
+            ASSERT_TRUE(request.ParseFromString(request_body));
+            EXPECT_THAT(request.url(),
+                        Eq(GURL("https://www.some-website.com/cart")));
+            std::move(callback).Run(net::HTTP_OK,
+                                    CreateTriggerScriptResponseForTest());
+          }));
+  EXPECT_CALL(*mock_trigger_script_ui_delegate_, ShowTriggerScript)
+      .WillOnce([&]() {
+        ASSERT_TRUE(trigger_script_coordinator_ != nullptr);
+        trigger_script_coordinator_->PerformTriggerScriptAction(
+            TriggerScriptProto::ACCEPT);
+      });
+  EXPECT_CALL(mock_start_regular_script_callback_,
+              Run(GURL("https://www.some-website.com/cart"),
+                  Pointee(Property(&TriggerContext::GetOnboardingShown, false)),
+                  testing::Ne(base::nullopt)));
+
+  // Implicit startup by navigating to an autofill-assistant-enabled site.
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL("https://www.some-website.com/cart"));
+  task_environment()->RunUntilIdle();
+
+  EXPECT_TRUE(UkmLiteScriptStarted(
+      Metrics::LiteScriptStarted::LITE_SCRIPT_RETURNING_USER,
+      GURL("https://www.some-website.com/cart")));
+  EXPECT_TRUE(UkmLiteScriptFinished(
+      Metrics::LiteScriptFinishedState::LITE_SCRIPT_PROMPT_SUCCEEDED,
+      GURL("https://www.some-website.com/cart")));
+  EXPECT_TRUE(UkmLiteScriptOnboarding(
+      Metrics::LiteScriptOnboarding::LITE_SCRIPT_ONBOARDING_ALREADY_ACCEPTED,
+      GURL("https://www.some-website.com/cart")));
+  histogram_tester_.ExpectUniqueSample(
+      "Android.AutofillAssistant.FeatureModuleInstallation",
+      Metrics::FeatureModuleInstallation::DFM_ALREADY_INSTALLED, 1u);
+  histogram_tester_.ExpectTotalCount("Android.AutofillAssistant.OnBoarding",
+                                     0u);
+}
+
+TEST_F(StarterTest, DoNotStartImplicitlyIfSettingDisabled) {
+  SetupPlatformDelegateForReturningUser();
+  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list->InitAndEnableFeature(
+      features::kAutofillAssistantInChromeTriggering);
+  fake_platform_delegate_.proactive_help_enabled_ = false;
+  starter_->CheckSettings();
+
+  EXPECT_CALL(*mock_trigger_script_service_request_sender_, OnSendRequest)
+      .Times(0);
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL("https://www.some-website.com/cart"));
+  task_environment()->RunUntilIdle();
+}
+
+TEST_F(StarterTest, ImplicitStartupOnCurrentUrlAfterSettingEnabled) {
+  SetupPlatformDelegateForReturningUser();
+  fake_platform_delegate_.proactive_help_enabled_ = false;
+  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list->InitAndEnableFeature(
+      features::kAutofillAssistantInChromeTriggering);
+  starter_->CheckSettings();
+
+  EXPECT_CALL(*mock_trigger_script_service_request_sender_, OnSendRequest)
+      .Times(0);
+  content::WebContentsTester::For(web_contents())
+      ->NavigateAndCommit(GURL("https://www.some-website.com/cart"));
+
+  EXPECT_CALL(*mock_trigger_script_service_request_sender_,
+              OnSendRequest(
+                  GURL("https://automate-pa.googleapis.com/v1/triggers"), _, _))
+      .WillOnce(RunOnceCallback<2>(net::HTTP_OK,
+                                   CreateTriggerScriptResponseForTest()));
+  EXPECT_CALL(*mock_trigger_script_ui_delegate_, ShowTriggerScript).Times(1);
+
+  // Implicit startup by enabling proactive help while already on an
+  // autofill-assistant-enabled site.
+  fake_platform_delegate_.proactive_help_enabled_ = true;
+  starter_->CheckSettings();
+  task_environment()->RunUntilIdle();
+
+  EXPECT_TRUE(UkmLiteScriptStarted(
+      Metrics::LiteScriptStarted::LITE_SCRIPT_RETURNING_USER,
+      GURL("https://www.some-website.com/cart")));
+  EXPECT_FALSE(UkmLiteScriptFinished());
+  EXPECT_FALSE(UkmLiteScriptOnboarding());
+  histogram_tester_.ExpectUniqueSample(
+      "Android.AutofillAssistant.FeatureModuleInstallation",
+      Metrics::FeatureModuleInstallation::DFM_ALREADY_INSTALLED, 1u);
+  histogram_tester_.ExpectTotalCount("Android.AutofillAssistant.OnBoarding",
+                                     0u);
+}
+
+TEST(MultipleStarterTest, HeuristicUsedByMultipleInstances) {
+  content::BrowserTaskEnvironment task_environment;
+  content::RenderViewHostTestEnabler rvh_test_enabler;
+  content::TestBrowserContext browser_context;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  FakeStarterPlatformDelegate fake_platform_delegate_01;
+  FakeStarterPlatformDelegate fake_platform_delegate_02;
+  MockRuntimeManager mock_runtime_manager;
+
+  auto web_contents_01 = content::WebContentsTester::CreateTestWebContents(
+      &browser_context, nullptr);
+  ukm::InitializeSourceUrlRecorderForWebContents(web_contents_01.get());
+  auto web_contents_02 = content::WebContentsTester::CreateTestWebContents(
+      &browser_context, nullptr);
+  ukm::InitializeSourceUrlRecorderForWebContents(web_contents_02.get());
+
+  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list->InitAndEnableFeature(
+      features::kAutofillAssistantInChromeTriggering);
+  Starter starter_01(web_contents_01.get(), &fake_platform_delegate_01,
+                     &ukm_recorder, mock_runtime_manager.GetWeakPtr());
+  Starter starter_02(web_contents_02.get(), &fake_platform_delegate_02,
+                     &ukm_recorder, mock_runtime_manager.GetWeakPtr());
+
+  auto service_request_sender_01 =
+      std::make_unique<NiceMock<MockServiceRequestSender>>();
+  auto* service_request_sender_01_ptr = service_request_sender_01.get();
+  fake_platform_delegate_01.trigger_script_request_sender_for_test_ =
+      std::move(service_request_sender_01);
+  auto service_request_sender_02 =
+      std::make_unique<NiceMock<MockServiceRequestSender>>();
+  auto* service_request_sender_02_ptr = service_request_sender_02.get();
+  fake_platform_delegate_02.trigger_script_request_sender_for_test_ =
+      std::move(service_request_sender_02);
+
+  EXPECT_CALL(*service_request_sender_01_ptr, OnSendRequest).Times(1);
+  EXPECT_CALL(*service_request_sender_02_ptr, OnSendRequest).Times(1);
+  content::WebContentsTester::For(web_contents_01.get())
+      ->NavigateAndCommit(GURL("https://www.some-website.com/cart"));
+  content::WebContentsTester::For(web_contents_02.get())
+      ->NavigateAndCommit(GURL("https://www.some-other-website.com/cart"));
+  task_environment.RunUntilIdle();
+}
+
 }  // namespace
-}  // namespace autofill_assistant
\ No newline at end of file
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
index ae2db88..5a25a2f 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.cc
@@ -349,6 +349,10 @@
   return *trigger_context_;
 }
 
+const GURL& TriggerScriptCoordinator::GetDeeplink() const {
+  return deeplink_url_;
+}
+
 void TriggerScriptCoordinator::OnEffectiveVisibilityChanged() {
   bool visible = web_contents_visible_ && web_contents_interactable_;
   if (visible) {
diff --git a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h
index 0ceec42..fb3e878 100644
--- a/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h
+++ b/components/autofill_assistant/browser/trigger_scripts/trigger_script_coordinator.h
@@ -83,6 +83,11 @@
                  std::unique_ptr<TriggerContext> trigger_context,
                  base::Optional<TriggerScriptProto> trigger_script)> callback);
 
+  // Stops the currently running trigger script. Hides any currently shown UI
+  // (both trigger script UI and onboarding, if applicable) and returns |state|
+  // as the reason for stopping in the pending callback.
+  void Stop(Metrics::LiteScriptFinishedState state);
+
   // Performs |action|. This is usually invoked by the UI as a result of user
   // interactions.
   void PerformTriggerScriptAction(
@@ -111,6 +116,9 @@
   // Const access to the trigger context associated with this coordinator.
   const TriggerContext& GetTriggerContext() const;
 
+  // Returns the deeplink that this coordinator was started on.
+  const GURL& GetDeeplink() const;
+
  private:
   friend class TriggerScriptCoordinatorTest;
 
@@ -127,7 +135,6 @@
   void CheckDynamicTriggerConditions();
   void OnDynamicTriggerConditionsEvaluated(bool is_out_of_schedule);
   void OnGetTriggerScripts(int http_status, const std::string& response);
-  void Stop(Metrics::LiteScriptFinishedState state);
   GURL GetCurrentURL() const;
   void OnEffectiveVisibilityChanged();
   void OnOnboardingFinished(bool onboardingShown, OnboardingResult result);
diff --git a/components/autofill_assistant/browser/url_utils.cc b/components/autofill_assistant/browser/url_utils.cc
index ebf070b..677ae8b8 100644
--- a/components/autofill_assistant/browser/url_utils.cc
+++ b/components/autofill_assistant/browser/url_utils.cc
@@ -18,12 +18,6 @@
                         base::CompareCase::INSENSITIVE_ASCII);
 }
 
-std::string GetRegistryControlledDomain(const GURL& signon_realm) {
-  return net::registry_controlled_domains::GetDomainAndRegistry(
-      signon_realm,
-      net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
-}
-
 }  // namespace
 
 namespace autofill_assistant {
@@ -55,9 +49,8 @@
     return true;
   }
 
-  auto domain1 = GetRegistryControlledDomain(url1);
-  auto domain2 = GetRegistryControlledDomain(url2);
-
+  auto domain1 = GetOrganizationIdentifyingDomain(url1);
+  auto domain2 = GetOrganizationIdentifyingDomain(url2);
   if (domain1.empty() || domain2.empty()) {
     return false;
   }
@@ -65,5 +58,10 @@
   return url1.scheme() == url2.scheme() && domain1 == domain2;
 }
 
+std::string GetOrganizationIdentifyingDomain(const GURL& url) {
+  return net::registry_controlled_domains::GetDomainAndRegistry(
+      url, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+}
+
 }  // namespace url_utils
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/url_utils.h b/components/autofill_assistant/browser/url_utils.h
index 7df2997..55fc1b8 100644
--- a/components/autofill_assistant/browser/url_utils.h
+++ b/components/autofill_assistant/browser/url_utils.h
@@ -27,6 +27,10 @@
 // false.
 bool IsSamePublicSuffixDomain(const GURL& url1, const GURL& url2);
 
+// Returns the organization-identifying domain for |url|. Returns the empty
+// string for invalid urls.
+std::string GetOrganizationIdentifyingDomain(const GURL& url);
+
 }  // namespace url_utils
 }  // namespace autofill_assistant
 
diff --git a/components/autofill_assistant/browser/url_utils_unittest.cc b/components/autofill_assistant/browser/url_utils_unittest.cc
index e76d9a3..d75fa044 100644
--- a/components/autofill_assistant/browser/url_utils_unittest.cc
+++ b/components/autofill_assistant/browser/url_utils_unittest.cc
@@ -9,6 +9,8 @@
 namespace url_utils {
 namespace {
 
+using testing::Eq;
+
 TEST(UrlUtilsTest, IsInDomainOrSubDomain) {
   std::vector<std::string> allowed_domains = {"example.com",
                                               "other-example.com"};
@@ -57,6 +59,16 @@
   EXPECT_FALSE(IsSamePublicSuffixDomain(GURL("invalid"), GURL("invalid")));
 }
 
+TEST(UrlUtilsTest, GetOrganizationIdentifyingDomain) {
+  EXPECT_THAT(GetOrganizationIdentifyingDomain(GURL("https://www.example.com")),
+              Eq("example.com"));
+  EXPECT_THAT(
+      GetOrganizationIdentifyingDomain(GURL("https://subdomain.example.com")),
+      Eq("example.com"));
+  EXPECT_THAT(GetOrganizationIdentifyingDomain(GURL("https://example.com")),
+              Eq("example.com"));
+}
+
 }  // namespace
 }  // namespace url_utils
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/web/element_store_unittest.cc b/components/autofill_assistant/browser/web/element_store_unittest.cc
index 23b61827..f638f592 100644
--- a/components/autofill_assistant/browser/web/element_store_unittest.cc
+++ b/components/autofill_assistant/browser/web/element_store_unittest.cc
@@ -9,6 +9,8 @@
 #include "components/autofill_assistant/browser/actions/action_test_utils.h"
 #include "components/autofill_assistant/browser/client_status.h"
 #include "components/autofill_assistant/browser/web/element_finder.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_renderer_host.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -16,18 +18,12 @@
 namespace autofill_assistant {
 namespace {
 
-class ElementStoreTest : public content::RenderViewHostTestHarness {
+class ElementStoreTest : public testing::Test {
  public:
-  ElementStoreTest()
-      : RenderViewHostTestHarness(
-            base::test::TaskEnvironment::MainThreadType::UI,
-            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
-  ~ElementStoreTest() override {}
-
   void SetUp() override {
-    RenderViewHostTestHarness::SetUp();
-
-    element_store_ = std::make_unique<ElementStore>(web_contents());
+    web_contents_ = content::WebContentsTester::CreateTestWebContents(
+        &browser_context_, nullptr);
+    element_store_ = std::make_unique<ElementStore>(web_contents_.get());
   }
 
  protected:
@@ -36,7 +32,7 @@
     auto element = std::make_unique<ElementFinder::Result>();
     element->dom_object.object_data.object_id = object_id;
     element->dom_object.object_data.node_frame_id =
-        web_contents()->GetMainFrame()->GetDevToolsFrameToken().ToString();
+        web_contents_->GetMainFrame()->GetDevToolsFrameToken().ToString();
     return element;
   }
 
@@ -47,6 +43,10 @@
     element_store_->AddElement(client_id, element->dom_object);
   }
 
+  content::BrowserTaskEnvironment task_environment_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+  content::TestBrowserContext browser_context_;
+  std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<ElementStore> element_store_;
 };
 
@@ -87,7 +87,7 @@
   ElementFinder::Result result;
   EXPECT_EQ(ACTION_APPLIED,
             element_store_->GetElement("1", &result).proto_status());
-  EXPECT_EQ(web_contents()->GetMainFrame(), result.container_frame_host);
+  EXPECT_EQ(web_contents_->GetMainFrame(), result.container_frame_host);
 }
 
 TEST_F(ElementStoreTest, AddElementToStoreOverwrites) {
diff --git a/components/autofill_assistant/browser/web/mock_web_controller.h b/components/autofill_assistant/browser/web/mock_web_controller.h
index 99ba41b4..6decdf2 100644
--- a/components/autofill_assistant/browser/web/mock_web_controller.h
+++ b/components/autofill_assistant/browser/web/mock_web_controller.h
@@ -42,11 +42,12 @@
   MOCK_METHOD2(CheckOnTop,
                void(const ElementFinder::Result&,
                     base::OnceCallback<void(const ClientStatus&)>));
-  MOCK_METHOD5(SelectOption,
+  MOCK_METHOD6(SelectOption,
                void(const std::string& re2,
                     bool case_sensitive,
                     SelectOptionProto::OptionComparisonAttribute
                         option_comparison_attribute,
+                    bool strict,
                     const ElementFinder::Result& element,
                     base::OnceCallback<void(const ClientStatus&)> callback));
   MOCK_METHOD3(CheckSelectedOptionElement,
diff --git a/components/autofill_assistant/browser/web/web_controller.cc b/components/autofill_assistant/browser/web/web_controller.cc
index 8c1f29b..e956567 100644
--- a/components/autofill_assistant/browser/web/web_controller.cc
+++ b/components/autofill_assistant/browser/web/web_controller.cc
@@ -84,47 +84,53 @@
 
 // Javascript to select a value from a select box. Also fires a "change" event
 // to trigger any listeners. Changing the index directly does not trigger this.
+// See |WebController::OnSelectOptionJavascriptResult| for result handling.
 const char* const kSelectOptionScript =
-    R"(function(re2, valueSourceAttribute, caseSensitive) {
-      if (this.options == null) return false;
+    R"(function(re2, valueSourceAttribute, caseSensitive, strict) {
+      if (this.options == null) return -1;
       const regexp = RegExp(re2, caseSensitive ? '' : 'i');
-      let found = false;
+      let numResults = 0;
+      let newIndex = -1;
       for (let i = 0; i < this.options.length; ++i) {
         if (regexp.test(this.options[i][valueSourceAttribute])) {
-          this.options.selectedIndex = i;
-          found = true;
-          break;
+          ++numResults;
+          if (newIndex === -1) {
+            newIndex = i;
+          }
         }
       }
-      if (!found) {
-        return false;
+      if (numResults == 1 || (numResults > 1 && !strict)) {
+        this.options.selectedIndex = newIndex;
+        const e = document.createEvent('HTMLEvents');
+        e.initEvent('change', true, true);
+        this.dispatchEvent(e);
+        return 1;
       }
-      const e = document.createEvent('HTMLEvents');
-      e.initEvent('change', true, true);
-      this.dispatchEvent(e);
-      return true;
+      return numResults;
     })";
 
 // Javascript to select the option element in a select element. This does *not*
 // fire a "change" event.
+// See |WebController::OnSelectOptionJavascriptResult| for result handling.
 const char* const kSelectOptionElementScript =
     R"(function(option) {
-      if (this.options == null) return false;
+      if (this.options == null) return -1;
       for (let i = 0; i < this.options.length; ++i) {
         if (this.options[i] === option) {
           this.options.selectedIndex = i;
-          return true;
+          return 1;
         }
       }
-      return false;
+      return 0;
     })";
 
 // Javascript to check the option element in a select element against an
 // expected match.
+// See |WebController::OnSelectOptionJavascriptResult| for result handling.
 const char* const kCheckOptionElementScript =
     R"(function(option) {
-      if (this.options == null) return false;
-      return this.options[this.options.selectedIndex] === option;
+      if (this.options == null) return -1;
+      return (this.options[this.options.selectedIndex] === option) ? 1 : 0;
     })";
 
 // Javascript to highlight an element.
@@ -924,6 +930,7 @@
     const std::string& re2,
     bool case_sensitive,
     SelectOptionProto::OptionComparisonAttribute option_comparison_attribute,
+    bool strict,
     const ElementFinder::Result& element,
     base::OnceCallback<void(const ClientStatus&)> callback) {
 #ifdef NDEBUG
@@ -953,6 +960,7 @@
       return;
   }
   AddRuntimeCallArgument(case_sensitive, &arguments);
+  AddRuntimeCallArgument(strict, &arguments);
 
   devtools_client_->GetRuntime()->CallFunctionOn(
       runtime::CallFunctionOnParams::Builder()
@@ -962,7 +970,7 @@
           .SetReturnByValue(true)
           .Build(),
       element.node_frame_id(),
-      base::BindOnce(&WebController::OnJavascriptResultExpectingTrue,
+      base::BindOnce(&WebController::OnSelectOptionJavascriptResult,
                      weak_ptr_factory_.GetWeakPtr(),
                      base::BindOnce(&DecorateWebControllerStatus,
                                     WebControllerErrorInfoProto::SELECT_OPTION,
@@ -985,7 +993,7 @@
           .Build(),
       element.node_frame_id(),
       base::BindOnce(
-          &WebController::OnJavascriptResultExpectingTrue,
+          &WebController::OnSelectOptionJavascriptResult,
           weak_ptr_factory_.GetWeakPtr(),
           base::BindOnce(&DecorateWebControllerStatus,
                          WebControllerErrorInfoProto::SELECT_OPTION_ELEMENT,
@@ -1008,7 +1016,7 @@
           .Build(),
       element.node_frame_id(),
       base::BindOnce(
-          &WebController::OnJavascriptResultExpectingTrue,
+          &WebController::OnSelectOptionJavascriptResult,
           weak_ptr_factory_.GetWeakPtr(),
           base::BindOnce(&DecorateWebControllerStatus,
                          WebControllerErrorInfoProto::CHECK_OPTION_ELEMENT,
@@ -1016,9 +1024,9 @@
           /* status_if_false= */ ELEMENT_MISMATCH));
 }
 
-void WebController::OnJavascriptResultExpectingTrue(
+void WebController::OnSelectOptionJavascriptResult(
     base::OnceCallback<void(const ClientStatus&)> callback,
-    ProcessedActionStatusProto status_if_false,
+    ProcessedActionStatusProto status_if_zero,
     const DevtoolsClient::ReplyStatus& reply_status,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
   ClientStatus status =
@@ -1027,14 +1035,22 @@
     std::move(callback).Run(status);
     return;
   }
-  bool bool_result;
-  if (!SafeGetBool(result->GetResult(), &bool_result)) {
+  int int_result;
+  if (!SafeGetIntValue(result->GetResult(), &int_result)) {
     std::move(callback).Run(
         UnexpectedDevtoolsErrorStatus(reply_status, __FILE__, __LINE__));
     return;
   }
-  if (!bool_result) {
-    std::move(callback).Run(ClientStatus(status_if_false));
+  if (int_result < 0) {
+    std::move(callback).Run(ClientStatus(INVALID_TARGET));
+    return;
+  }
+  if (int_result == 0) {
+    std::move(callback).Run(ClientStatus(status_if_zero));
+    return;
+  }
+  if (int_result > 1) {
+    std::move(callback).Run(ClientStatus(TOO_MANY_OPTION_VALUES_FOUND));
     return;
   }
   std::move(callback).Run(OkClientStatus());
diff --git a/components/autofill_assistant/browser/web/web_controller.h b/components/autofill_assistant/browser/web/web_controller.h
index 3c85c82..de67524 100644
--- a/components/autofill_assistant/browser/web/web_controller.h
+++ b/components/autofill_assistant/browser/web/web_controller.h
@@ -151,6 +151,7 @@
       const std::string& re2,
       bool case_sensitive,
       SelectOptionProto::OptionComparisonAttribute option_comparison_attribute,
+      bool strict,
       const ElementFinder::Result& element,
       base::OnceCallback<void(const ClientStatus&)> callback);
 
@@ -465,9 +466,15 @@
       autofill::ContentAutofillDriver* driver,
       const autofill::FormData& form_data,
       const autofill::FormFieldData& form_field);
-  void OnJavascriptResultExpectingTrue(
+  // Handling a JS result for a "SelectOption" action. This expects the JS
+  // result to contain an integer and returns the following status:
+  // * -1 -> INVALID_TARGET
+  // *  0 -> OPTION_ELEMENT_NOT_FOUND
+  // *  1 -> ACTION_APPLIED
+  // *  n -> TOO_MANY_OPTION_VALUES_FOUND
+  void OnSelectOptionJavascriptResult(
       base::OnceCallback<void(const ClientStatus&)> callback,
-      ProcessedActionStatusProto status_if_false,
+      ProcessedActionStatusProto status_if_zero,
       const DevtoolsClient::ReplyStatus& reply_status,
       std::unique_ptr<runtime::CallFunctionOnResult> result);
 
diff --git a/components/autofill_assistant/browser/web/web_controller_browsertest.cc b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
index db9eb00..8c6ae96 100644
--- a/components/autofill_assistant/browser/web/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web/web_controller_browsertest.cc
@@ -322,11 +322,12 @@
                        std::move(done_callback), result_output));
   }
 
-  ClientStatus SelectOption(const Selector& selector,
-                            const std::string& re2,
-                            bool case_sensitive,
-                            SelectOptionProto::OptionComparisonAttribute
-                                option_comparison_attribute) {
+  ClientStatus SelectOption(
+      const Selector& selector,
+      const std::string& re2,
+      bool case_sensitive,
+      SelectOptionProto::OptionComparisonAttribute option_comparison_attribute,
+      bool strict) {
     base::RunLoop run_loop;
     ClientStatus result;
 
@@ -335,7 +336,8 @@
         base::BindOnce(
             &WebControllerBrowserTest::FindSelectOptionElementCallback,
             base::Unretained(this), re2, case_sensitive,
-            option_comparison_attribute, run_loop.QuitClosure(), &result));
+            option_comparison_attribute, strict, run_loop.QuitClosure(),
+            &result));
 
     run_loop.Run();
     return result;
@@ -345,6 +347,7 @@
       const std::string& re2,
       bool case_sensitive,
       SelectOptionProto::OptionComparisonAttribute option_comparison_attribute,
+      bool strict,
       base::OnceClosure done_callback,
       ClientStatus* result_output,
       const ClientStatus& status,
@@ -358,7 +361,8 @@
     ASSERT_TRUE(element_result != nullptr);
     const ElementFinder::Result* element_result_ptr = element_result.get();
     web_controller_->SelectOption(
-        re2, case_sensitive, option_comparison_attribute, *element_result_ptr,
+        re2, case_sensitive, option_comparison_attribute, strict,
+        *element_result_ptr,
         base::BindOnce(&WebControllerBrowserTest::ElementRetainingCallback,
                        base::Unretained(this), std::move(element_result),
                        std::move(done_callback), result_output));
@@ -2053,48 +2057,61 @@
   )";
 
   // Selecting on a non-<select> element.
-  EXPECT_EQ(OPTION_VALUE_NOT_FOUND,
+  EXPECT_EQ(INVALID_TARGET,
             SelectOption(Selector({"#input1"}), std::string(),
-                         /* case_sensitive= */ false, SelectOptionProto::LABEL)
+                         /* case_sensitive= */ false, SelectOptionProto::LABEL,
+                         /* strict= */ true)
                 .proto_status());
 
   // Fails if no comparison attribute is set.
   EXPECT_EQ(INVALID_ACTION,
             SelectOption(selector, "one", /* case_sensitive= */ false,
-                         SelectOptionProto::NOT_SET)
+                         SelectOptionProto::NOT_SET, /* strict= */ true)
                 .proto_status());
 
   // Select value not matching anything.
   EXPECT_EQ(OPTION_VALUE_NOT_FOUND,
             SelectOption(selector, "incorrect label",
-                         /* case_sensitive= */ false, SelectOptionProto::LABEL)
+                         /* case_sensitive= */ false, SelectOptionProto::LABEL,
+                         /* strict= */ true)
                 .proto_status());
 
+  // Select value matching everything.
+  EXPECT_EQ(TOO_MANY_OPTION_VALUES_FOUND,
+            SelectOption(selector, ".*", /* case_sensitive= */ false,
+                         SelectOptionProto::LABEL, /* strict= */ true)
+                .proto_status());
+  EXPECT_EQ(ACTION_APPLIED,
+            SelectOption(selector, ".*", /* case_sensitive= */ false,
+                         SelectOptionProto::LABEL, /* strict= */ false)
+                .proto_status());
+  EXPECT_EQ("One", content::EvalJs(shell(), javascript));
+
   // Select value matching the option's label.
   EXPECT_EQ(ACTION_APPLIED,
             SelectOption(selector, "^ZÜRICH", /* case_sensitive= */ false,
-                         SelectOptionProto::LABEL)
+                         SelectOptionProto::LABEL, /* strict= */ true)
                 .proto_status());
   EXPECT_EQ("Zürich Hauptbahnhof", content::EvalJs(shell(), javascript));
 
   // Select value matching the option's value.
   EXPECT_EQ(ACTION_APPLIED,
             SelectOption(selector, "^Aü万𠜎$", /* case_sensitive= */ false,
-                         SelectOptionProto::VALUE)
+                         SelectOptionProto::VALUE, /* strict= */ true)
                 .proto_status());
   EXPECT_EQ("Character Test Entry", content::EvalJs(shell(), javascript));
 
   // With a regular expression matching the option's value.
   EXPECT_EQ(ACTION_APPLIED,
             SelectOption(selector, "^O.E$", /* case_sensitive= */ false,
-                         SelectOptionProto::VALUE)
+                         SelectOptionProto::VALUE, /* strict= */ true)
                 .proto_status());
   EXPECT_EQ("One", content::EvalJs(shell(), javascript));
 
   // With a regular expression matching the option's value case sensitive.
   EXPECT_EQ(OPTION_VALUE_NOT_FOUND,
             SelectOption(selector, "^O.E$", /* case_sensitive= */ true,
-                         SelectOptionProto::VALUE)
+                         SelectOptionProto::VALUE, /* strict= */ true)
                 .proto_status());
   EXPECT_EQ("One", content::EvalJs(shell(), javascript));
 }
@@ -2104,7 +2121,7 @@
   Selector select_selector({"#iframe", "select[name=state]"});
   EXPECT_EQ(ACTION_APPLIED,
             SelectOption(select_selector, "^NY", /* case_sensitive= */ false,
-                         SelectOptionProto::LABEL)
+                         SelectOptionProto::LABEL, /* strict= */ true)
                 .proto_status());
 
   const std::string javascript = R"(
@@ -2119,7 +2136,7 @@
   select_selector = Selector({"#iframeExternal", "select[name=pet]"});
   EXPECT_EQ(ACTION_APPLIED,
             SelectOption(select_selector, "^Cat", /* case_sensitive= */ false,
-                         SelectOptionProto::LABEL)
+                         SelectOptionProto::LABEL, /* strict= */ true)
                 .proto_status());
 
   Selector result_selector({"#iframeExternal", "#myPet"});
@@ -2929,7 +2946,7 @@
   GetFieldsValue({selector}, {"two"});
 
   // Using on a non-<select> element.
-  EXPECT_EQ(OPTION_VALUE_NOT_FOUND,
+  EXPECT_EQ(INVALID_TARGET,
             SelectOptionElement(Selector({"#input1"}), option).proto_status());
 
   // Random element that is certainly not an option in the <select>.
@@ -2967,7 +2984,7 @@
       CheckSelectedOptionElement(select, not_selected_option).proto_status());
 
   // Using on a non-<select> element.
-  EXPECT_EQ(ELEMENT_MISMATCH,
+  EXPECT_EQ(INVALID_TARGET,
             CheckSelectedOptionElement(input, selected_option).proto_status());
 }
 
diff --git a/components/consent_auditor/OWNERS b/components/consent_auditor/OWNERS
index 0e2c1661..745d3ad 100644
--- a/components/consent_auditor/OWNERS
+++ b/components/consent_auditor/OWNERS
@@ -1,4 +1,3 @@
 dullweber@chromium.org
 markusheintz@chromium.org
 msramek@chromium.org
-vitaliii@chromium.org
diff --git a/components/content_settings/core/browser/content_settings_pref.cc b/components/content_settings/core/browser/content_settings_pref.cc
index 633f352..933dcb4 100644
--- a/components/content_settings/core/browser/content_settings_pref.cc
+++ b/components/content_settings/core/browser/content_settings_pref.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
+#include "base/value_iterators.h"
 #include "components/content_settings/core/browser/content_settings_info.h"
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/content_settings_rule.h"
@@ -410,8 +411,8 @@
         settings_dictionary->RemoveWithoutPathExpansion(kSessionModelPath,
                                                         nullptr);
       } else {
-        settings_dictionary->SetWithoutPathExpansion(kSettingPath,
-                                                     value->CreateDeepCopy());
+        settings_dictionary->SetWithoutPathExpansion(
+            kSettingPath, base::Value::ToUniquePtrValue(value->Clone()));
         settings_dictionary->SetKey(
             kLastModifiedPath,
             base::Value(base::NumberToString(
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc
index 2cbf0058..c3b028c 100644
--- a/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -964,7 +964,7 @@
           *secondary_pattern = rule.secondary_pattern;
         if (session_model)
           *session_model = rule.session_model;
-        return rule.value.CreateDeepCopy();
+        return base::Value::ToUniquePtrValue(rule.value.Clone());
       }
     }
   }
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index 64702a98..16392f3 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -188,10 +188,13 @@
     if (dns_client) {
       inner_resolver->GetManagerForTesting()->SetDnsClientForTesting(
           std::move(dns_client));
-      inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(true);
+      inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(
+          /*enabled=*/true,
+          /*additional_dns_types_enabled=*/true);
     } else {
       inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(
-          false);
+          /*enabled=*/false,
+          /*additional_dns_types_enabled=*/false);
     }
     return inner_resolver;
   }
diff --git a/components/password_manager/core/browser/form_fetcher_impl.cc b/components/password_manager/core/browser/form_fetcher_impl.cc
index f6f9f2f..d2bb626 100644
--- a/components/password_manager/core/browser/form_fetcher_impl.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl.cc
@@ -126,6 +126,12 @@
   // The desktop bubble needs this information.
   password_store->GetMatchingInsecureCredentials(form_digest_.signon_realm,
                                                  this);
+#else
+  if (base::FeatureList::IsEnabled(features::kMutingCompromisedCredentials)) {
+    // We need this information to mute leak detection warming.
+    password_store->GetMatchingInsecureCredentials(form_digest_.signon_realm,
+                                                   this);
+  }
 #endif
 }
 
diff --git a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
index 2dcf7d8..7afd341 100644
--- a/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
+++ b/components/password_manager/core/browser/form_fetcher_impl_unittest.cc
@@ -536,10 +536,30 @@
 }
 
 TEST_P(FormFetcherImplTest, DontFetchInsecure) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(features::kMutingCompromisedCredentials,
+                                    false);
   EXPECT_CALL(*mock_store_, GetMatchingInsecureCredentialsImpl).Times(0);
   form_fetcher_->Fetch();
   task_environment_.RunUntilIdle();
 }
+
+TEST_P(FormFetcherImplTest, FetchInsecure) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(features::kMutingCompromisedCredentials,
+                                    true);
+  std::vector<InsecureCredential> list = {InsecureCredential(
+      form_digest_.signon_realm, u"username_value", base::Time::FromTimeT(1),
+      InsecureType::kLeaked, IsMuted(false))};
+  EXPECT_CALL(*mock_store_,
+              GetMatchingInsecureCredentialsImpl(form_digest_.signon_realm))
+      .WillOnce(Return(list));
+  form_fetcher_->Fetch();
+  task_environment_.RunUntilIdle();
+
+  EXPECT_THAT(form_fetcher_->GetInsecureCredentials(),
+              UnorderedElementsAreArray(list));
+}
 #endif
 
 // Test that ensures HTTP passwords are not migrated on HTTP sites.
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index 8828f86..ec056841 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -192,6 +192,15 @@
   return form_data;
 }
 
+bool HasMutedCredentials(base::span<const InsecureCredential> credentials,
+                         const std::u16string& username) {
+  return base::ranges::any_of(credentials, [&username](const auto& credential) {
+    return credential.username == username && credential.is_muted &&
+           (credential.insecure_type == InsecureType::kLeaked ||
+            credential.insecure_type == InsecureType::kPhished);
+  });
+}
+
 }  // namespace
 
 // static
@@ -950,7 +959,13 @@
   DCHECK(submitted_manager->GetSubmittedForm());
 
   client_->GetStoreResultFilter()->ReportFormLoginSuccess(*submitted_manager);
-  leak_delegate_.StartLeakCheck(submitted_manager->GetPendingCredentials());
+  // Check for leaks only if there are no muted credentials.
+  if (!HasMutedCredentials(
+          submitted_manager->GetInsecureCredentials(),
+          submitted_manager->GetSubmittedForm()->username_value) ||
+      !base::FeatureList::IsEnabled(features::kMutingCompromisedCredentials)) {
+    leak_delegate_.StartLeakCheck(submitted_manager->GetPendingCredentials());
+  }
 
   auto submission_event =
       submitted_manager->GetSubmittedForm()->submission_event;
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 2e9e73f..2df3ed92 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -4088,6 +4088,85 @@
   EXPECT_FALSE(manager()->GetSubmittedManagerForTest());
 }
 
+// Check that on successful login the credentials are checked for leak depending
+// on mute state of insecure credential.
+TEST_P(PasswordManagerTest, DontStartLeakDetectionWhenMuted) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(features::kMutingCompromisedCredentials,
+                                    true);
+
+  auto mock_factory =
+      std::make_unique<testing::StrictMock<MockLeakDetectionCheckFactory>>();
+  manager()->set_leak_factory(std::move(mock_factory));
+
+  const PasswordForm form = MakeSimpleForm();
+  std::vector<FormData> observed = {form.form_data};
+  EXPECT_CALL(*store_, GetLogins)
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+  // Add muted insecure credentials.
+  std::vector<InsecureCredential> insecure_credentials = {InsecureCredential(
+      form.signon_realm, form.username_value, base::Time::FromTimeT(1),
+      InsecureType::kLeaked, IsMuted(true))};
+  EXPECT_CALL(*store_, GetMatchingInsecureCredentialsImpl(form.signon_realm))
+      .WillOnce(Return(insecure_credentials));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
+  OnPasswordFormSubmitted(form.form_data);
+
+  auto check_instance = std::make_unique<MockLeakDetectionCheck>();
+  EXPECT_CALL(*check_instance, Start).Times(0);
+
+  // Now the password manager waits for the navigation to complete.
+  observed.clear();
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+
+// Tests that check for leaks happens even if there are muted credentials for
+// the same domain, but with different username.
+TEST_P(PasswordManagerTest, StartLeakCheckWhenForUsernameNotMuted) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(features::kMutingCompromisedCredentials,
+                                    true);
+
+  auto mock_factory =
+      std::make_unique<testing::StrictMock<MockLeakDetectionCheckFactory>>();
+  MockLeakDetectionCheckFactory* weak_factory = mock_factory.get();
+  manager()->set_leak_factory(std::move(mock_factory));
+
+  const PasswordForm form = MakeSimpleForm();
+  std::vector<FormData> observed = {form.form_data};
+  EXPECT_CALL(*store_, GetLogins)
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms(store_.get())));
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+
+  // Add muted insecure credentials.
+  std::vector<InsecureCredential> insecure_credentials = {InsecureCredential(
+      form.signon_realm, u"different_username", base::Time::FromTimeT(1),
+      InsecureType::kLeaked, IsMuted(true))};
+  EXPECT_CALL(*store_, GetMatchingInsecureCredentialsImpl(form.signon_realm))
+      .WillOnce(Return(insecure_credentials));
+  task_environment_.RunUntilIdle();
+
+  EXPECT_CALL(client_, IsSavingAndFillingEnabled).WillRepeatedly(Return(true));
+  OnPasswordFormSubmitted(form.form_data);
+
+  auto check_instance = std::make_unique<MockLeakDetectionCheck>();
+  EXPECT_CALL(*check_instance, Start);
+  EXPECT_CALL(*weak_factory, TryCreateLeakCheck)
+      .WillOnce(Return(ByMove(std::move(check_instance))));
+
+  // Now the password manager waits for the navigation to complete.
+  observed.clear();
+  manager()->OnPasswordFormsParsed(&driver_, observed);
+  manager()->OnPasswordFormsRendered(&driver_, observed, true);
+}
+
 INSTANTIATE_TEST_SUITE_P(, PasswordManagerTest, testing::Bool());
 
 }  // namespace password_manager
diff --git a/components/policy/core/common/cloud/cloud_policy_store.cc b/components/policy/core/common/cloud/cloud_policy_store.cc
index d4a10da..f06b8df 100644
--- a/components/policy/core/common/cloud/cloud_policy_store.cc
+++ b/components/policy/core/common/cloud/cloud_policy_store.cc
@@ -49,7 +49,8 @@
 
 void CloudPolicyStore::NotifyStoreLoaded() {
   is_initialized_ = true;
-  first_policies_loaded_ |= has_policy();
+  UpdateFirstPoliciesLoaded();
+
   // The |external_data_manager_| must be notified first so that when other
   // observers are informed about the changed policies and try to fetch external
   // data referenced by these, the |external_data_manager_| has the required
@@ -60,9 +61,14 @@
     observer.OnStoreLoaded(this);
 }
 
+void CloudPolicyStore::UpdateFirstPoliciesLoaded() {
+  first_policies_loaded_ |= has_policy();
+}
+
 void CloudPolicyStore::NotifyStoreError() {
   is_initialized_ = true;
-  first_policies_loaded_ |= has_policy();
+  UpdateFirstPoliciesLoaded();
+
   for (auto& observer : observers_)
     observer.OnStoreError(this);
 }
diff --git a/components/policy/core/common/cloud/cloud_policy_store.h b/components/policy/core/common/cloud/cloud_policy_store.h
index 7ed3dfc..25b1e1e 100644
--- a/components/policy/core/common/cloud/cloud_policy_store.h
+++ b/components/policy/core/common/cloud/cloud_policy_store.h
@@ -170,6 +170,9 @@
   void NotifyStoreLoaded();
   void NotifyStoreError();
 
+  // Updates whether or not the first policies were loaded.
+  virtual void UpdateFirstPoliciesLoaded();
+
   // Assert non-concurrent usage in debug builds.
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/components/power_scheduler/power_mode_arbiter.cc b/components/power_scheduler/power_mode_arbiter.cc
index db926ae3..87c0254d6 100644
--- a/components/power_scheduler/power_mode_arbiter.cc
+++ b/components/power_scheduler/power_mode_arbiter.cc
@@ -37,17 +37,31 @@
 // power_scheduler component.
 class PowerModeArbiter::ChargingPowerModeVoter : base::PowerStateObserver {
  public:
-  ChargingPowerModeVoter()
-      : charging_voter_(PowerModeArbiter::GetInstance()->NewVoter(
-            "PowerModeVoter.Charging")) {
-    const bool on_battery =
-        base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(this);
-    if (base::PowerMonitor::IsInitialized())
-      OnPowerStateChange(on_battery);
+  explicit ChargingPowerModeVoter(PowerModeArbiter* arbiter)
+      : charging_voter_(arbiter->NewVoter("PowerModeVoter.Charging")) {
+    // Start out in charging mode until we can register the observer with
+    // PowerMonitor, which itself also starts out in charging mode.
+    charging_voter_->VoteFor(PowerMode::kCharging);
   }
 
   ~ChargingPowerModeVoter() override {
-    base::PowerMonitor::RemovePowerStateObserver(this);
+    if (was_setup_)
+      base::PowerMonitor::RemovePowerStateObserver(this);
+  }
+
+  void Setup() {
+    if (was_setup_)
+      return;
+    was_setup_ = true;
+
+    const bool on_battery =
+        base::PowerMonitor::AddPowerStateObserverAndReturnOnBatteryState(this);
+    OnPowerStateChange(on_battery);
+  }
+
+  void SetOnBatteryPowerForTesting(bool on_battery_power) {
+    OnPowerStateChange(on_battery_power);
+    was_setup_ = true;  // Prevent real setup in the test.
   }
 
   void OnPowerStateChange(bool on_battery_power) override {
@@ -57,6 +71,7 @@
 
  private:
   std::unique_ptr<PowerModeVoter> charging_voter_;
+  bool was_setup_ = false;
 };
 
 PowerModeArbiter::Observer::~Observer() = default;
@@ -73,7 +88,8 @@
     : trace_observer_(std::make_unique<TraceObserver>()),
       active_mode_("PowerModeArbiter", this),
       observers_(
-          base::MakeRefCounted<base::ObserverListThreadSafe<Observer>>()) {
+          base::MakeRefCounted<base::ObserverListThreadSafe<Observer>>()),
+      charging_voter_(std::make_unique<ChargingPowerModeVoter>(this)) {
   base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
 }
 
@@ -111,10 +127,10 @@
 
   // Create the charging voter on the task runner sequence, so that charging
   // state notifications are received there.
-  task_runner->PostTask(FROM_HERE, base::BindOnce([] {
-                          PowerModeArbiter::GetInstance()->charging_voter_ =
-                              std::make_unique<ChargingPowerModeVoter>();
-                        }));
+  task_runner->PostTask(
+      FROM_HERE, base::BindOnce([] {
+        PowerModeArbiter::GetInstance()->charging_voter_->Setup();
+      }));
 }
 
 std::unique_ptr<PowerModeVoter> PowerModeArbiter::NewVoter(const char* name) {
@@ -313,6 +329,10 @@
   return active_mode_.mode();
 }
 
+void PowerModeArbiter::SetOnBatteryPowerForTesting(bool on_battery_power) {
+  charging_voter_->SetOnBatteryPowerForTesting(on_battery_power);  // IN-TEST
+}
+
 void PowerModeArbiter::OnTraceLogEnabled() {
   {
     base::AutoLock lock(lock_);
diff --git a/components/power_scheduler/power_mode_arbiter.h b/components/power_scheduler/power_mode_arbiter.h
index d685ca8..644b4d5 100644
--- a/components/power_scheduler/power_mode_arbiter.h
+++ b/components/power_scheduler/power_mode_arbiter.h
@@ -66,8 +66,8 @@
   // pool is available.
   void OnThreadPoolAvailable();
 
-  // Returns the currently active PowerMode. Public for testing.
   PowerMode GetActiveModeForTesting();
+  void SetOnBatteryPowerForTesting(bool on_battery_power);
 
  private:
   FRIEND_TEST_ALL_PREFIXES(PowerModeArbiterTest, ResetVoteAfterTimeout);
diff --git a/components/power_scheduler/power_mode_arbiter_unittest.cc b/components/power_scheduler/power_mode_arbiter_unittest.cc
index feaaadc8..3b4349e 100644
--- a/components/power_scheduler/power_mode_arbiter_unittest.cc
+++ b/components/power_scheduler/power_mode_arbiter_unittest.cc
@@ -12,6 +12,10 @@
 
 TEST(PowerModeArbiterTest, SingleVote) {
   PowerModeArbiter arbiter;
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
+
+  // Clear the initial kCharging vote.
+  arbiter.SetOnBatteryPowerForTesting(/*on_battery_power=*/true);
   EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
 
   std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
@@ -29,6 +33,12 @@
 
 TEST(PowerModeArbiterTest, MultipleVotes) {
   PowerModeArbiter arbiter;
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
+
+  // Clear the initial kCharging vote.
+  arbiter.SetOnBatteryPowerForTesting(/*on_battery_power=*/true);
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
+
   EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
 
   std::unique_ptr<PowerModeVoter> voter1 = arbiter.NewVoter("voter1");
@@ -119,6 +129,9 @@
   PowerModeArbiter arbiter;
   MockObserver observer;
 
+  // Clear the initial kCharging vote.
+  arbiter.SetOnBatteryPowerForTesting(/*on_battery_power=*/true);
+
   // Observer is notified of initial mode right away.
   EXPECT_CALL(observer, OnPowerModeChanged(PowerMode::kIdle, PowerMode::kIdle));
 
@@ -163,6 +176,12 @@
   env.AdvanceClock(target_time - env.NowTicks());
 
   PowerModeArbiter arbiter;
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
+
+  // Clear the initial kCharging vote.
+  arbiter.SetOnBatteryPowerForTesting(/*on_battery_power=*/true);
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
+
   // Add a fake observer to enable reset tasks.
   FakeObserver observer;
   arbiter.AddObserver(&observer);
@@ -261,6 +280,12 @@
   env.AdvanceClock(target_time - env.NowTicks());
 
   PowerModeArbiter arbiter;
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kCharging);
+
+  // Clear the initial kCharging vote.
+  arbiter.SetOnBatteryPowerForTesting(/*on_battery_power=*/true);
+  EXPECT_EQ(arbiter.GetActiveModeForTesting(), PowerMode::kIdle);
+
   FakeObserver observer;
   base::TimeDelta delta1s = base::TimeDelta::FromSeconds(1);
 
diff --git a/components/send_tab_to_self/BUILD.gn b/components/send_tab_to_self/BUILD.gn
index 67f5f6d6..d53f76f 100644
--- a/components/send_tab_to_self/BUILD.gn
+++ b/components/send_tab_to_self/BUILD.gn
@@ -8,6 +8,8 @@
     "fake_send_tab_to_self_model.h",
     "features.cc",
     "features.h",
+    "metrics_util.cc",
+    "metrics_util.h",
     "pref_names.cc",
     "pref_names.h",
     "send_tab_to_self_bridge.cc",
diff --git a/components/send_tab_to_self/metrics_util.cc b/components/send_tab_to_self/metrics_util.cc
new file mode 100644
index 0000000..724f2d08
--- /dev/null
+++ b/components/send_tab_to_self/metrics_util.cc
@@ -0,0 +1,54 @@
+// 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 "components/send_tab_to_self/metrics_util.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/strings/strcat.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+// State of the send tab to self option in the UI.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ClickResult {
+  // kShowItem = 0,
+  kClickItem = 1,
+  // kShowDeviceList = 2,
+  kMaxValue = kClickItem,
+};
+
+std::string GetEntryPointHistogramString(ShareEntryPoint entry_point) {
+  switch (entry_point) {
+    case ShareEntryPoint::kShareSheet:
+      return "ShareSheet";
+    case ShareEntryPoint::kOmniboxIcon:
+      return "OmniboxIcon";
+    case ShareEntryPoint::kContentMenu:
+      return "ContentMenu";
+    case ShareEntryPoint::kLinkMenu:
+      return "LinkMenu";
+    case ShareEntryPoint::kOmniboxMenu:
+      return "OmniboxMenu";
+    case ShareEntryPoint::kTabMenu:
+      return "TabMenu";
+    case ShareEntryPoint::kShareMenu:
+      return "ShareMenu";
+  }
+}
+
+}  // namespace
+
+void RecordDeviceClicked(ShareEntryPoint entry_point) {
+  // TODO(crbug.com/956722): Only kClickItem is used today, so we should replace
+  // this with a histogram which doesn't use the ClickResult enum.
+  base::UmaHistogramEnumeration(
+      base::StrCat({"SendTabToSelf.", GetEntryPointHistogramString(entry_point),
+                    ".ClickResult"}),
+      ClickResult::kClickItem);
+}
+
+}  // namespace send_tab_to_self
diff --git a/components/send_tab_to_self/metrics_util.h b/components/send_tab_to_self/metrics_util.h
new file mode 100644
index 0000000..c791969
--- /dev/null
+++ b/components/send_tab_to_self/metrics_util.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEND_TAB_TO_SELF_METRICS_UTIL_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_METRICS_UTIL_H_
+
+#include <string>
+
+namespace send_tab_to_self {
+
+enum class ShareEntryPoint {
+  kContentMenu,
+  kLinkMenu,
+  kOmniboxIcon,
+  kOmniboxMenu,
+  kShareMenu,
+  kShareSheet,
+  kTabMenu,
+};
+
+// Records whether the user clicked to send a tab to a device.
+void RecordDeviceClicked(ShareEntryPoint entry_point);
+
+}  // namespace send_tab_to_self
+
+#endif  // COMPONENTS_SEND_TAB_TO_SELF_METRICS_UTIL_H_
diff --git a/components/signin/public/android/BUILD.gn b/components/signin/public/android/BUILD.gn
index dd15ee8e..ed854c6 100644
--- a/components/signin/public/android/BUILD.gn
+++ b/components/signin/public/android/BUILD.gn
@@ -24,7 +24,6 @@
     "java/src/org/chromium/components/signin/AccountManagerFacade.java",
     "java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java",
     "java/src/org/chromium/components/signin/AccountManagerFacadeProvider.java",
-    "java/src/org/chromium/components/signin/AccountManagerResult.java",
     "java/src/org/chromium/components/signin/AccountRenameChecker.java",
     "java/src/org/chromium/components/signin/AccountRestrictionPatternReceiver.java",
     "java/src/org/chromium/components/signin/AccountUtils.java",
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
index b975c0b4..e67d3c54 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
@@ -50,19 +50,18 @@
 
     private final AccountManagerDelegate mDelegate;
     private final AccountRestrictionPatternReceiver mAccountRestrictionPatternReceiver;
-    private final AtomicReference<List<PatternMatcher>> mAccountRestrictionPatterns =
-            new AtomicReference<>();
 
     private final ObserverList<AccountsChangeObserver> mObservers = new ObserverList<>();
 
-    private AccountManagerResult<List<Account>> mAllAccounts;
-
-    private final AtomicReference<AccountManagerResult<List<Account>>> mFilteredAccounts =
+    private final AtomicReference<List<Account>> mAllAccounts = new AtomicReference<>();
+    private final AtomicReference<List<PatternMatcher>> mAccountRestrictionPatterns =
             new AtomicReference<>();
+    private final AtomicReference<List<Account>> mFilteredAccounts = new AtomicReference<>();
     private final CountDownLatch mPopulateAccountCacheLatch = new CountDownLatch(1);
 
     private int mUpdateTasksCounter;
-    private final Queue<Runnable> mCallbacksWaitingForAccountsFetch = new ArrayDeque<>();
+    private final Queue<Callback<List<Account>>> mCallbacksWaitingForAccountsFetch =
+            new ArrayDeque<>();
 
     /**
      * @param delegate the AccountManagerDelegate to use as a backend
@@ -113,8 +112,9 @@
         return mFilteredAccounts.get() != null;
     }
 
-    private List<Account> getGoogleAccounts() throws AccountManagerDelegateException {
-        AccountManagerResult<List<Account>> maybeAccounts = mFilteredAccounts.get();
+    @Override
+    public List<Account> tryGetGoogleAccounts() {
+        List<Account> maybeAccounts = mFilteredAccounts.get();
         if (maybeAccounts == null) {
             try {
                 // First call to update hasn't finished executing yet, should wait for it
@@ -130,16 +130,7 @@
                 throw new RuntimeException("Interrupted waiting for accounts", e);
             }
         }
-        return maybeAccounts.get();
-    }
-
-    @Override
-    public List<Account> tryGetGoogleAccounts() {
-        try {
-            return getGoogleAccounts();
-        } catch (AccountManagerDelegateException e) {
-            return Collections.emptyList();
-        }
+        return maybeAccounts;
     }
 
     /**
@@ -147,7 +138,12 @@
      */
     @Override
     public void tryGetGoogleAccounts(Callback<List<Account>> callback) {
-        runAfterCacheIsPopulated(() -> callback.onResult(tryGetGoogleAccounts()));
+        ThreadUtils.assertOnUiThread();
+        if (isCachePopulated()) {
+            ThreadUtils.postOnUiThread(callback.bind(tryGetGoogleAccounts()));
+        } else {
+            mCallbacksWaitingForAccountsFetch.add(callback);
+        }
     }
 
     /**
@@ -265,38 +261,25 @@
         return mDelegate.isGooglePlayServicesAvailable();
     }
 
-    /**
-     * Runs a callback after the account list cache is populated.
-     */
-    private void runAfterCacheIsPopulated(Runnable runnable) {
-        ThreadUtils.assertOnUiThread();
-        if (isCachePopulated()) {
-            ThreadUtils.postOnUiThread(runnable);
-        } else {
-            mCallbacksWaitingForAccountsFetch.add(runnable);
-        }
-    }
-
     private void updateAccounts() {
         ThreadUtils.assertOnUiThread();
         new UpdateAccountsTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
     }
 
-    private AccountManagerResult<List<Account>> getAllAccounts() {
+    private List<Account> getAllAccounts() {
         try {
-            List<Account> accounts = Arrays.asList(mDelegate.getAccountsSync());
-            return new AccountManagerResult<>(Collections.unmodifiableList(accounts));
+            return Collections.unmodifiableList(Arrays.asList(mDelegate.getAccountsSync()));
         } catch (AccountManagerDelegateException ex) {
-            return new AccountManagerResult<>(ex);
+            return Collections.emptyList();
         }
     }
 
-    private AccountManagerResult<List<Account>> getFilteredAccounts() {
-        if (mAllAccounts.hasException() || mAccountRestrictionPatterns.get().isEmpty()) {
-            return mAllAccounts;
+    private List<Account> getFilteredAccounts() {
+        if (mAccountRestrictionPatterns.get().isEmpty()) {
+            return mAllAccounts.get();
         }
-        ArrayList<Account> filteredAccounts = new ArrayList<>();
-        for (Account account : mAllAccounts.getValue()) {
+        final List<Account> filteredAccounts = new ArrayList<>();
+        for (Account account : mAllAccounts.get()) {
             for (PatternMatcher pattern : mAccountRestrictionPatterns.get()) {
                 if (pattern.matches(account.name)) {
                     filteredAccounts.add(account);
@@ -304,7 +287,7 @@
                 }
             }
         }
-        return new AccountManagerResult<>(Collections.unmodifiableList(filteredAccounts));
+        return Collections.unmodifiableList(filteredAccounts);
     }
 
     private void onAccountRestrictionPatternsUpdated(List<PatternMatcher> patternMatchers) {
@@ -313,8 +296,8 @@
         fireOnAccountsChangedNotification();
     }
 
-    private void setAllAccounts(AccountManagerResult<List<Account>> allAccounts) {
-        mAllAccounts = allAccounts;
+    private void setAllAccounts(List<Account> allAccounts) {
+        mAllAccounts.set(allAccounts);
         mFilteredAccounts.set(getFilteredAccounts());
         fireOnAccountsChangedNotification();
     }
@@ -335,8 +318,8 @@
         if (--mUpdateTasksCounter > 0) return;
 
         while (!mCallbacksWaitingForAccountsFetch.isEmpty()) {
-            final Runnable runnable = mCallbacksWaitingForAccountsFetch.remove();
-            runnable.run();
+            final Callback<List<Account>> callback = mCallbacksWaitingForAccountsFetch.remove();
+            callback.onResult(tryGetGoogleAccounts());
         }
     }
 
@@ -350,7 +333,7 @@
         protected Void doInBackground() {
             mAccountRestrictionPatterns.set(
                     mAccountRestrictionPatternReceiver.getRestrictionPatterns());
-            mAllAccounts = getAllAccounts();
+            mAllAccounts.set(getAllAccounts());
             mFilteredAccounts.set(getFilteredAccounts());
             // It's important that countDown() is called on background thread and not in
             // onPostExecute, as UI thread may be blocked in getGoogleAccounts waiting on the latch.
@@ -361,27 +344,27 @@
         @Override
         protected void onPostExecute(Void v) {
             while (!mCallbacksWaitingForAccountsFetch.isEmpty()) {
-                final Runnable runnable = mCallbacksWaitingForAccountsFetch.remove();
-                runnable.run();
+                final Callback<List<Account>> callback = mCallbacksWaitingForAccountsFetch.remove();
+                callback.onResult(tryGetGoogleAccounts());
             }
             fireOnAccountsChangedNotification();
             decrementUpdateCounter();
         }
     }
 
-    private class UpdateAccountsTask extends AsyncTask<AccountManagerResult<List<Account>>> {
+    private class UpdateAccountsTask extends AsyncTask<List<Account>> {
         @Override
         protected void onPreExecute() {
             incrementUpdateCounter();
         }
 
         @Override
-        protected AccountManagerResult<List<Account>> doInBackground() {
+        protected List<Account> doInBackground() {
             return getAllAccounts();
         }
 
         @Override
-        protected void onPostExecute(AccountManagerResult<List<Account>> allAccounts) {
+        protected void onPostExecute(List<Account> allAccounts) {
             setAllAccounts(allAccounts);
             decrementUpdateCounter();
         }
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerResult.java b/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerResult.java
deleted file mode 100644
index aadf43d..0000000
--- a/components/signin/public/android/java/src/org/chromium/components/signin/AccountManagerResult.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.signin;
-
-import androidx.annotation.Nullable;
-
-/**
- * AccountManagerResult encapsulates result of {@link AccountManagerFacade} method call. It is a
- * holder that contains either a value or an AccountManagerDelegateException that occurred during
- * the call.
- *
- * @param <T> The type of value this class should contain.
- */
-class AccountManagerResult<T> {
-    /**
-     * Two possible states of AccountManagerResult are distinguished by mException field. If
-     * mException is null, then this instance is in 'value' state. If mException is non-null,
-     * then this instance is in 'exception' state. Please note that mException and mValue can both
-     * be null - it corresponds to 'value' state with the value being null. Both mValue and
-     * mException can't be non-null, though: this invariant is enforced by constructors.
-     */
-    @Nullable
-    private final T mValue;
-    @Nullable
-    private final AccountManagerDelegateException mException;
-
-    public AccountManagerResult(@Nullable T value) {
-        mValue = value;
-        mException = null;
-    }
-
-    public AccountManagerResult(AccountManagerDelegateException ex) {
-        assert ex != null;
-        mValue = null;
-        mException = ex;
-    }
-
-    @Nullable
-    public T get() throws AccountManagerDelegateException {
-        if (mException != null) {
-            throw mException;
-        }
-        return mValue;
-    }
-
-    public boolean hasValue() {
-        return mException == null;
-    }
-
-    public boolean hasException() {
-        return mException != null;
-    }
-
-    @Nullable
-    public T getValue() {
-        assert hasValue();
-        return mValue;
-    }
-
-    public AccountManagerDelegateException getException() {
-        assert hasException();
-        return mException;
-    }
-}
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index a946027..1708728 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -4,6 +4,7 @@
 
 #include "components/signin/public/base/signin_switches.h"
 
+#include "base/feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 
@@ -35,4 +36,7 @@
 const base::Feature kUseAccountManagerFacade{"kUseAccountManagerFacade",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
+
+const base::Feature kMinorModeSupport{"MinorModeSupport",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
 }  // namespace switches
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h
index c95c07a..a396c9a 100644
--- a/components/signin/public/base/signin_switches.h
+++ b/components/signin/public/base/signin_switches.h
@@ -37,6 +37,9 @@
 // Killswitch for PO2TS migration to AccountManagerFacade.
 extern const base::Feature kUseAccountManagerFacade;
 #endif
+
+// Support for the minor mode.
+extern const base::Feature kMinorModeSupport;
 }  // namespace switches
 
 #endif  // COMPONENTS_SIGNIN_PUBLIC_BASE_SIGNIN_SWITCHES_H_
diff --git a/components/signin/public/identity_manager/identity_manager.h b/components/signin/public/identity_manager/identity_manager.h
index ce7d2a4c..4c1364fef 100644
--- a/components/signin/public/identity_manager/identity_manager.h
+++ b/components/signin/public/identity_manager/identity_manager.h
@@ -486,10 +486,8 @@
  private:
   // These test helpers need to use some of the private methods below.
   friend CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
-                                           const std::string& email);
-  friend CoreAccountInfo SetUnconsentedPrimaryAccount(
-      IdentityManager* identity_manager,
-      const std::string& email);
+                                           const std::string& email,
+                                           ConsentLevel consent_level);
   friend void SetRefreshTokenForPrimaryAccount(
       IdentityManager* identity_manager,
       const std::string& token_value);
diff --git a/components/signin/public/identity_manager/identity_manager_unittest.cc b/components/signin/public/identity_manager/identity_manager_unittest.cc
index 0599721..4bbec9e8 100644
--- a/components/signin/public/identity_manager/identity_manager_unittest.cc
+++ b/components/signin/public/identity_manager/identity_manager_unittest.cc
@@ -562,7 +562,7 @@
 TEST_F(IdentityManagerTest, PrimaryAccountInfoAfterSignin) {
   ClearPrimaryAccount(identity_manager());
 
-  SetPrimaryAccount(identity_manager(), kTestEmail);
+  SetPrimaryAccount(identity_manager(), kTestEmail, ConsentLevel::kSync);
   auto event = identity_manager_observer()->GetPrimaryAccountChangedEvent();
   EXPECT_EQ(PrimaryAccountChangeEvent::Type::kSet,
             event.GetEventTypeFor(ConsentLevel::kSync));
@@ -598,7 +598,7 @@
   ClearPrimaryAccount(identity_manager());
   // First ensure that the user is signed in from the POV of the
   // IdentityManager.
-  SetPrimaryAccount(identity_manager(), kTestEmail);
+  SetPrimaryAccount(identity_manager(), kTestEmail, ConsentLevel::kSync);
 
   // Sign the user out and check that the IdentityManager responds
   // appropriately.
@@ -642,7 +642,7 @@
   ClearPrimaryAccount(identity_manager());
   // First ensure that the user is signed in from the POV of the
   // IdentityManager.
-  SetPrimaryAccount(identity_manager(), kTestEmail);
+  SetPrimaryAccount(identity_manager(), kTestEmail, ConsentLevel::kSync);
 
   identity_manager()->account_fetcher_service_->EnableAccountRemovalForTest();
   // Revoke the primary's account credentials from the token service and
diff --git a/components/signin/public/identity_manager/identity_test_environment.cc b/components/signin/public/identity_manager/identity_test_environment.cc
index 1d9eac7..e08bc67 100644
--- a/components/signin/public/identity_manager/identity_test_environment.cc
+++ b/components/signin/public/identity_manager/identity_test_environment.cc
@@ -376,12 +376,14 @@
 
 CoreAccountInfo IdentityTestEnvironment::SetPrimaryAccount(
     const std::string& email) {
-  return signin::SetPrimaryAccount(identity_manager(), email);
+  return signin::SetPrimaryAccount(identity_manager(), email,
+                                   signin::ConsentLevel::kSync);
 }
 
 CoreAccountInfo IdentityTestEnvironment::SetUnconsentedPrimaryAccount(
     const std::string& email) {
-  return signin::SetUnconsentedPrimaryAccount(identity_manager(), email);
+  return signin::SetPrimaryAccount(identity_manager(), email,
+                                   signin::ConsentLevel::kSignin);
 }
 
 void IdentityTestEnvironment::SetRefreshTokenForPrimaryAccount() {
diff --git a/components/signin/public/identity_manager/identity_test_utils.cc b/components/signin/public/identity_manager/identity_test_utils.cc
index 5562e06..f5aa574 100644
--- a/components/signin/public/identity_manager/identity_test_utils.cc
+++ b/components/signin/public/identity_manager/identity_test_utils.cc
@@ -16,7 +16,6 @@
 #include "components/signin/internal/identity_manager/profile_oauth2_token_service.h"
 #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h"
 #include "components/signin/public/base/list_accounts_test_utils.h"
-#include "components/signin/public/identity_manager/consent_level.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
 #include "components/signin/public/identity_manager/test_identity_manager_observer.h"
@@ -132,26 +131,9 @@
 }
 
 CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
-                                  const std::string& email) {
-  DCHECK(!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
-  PrimaryAccountManager* primary_account_manager =
-      identity_manager->GetPrimaryAccountManager();
-  DCHECK(!primary_account_manager->HasPrimaryAccount(ConsentLevel::kSync));
-
-  AccountInfo account_info =
-      EnsureAccountExists(identity_manager->GetAccountTrackerService(), email);
-  DCHECK(!account_info.gaia.empty());
-
-  primary_account_manager->SetSyncPrimaryAccountInfo(account_info);
-
-  DCHECK(primary_account_manager->HasPrimaryAccount(ConsentLevel::kSync));
-  DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
-  return identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync);
-}
-
-CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager,
-                                             const std::string& email) {
-  DCHECK(!identity_manager->HasPrimaryAccount(ConsentLevel::kSignin));
+                                  const std::string& email,
+                                  ConsentLevel consent_level) {
+  DCHECK(!identity_manager->HasPrimaryAccount(consent_level));
 
   AccountInfo account_info =
       EnsureAccountExists(identity_manager->GetAccountTrackerService(), email);
@@ -159,46 +141,51 @@
 
   PrimaryAccountManager* primary_account_manager =
       identity_manager->GetPrimaryAccountManager();
-  primary_account_manager->SetUnconsentedPrimaryAccountInfo(account_info);
+  switch (consent_level) {
+    case ConsentLevel::kSync:
+      primary_account_manager->SetSyncPrimaryAccountInfo(account_info);
+      break;
+    case ConsentLevel::kSignin:
+      primary_account_manager->SetUnconsentedPrimaryAccountInfo(account_info);
+  }
 
-  DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSignin));
-  DCHECK_EQ(
-      account_info.gaia,
-      identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
-          .gaia);
-  return identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
+  DCHECK(identity_manager->HasPrimaryAccount(consent_level));
+  DCHECK_EQ(account_info.gaia,
+            identity_manager->GetPrimaryAccountInfo(consent_level).gaia);
+  return identity_manager->GetPrimaryAccountInfo(consent_level);
 }
 
 void SetRefreshTokenForPrimaryAccount(IdentityManager* identity_manager,
                                       const std::string& token_value) {
-  DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
+  DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync));
   CoreAccountId account_id =
-      identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync);
+      identity_manager->GetPrimaryAccountId(ConsentLevel::kSync);
   SetRefreshTokenForAccount(identity_manager, account_id, token_value);
 }
 
 void SetInvalidRefreshTokenForPrimaryAccount(
     IdentityManager* identity_manager) {
-  DCHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync));
+  DCHECK(identity_manager->HasPrimaryAccount(ConsentLevel::kSync));
   CoreAccountId account_id =
-      identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync);
+      identity_manager->GetPrimaryAccountId(ConsentLevel::kSync);
 
   SetInvalidRefreshTokenForAccount(identity_manager, account_id);
 }
 
 void RemoveRefreshTokenForPrimaryAccount(IdentityManager* identity_manager) {
-  if (!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync))
+  if (!identity_manager->HasPrimaryAccount(ConsentLevel::kSync))
     return;
 
   CoreAccountId account_id =
-      identity_manager->GetPrimaryAccountId(signin::ConsentLevel::kSync);
+      identity_manager->GetPrimaryAccountId(ConsentLevel::kSync);
 
   RemoveRefreshTokenForAccount(identity_manager, account_id);
 }
 
 AccountInfo MakePrimaryAccountAvailable(IdentityManager* identity_manager,
                                         const std::string& email) {
-  CoreAccountInfo account_info = SetPrimaryAccount(identity_manager, email);
+  CoreAccountInfo account_info =
+      SetPrimaryAccount(identity_manager, email, ConsentLevel::kSync);
   SetRefreshTokenForPrimaryAccount(identity_manager);
   base::Optional<AccountInfo> primary_account_info =
       identity_manager
diff --git a/components/signin/public/identity_manager/identity_test_utils.h b/components/signin/public/identity_manager/identity_test_utils.h
index 619d83b..1c58c025 100644
--- a/components/signin/public/identity_manager/identity_test_utils.h
+++ b/components/signin/public/identity_manager/identity_test_utils.h
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "build/build_config.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/signin/public/identity_manager/consent_level.h"
 
 namespace network {
 class TestURLLoaderFactory;
@@ -39,18 +40,15 @@
 void WaitForRefreshTokensLoaded(IdentityManager* identity_manager);
 
 // Sets the primary account (which must not already be set) to the given email
-// address, generating a GAIA ID that corresponds uniquely to that email
-// address. On non-ChromeOS, results in the firing of the IdentityManager and
-// PrimaryAccountManager callbacks for signin success. Blocks until the primary
-// account is set. Returns the CoreAccountInfo of the newly-set account.
+// address with corresponding consent level, generating a GAIA ID that
+// corresponds uniquely to that email address. On non-ChromeOS, results in the
+// firing of the IdentityManager and PrimaryAccountManager callbacks for signin
+// success. Blocks until the primary account is set. Returns the CoreAccountInfo
+// of the newly-set account.
 // NOTE: See disclaimer at top of file re: direct usage.
 CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
-                                  const std::string& email);
-
-// As above, but adds an "unconsented" primary account. See ./README.md for
-// the distinction between primary and unconsented primary accounts.
-CoreAccountInfo SetUnconsentedPrimaryAccount(IdentityManager* identity_manager,
-                                             const std::string& email);
+                                  const std::string& email,
+                                  ConsentLevel consent_level);
 
 // Sets a refresh token for the primary account (which must already be set).
 // Blocks until the refresh token is set. If |token_value| is empty a default
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
index 68018a6b2..22168e2 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.cc
@@ -90,7 +90,6 @@
 void ContentSubresourceFilterThrottleManager::CreateForWebContents(
     content::WebContents* web_contents,
     SubresourceFilterProfileContext* profile_context,
-    infobars::ContentInfoBarManager* infobar_manager,
     scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager,
     VerifiedRulesetDealer::Handle* dealer_handle) {
   if (!base::FeatureList::IsEnabled(kSafeBrowsingSubresourceFilter))
@@ -102,8 +101,7 @@
   web_contents->SetUserData(
       kContentSubresourceFilterThrottleManagerWebContentsUserDataKey,
       std::make_unique<ContentSubresourceFilterThrottleManager>(
-          profile_context, infobar_manager, database_manager, dealer_handle,
-          web_contents));
+          profile_context, database_manager, dealer_handle, web_contents));
 }
 
 // static
@@ -118,7 +116,6 @@
 ContentSubresourceFilterThrottleManager::
     ContentSubresourceFilterThrottleManager(
         SubresourceFilterProfileContext* profile_context,
-        infobars::ContentInfoBarManager* infobar_manager,
         scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
             database_manager,
         VerifiedRulesetDealer::Handle* dealer_handle,
@@ -130,8 +127,7 @@
       profile_interaction_manager_(
           std::make_unique<subresource_filter::ProfileInteractionManager>(
               web_contents,
-              profile_context,
-              infobar_manager)) {
+              profile_context)) {
   SubresourceFilterObserverManager::CreateForWebContents(web_contents);
   scoped_observation_.Observe(
       SubresourceFilterObserverManager::FromWebContents(web_contents));
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
index ccf1d3d..64707b3 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager.h
@@ -34,10 +34,6 @@
 class RenderFrameHost;
 }  // namespace content
 
-namespace infobars {
-class ContentInfoBarManager;
-}
-
 namespace subresource_filter {
 
 class AsyncDocumentSubresourceFilter;
@@ -97,7 +93,6 @@
   static void CreateForWebContents(
       content::WebContents* web_contents,
       SubresourceFilterProfileContext* profile_context,
-      infobars::ContentInfoBarManager* infobar_manager,
       scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
           database_manager,
       VerifiedRulesetDealer::Handle* dealer_handle);
@@ -107,7 +102,6 @@
 
   ContentSubresourceFilterThrottleManager(
       SubresourceFilterProfileContext* profile_context,
-      infobars::ContentInfoBarManager* infobar_manager,
       scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
           database_manager,
       VerifiedRulesetDealer::Handle* dealer_handle,
diff --git a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
index f50c2a2..7c41686 100644
--- a/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
+++ b/components/subresource_filter/content/browser/content_subresource_filter_throttle_manager_unittest.cc
@@ -211,13 +211,10 @@
     // ShowNotification() being invoked.
     throttle_manager_test_support_->SetShouldUseSmartUI(false);
 
-    infobar_manager_ =
-        std::make_unique<infobars::ContentInfoBarManager>(web_contents);
     throttle_manager_ =
         std::make_unique<ContentSubresourceFilterThrottleManager>(
             throttle_manager_test_support_->profile_context(),
-            infobar_manager_.get(), /*database_manager=*/nullptr,
-            dealer_handle_.get(), web_contents);
+            /*database_manager=*/nullptr, dealer_handle_.get(), web_contents);
 
     Observe(web_contents);
   }
@@ -286,14 +283,16 @@
     return content_settings->IsContentBlocked(ContentSettingsType::ADS);
   }
 
-  bool presenting_ads_blocked_infobar() const {
-    if (infobar_manager_->infobar_count() == 0)
+  bool presenting_ads_blocked_infobar() {
+    auto* infobar_manager = infobars::ContentInfoBarManager::FromWebContents(
+        content::RenderViewHostTestHarness::web_contents());
+    if (infobar_manager->infobar_count() == 0)
       return false;
 
     // No infobars other than the ads blocked infobar should be displayed in the
     // context of these tests.
-    EXPECT_EQ(infobar_manager_->infobar_count(), 1u);
-    auto* infobar = infobar_manager_->infobar_at(0);
+    EXPECT_EQ(infobar_manager->infobar_count(), 1u);
+    auto* infobar = infobar_manager->infobar_at(0);
     EXPECT_EQ(infobar->delegate()->GetIdentifier(),
               infobars::InfoBarDelegate::ADS_BLOCKED_INFOBAR_DELEGATE_ANDROID);
 
@@ -371,7 +370,6 @@
   testing::TestRulesetCreator test_ruleset_creator_;
   testing::TestRulesetPair test_ruleset_pair_;
   std::unique_ptr<ThrottleManagerTestSupport> throttle_manager_test_support_;
-  std::unique_ptr<infobars::ContentInfoBarManager> infobar_manager_;
 
   std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle_;
 
@@ -411,6 +409,27 @@
 #endif
 }
 
+#if defined(OS_ANDROID)
+TEST_P(ContentSubresourceFilterThrottleManagerTest,
+       NoCrashWhenInfoBarManagerIsNotPresent) {
+  auto* web_contents = RenderViewHostTestHarness::web_contents();
+  web_contents->RemoveUserData(infobars::ContentInfoBarManager::UserDataKey());
+
+  // Commit a navigation that triggers page level activation.
+  NavigateAndCommitMainFrame(GURL(kTestURLWithActivation));
+  ExpectActivationSignalForFrame(main_rfh(), true /* expect_activation */);
+
+  // A disallowed subframe navigation should be successfully filtered, and the
+  // lack of infobar manager should not cause a crash.
+  CreateSubframeWithTestNavigation(
+      GURL("https://www.example.com/disallowed.html"), main_rfh());
+  EXPECT_EQ(content::NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE,
+            SimulateStartAndGetResult(navigation_simulator()));
+
+  EXPECT_TRUE(ads_blocked_in_content_settings());
+}
+#endif
+
 TEST_P(ContentSubresourceFilterThrottleManagerTest, NoPageActivation) {
   // This test assumes that we're not in DryRun mode.
   base::test::ScopedFeatureList scoped_feature;
@@ -916,8 +935,6 @@
   ThrottleManagerTestSupport throttle_manager_test_support(web_contents.get());
   SubresourceFilterProfileContext* profile_context =
       throttle_manager_test_support.profile_context();
-  auto infobar_manager =
-      std::make_unique<infobars::ContentInfoBarManager>(web_contents.get());
 
   {
     base::test::ScopedFeatureList scoped_feature;
@@ -926,8 +943,8 @@
     // CreateForWebContents() should not do anything if the subresource filter
     // feature is not enabled.
     ContentSubresourceFilterThrottleManager::CreateForWebContents(
-        web_contents.get(), profile_context, infobar_manager.get(),
-        /*database_manager=*/nullptr, dealer_handle());
+        web_contents.get(), profile_context, /*database_manager=*/nullptr,
+        dealer_handle());
     EXPECT_EQ(ContentSubresourceFilterThrottleManager::FromWebContents(
                   web_contents.get()),
               nullptr);
@@ -936,7 +953,7 @@
   // If the subresource filter feature is enabled (as it is by default),
   // CreateForWebContents() should create and attach an instance.
   ContentSubresourceFilterThrottleManager::CreateForWebContents(
-      web_contents.get(), profile_context, infobar_manager.get(),
+      web_contents.get(), profile_context,
       /*database_manager=*/nullptr, dealer_handle());
   auto* throttle_manager =
       ContentSubresourceFilterThrottleManager::FromWebContents(
@@ -945,7 +962,7 @@
 
   // A second call should not attach a different instance.
   ContentSubresourceFilterThrottleManager::CreateForWebContents(
-      web_contents.get(), profile_context, infobar_manager.get(),
+      web_contents.get(), profile_context,
       /*database_manager=*/nullptr, dealer_handle());
   EXPECT_EQ(ContentSubresourceFilterThrottleManager::FromWebContents(
                 web_contents.get()),
diff --git a/components/subresource_filter/content/browser/profile_interaction_manager.cc b/components/subresource_filter/content/browser/profile_interaction_manager.cc
index 6b8321109..f3a96572b 100644
--- a/components/subresource_filter/content/browser/profile_interaction_manager.cc
+++ b/components/subresource_filter/content/browser/profile_interaction_manager.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/web_contents.h"
 
 #if defined(OS_ANDROID)
+#include "components/infobars/content/content_infobar_manager.h"  // nogncheck
 #include "components/subresource_filter/content/browser/ads_blocked_infobar_delegate.h"
 #endif
 
@@ -24,15 +25,9 @@
 
 ProfileInteractionManager::ProfileInteractionManager(
     content::WebContents* web_contents,
-    SubresourceFilterProfileContext* profile_context,
-    infobars::ContentInfoBarManager* infobar_manager)
+    SubresourceFilterProfileContext* profile_context)
     : content::WebContentsObserver(web_contents),
-      profile_context_(profile_context)
-#if defined(OS_ANDROID)
-      ,
-      infobar_manager_(infobar_manager)
-#endif
-{
+      profile_context_(profile_context) {
   DCHECK(web_contents);
 }
 
@@ -133,7 +128,12 @@
   if (profile_context_->settings_manager()->ShouldShowUIForSite(
           top_level_url)) {
 #if defined(OS_ANDROID)
-    subresource_filter::AdsBlockedInfobarDelegate::Create(infobar_manager_);
+    // NOTE: It is acceptable for the embedder to not have installed an infobar
+    // manager.
+    if (auto* infobar_manager =
+            infobars::ContentInfoBarManager::FromWebContents(web_contents())) {
+      subresource_filter::AdsBlockedInfobarDelegate::Create(infobar_manager);
+    }
 #endif
 
     // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
diff --git a/components/subresource_filter/content/browser/profile_interaction_manager.h b/components/subresource_filter/content/browser/profile_interaction_manager.h
index 2724fec..c976b122 100644
--- a/components/subresource_filter/content/browser/profile_interaction_manager.h
+++ b/components/subresource_filter/content/browser/profile_interaction_manager.h
@@ -16,10 +16,6 @@
 class WebContents;
 }  // namespace content
 
-namespace infobars {
-class ContentInfoBarManager;
-}
-
 namespace subresource_filter {
 
 class SubresourceFilterProfileContext;
@@ -32,8 +28,7 @@
       public SubresourceFilterSafeBrowsingActivationThrottle::Delegate {
  public:
   ProfileInteractionManager(content::WebContents* web_contents,
-                            SubresourceFilterProfileContext* profile_context,
-                            infobars::ContentInfoBarManager* infobar_manager);
+                            SubresourceFilterProfileContext* profile_context);
   ~ProfileInteractionManager() override;
 
   ProfileInteractionManager(const ProfileInteractionManager&) = delete;
@@ -54,7 +49,9 @@
 
   // Invoked when a notification should potentially be shown to the user that
   // ads are being blocked on this page. Will make the final determination as to
-  // whether the notification should be shown.
+  // whether the notification should be shown. On Android this will show an
+  // infobar if appropriate and if an infobar::ContentInfoBarManager instance
+  // has been installed in web_contents() by the embedder.
   void MaybeShowNotification();
 
   // SubresourceFilterSafeBrowsingActivationThrottle::Delegate:
@@ -67,11 +64,6 @@
   // Unowned and must outlive this object.
   SubresourceFilterProfileContext* profile_context_ = nullptr;
 
-#if defined(OS_ANDROID)
-  // Unowned and must outlive this object.
-  infobars::ContentInfoBarManager* infobar_manager_ = nullptr;
-#endif
-
   bool ads_violation_triggered_for_last_committed_navigation_ = false;
 };
 
diff --git a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
index 77be7de..af6c7f8 100644
--- a/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_safe_browsing_activation_throttle_unittest.cc
@@ -166,13 +166,10 @@
     auto* contents = RenderViewHostTestHarness::web_contents();
     throttle_manager_test_support_ =
         std::make_unique<ThrottleManagerTestSupport>(contents);
-    infobar_manager_ =
-        std::make_unique<infobars::ContentInfoBarManager>(contents);
     throttle_manager_ =
         std::make_unique<ContentSubresourceFilterThrottleManager>(
             throttle_manager_test_support_->profile_context(),
-            infobar_manager_.get(), /*database_manager=*/nullptr,
-            ruleset_dealer_.get(), contents);
+            /*database_manager=*/nullptr, ruleset_dealer_.get(), contents);
     fake_safe_browsing_database_ = new FakeSafeBrowsingDatabaseManager();
     NavigateAndCommit(GURL("https://test.com"));
     Observe(contents);
@@ -341,14 +338,16 @@
     return &scoped_configuration_;
   }
 
-  bool presenting_ads_blocked_infobar() const {
-    if (infobar_manager_->infobar_count() == 0)
+  bool presenting_ads_blocked_infobar() {
+    auto* infobar_manager = infobars::ContentInfoBarManager::FromWebContents(
+        content::RenderViewHostTestHarness::web_contents());
+    if (infobar_manager->infobar_count() == 0)
       return false;
 
     // No infobars other than the ads blocked infobar should be displayed in the
     // context of these tests.
-    EXPECT_EQ(infobar_manager_->infobar_count(), 1u);
-    auto* infobar = infobar_manager_->infobar_at(0);
+    EXPECT_EQ(infobar_manager->infobar_count(), 1u);
+    auto* infobar = infobar_manager->infobar_at(0);
     EXPECT_EQ(infobar->delegate()->GetIdentifier(),
               infobars::InfoBarDelegate::ADS_BLOCKED_INFOBAR_DELEGATE_ANDROID);
 
@@ -369,7 +368,6 @@
 
   std::unique_ptr<content::NavigationSimulator> navigation_simulator_;
   std::unique_ptr<ThrottleManagerTestSupport> throttle_manager_test_support_;
-  std::unique_ptr<infobars::ContentInfoBarManager> infobar_manager_;
   std::unique_ptr<TestSubresourceFilterObserver> observer_;
   scoped_refptr<FakeSafeBrowsingDatabaseManager> fake_safe_browsing_database_;
   base::HistogramTester tester_;
diff --git a/components/subresource_filter/content/browser/subresource_filter_test_harness.cc b/components/subresource_filter/content/browser/subresource_filter_test_harness.cc
index ce55742..8a73fed7 100644
--- a/components/subresource_filter/content/browser/subresource_filter_test_harness.cc
+++ b/components/subresource_filter/content/browser/subresource_filter_test_harness.cc
@@ -92,11 +92,10 @@
   throttle_manager_test_support_ =
       std::make_unique<ThrottleManagerTestSupport>(web_contents());
   database_manager_ = base::MakeRefCounted<FakeSafeBrowsingDatabaseManager>();
-  infobar_manager_ =
-      std::make_unique<infobars::ContentInfoBarManager>(web_contents());
+  infobars::ContentInfoBarManager::CreateForWebContents(web_contents());
   ContentSubresourceFilterThrottleManager::CreateForWebContents(
       web_contents(), throttle_manager_test_support_->profile_context(),
-      infobar_manager_.get(), database_manager_, dealer);
+      database_manager_, dealer);
 
   // Observe web_contents() to add subresource filter navigation throttles at
   // the start of navigations.
diff --git a/components/subresource_filter/content/browser/throttle_manager_test_support.cc b/components/subresource_filter/content/browser/throttle_manager_test_support.cc
index d4ab654..ed5402c8b 100644
--- a/components/subresource_filter/content/browser/throttle_manager_test_support.cc
+++ b/components/subresource_filter/content/browser/throttle_manager_test_support.cc
@@ -7,6 +7,7 @@
 #include "components/content_settings/browser/page_specific_content_settings.h"
 #include "components/content_settings/browser/test_page_specific_content_settings_delegate.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/infobars/content/content_infobar_manager.h"
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_content_settings_manager.h"
 #include "components/subresource_filter/content/browser/subresource_filter_profile_context.h"
@@ -30,6 +31,7 @@
       std::make_unique<
           content_settings::TestPageSpecificContentSettingsDelegate>(
           /*prefs=*/nullptr, settings_map_.get()));
+  infobars::ContentInfoBarManager::CreateForWebContents(web_contents);
 }
 
 ThrottleManagerTestSupport::~ThrottleManagerTestSupport() {
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc
index d7758b3..68748ca 100644
--- a/components/sync/driver/sync_service_crypto.cc
+++ b/components/sync/driver/sync_service_crypto.cc
@@ -392,8 +392,7 @@
 
 ModelTypeSet SyncServiceCrypto::GetEncryptedDataTypes() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(state_.encrypted_types.Has(PASSWORDS));
-  DCHECK(state_.encrypted_types.Has(WIFI_CONFIGURATIONS));
+  DCHECK(state_.encrypted_types.HasAll(AlwaysEncryptedUserTypes()));
   // We may be called during the setup process before we're
   // initialized. In this case, we default to the sensitive types.
   return state_.encrypted_types;
@@ -526,8 +525,7 @@
            << ModelTypeSetToString(state_.encrypted_types)
            << " (encrypt everything is set to "
            << (state_.encrypt_everything ? "true" : "false") << ")";
-  DCHECK(state_.encrypted_types.Has(PASSWORDS));
-  DCHECK(state_.encrypted_types.Has(WIFI_CONFIGURATIONS));
+  DCHECK(state_.encrypted_types.HasAll(AlwaysEncryptedUserTypes()));
 
   delegate_->CryptoStateChanged();
 }
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
index dbbfa3a..b391e3d 100644
--- a/components/sync/driver/sync_user_settings_impl.cc
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -270,7 +270,7 @@
     return true;
   const ModelTypeSet preferred_types = GetPreferredDataTypes();
   const ModelTypeSet encrypted_types = GetEncryptedDataTypes();
-  DCHECK(encrypted_types.Has(PASSWORDS));
+  DCHECK(encrypted_types.HasAll(AlwaysEncryptedUserTypes()));
   return !Intersection(preferred_types, encrypted_types).Empty();
 }
 
diff --git a/components/sync/engine/get_updates_processor.cc b/components/sync/engine/get_updates_processor.cc
index 058cca5..6ba5fb1 100644
--- a/components/sync/engine/get_updates_processor.cc
+++ b/components/sync/engine/get_updates_processor.cc
@@ -5,8 +5,10 @@
 #include "components/sync/engine/get_updates_processor.h"
 
 #include <stddef.h>
+#include <map>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
@@ -347,12 +349,9 @@
       context.CopyFrom(gu_response.context_mutations(context_iter->second));
 
     if (update_handler_iter != update_handler_map_->end()) {
-      SyncerError result =
-          update_handler_iter->second->ProcessGetUpdatesResponse(
-              gu_response.new_progress_marker(progress_marker_iter->second),
-              context, updates_iter->second, status_controller);
-      if (result.value() != SyncerError::SYNCER_OK)
-        return result;
+      update_handler_iter->second->ProcessGetUpdatesResponse(
+          gu_response.new_progress_marker(progress_marker_iter->second),
+          context, updates_iter->second, status_controller);
     } else {
       DLOG(WARNING) << "Ignoring received updates of a type we can't handle.  "
                     << "Type is: " << ModelTypeToString(type);
diff --git a/components/sync/engine/model_type_registry.cc b/components/sync/engine/model_type_registry.cc
index 9b6606ff..24913feeb 100644
--- a/components/sync/engine/model_type_registry.cc
+++ b/components/sync/engine/model_type_registry.cc
@@ -12,8 +12,6 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "components/sync/engine/commit_queue.h"
 #include "components/sync/engine/data_type_activation_response.h"
 #include "components/sync/engine/model_type_processor.h"
 #include "components/sync/engine/model_type_worker.h"
@@ -22,35 +20,6 @@
 
 namespace syncer {
 
-namespace {
-
-class CommitQueueProxy : public CommitQueue {
- public:
-  CommitQueueProxy(const base::WeakPtr<CommitQueue>& worker,
-                   const scoped_refptr<base::SequencedTaskRunner>& sync_thread);
-  ~CommitQueueProxy() override;
-
-  void NudgeForCommit() override;
-
- private:
-  base::WeakPtr<CommitQueue> worker_;
-  scoped_refptr<base::SequencedTaskRunner> sync_thread_;
-};
-
-CommitQueueProxy::CommitQueueProxy(
-    const base::WeakPtr<CommitQueue>& worker,
-    const scoped_refptr<base::SequencedTaskRunner>& sync_thread)
-    : worker_(worker), sync_thread_(sync_thread) {}
-
-CommitQueueProxy::~CommitQueueProxy() {}
-
-void CommitQueueProxy::NudgeForCommit() {
-  sync_thread_->PostTask(FROM_HERE,
-                         base::BindOnce(&CommitQueue::NudgeForCommit, worker_));
-}
-
-}  // namespace
-
 ModelTypeRegistry::ModelTypeRegistry(
     NudgeHandler* nudge_handler,
     CancelationSignal* cancelation_signal,
@@ -73,20 +42,11 @@
   DCHECK(commit_contributor_map_.find(type) == commit_contributor_map_.end());
   DVLOG(1) << "Enabling an off-thread sync type: " << ModelTypeToString(type);
 
-  // Save a raw pointer to the processor for connecting later.
-  ModelTypeProcessor* type_processor =
-      activation_response->type_processor.get();
-
-  bool initial_sync_done =
-      activation_response->model_type_state.initial_sync_done();
-
   auto worker = std::make_unique<ModelTypeWorker>(
       type, activation_response->model_type_state,
-      /*trigger_initial_sync=*/!initial_sync_done,
       sync_encryption_handler_->GetCryptographer(),
       /*encryption_enabled=*/encrypted_types_.Has(type), passphrase_type_,
-      nudge_handler_, std::move(activation_response->type_processor),
-      cancelation_signal_);
+      nudge_handler_, cancelation_signal_);
 
   // Save a raw pointer and add the worker to our structures.
   ModelTypeWorker* worker_ptr = worker.get();
@@ -94,9 +54,7 @@
   update_handler_map_.insert(std::make_pair(type, worker_ptr));
   commit_contributor_map_.insert(std::make_pair(type, worker_ptr));
 
-  // Initialize Processor -> Worker communication channel.
-  type_processor->ConnectSync(std::make_unique<CommitQueueProxy>(
-      worker_ptr->AsWeakPtr(), base::SequencedTaskRunnerHandle::Get()));
+  worker_ptr->ConnectSync(std::move(activation_response->type_processor));
 }
 
 void ModelTypeRegistry::DisconnectDataType(ModelType type) {
@@ -189,23 +147,11 @@
     const KeyDerivationParams& key_derivation_params,
     const sync_pb::EncryptedData& pending_keys) {}
 
-void ModelTypeRegistry::OnPassphraseAccepted() {
-  for (const auto& worker : connected_model_type_workers_) {
-    if (encrypted_types_.Has(worker->GetModelType())) {
-      worker->EncryptionAcceptedMaybeApplyUpdates();
-    }
-  }
-}
+void ModelTypeRegistry::OnPassphraseAccepted() {}
 
 void ModelTypeRegistry::OnTrustedVaultKeyRequired() {}
 
-void ModelTypeRegistry::OnTrustedVaultKeyAccepted() {
-  for (const auto& worker : connected_model_type_workers_) {
-    if (encrypted_types_.Has(worker->GetModelType())) {
-      worker->EncryptionAcceptedMaybeApplyUpdates();
-    }
-  }
-}
+void ModelTypeRegistry::OnTrustedVaultKeyAccepted() {}
 
 void ModelTypeRegistry::OnBootstrapTokenUpdated(
     const std::string& bootstrap_token,
diff --git a/components/sync/engine/model_type_worker.cc b/components/sync/engine/model_type_worker.cc
index 1d137c4..2b3b5d1c8 100644
--- a/components/sync/engine/model_type_worker.cc
+++ b/components/sync/engine/model_type_worker.cc
@@ -19,6 +19,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/trace_event/memory_usage_estimator.h"
 #include "components/sync/base/client_tag_hash.h"
@@ -49,6 +50,26 @@
 
 const int kMinGuResponsesToIgnoreKey = 50;
 
+// A proxy which can be called from any sequence and delegates the work to the
+// commit queue injected on construction.
+class CommitQueueProxy : public CommitQueue {
+ public:
+  // Must be called from the sequence where |commit_queue| lives.
+  explicit CommitQueueProxy(const base::WeakPtr<CommitQueue>& commit_queue)
+      : commit_queue_(commit_queue) {}
+  ~CommitQueueProxy() override = default;
+
+  void NudgeForCommit() override {
+    commit_queue_thread_->PostTask(
+        FROM_HERE, base::BindOnce(&CommitQueue::NudgeForCommit, commit_queue_));
+  }
+
+ private:
+  const base::WeakPtr<CommitQueue> commit_queue_;
+  const scoped_refptr<base::SequencedTaskRunner> commit_queue_thread_ =
+      base::SequencedTaskRunnerHandle::Get();
+};
+
 void AdaptClientTagForFullUpdateData(ModelType model_type,
                                      syncer::EntityData* data) {
   // Server does not send any client tags for wallet data entities or offer data
@@ -150,15 +171,12 @@
 ModelTypeWorker::ModelTypeWorker(
     ModelType type,
     const sync_pb::ModelTypeState& initial_state,
-    bool trigger_initial_sync,
     Cryptographer* cryptographer,
     bool encryption_enabled,
     PassphraseType passphrase_type,
     NudgeHandler* nudge_handler,
-    std::unique_ptr<ModelTypeProcessor> model_type_processor,
     CancelationSignal* cancelation_signal)
     : type_(type),
-      model_type_processor_(std::move(model_type_processor)),
       cryptographer_(cryptographer),
       nudge_handler_(nudge_handler),
       cancelation_signal_(cancelation_signal),
@@ -167,33 +185,12 @@
       passphrase_type_(passphrase_type),
       min_gu_responses_to_ignore_key_(kMinGuResponsesToIgnoreKey) {
   DCHECK(cryptographer_);
-  DCHECK(model_type_processor_);
-  DCHECK(type_ != PASSWORDS || encryption_enabled_);
+  DCHECK(!AlwaysEncryptedUserTypes().Has(type_) || encryption_enabled_);
 
   if (!CommitOnlyTypes().Has(GetModelType())) {
     DCHECK_EQ(type, GetModelTypeFromSpecificsFieldNumber(
                         initial_state.progress_marker().data_type_id()));
   }
-
-  // Request an initial sync if it hasn't been completed yet.
-  if (trigger_initial_sync) {
-    nudge_handler_->NudgeForInitialDownload(type_);
-  }
-
-  // The persisted ModelTypeState might not have the most recent encryption
-  // key name, e.g. due to disk corruption, or because the cryptographer was
-  // updated before this worker was constructed. There will be no calls to
-  // EncryptionAcceptedMaybeApplyUpdates() or OnCryptographerChange() to update
-  // the key name in this case, so do it manually here.
-  bool had_outdated_key_name = UpdateTypeEncryptionKeyName();
-  // If the key was outdated and initial sync is already done, the processor
-  // risks relying on the incorrect key, so call ApplyPendingUpdates() to push
-  // the correct one. If initial sync isn't done yet, there's no risk and the
-  // first call to ApplyUpdates() will push the now-updated key.
-  if (had_outdated_key_name && model_type_state_.initial_sync_done()) {
-    // Might be a no-op as per BlockForEncryption().
-    ApplyPendingUpdates();
-  }
 }
 
 ModelTypeWorker::~ModelTypeWorker() {
@@ -201,7 +198,40 @@
       std::string("Sync.UndecryptedEntitiesOnDataTypeDisabled.") +
           ModelTypeToHistogramSuffix(type_),
       entries_pending_decryption_.size());
-  model_type_processor_->DisconnectSync();
+  if (model_type_processor_) {
+    // This will always be the case in production today.
+    model_type_processor_->DisconnectSync();
+  }
+}
+
+void ModelTypeWorker::ConnectSync(
+    std::unique_ptr<ModelTypeProcessor> model_type_processor) {
+  DCHECK(!model_type_processor_);
+  DCHECK(model_type_processor);
+
+  model_type_processor_ = std::move(model_type_processor);
+  // TODO(victorvianna): CommitQueueProxy is only needed by the
+  // ModelTypeProcessorProxy implementation, so it could possibly be moved
+  // there % changing ConnectSync() to take a raw pointer. This then allows
+  // removing base::test::SingleThreadTaskEnvironment from the unit test.
+  model_type_processor_->ConnectSync(
+      std::make_unique<CommitQueueProxy>(weak_ptr_factory_.GetWeakPtr()));
+
+  if (!model_type_state_.initial_sync_done()) {
+    nudge_handler_->NudgeForInitialDownload(type_);
+  }
+
+  // |model_type_state_| might have an outdated encryption key name, e.g.
+  // because |cryptographer_| was updated before this worker was constructed.
+  // OnCryptographerChange() might never be called, so update the key manually
+  // here and push it to the processor. Only push if initial sync is done,
+  // otherwise this violates some of the processor assumptions; if initial sync
+  // isn't done, the now-updated key will be pushed on the first ApplyUpdates()
+  // call anyway.
+  bool had_outdated_key_name = UpdateTypeEncryptionKeyName();
+  if (had_outdated_key_name && model_type_state_.initial_sync_done()) {
+    SendPendingUpdatesToProcessorIfReady();
+  }
 }
 
 ModelType ModelTypeWorker::GetModelType() const {
@@ -211,15 +241,33 @@
 
 void ModelTypeWorker::EnableEncryption() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (encryption_enabled_) {
+    // No-op.
+    return;
+  }
+
   encryption_enabled_ = true;
-  UpdateTypeEncryptionKeyName();
+  // UpdateTypeEncryptionKeyName() might return false if the cryptographer does
+  // not have a default key yet.
+  if (UpdateTypeEncryptionKeyName()) {
+    // Push the new key name to the processor.
+    SendPendingUpdatesToProcessorIfReady();
+  }
 }
 
 void ModelTypeWorker::OnCryptographerChange() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  UpdateTypeEncryptionKeyName();
-  // Always try to decrypt, regardless of |encryption_enabled_|.
+  // Always try to decrypt, regardless of |encryption_enabled_|. This might
+  // add some elements to |pending_updates_|.
   DecryptStoredEntities();
+  bool had_oudated_key_name = UpdateTypeEncryptionKeyName();
+  if (had_oudated_key_name || !pending_updates_.empty()) {
+    // Push the newly decrypted updates and/or the new key name to the
+    // processor.
+    SendPendingUpdatesToProcessorIfReady();
+  }
+  // If the worker couldn't commit before due to BlockForEncryption(), this
+  // might now be resolved. The call is a no-op if there's nothing to commit.
   NudgeIfReadyToCommit();
 }
 
@@ -228,7 +276,6 @@
   passphrase_type_ = type;
 }
 
-// UpdateHandler implementation.
 bool ModelTypeWorker::IsInitialSyncEnded() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return model_type_state_.initial_sync_done();
@@ -245,7 +292,7 @@
   return model_type_state_.type_context();
 }
 
-SyncerError ModelTypeWorker::ProcessGetUpdatesResponse(
+void ModelTypeWorker::ProcessGetUpdatesResponse(
     const sync_pb::DataTypeProgressMarker& progress_marker,
     const sync_pb::DataTypeContext& mutated_context,
     const SyncEntityList& applicable_updates,
@@ -333,8 +380,6 @@
       MaybeDropPendingUpdatesEncryptedWith(key_and_info.first);
     }
   }
-
-  return SyncerError(SyncerError::SYNCER_OK);
 }
 
 // static
@@ -421,37 +466,25 @@
   // Indicate to the processor that the initial download is done. The initial
   // sync technically isn't done yet but by the time this value is persisted to
   // disk on the model thread it will be.
-  //
-  // This should be mostly relevant for the call from ApplyUpdates(), but in
-  // rare cases we may end up receiving initial updates outside configuration
-  // cycles (e.g. polling cycles).
   model_type_state_.set_initial_sync_done(true);
   // Download cycle is done, pass all updates to the processor.
-  ApplyPendingUpdates();
+  SendPendingUpdatesToProcessorIfReady();
 }
 
-void ModelTypeWorker::EncryptionAcceptedMaybeApplyUpdates() {
-  // TODO(crbug.com/1109221): Consider always trying to apply updates in
-  // EnableEncryption() and OnCryptographerChange() instead of having this
-  // method. If so, the enable_encryption argument in the constructor can be
-  // replaced with an immediate call to EnableEncryption() without losing the
-  // edge case ApplyUpdates() in the constructor body.
-  DCHECK(encryption_enabled_);
-  DCHECK(cryptographer_->CanEncrypt());
+void ModelTypeWorker::SendPendingUpdatesToProcessorIfReady() {
+  DCHECK(model_type_processor_);
 
-  // Only push the encryption to the processor if we're already connected.
-  // Otherwise this information can wait for the initial sync's first apply.
-  if (model_type_state_.initial_sync_done()) {
-    // Reuse ApplyUpdates(...) to get its DCHECKs as well.
-    ApplyUpdates(nullptr);
+  if (!model_type_state_.initial_sync_done()) {
+    return;
   }
-}
 
-void ModelTypeWorker::ApplyPendingUpdates() {
   if (BlockForEncryption()) {
     return;
   }
 
+  DCHECK(!AlwaysEncryptedUserTypes().Has(type_) || encryption_enabled_);
+  DCHECK(!encryption_enabled_ ||
+         !model_type_state_.encryption_key_name().empty());
   DCHECK(entries_pending_decryption_.empty());
 
   DVLOG(1) << ModelTypeToString(type_) << ": "
@@ -486,15 +519,16 @@
   // existing behaviour. But perhaps there is no need to nudge for commit if all
   // known changes are already in flight.
   if (has_local_changes_state_ != kNoNudgedLocalChanges && CanCommitItems()) {
-    nudge_handler_->NudgeForCommit(GetModelType());
+    nudge_handler_->NudgeForCommit(type_);
   }
 }
 
-// CommitContributor implementation.
 std::unique_ptr<CommitContribution> ModelTypeWorker::GetContribution(
     size_t max_entries) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(model_type_state_.initial_sync_done());
+  DCHECK(model_type_processor_);
+
   // Early return if type is not ready to commit (initial sync isn't done or
   // cryptographer has pending keys).
   if (!CanCommitItems()) {
@@ -536,14 +570,18 @@
     has_local_changes_state_ = kAllNudgedLocalChangesInFlight;
   }
 
+  DCHECK(!AlwaysEncryptedUserTypes().Has(type_) || encryption_enabled_);
+  DCHECK(!encryption_enabled_ ||
+         (model_type_state_.encryption_key_name() ==
+          cryptographer_->GetDefaultEncryptionKeyName()));
   return std::make_unique<CommitContributionImpl>(
-      GetModelType(), model_type_state_.type_context(), std::move(response),
+      type_, model_type_state_.type_context(), std::move(response),
       base::BindOnce(&ModelTypeWorker::OnCommitResponse,
                      weak_ptr_factory_.GetWeakPtr()),
       base::BindOnce(&ModelTypeWorker::OnFullCommitFailure,
                      weak_ptr_factory_.GetWeakPtr()),
       encryption_enabled_ ? cryptographer_ : nullptr, passphrase_type_,
-      CommitOnlyTypes().Has(GetModelType()));
+      CommitOnlyTypes().Has(type_));
 }
 
 bool ModelTypeWorker::HasLocalChangesForTest() const {
@@ -582,10 +620,6 @@
   return memory_usage;
 }
 
-base::WeakPtr<ModelTypeWorker> ModelTypeWorker::AsWeakPtr() {
-  return weak_ptr_factory_.GetWeakPtr();
-}
-
 bool ModelTypeWorker::IsTypeInitialized() const {
   return model_type_state_.initial_sync_done();
 }
diff --git a/components/sync/engine/model_type_worker.h b/components/sync/engine/model_type_worker.h
index ad5e4c7..a2ee15b 100644
--- a/components/sync/engine/model_type_worker.h
+++ b/components/sync/engine/model_type_worker.h
@@ -75,15 +75,14 @@
   };
 
   // |cryptographer|, |nudge_handler| and |cancelation_signal| must be non-null
-  // and outlive this object.
+  // and outlive the worker. Calling this will construct the object but not
+  // more, ConnectSync() must be called immediately afterwards.
   ModelTypeWorker(ModelType type,
                   const sync_pb::ModelTypeState& initial_state,
-                  bool trigger_initial_sync,
                   Cryptographer* cryptographer,
                   bool encryption_enabled,
                   PassphraseType passphrase_type,
                   NudgeHandler* nudge_handler,
-                  std::unique_ptr<ModelTypeProcessor> model_type_processor,
                   CancelationSignal* cancelation_signal);
   ~ModelTypeWorker() override;
 
@@ -95,6 +94,15 @@
       const sync_pb::SyncEntity& update_entity,
       UpdateResponseData* response_data);
 
+  // Initializes the two relevant communication channels: ModelTypeWorker ->
+  // ModelTypeProcessor (GetUpdates) and ModelTypeProcessor -> ModelTypeWorker
+  // (Commit). Both channels are closed when the worker is destroyed. This is
+  // done outside of the constructor to avoid the object being used while it's
+  // still being built.
+  // Must be called immediately after the constructor, prior to using other
+  // methods.
+  void ConnectSync(std::unique_ptr<ModelTypeProcessor> model_type_processor);
+
   ModelType GetModelType() const;
 
   // Makes this an encrypted type, which means:
@@ -118,7 +126,7 @@
   bool IsInitialSyncEnded() const override;
   const sync_pb::DataTypeProgressMarker& GetDownloadProgress() const override;
   const sync_pb::DataTypeContext& GetDataTypeContext() const override;
-  SyncerError ProcessGetUpdatesResponse(
+  void ProcessGetUpdatesResponse(
       const sync_pb::DataTypeProgressMarker& progress_marker,
       const sync_pb::DataTypeContext& mutated_context,
       const SyncEntityList& applicable_updates,
@@ -132,10 +140,6 @@
   std::unique_ptr<CommitContribution> GetContribution(
       size_t max_entries) override;
 
-  // An alternative way to drive sending data to the processor, that should be
-  // called when a new encryption mechanism is ready.
-  void EncryptionAcceptedMaybeApplyUpdates();
-
   // Public for testing.
   // Returns true if this type should stop communicating because of outstanding
   // encryption issues and must wait for keys to be updated.
@@ -144,8 +148,6 @@
   // Returns the estimate of dynamically allocated memory in bytes.
   size_t EstimateMemoryUsage() const;
 
-  base::WeakPtr<ModelTypeWorker> AsWeakPtr();
-
   bool HasLocalChangesForTest() const;
 
   void SetMinGuResponsesToIgnoreKeyForTest(int min_gu_responses_to_ignore_key) {
@@ -161,8 +163,14 @@
     int gu_responses_while_should_have_been_known = 0;
   };
 
-  // Helper function to actually send |pending_updates_| to the processor.
-  void ApplyPendingUpdates();
+  // Sends |pending_updates_| and |model_type_state_| to the processor if there
+  // are no encryption pendencies and initial sync is done. This is called in
+  // ApplyUpdates() during a GetUpdates cycle, but also if the processor must be
+  // informed of a new encryption key, or the worker just managed to decrypt
+  // some pending updates.
+  // If initial sync isn't done yet, the first ApplyUpdates() will take care of
+  // pushing the data in such cases instead (the processor relies on this).
+  void SendPendingUpdatesToProcessorIfReady();
 
   // Returns true if this type has successfully fetched all available updates
   // from the server at least once. Our state may or may not be stale, but at
@@ -229,9 +237,6 @@
 
   const ModelType type_;
 
-  // Pointer to the ModelTypeProcessor associated with this worker. Never null.
-  const std::unique_ptr<ModelTypeProcessor> model_type_processor_;
-
   Cryptographer* const cryptographer_;
 
   // Interface used to access and send nudges to the sync scheduler. Not owned.
@@ -241,6 +246,10 @@
   // shutdown.
   CancelationSignal* const cancelation_signal_;
 
+  // Pointer to the ModelTypeProcessor associated with this worker. Initialized
+  // with ConnectSync().
+  std::unique_ptr<ModelTypeProcessor> model_type_processor_;
+
   // State that applies to the entire model type.
   sync_pb::ModelTypeState model_type_state_;
 
diff --git a/components/sync/engine/model_type_worker_unittest.cc b/components/sync/engine/model_type_worker_unittest.cc
index 6b7afd6..f1df964 100644
--- a/components/sync/engine/model_type_worker_unittest.cc
+++ b/components/sync/engine/model_type_worker_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/unique_position.h"
@@ -123,11 +124,7 @@
   ModelTypeWorkerTest(ModelType model_type, bool is_encrypted_type)
       : model_type_(model_type),
         is_encrypted_type_(is_encrypted_type),
-        encryption_keys_count_(0),
-        update_encryption_filter_index_(0),
-        mock_type_processor_(nullptr),
-        mock_server_(std::make_unique<SingleTypeMockServer>(model_type)),
-        is_processor_disconnected_(false) {}
+        mock_server_(std::make_unique<SingleTypeMockServer>(model_type)) {}
 
   ~ModelTypeWorkerTest() override {}
 
@@ -173,36 +170,18 @@
 
   // Initialize with a custom initial ModelTypeState and pending updates.
   void InitializeWithState(const ModelType type, const ModelTypeState& state) {
-    DCHECK(!worker());
+    DCHECK(!worker_);
+    worker_ = std::make_unique<ModelTypeWorker>(
+        type, state, &cryptographer_, is_encrypted_type_,
+        PassphraseType::kImplicitPassphrase, &mock_nudge_handler_,
+        &cancelation_signal_);
 
     // We don't get to own this object. The |worker_| keeps a unique_ptr to it.
     auto processor = std::make_unique<MockModelTypeProcessor>();
     mock_type_processor_ = processor.get();
     processor->SetDisconnectCallback(base::BindOnce(
         &ModelTypeWorkerTest::DisconnectProcessor, base::Unretained(this)));
-
-    worker_ = std::make_unique<ModelTypeWorker>(
-        type, state, !state.initial_sync_done(), &cryptographer_,
-        is_encrypted_type_, PassphraseType::kImplicitPassphrase,
-        &mock_nudge_handler_, std::move(processor), &cancelation_signal_);
-  }
-
-  // If the type isn't encrypted yet, makes the cryptographer available to the
-  // worker and marks the type as encrypted. Otherwise, just notifies a change
-  // in the cryptographer state.
-  void EnableEncryptionOrNotify() {
-    if (!worker()) {
-      // No worker to notify, just ensure |is_encrypted_type_| is true.
-      is_encrypted_type_ = true;
-      return;
-    }
-
-    if (is_encrypted_type_) {
-      worker()->OnCryptographerChange();
-    } else {
-      is_encrypted_type_ = true;
-      worker()->EnableEncryption();
-    }
+    worker_->ConnectSync(std::move(processor));
   }
 
   // Mimic a Nigori update with a keybag that cannot be decrypted, which means
@@ -236,12 +215,8 @@
     cryptographer_.AddEncryptionKey(last_key_name);
     cryptographer_.SelectDefaultEncryptionKey(last_key_name);
 
-    if (!worker()) {
-      return;
-    }
-    worker()->OnCryptographerChange();
-    if (is_encrypted_type_) {
-      worker()->EncryptionAcceptedMaybeApplyUpdates();
+    if (worker()) {
+      worker()->OnCryptographerChange();
     }
   }
 
@@ -437,22 +412,23 @@
   }
 
  private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+
   const ModelType model_type_;
 
   FakeCryptographer cryptographer_;
 
   // Determines whether |worker_| has access to the cryptographer or not.
-  // Can be set to true via EnableEncryptionOrNotify().
-  bool is_encrypted_type_;
+  bool is_encrypted_type_ = false;
 
   // The number of encryption keys known to the cryptographer. Keys are
   // identified by an index from 1 to |encryption_keys_count_| and the last one
   // might not have been decrypted yet.
-  int encryption_keys_count_;
+  int encryption_keys_count_ = 0;
 
   // The number of the encryption key used to encrypt incoming updates. A zero
   // value implies no encryption.
-  int update_encryption_filter_index_;
+  int update_encryption_filter_index_ = 0;
 
   CancelationSignal cancelation_signal_;
 
@@ -461,7 +437,7 @@
 
   // Non-owned, possibly null pointer. This object belongs to the
   // ModelTypeWorker under test.
-  MockModelTypeProcessor* mock_type_processor_;
+  MockModelTypeProcessor* mock_type_processor_ = nullptr;
 
   // A mock that emulates enough of the sync server that it can be used
   // a single UpdateHandler and CommitContributor pair. In this test
@@ -472,7 +448,7 @@
   // sync.
   MockNudgeHandler mock_nudge_handler_;
 
-  bool is_processor_disconnected_;
+  bool is_processor_disconnected_ = false;
 
   StatusController status_controller_;
 };
diff --git a/components/sync/engine/nudge_handler.h b/components/sync/engine/nudge_handler.h
index 17522c98..6b03d256 100644
--- a/components/sync/engine/nudge_handler.h
+++ b/components/sync/engine/nudge_handler.h
@@ -15,7 +15,9 @@
   NudgeHandler() = default;
   virtual ~NudgeHandler() = default;
 
+  // Schedules initial sync for |type| and returns.
   virtual void NudgeForInitialDownload(ModelType type) = 0;
+  // Schedules a commit for |type| and returns.
   virtual void NudgeForCommit(ModelType type) = 0;
 };
 
diff --git a/components/sync/engine/update_handler.h b/components/sync/engine/update_handler.h
index 8dffadba..e1ef57a 100644
--- a/components/sync/engine/update_handler.h
+++ b/components/sync/engine/update_handler.h
@@ -46,10 +46,7 @@
   //
   // In this context, "applicable_updates" means the set of updates belonging to
   // this type.
-  //
-  // Returns SYNCER_OK if the all data was processed successfully, a syncer
-  // error otherwise.
-  virtual SyncerError ProcessGetUpdatesResponse(
+  virtual void ProcessGetUpdatesResponse(
       const sync_pb::DataTypeProgressMarker& progress_marker,
       const sync_pb::DataTypeContext& mutated_context,
       const SyncEntityList& applicable_updates,
diff --git a/components/sync/model/sync_change.cc b/components/sync/model/sync_change.cc
index 8d2b13b..f28edd07 100644
--- a/components/sync/model/sync_change.cc
+++ b/components/sync/model/sync_change.cc
@@ -15,7 +15,7 @@
                        SyncChangeType change_type,
                        const SyncData& sync_data)
     : location_(from_here), change_type_(change_type), sync_data_(sync_data) {
-  DCHECK(IsValid());
+  DCHECK(IsValid()) << " from " << from_here.ToString();
 }
 
 SyncChange::~SyncChange() {}
@@ -31,21 +31,11 @@
     return false;
   }
 
-  // Data from the syncer must always have valid specifics.
-  if (!sync_data_.IsLocal()) {
-    return true;
-  }
-
-  // Local changes must always have a unique tag.
+  // Changes must always have a unique tag.
   if (sync_data_.GetClientTagHash().value().empty()) {
     return false;
   }
 
-  // Adds and updates must have a non-unique-title.
-  if (change_type_ == ACTION_ADD || change_type_ == ACTION_UPDATE) {
-    return !sync_data_.GetTitle().empty();
-  }
-
   return true;
 }
 
diff --git a/components/sync/model/sync_data.cc b/components/sync/model/sync_data.cc
index a13a908..9e0b9bc 100644
--- a/components/sync/model/sync_data.cc
+++ b/components/sync/model/sync_data.cc
@@ -39,14 +39,14 @@
   t1->Swap(t2);
 }
 
-SyncData::SyncData() : is_local_(false), is_valid_(false) {}
+SyncData::SyncData() : is_valid_(false) {}
 
-SyncData::SyncData(bool is_local, sync_pb::SyncEntity* entity)
-    : immutable_entity_(entity), is_local_(is_local), is_valid_(true) {}
+SyncData::SyncData(sync_pb::SyncEntity* entity)
+    : immutable_entity_(entity), is_valid_(true) {}
 
 SyncData::SyncData(const SyncData& other) = default;
 
-SyncData::~SyncData() {}
+SyncData::~SyncData() = default;
 
 // Static.
 SyncData SyncData::CreateLocalDelete(const std::string& client_tag_unhashed,
@@ -72,7 +72,7 @@
   entity.set_non_unique_name(non_unique_title);
   entity.mutable_specifics()->CopyFrom(specifics);
 
-  return SyncData(/*is_local=*/true, &entity);
+  return SyncData(&entity);
 }
 
 // Static.
@@ -81,7 +81,7 @@
   sync_pb::SyncEntity entity;
   *entity.mutable_specifics() = std::move(specifics);
   entity.set_client_defined_unique_tag(client_tag_hash.value());
-  return SyncData(/*is_local=*/false, &entity);
+  return SyncData(&entity);
 }
 
 bool SyncData::IsValid() const {
@@ -105,10 +105,6 @@
   return immutable_entity_.Get().non_unique_name();
 }
 
-bool SyncData::IsLocal() const {
-  return is_local_;
-}
-
 std::string SyncData::ToString() const {
   if (!IsValid())
     return "<Invalid SyncData>";
@@ -118,11 +114,9 @@
   base::JSONWriter::WriteWithOptions(*EntitySpecificsToValue(GetSpecifics()),
                                      base::JSONWriter::OPTIONS_PRETTY_PRINT,
                                      &specifics);
-  std::string is_local_string = IsLocal() ? "true" : "false";
 
-  return "{ isLocal: " + is_local_string + ", type: " + type +
-         ", tagHash: " + GetClientTagHash().value() + ", title: " + GetTitle() +
-         ", specifics: " + specifics + "}";
+  return "{ type: " + type + ", tagHash: " + GetClientTagHash().value() +
+         ", title: " + GetTitle() + ", specifics: " + specifics + "}";
 }
 
 void PrintTo(const SyncData& sync_data, std::ostream* os) {
diff --git a/components/sync/model/sync_data.h b/components/sync/model/sync_data.h
index 54f547582..afccb15c 100644
--- a/components/sync/model/sync_data.h
+++ b/components/sync/model/sync_data.h
@@ -72,9 +72,6 @@
   // going TO the syncer, not from.
   const std::string& GetTitle() const;
 
-  // Whether this sync data is for local data or data coming from the syncer.
-  bool IsLocal() const;
-
   std::string ToString() const;
 
  private:
@@ -100,14 +97,11 @@
   // The actual shared sync entity being held.
   ImmutableSyncEntity immutable_entity_;
 
-  // Whether this SyncData represents a local change.
-  bool is_local_;
-
   // Whether this SyncData holds valid data.
   bool is_valid_;
 
   // Clears |entity|.
-  SyncData(bool is_local_, sync_pb::SyncEntity* entity);
+  explicit SyncData(sync_pb::SyncEntity* entity);
 };
 
 // gmock printer helper.
diff --git a/components/sync/model/sync_data_unittest.cc b/components/sync/model/sync_data_unittest.cc
index 39b85e1..a34fb79a 100644
--- a/components/sync/model/sync_data_unittest.cc
+++ b/components/sync/model/sync_data_unittest.cc
@@ -37,7 +37,6 @@
 TEST_F(SyncDataTest, CreateLocalDelete) {
   SyncData data = SyncData::CreateLocalDelete(kSyncTag, kDatatype);
   EXPECT_TRUE(data.IsValid());
-  EXPECT_TRUE(data.IsLocal());
   EXPECT_EQ(ClientTagHash::FromUnhashed(PREFERENCES, kSyncTag),
             data.GetClientTagHash());
   EXPECT_EQ(kDatatype, data.GetDataType());
@@ -48,7 +47,6 @@
   SyncData data =
       SyncData::CreateLocalData(kSyncTag, kNonUniqueTitle, specifics);
   EXPECT_TRUE(data.IsValid());
-  EXPECT_TRUE(data.IsLocal());
   EXPECT_EQ(ClientTagHash::FromUnhashed(PREFERENCES, kSyncTag),
             data.GetClientTagHash());
   EXPECT_EQ(kDatatype, data.GetDataType());
@@ -62,7 +60,6 @@
   SyncData data = SyncData::CreateRemoteData(
       specifics, ClientTagHash::FromUnhashed(PREFERENCES, kSyncTag));
   EXPECT_TRUE(data.IsValid());
-  EXPECT_FALSE(data.IsLocal());
   EXPECT_EQ(ClientTagHash::FromUnhashed(PREFERENCES, kSyncTag),
             data.GetClientTagHash());
   EXPECT_TRUE(data.GetSpecifics().has_preference());
diff --git a/components/sync/model/syncable_service_based_bridge.cc b/components/sync/model/syncable_service_based_bridge.cc
index 94e52e93..6cd71a4 100644
--- a/components/sync/model/syncable_service_based_bridge.cc
+++ b/components/sync/model/syncable_service_based_bridge.cc
@@ -52,7 +52,6 @@
 
 sync_pb::PersistedEntityData CreatePersistedFromLocalData(
     const SyncData& sync_data) {
-  DCHECK(sync_data.IsLocal());
   DCHECK(sync_data.IsValid());
   DCHECK(!sync_data.GetTitle().empty());
 
@@ -139,10 +138,11 @@
         case SyncChange::ACTION_ADD:
         case SyncChange::ACTION_UPDATE: {
           DCHECK_EQ(type_, change.sync_data().GetDataType());
-          DCHECK(change.sync_data().IsLocal())
-              << " from " << change.location().ToString();
           DCHECK(change.sync_data().IsValid())
               << " from " << change.location().ToString();
+          // Local adds and updates must have a non-unique-title.
+          DCHECK(!change.sync_data().GetTitle().empty())
+              << " from " << change.location().ToString();
 
           const ClientTagHash client_tag_hash =
               change.sync_data().GetClientTagHash();
diff --git a/components/sync/model/syncable_service_based_bridge_unittest.cc b/components/sync/model/syncable_service_based_bridge_unittest.cc
index 6e1e7fd..9cbb24a 100644
--- a/components/sync/model/syncable_service_based_bridge_unittest.cc
+++ b/components/sync/model/syncable_service_based_bridge_unittest.cc
@@ -48,8 +48,8 @@
   return specifics;
 }
 
-MATCHER_P(SyncDataRemoteMatches, name, "") {
-  return arg.IsValid() && !arg.IsLocal() && arg.GetDataType() == kModelType &&
+MATCHER_P(SyncDataMatches, name, "") {
+  return arg.IsValid() && arg.GetDataType() == kModelType &&
          arg.GetSpecifics().preference().name() == name;
 }
 
@@ -207,9 +207,9 @@
   // Once the initial data is fetched from the server,
   // MergeDataAndStartSyncing() should be exercised.
   EXPECT_CALL(syncable_service_,
-              MergeDataAndStartSyncing(
-                  kModelType, ElementsAre(SyncDataRemoteMatches("name1")),
-                  NotNull(), NotNull()));
+              MergeDataAndStartSyncing(kModelType,
+                                       ElementsAre(SyncDataMatches("name1")),
+                                       NotNull(), NotNull()));
   worker_->UpdateFromServer(kClientTagHash, GetTestSpecifics("name1"));
   EXPECT_THAT(GetAllData(), ElementsAre(Pair(kClientTagHash.value(), _)));
 }
@@ -336,9 +336,9 @@
   InitializeBridge();
 
   EXPECT_CALL(syncable_service_,
-              MergeDataAndStartSyncing(
-                  kModelType, ElementsAre(SyncDataRemoteMatches("name1")),
-                  NotNull(), NotNull()));
+              MergeDataAndStartSyncing(kModelType,
+                                       ElementsAre(SyncDataMatches("name1")),
+                                       NotNull(), NotNull()));
   StartSyncing();
 }
 
diff --git a/components/sync/test/engine/mock_update_handler.cc b/components/sync/test/engine/mock_update_handler.cc
index ea20332..2ba3b4af 100644
--- a/components/sync/test/engine/mock_update_handler.cc
+++ b/components/sync/test/engine/mock_update_handler.cc
@@ -30,13 +30,12 @@
   return kEmptyDataTypeContext;
 }
 
-SyncerError MockUpdateHandler::ProcessGetUpdatesResponse(
+void MockUpdateHandler::ProcessGetUpdatesResponse(
     const sync_pb::DataTypeProgressMarker& progress_marker,
     const sync_pb::DataTypeContext& mutated_context,
     const SyncEntityList& applicable_updates,
     StatusController* status) {
   progress_marker_.CopyFrom(progress_marker);
-  return SyncerError(SyncerError::SYNCER_OK);
 }
 
 void MockUpdateHandler::ApplyUpdates(StatusController* status) {
diff --git a/components/sync/test/engine/mock_update_handler.h b/components/sync/test/engine/mock_update_handler.h
index 81938a0..b201e8e 100644
--- a/components/sync/test/engine/mock_update_handler.h
+++ b/components/sync/test/engine/mock_update_handler.h
@@ -21,7 +21,7 @@
   bool IsInitialSyncEnded() const override;
   const sync_pb::DataTypeProgressMarker& GetDownloadProgress() const override;
   const sync_pb::DataTypeContext& GetDataTypeContext() const override;
-  SyncerError ProcessGetUpdatesResponse(
+  void ProcessGetUpdatesResponse(
       const sync_pb::DataTypeProgressMarker& progress_marker,
       const sync_pb::DataTypeContext& mutated_context,
       const SyncEntityList& applicable_updates,
diff --git a/components/test/data/autofill_assistant/html/cart.html b/components/test/data/autofill_assistant/html/cart.html
new file mode 100644
index 0000000..69e217cb
--- /dev/null
+++ b/components/test/data/autofill_assistant/html/cart.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+
+<!--
+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.
+-->
+<html>
+  <head>
+    <title>Example shopping cart</title>
+  </head>
+</html>
\ No newline at end of file
diff --git a/components/tracing/test/trace_event_perftest.cc b/components/tracing/test/trace_event_perftest.cc
index c2317ce..9416550e 100644
--- a/components/tracing/test/trace_event_perftest.cc
+++ b/components/tracing/test/trace_event_perftest.cc
@@ -49,7 +49,7 @@
     base::RunLoop run_loop;
     TraceLog::GetInstance()->SetDisabled();
     TraceLog::GetInstance()->Flush(
-        Bind(&OnTraceDataCollected, run_loop.QuitClosure()));
+        BindRepeating(&OnTraceDataCollected, run_loop.QuitClosure()));
     run_loop.Run();
   }
 
diff --git a/components/variations/variations_crash_keys.cc b/components/variations/variations_crash_keys.cc
index e0df470..9ff3f31 100644
--- a/components/variations/variations_crash_keys.cc
+++ b/components/variations/variations_crash_keys.cc
@@ -104,11 +104,11 @@
   UpdateCrashKeys();
 
   ui_thread_task_runner_ = base::SequencedTaskRunnerHandle::Get();
-  base::FieldTrialList::AddObserver(this);
+  base::FieldTrialList::SetSynchronousObserver(this);
 }
 
 VariationsCrashKeys::~VariationsCrashKeys() {
-  base::FieldTrialList::RemoveObserver(this);
+  base::FieldTrialList::RemoveSynchronousObserver(this);
   g_num_variations_crash_key.Clear();
   g_variations_crash_key.Clear();
 }
diff --git a/components/variations/variations_ids_provider.cc b/components/variations/variations_ids_provider.cc
index f04b45d63..7237221 100644
--- a/components/variations/variations_ids_provider.cc
+++ b/components/variations/variations_ids_provider.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 
 #include "base/base64.h"
+#include "base/memory/singleton.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -43,8 +44,7 @@
 
 // static
 VariationsIdsProvider* VariationsIdsProvider::GetInstance() {
-  static base::NoDestructor<VariationsIdsProvider> instance;
-  return instance.get();
+  return base::Singleton<VariationsIdsProvider>::get();
 }
 
 variations::mojom::VariationsHeadersPtr
diff --git a/components/variations/variations_ids_provider.h b/components/variations/variations_ids_provider.h
index 273407b..2c63f04 100644
--- a/components/variations/variations_ids_provider.h
+++ b/components/variations/variations_ids_provider.h
@@ -14,7 +14,6 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
-#include "base/no_destructor.h"
 #include "base/observer_list.h"
 #include "base/synchronization/lock.h"
 #include "components/variations/proto/study.pb.h"
@@ -22,6 +21,11 @@
 #include "components/variations/variations.mojom.h"
 #include "components/variations/variations_associated_data.h"
 
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}
+
 namespace variations {
 class VariationsClient;
 
@@ -127,7 +131,7 @@
   void ResetForTesting();
 
  private:
-  friend class base::NoDestructor<VariationsIdsProvider>;
+  friend struct base::DefaultSingletonTraits<VariationsIdsProvider>;
 
   typedef std::pair<VariationID, IDCollectionKey> VariationIDEntry;
 
diff --git a/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java b/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java
index 88e4966b..05a84a8f 100644
--- a/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java
+++ b/components/webxr/android/java/src/org/chromium/components/webxr/ArImmersiveOverlay.java
@@ -6,15 +6,20 @@
 
 import android.annotation.SuppressLint;
 import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
+import android.os.Build;
 import android.view.Display;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 
 import androidx.annotation.NonNull;
 
@@ -24,6 +29,7 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.WebContentsObserver;
 import org.chromium.ui.display.DisplayAndroidManager;
+import org.chromium.ui.widget.Toast;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -42,9 +48,8 @@
     private boolean mSurfaceReportedReady;
     private Integer mRestoreOrientation;
     private boolean mCleanupInProgress;
-    private ArSurfaceView mArSurfaceView;
+    private SurfaceUiWrapper mSurfaceUi;
     private WebContents mWebContents;
-    private boolean mUseOverlay;
 
     // Set containing all currently touching pointers.
     private HashMap<Integer, PointerData> mPointerIdToData;
@@ -65,11 +70,95 @@
         mPointerIdToData = new HashMap<Integer, PointerData>();
         mPrimaryPointerId = null;
 
-        mUseOverlay = useOverlay;
-
         // Choose a concrete implementation to create a drawable Surface and make it fullscreen.
         // It forwards SurfaceHolder callbacks and touch events to this ArImmersiveOverlay object.
-        mArSurfaceView = new ArSurfaceView(canRenderDomContent);
+        if (useOverlay) {
+            mSurfaceUi = new SurfaceUiCompositor(canRenderDomContent);
+        } else {
+            mSurfaceUi = new SurfaceUiDialog();
+        }
+    }
+
+    private interface SurfaceUiWrapper {
+        public void onSurfaceVisible();
+        public void forwardMotionEvent(MotionEvent ev);
+        public void destroy();
+    }
+
+    // The default Dialog cancellation behavior destroys the Surface before we get notified via the
+    // Cancelation callback. This is unfortunate, because we need to ensure that the compositor is
+    // stopped before the surface is destroyed. This class allows us to override the default
+    // cancellation behavior to properly shutdown the compositor before the surface is destroyed. It
+    // is unclear why the SurfaceHolder callbacks are not triggered.
+    private class ArDialog extends Dialog {
+        public ArDialog(Context context, int themeResId) {
+            super(context, themeResId);
+        }
+
+        @Override
+        public void cancel() {
+            ArCoreJavaUtils.onBackPressed();
+            super.cancel();
+        }
+    }
+
+    private class SurfaceUiDialog implements SurfaceUiWrapper {
+        private Toast mNotificationToast;
+        private ArDialog mDialog;
+        // Android supports multiple variants of fullscreen applications. Use fully-immersive
+        // "sticky" mode without navigation or status bars, and show a toast with a "pull from top
+        // and press back button to exit" prompt.
+        private static final int VISIBILITY_FLAGS_IMMERSIVE = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+
+        public SurfaceUiDialog() {
+            // Create a fullscreen dialog and use its backing Surface for drawing.
+            mDialog = new ArDialog(mActivity, android.R.style.Theme_NoTitleBar_Fullscreen);
+            mDialog.getWindow().setBackgroundDrawable(null);
+            mDialog.getWindow().takeSurface(ArImmersiveOverlay.this);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                // Use maximum fullscreen, ignoring a notch if present. This code path is used
+                // for non-DOM-Overlay mode where the browser compositor view isn't visible.
+                // In DOM Overlay mode (SurfaceUiCompositor), Blink configures this separately
+                // via ViewportData::SetExpandIntoDisplayCutout.
+                mDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
+                        WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+                mDialog.getWindow().getAttributes().layoutInDisplayCutoutMode =
+                        WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+            }
+            View view = mDialog.getWindow().getDecorView();
+            view.setSystemUiVisibility(VISIBILITY_FLAGS_IMMERSIVE);
+            view.setOnTouchListener(ArImmersiveOverlay.this);
+            view.setKeepScreenOn(true);
+            mDialog.getWindow().setLayout(
+                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+            mDialog.show();
+        }
+
+        @Override // SurfaceUiWrapper
+        public void onSurfaceVisible() {
+            if (mNotificationToast != null) {
+                mNotificationToast.cancel();
+            }
+            int resId = R.string.immersive_fullscreen_api_notification;
+            mNotificationToast = Toast.makeText(mActivity, resId, Toast.LENGTH_LONG);
+            mNotificationToast.setGravity(Gravity.TOP | Gravity.CENTER, 0, 0);
+            mNotificationToast.show();
+        }
+
+        @Override // SurfaceUiWrapper
+        public void forwardMotionEvent(MotionEvent ev) {}
+
+        @Override // SurfaceUiWrapper
+        public void destroy() {
+            if (mNotificationToast != null) {
+                mNotificationToast.cancel();
+                mNotificationToast = null;
+            }
+            mDialog.dismiss();
+        }
     }
 
     private class PointerData {
@@ -84,18 +173,17 @@
         }
     }
 
-    private class ArSurfaceView {
+    private class SurfaceUiCompositor implements SurfaceUiWrapper {
         private SurfaceView mSurfaceView;
         private WebContentsObserver mWebContentsObserver;
         private boolean mDomSurfaceNeedsConfiguring;
 
         @SuppressLint("ClickableViewAccessibility")
-        public ArSurfaceView(boolean canRenderDomContent) {
-            // If we need to show the dom content, but can't render it on top of the camera/gl
-            // layers manually, then we need to configure the DOM content's surface view to
-            // overlay ours. We need to track this so that we ensure we teardown everything
-            // we need to teardown as well.
-            mDomSurfaceNeedsConfiguring = mUseOverlay && !canRenderDomContent;
+        public SurfaceUiCompositor(boolean canRenderDomContent) {
+            // If we can't render the dom content on top of the camera/gl layers manually, then
+            // we need to configure the DOM content's surface view to overlay ours. We need to
+            // track this so that we ensure we teardown everything we need to teardown as well.
+            mDomSurfaceNeedsConfiguring = !canRenderDomContent;
 
             // Enable alpha channel for the compositor and make the background transparent.
             // Note that this needs to happen before we create and parent our SurfaceView, so that
@@ -103,10 +191,6 @@
             if (DEBUG_LOGS) {
                 Log.i(TAG, "calling mArCompositorDelegate.setOverlayImmersiveArMode(true)");
             }
-
-            // While it's fine to omit if the page does not use DOMOverlay, once the page does
-            // use DOMOverlay, something appears to have changed such that it becomes required,
-            // otherwies the DOM SurfaceView will be in front of the XR content.
             mArCompositorDelegate.setOverlayImmersiveArMode(true, mDomSurfaceNeedsConfiguring);
 
             mSurfaceView = new SurfaceView(mActivity);
@@ -142,6 +226,15 @@
             mWebContents.addObserver(mWebContentsObserver);
         }
 
+        @Override // SurfaceUiWrapper
+        public void onSurfaceVisible() {}
+
+        @Override // SurfaceUiWrapper
+        public void forwardMotionEvent(MotionEvent ev) {
+            mArCompositorDelegate.dispatchTouchEvent(ev);
+        }
+
+        @Override // SurfaceUiWrapper
         public void destroy() {
             mWebContents.removeObserver(mWebContentsObserver);
             View content = mActivity.getWindow().findViewById(android.R.id.content);
@@ -307,9 +400,7 @@
         // We need to consume the touch (returning true) to ensure that we get
         // followup events such as MOVE and UP. DOM Overlay mode needs to forward
         // the touch to the content view so that its UI elements keep working.
-        if (mUseOverlay) {
-            mArCompositorDelegate.dispatchTouchEvent(ev);
-        }
+        mSurfaceUi.forwardMotionEvent(ev);
         return true;
     }
 
@@ -389,7 +480,7 @@
         //
         // While it would be preferable to wait until the surface is at the desired fullscreen
         // resolution, i.e. via mActivity.getFullscreenManager().getPersistentFullscreenMode(), that
-        // causes a chicken-and-egg problem for ArSurfaceView mode as used for DOM overlay.
+        // causes a chicken-and-egg problem for SurfaceUiCompositor mode as used for DOM overlay.
         // Chrome's fullscreen mode is triggered by the Blink side setting an element fullscreen
         // after the session starts, but the session doesn't start until we report the drawing
         // surface being ready (including a configured size), so we use this reported size assuming
@@ -414,6 +505,10 @@
         mArCoreJavaUtils.onDrawingSurfaceReady(holder.getSurface(),
                 mWebContents.getTopLevelNativeWindow(), rotation, width, height);
         mSurfaceReportedReady = true;
+
+        // Show the toast with instructions how to exit fullscreen mode now if necessary.
+        // Not needed in DOM overlay mode which uses FullscreenHtmlApiHandler to do so.
+        mSurfaceUi.onSurfaceVisible();
     }
 
     @Override // SurfaceHolder.Callback2
@@ -438,7 +533,7 @@
         // the destroy callbacks to ensure consistent state after non-exiting lifecycle events.
         mArCoreJavaUtils.onDrawingSurfaceDestroyed();
 
-        mArSurfaceView.destroy();
+        mSurfaceUi.destroy();
 
         // The JS app may have put an element into fullscreen mode during the immersive session,
         // even if this wasn't visible to the user. Ensure that we fully exit out of any active
diff --git a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
index 9e4534f0..48d8e16 100644
--- a/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
+++ b/content/browser/bluetooth/bluetooth_device_chooser_controller.cc
@@ -30,6 +30,7 @@
 
 using device::BluetoothUUID;
 using UUIDSet = device::BluetoothDevice::UUIDSet;
+using ManufacturerDataMap = device::BluetoothDevice::ManufacturerDataMap;
 using blink::mojom::WebBluetoothResult;
 
 namespace {
@@ -88,6 +89,10 @@
 
 namespace {
 
+#if DCHECK_IS_ON()
+void LogRequestDeviceOptions(
+    const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {}
+#else
 void LogRequestDeviceOptions(
     const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) {
   DVLOG(1) << "requestDevice called with the following filters: ";
@@ -106,17 +111,37 @@
       DVLOG(1) << "Name Prefix: " << filter->name_prefix.value();
 
     if (filter->services) {
-      DVLOG(1) << "Services: ";
-      DVLOG(1) << "\t[";
+      base::Value services_list(base::Value::Type::LIST);
       for (const auto& service : filter->services.value())
-        DVLOG(1) << "\t\t" << service.canonical_value();
-      DVLOG(1) << "\t]";
+        services_list.Append(service.canonical_value());
+      DVLOG(1) << "Services: " << services_list;
+    }
+
+    if (filter->manufacturer_data) {
+      base::Value manufacturer_data_list(base::Value::Type::LIST);
+      for (const auto& manufacturer_data : filter->manufacturer_data.value()) {
+        base::Value filter_data_list(base::Value::Type::LIST);
+        base::Value filter_mask_list(base::Value::Type::LIST);
+        for (const auto& data_filter : manufacturer_data.second) {
+          filter_data_list.Append(base::Value(data_filter->data));
+          filter_mask_list.Append(base::Value(data_filter->mask));
+        }
+        base::Value data_filter_dict(base::Value::Type::DICTIONARY);
+        data_filter_dict.SetKey("Company Identifier",
+                                base::Value(manufacturer_data.first->id));
+        data_filter_dict.SetKey("Data", std::move(filter_data_list));
+        data_filter_dict.SetKey("Mask", std::move(filter_mask_list));
+        manufacturer_data_list.Append(std::move(data_filter_dict));
+      }
+      DVLOG(1) << "Manufacturer Data: " << manufacturer_data_list;
     }
   }
 }
+#endif
 
 bool MatchesFilter(const std::string* device_name,
                    const UUIDSet& device_uuids,
+                   const ManufacturerDataMap& device_manufacturer_data,
                    const blink::mojom::WebBluetoothLeScanFilterPtr& filter) {
   if (filter->name) {
     if (device_name == nullptr)
@@ -141,17 +166,41 @@
     }
   }
 
+  if (filter->manufacturer_data) {
+    for (const auto& filter_data : filter->manufacturer_data.value()) {
+      // Check the company identifier.
+      auto it = device_manufacturer_data.find(filter_data.first->id);
+      if (it == device_manufacturer_data.end())
+        return false;
+      // Check data filter size is less than device manufacturer data size.
+      const auto& device_data = it->second;
+      if (filter_data.second.size() > device_data.size())
+        return false;
+      // For each bit in mask, check the corresponding bit in device
+      // manufacturer data is equal to the corresponding bit in expected data.
+      size_t i = 0;
+      for (const auto& filter_byte : filter_data.second) {
+        if ((filter_byte->mask & filter_byte->data) !=
+            (filter_byte->mask & device_data.at(i++))) {
+          return false;
+        }
+      }
+    }
+  }
+
   return true;
 }
 
 bool MatchesFilters(
     const std::string* device_name,
     const UUIDSet& device_uuids,
+    const ManufacturerDataMap& device_manufacturer_data,
     const base::Optional<
         std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>>& filters) {
   DCHECK(HasValidFilter(filters));
   for (const auto& filter : filters.value()) {
-    if (MatchesFilter(device_name, device_uuids, filter)) {
+    if (MatchesFilter(device_name, device_uuids, device_manufacturer_data,
+                      filter)) {
       return true;
     }
   }
@@ -292,7 +341,8 @@
   if (chooser_.get()) {
     if (options_->accept_all_devices ||
         MatchesFilters(device_name ? &device_name.value() : nullptr,
-                       device.GetUUIDs(), options_->filters)) {
+                       device.GetUUIDs(), device.GetManufacturerData(),
+                       options_->filters)) {
       base::Optional<int8_t> rssi = device.GetInquiryRSSI();
       std::string device_id = device.GetAddress();
       device_ids_.insert(device_id);
diff --git a/content/browser/bluetooth/bluetooth_util.cc b/content/browser/bluetooth/bluetooth_util.cc
index ce002db..4f768c4 100644
--- a/content/browser/bluetooth/bluetooth_util.cc
+++ b/content/browser/bluetooth/bluetooth_util.cc
@@ -39,6 +39,9 @@
       return false;
   }
 
+  if (filter_1.manufacturer_data != filter_2.manufacturer_data)
+    return false;
+
   return true;
 }
 
diff --git a/content/browser/bluetooth/bluetooth_util_unittest.cc b/content/browser/bluetooth/bluetooth_util_unittest.cc
index 8f686860..7545c8a8 100644
--- a/content/browser/bluetooth/bluetooth_util_unittest.cc
+++ b/content/browser/bluetooth/bluetooth_util_unittest.cc
@@ -7,6 +7,10 @@
 #include "base/macros.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using WebBluetoothManufacturerDataMap =
+    base::flat_map<blink::mojom::WebBluetoothCompanyPtr,
+                   std::vector<blink::mojom::WebBluetoothDataFilterPtr>>;
+
 namespace content {
 
 namespace {
@@ -14,6 +18,20 @@
 const char kBatteryServiceUUIDString[] = "0000180f-0000-1000-8000-00805f9b34fb";
 const char kCyclingPowerUUIDString[] = "00001818-0000-1000-8000-00805f9b34fb";
 
+std::vector<blink::mojom::WebBluetoothDataFilterPtr> CreateDataFilters(
+    std::vector<uint8_t> filter_data,
+    std::vector<uint8_t> filter_mask) {
+  EXPECT_EQ(filter_data.size(), filter_mask.size());
+  std::vector<blink::mojom::WebBluetoothDataFilterPtr> data_filters;
+  for (size_t i = 0; i < filter_data.size(); ++i) {
+    auto data_filter = blink::mojom::WebBluetoothDataFilter::New();
+    data_filter->data = filter_data[i];
+    data_filter->mask = filter_mask[i];
+    data_filters.push_back(std::move(data_filter));
+  }
+  return data_filters;
+}
+
 }  // namespace
 
 class BluetoothUtilTest : public testing::Test {
@@ -29,10 +47,22 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
+
+  std::vector<uint8_t> filter_data = {0x01, 0x02, 0x03, 0x04};
+  std::vector<uint8_t> filter_mask = {0xff, 0xff, 0xff, 0xff};
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  manufacturer_data_1.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data, filter_mask)});
+
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data, filter_mask)});
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
   EXPECT_TRUE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -40,10 +70,12 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, base::nullopt, "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, base::nullopt, "a");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, /*name=*/base::nullopt, "a",
+      /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, /*name=*/base::nullopt, "a",
+      /*manufacturer_data=*/base::nullopt);
   EXPECT_TRUE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -51,10 +83,11 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, base::nullopt, "a");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, /*name=*/base::nullopt, "a",
+      /*manufacturer_data=*/base::nullopt);
   EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -62,10 +95,10 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "cd", "a");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "cd", "a", /*manufacturer_data=*/base::nullopt);
   EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -73,10 +106,12 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(services, "ab",
-                                                              base::nullopt);
-  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(services, "ab",
-                                                              base::nullopt);
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", /*name_prefix=*/base::nullopt,
+      /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", /*name_prefix=*/base::nullopt,
+      /*manufacturer_data=*/base::nullopt);
   EXPECT_TRUE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -84,10 +119,11 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
-  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(services, "ab",
-                                                              base::nullopt);
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", /*name_prefix=*/base::nullopt,
+      /*manufacturer_data=*/base::nullopt);
   EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -95,18 +131,20 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "ab");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "ab", /*manufacturer_data=*/base::nullopt);
   EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
 TEST_F(BluetoothUtilTest, BothNoServicesUUID) {
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(base::nullopt, "ab", "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(base::nullopt, "ab", "a");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      /*services=*/base::nullopt, "ab", "a",
+      /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      /*services=*/base::nullopt, "ab", "a",
+      /*manufacturer_data=*/base::nullopt);
   EXPECT_TRUE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -114,10 +152,11 @@
   base::Optional<std::vector<device::BluetoothUUID>> services;
   services.emplace();
   services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services, "ab", "a");
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(base::nullopt, "ab", "ab");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", /*manufacturer_data=*/base::nullopt);
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      /*services=*/base::nullopt, "ab", "ab",
+      /*manufacturer_data=*/base::nullopt);
   EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
@@ -125,14 +164,14 @@
   base::Optional<std::vector<device::BluetoothUUID>> services_1;
   services_1.emplace();
   services_1->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services_1, "ab", "a");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services_1, "ab", "a", /*manufacturer_data=*/base::nullopt);
 
   base::Optional<std::vector<device::BluetoothUUID>> services_2;
   services_2.emplace();
   services_2->push_back(device::BluetoothUUID(kCyclingPowerUUIDString));
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services_2, "ab", "a");
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services_2, "ab", "a", /*manufacturer_data=*/base::nullopt);
 
   EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
 }
@@ -142,17 +181,160 @@
   services_1.emplace();
   services_1->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
   services_1->push_back(device::BluetoothUUID(kCyclingPowerUUIDString));
-  auto filter_1 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services_1, "ab", "a");
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services_1, "ab", "a", /*manufacturer_data=*/base::nullopt);
 
   base::Optional<std::vector<device::BluetoothUUID>> services_2;
   services_2.emplace();
-  services_2->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
   services_2->push_back(device::BluetoothUUID(kCyclingPowerUUIDString));
-  auto filter_2 =
-      blink::mojom::WebBluetoothLeScanFilter::New(services_2, "ab", "a");
+  services_2->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services_2, "ab", "a", /*manufacturer_data=*/base::nullopt);
 
   EXPECT_TRUE(AreScanFiltersSame(*filter_1, *filter_2));
 }
 
+TEST_F(BluetoothUtilTest, BothNoManufacturerData) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
+  EXPECT_TRUE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
+TEST_F(BluetoothUtilTest, OnlyOneHasManufacturerData) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  WebBluetoothManufacturerDataMap manufacturer_data;
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", /*manufacturer_data=*/base::nullopt);
+  EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
+TEST_F(BluetoothUtilTest, DifferentManufacturerDataSize) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  manufacturer_data_1.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters({}, {})});
+
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters({}, {})});
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0002),
+                              CreateDataFilters({}, {})});
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
+  EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
+TEST_F(BluetoothUtilTest, DifferentManufacturerDataCompanyIdentifier) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  manufacturer_data_1.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters({}, {})});
+
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0002),
+                              CreateDataFilters({}, {})});
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
+  EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
+TEST_F(BluetoothUtilTest, DifferentManufacturerDataFilterSize) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  std::vector<uint8_t> filter_data_1 = {0x01};
+  std::vector<uint8_t> filter_mask_1 = {0xff};
+  manufacturer_data_1.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data_1, filter_mask_1)});
+
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+  std::vector<uint8_t> filter_data_2 = {0x01, 0x02};
+  std::vector<uint8_t> filter_mask_2 = {0xff, 0xff};
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data_2, filter_mask_2)});
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
+  EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
+TEST_F(BluetoothUtilTest, DifferentManufacturerData) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  std::vector<uint8_t> filter_mask = {0xff, 0xff, 0xff, 0xff};
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  std::vector<uint8_t> filter_data_1 = {0x01, 0x02, 0x03, 0x04};
+  manufacturer_data_1.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data_1, filter_mask)});
+
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+  std::vector<uint8_t> filter_data_2 = {0x05, 0x06, 0x07, 0x08};
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data_2, filter_mask)});
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
+  EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
+TEST_F(BluetoothUtilTest, DifferentManufacturerDataMask) {
+  base::Optional<std::vector<device::BluetoothUUID>> services;
+  services.emplace();
+  services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
+
+  std::vector<uint8_t> filter_data = {0x01, 0x02, 0x03, 0x04};
+
+  WebBluetoothManufacturerDataMap manufacturer_data_1;
+  std::vector<uint8_t> filter_mask_1 = {0xff, 0xff, 0xff, 0xff};
+  manufacturer_data_1.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data, filter_mask_1)});
+
+  WebBluetoothManufacturerDataMap manufacturer_data_2;
+  std::vector<uint8_t> filter_mask_2 = {0xff, 0xff, 0xff, 0x00};
+  manufacturer_data_2.insert({blink::mojom::WebBluetoothCompany::New(0x0001),
+                              CreateDataFilters(filter_data, filter_mask_2)});
+
+  auto filter_1 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_1));
+  auto filter_2 = blink::mojom::WebBluetoothLeScanFilter::New(
+      services, "ab", "a", std::move(manufacturer_data_2));
+  EXPECT_FALSE(AreScanFiltersSame(*filter_1, *filter_2));
+}
+
 }  // namespace content
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index ef657ad3..dba28aae 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -128,7 +128,13 @@
 
 bool IsValidFilter(const blink::mojom::WebBluetoothLeScanFilterPtr& filter) {
   // At least one member needs to be present.
-  if (!filter->name && !filter->name_prefix && !filter->services)
+  if (!filter->name && !filter->name_prefix && !filter->services &&
+      !filter->manufacturer_data) {
+    return false;
+  }
+
+  // The |services| should not be empty.
+  if (filter->services && filter->services->empty())
     return false;
 
   // The renderer will never send a |name| or a |name_prefix| longer than
@@ -140,10 +146,14 @@
       filter->name_prefix->size() > kMaxLengthForDeviceName)
     return false;
 
-  // The |name_prefix| should not be empty
+  // The |name_prefix| should not be empty.
   if (filter->name_prefix && filter->name_prefix->empty())
     return false;
 
+  // The |manufacturer_data| should not be empty.
+  if (filter->manufacturer_data && filter->manufacturer_data->empty())
+    return false;
+
   return true;
 }
 
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
index f002398a..5ca9d40 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
@@ -259,8 +259,8 @@
     base::Optional<std::vector<device::BluetoothUUID>> services;
     services.emplace();
     services->push_back(device::BluetoothUUID(kBatteryServiceUUIDString));
-    return blink::mojom::WebBluetoothLeScanFilter::New(services, name,
-                                                       name_prefix);
+    return blink::mojom::WebBluetoothLeScanFilter::New(
+        services, name, name_prefix, /*manufacturer_data=*/base::nullopt);
   }
 
   blink::mojom::WebBluetoothResult RequestScanningStartAndSimulatePromptEvent(
@@ -272,7 +272,9 @@
     client_impl->BindReceiver(client.InitWithNewEndpointAndPassReceiver());
     auto options = blink::mojom::WebBluetoothRequestLEScanOptions::New();
     options->filters.emplace();
-    auto filter_ptr = blink::mojom::WebBluetoothLeScanFilter::New(filter);
+    auto filter_ptr = blink::mojom::WebBluetoothLeScanFilter::New(
+        filter.services, filter.name, filter.name_prefix,
+        /*manufacturer_data=*/base::nullopt);
     options->filters->push_back(std::move(filter_ptr));
 
     // Use two RunLoops to guarantee the order of operations for this test.
@@ -351,8 +353,7 @@
 
   auto options = blink::mojom::WebBluetoothRequestLEScanOptions::New();
   options->filters.emplace();
-  auto filter_ptr = blink::mojom::WebBluetoothLeScanFilter::New(*filter);
-  options->filters->push_back(std::move(filter_ptr));
+  options->filters->push_back(std::move(filter));
 
   // Use two RunLoops to guarantee the order of operations for this test.
   // |callback_loop| guarantees that RequestScanningStartCallback has finished
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index eaf7363a..db9fd6f 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1168,7 +1168,7 @@
 
   HistogramSynchronizer::GetInstance();
 
-  FieldTrialSynchronizer::CreateInstance();
+  field_trial_synchronizer_ = base::MakeRefCounted<FieldTrialSynchronizer>();
 
   // cc assumes a single client name for metrics in a process, which is
   // is inconsistent with single process mode where both the renderer and
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index e116a3d..69b0916 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -91,6 +91,7 @@
 class BrowserMainParts;
 class BrowserOnlineStateObserver;
 class BrowserThreadImpl;
+class FieldTrialSynchronizer;
 class MediaKeysListenerManagerImpl;
 class MediaStreamManager;
 class SaveFileManager;
@@ -350,6 +351,12 @@
   std::unique_ptr<mojo::core::ScopedIPCSupport> mojo_ipc_support_;
   std::unique_ptr<MediaKeysListenerManagerImpl> media_keys_listener_manager_;
 
+  // The FieldTrialSynchronizer tells child processes when a trial gets
+  // activated. This is mostly an optimization, as a consequence if renderers
+  // know a trial is already active they don't need to send anything to the
+  // browser.
+  scoped_refptr<FieldTrialSynchronizer> field_trial_synchronizer_;
+
   // |user_input_monitor_| has to outlive |audio_manager_|, so declared first.
   std::unique_ptr<media::UserInputMonitor> user_input_monitor_;
 
diff --git a/content/browser/field_trial_synchronizer.cc b/content/browser/field_trial_synchronizer.cc
index 0af9ed30..98643ff5 100644
--- a/content/browser/field_trial_synchronizer.cc
+++ b/content/browser/field_trial_synchronizer.cc
@@ -20,20 +20,33 @@
 
 namespace {
 
-FieldTrialSynchronizer* g_instance = nullptr;
-
-// Notifies all renderer processes about the |group_name| that is finalized for
-// the given field trail (|field_trial_name|). This is called on UI thread.
-void NotifyAllRenderersOfFieldTrial(const std::string& field_trial_name,
-                                    const std::string& group_name) {
-  // To iterate over RenderProcessHosts, or to send messages to the hosts, we
-  // need to be on the UI thread.
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
+void AddFieldTrialToPersistentSystemProfile(const std::string& field_trial_name,
+                                            const std::string& group_name) {
   // Note this in the persistent profile as it will take a while for a new
   // "complete" profile to be generated.
   metrics::GlobalPersistentSystemProfile::GetInstance()->AddFieldTrial(
       field_trial_name, group_name);
+}
+
+}  // namespace
+
+FieldTrialSynchronizer::FieldTrialSynchronizer() {
+  bool success = base::FieldTrialList::AddObserver(this);
+  // Ensure the observer was actually registered.
+  DCHECK(success);
+
+  variations::VariationsIdsProvider::GetInstance()->AddObserver(this);
+  NotifyAllRenderersOfVariationsHeader();
+}
+
+void FieldTrialSynchronizer::NotifyAllRenderersOfFieldTrial(
+    const std::string& field_trial_name,
+    const std::string& group_name) {
+  // To iterate over RenderProcessHosts, or to send messages to the hosts, we
+  // need to be on the UI thread.
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  AddFieldTrialToPersistentSystemProfile(field_trial_name, group_name);
 
   for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
        !it.IsAtEnd(); it.Advance()) {
@@ -50,30 +63,23 @@
   }
 }
 
-}  // namespace
-
-// static
-void FieldTrialSynchronizer::CreateInstance() {
-  // Only 1 instance is allowed per process.
-  DCHECK(!g_instance);
-  g_instance = new FieldTrialSynchronizer();
-}
-
-FieldTrialSynchronizer::FieldTrialSynchronizer() {
-  bool success = base::FieldTrialList::AddObserver(this);
-  // Ensure the observer was actually registered.
-  DCHECK(success);
-
-  variations::VariationsIdsProvider::GetInstance()->AddObserver(this);
-  NotifyAllRenderersOfVariationsHeader();
-}
-
 void FieldTrialSynchronizer::OnFieldTrialGroupFinalized(
     const std::string& field_trial_name,
     const std::string& group_name) {
-  RunOrPostTaskOnThread(FROM_HERE, BrowserThread::UI,
-                        base::BindOnce(&NotifyAllRenderersOfFieldTrial,
-                                       field_trial_name, group_name));
+  // The FieldTrialSynchronizer may have been created before any BrowserThread
+  // is created, so we don't need to synchronize with child processes in which
+  // case there are no child processes to notify yet. But we want to update the
+  // persistent system profile, thus the histogram data recorded in the reduced
+  // mode will be tagged to its corresponding field trial experiment.
+  if (!BrowserThread::IsThreadInitialized(BrowserThread::UI)) {
+    AddFieldTrialToPersistentSystemProfile(field_trial_name, group_name);
+    return;
+  }
+
+  RunOrPostTaskOnThread(
+      FROM_HERE, BrowserThread::UI,
+      base::BindOnce(&FieldTrialSynchronizer::NotifyAllRenderersOfFieldTrial,
+                     this, field_trial_name, group_name));
 }
 
 // static
@@ -122,7 +128,8 @@
 }
 
 FieldTrialSynchronizer::~FieldTrialSynchronizer() {
-  NOTREACHED();
+  base::FieldTrialList::RemoveObserver(this);
+  variations::VariationsIdsProvider::GetInstance()->RemoveObserver(this);
 }
 
 }  // namespace content
diff --git a/content/browser/field_trial_synchronizer.h b/content/browser/field_trial_synchronizer.h
index 47c8264..9a6bd7c5 100644
--- a/content/browser/field_trial_synchronizer.h
+++ b/content/browser/field_trial_synchronizer.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "base/memory/ref_counted.h"
 #include "base/metrics/field_trial.h"
 #include "components/variations/variations_ids_provider.h"
 
@@ -20,24 +21,26 @@
 // renderers.
 //
 // This class registers itself as an observer of FieldTrialList. FieldTrialList
-// notifies this class by calling its OnFieldTrialGroupFinalized method when a
+// notifies this class by calling it's OnFieldTrialGroupFinalized method when a
 // group is selected (finalized) for a FieldTrial and OnFieldTrialGroupFinalized
 // method sends the FieldTrial's name and the group to all renderer processes.
 // Each renderer process creates the FieldTrial, and by using a 100% probability
-// for the FieldTrial, forces the FieldTrial to have the same group string. This
-// is mostly an optimization so that renderers don't send anything to the
-// browser when they know that a trial is already active.
-//
-// This class also registers itself as a VariationsIdsProvider Observer and
-// updates the renderers if the variations header changes.
+// for the FieldTrial, forces the FieldTrial to have the same group string.
+// This class also registers itself as a VariationsIdsProvider Observer
+// and updates the renderers if the variations header changes.
+
 class FieldTrialSynchronizer
-    : public base::FieldTrialList::Observer,
+    : public base::RefCountedThreadSafe<FieldTrialSynchronizer>,
+      public base::FieldTrialList::Observer,
       public variations::VariationsIdsProvider::Observer {
  public:
-  // Creates the global FieldTrialSynchronizer instance for this process. After
-  // this is invoked, renderers are notified whenever a field trial group is
-  // finalized.
-  static void CreateInstance();
+  // Construction also sets up the global singleton instance.  This instance is
+  // used to communicate between the UI and other threads, and is destroyed only
+  // as the main thread (browser_main) terminates, which means all other threads
+  // have completed, and will not need this instance any further. It adds itself
+  // as an observer of FieldTrialList so that it gets notified whenever a group
+  // is finalized in the browser process.
+  FieldTrialSynchronizer();
 
   // FieldTrialList::Observer methods:
 
@@ -55,11 +58,16 @@
   static void UpdateRendererVariationsHeader(RenderProcessHost* host);
 
  private:
-  FieldTrialSynchronizer();
-  ~FieldTrialSynchronizer() override;
+  // Notify all renderer processes about the |group_name| that is finalized for
+  // the given field trail (|field_trial_name|). This is called on UI thread.
+  void NotifyAllRenderersOfFieldTrial(const std::string& field_trial_name,
+                                      const std::string& group_name);
 
   static void NotifyAllRenderersOfVariationsHeader();
 
+  friend class base::RefCountedThreadSafe<FieldTrialSynchronizer>;
+  ~FieldTrialSynchronizer() override;
+
   DISALLOW_COPY_AND_ASSIGN(FieldTrialSynchronizer);
 };
 
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 0769c21e..0258315 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -42,6 +42,7 @@
 
 namespace {
 
+constexpr char kRpTestOrigin[] = "https://rp.example";
 constexpr char kIdpTestOrigin[] = "https://idp.example";
 constexpr char kIdpEndpoint[] = "https://idp.example/webid";
 constexpr char kAccountsEndpoint[] = "https://idp.example/accounts";
@@ -362,8 +363,8 @@
     auth_request_impl_ = std::make_unique<FederatedAuthRequestImpl>(
         main_rfh(), request_remote_.BindNewPipeAndPassReceiver());
     mock_request_manager_ =
-        std::make_unique<NiceMock<MockIdpNetworkRequestManager>>(provider,
-                                                                 main_rfh());
+        std::make_unique<NiceMock<MockIdpNetworkRequestManager>>(
+            provider, url::Origin::Create(GURL(kRpTestOrigin)));
     mock_dialog_controller_ =
         std::make_unique<NiceMock<MockIdentityRequestDialogController>>();
 
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 00634e5d..884792bec 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -79,11 +79,6 @@
         })");
 }
 
-scoped_refptr<network::SharedURLLoaderFactory> GetUrlLoaderFactory(
-    content::RenderFrameHost* host) {
-  return host->GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess();
-}
-
 std::unique_ptr<network::ResourceRequest> CreateCredentialedResourceRequest(
     GURL target_url,
     url::Origin initiator) {
@@ -123,6 +118,24 @@
                                          picture ? *picture : "");
 }
 
+// Parses accounts from given Value. Returns true if parse is successful and
+// adds parsed accounts to the |account_list|.
+bool ParseAccounts(const base::Value* accounts,
+                   IdpNetworkRequestManager::AccountList& account_list) {
+  if (!accounts->is_list())
+    return false;
+
+  for (auto& account : accounts->GetList()) {
+    if (!account.is_dict())
+      return false;
+
+    auto parsed_account = ParseAccount(account);
+    if (parsed_account)
+      account_list.push_back(parsed_account.value());
+  }
+  return true;
+}
+
 }  // namespace
 
 // static
@@ -136,12 +149,20 @@
   if (!network::IsOriginPotentiallyTrustworthy(url::Origin::Create(provider)))
     return nullptr;
 
-  return std::make_unique<IdpNetworkRequestManager>(provider, host);
+  // Use the browser process URL loader factory because it has cross-origin
+  // read blocking disabled.
+  return std::make_unique<IdpNetworkRequestManager>(
+      provider, host->GetLastCommittedOrigin(),
+      host->GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess());
 }
 
-IdpNetworkRequestManager::IdpNetworkRequestManager(const GURL& provider,
-                                                   RenderFrameHost* host)
-    : provider_(provider), render_frame_host_(host) {}
+IdpNetworkRequestManager::IdpNetworkRequestManager(
+    const GURL& provider,
+    const url::Origin& relying_party_origin,
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory)
+    : provider_(provider),
+      relying_party_origin_(relying_party_origin),
+      loader_factory_(loader_factory) {}
 
 IdpNetworkRequestManager::~IdpNetworkRequestManager() = default;
 
@@ -174,8 +195,7 @@
   // this bypasses CORB. Ensure there is a test added.
   // https://crbug.com/1155312.
   resource_request->redirect_mode = network::mojom::RedirectMode::kError;
-  resource_request->request_initiator =
-      render_frame_host_->GetLastCommittedOrigin();
+  resource_request->request_initiator = relying_party_origin_;
   resource_request->trusted_params = network::ResourceRequest::TrustedParams();
   resource_request->trusted_params->isolation_info =
       net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
@@ -184,12 +204,8 @@
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
 
-  // Use the browser process URL loader factory because it has cross-origin
-  // read blocking disabled.
-  auto loader_factory = GetUrlLoaderFactory(render_frame_host_);
-
   url_loader_->DownloadToString(
-      loader_factory.get(),
+      loader_factory_.get(),
       base::BindOnce(&IdpNetworkRequestManager::OnWellKnownLoaded,
                      weak_ptr_factory_.GetWeakPtr()),
       maxResponseSizeInKiB * 1024);
@@ -214,17 +230,14 @@
   // TODO: Should this be a POST, rather than a GET using query parameters?
   // https://crbug.com/1141125.
   GURL target_url = GURL(signin_url.spec() + "?" + encoded_request);
-  auto resource_request = CreateCredentialedResourceRequest(
-      target_url, render_frame_host_->GetLastCommittedOrigin());
+  auto resource_request =
+      CreateCredentialedResourceRequest(target_url, relying_party_origin_);
   auto traffic_annotation = CreateTrafficAnnotation();
   // TODO(kenrb): Make this not send cookies. https://crbug.com/1141125.
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
-
-  auto loader_factory = GetUrlLoaderFactory(render_frame_host_);
-
   url_loader_->DownloadToString(
-      loader_factory.get(),
+      loader_factory_.get(),
       base::BindOnce(&IdpNetworkRequestManager::OnSigninRequestResponse,
                      weak_ptr_factory_.GetWeakPtr()),
       maxResponseSizeInKiB * 1024);
@@ -237,8 +250,8 @@
   DCHECK(!accounts_request_callback_);
   accounts_request_callback_ = std::move(callback);
 
-  auto resource_request = CreateCredentialedResourceRequest(
-      accounts_url, render_frame_host_->GetLastCommittedOrigin());
+  auto resource_request =
+      CreateCredentialedResourceRequest(accounts_url, relying_party_origin_);
   // Use ReferrerPolicy::NO_REFERRER for this request so that relying party
   // identity is not exposed to the Identity provider via referror.
   resource_request->referrer_policy = net::ReferrerPolicy::NO_REFERRER;
@@ -247,10 +260,8 @@
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
 
-  auto loader_factory = GetUrlLoaderFactory(render_frame_host_);
-
   url_loader_->DownloadToString(
-      loader_factory.get(),
+      loader_factory_.get(),
       base::BindOnce(&IdpNetworkRequestManager::OnAccountsRequestResponse,
                      weak_ptr_factory_.GetWeakPtr()),
       maxResponseSizeInKiB * 1024);
@@ -295,8 +306,8 @@
     return;
   }
 
-  auto resource_request = CreateCredentialedResourceRequest(
-      token_url, render_frame_host_->GetLastCommittedOrigin());
+  auto resource_request =
+      CreateCredentialedResourceRequest(token_url, relying_party_origin_);
   resource_request->method = net::HttpRequestHeaders::kPostMethod;
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
                                       kJSONMimeType);
@@ -307,10 +318,8 @@
                                                  traffic_annotation);
   url_loader_->AttachStringForUpload(token_request_body, kJSONMimeType);
 
-  auto loader_factory = GetUrlLoaderFactory(render_frame_host_);
-
   url_loader_->DownloadToString(
-      loader_factory.get(),
+      loader_factory_.get(),
       base::BindOnce(&IdpNetworkRequestManager::OnTokenRequestResponse,
                      weak_ptr_factory_.GetWeakPtr()),
       maxResponseSizeInKiB * 1024);
@@ -325,8 +334,8 @@
 
   logout_callback_ = std::move(callback);
 
-  auto resource_request = CreateCredentialedResourceRequest(
-      logout_url, render_frame_host_->GetLastCommittedOrigin());
+  auto resource_request =
+      CreateCredentialedResourceRequest(logout_url, relying_party_origin_);
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept, "*/*");
 
   auto traffic_annotation = CreateTrafficAnnotation();
@@ -334,33 +343,13 @@
   url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
                                                  traffic_annotation);
 
-  auto loader_factory = GetUrlLoaderFactory(render_frame_host_);
-
   url_loader_->DownloadToString(
-      loader_factory.get(),
+      loader_factory_.get(),
       base::BindOnce(&IdpNetworkRequestManager::OnLogoutCompleted,
                      weak_ptr_factory_.GetWeakPtr()),
       maxResponseSizeInKiB * 1024);
 }
 
-// static
-bool IdpNetworkRequestManager::ParseAccounts(
-    const base::Value* accounts,
-    IdpNetworkRequestManager::AccountList& account_list) {
-  if (!accounts->is_list())
-    return false;
-
-  for (auto& account : accounts->GetList()) {
-    if (!account.is_dict())
-      return false;
-
-    auto parsed_account = ParseAccount(account);
-    if (parsed_account)
-      account_list.push_back(parsed_account.value());
-  }
-  return true;
-}
-
 void IdpNetworkRequestManager::OnWellKnownLoaded(
     std::unique_ptr<std::string> response_body) {
   int response_code = -1;
diff --git a/content/browser/webid/idp_network_request_manager.h b/content/browser/webid/idp_network_request_manager.h
index a90c741a..61c0cdb1e6 100644
--- a/content/browser/webid/idp_network_request_manager.h
+++ b/content/browser/webid/idp_network_request_manager.h
@@ -13,7 +13,9 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/identity_request_dialog_controller.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace network {
 class SimpleURLLoader;
@@ -108,7 +110,10 @@
       const GURL& provider,
       RenderFrameHost* host);
 
-  IdpNetworkRequestManager(const GURL& provider, RenderFrameHost* host);
+  IdpNetworkRequestManager(
+      const GURL& provider,
+      const url::Origin& relying_party,
+      scoped_refptr<network::SharedURLLoaderFactory> loader_factory);
 
   virtual ~IdpNetworkRequestManager();
 
@@ -136,15 +141,6 @@
   // Send logout request to a single target.
   virtual void SendLogout(const GURL& logout_url, LogoutCallback);
 
-  // Parses accounts from given Value. Returns true if parse is successful and
-  // adds parsed accounts to the |account_list|.
-  // TODO(majidvp): Make this function private and update tests to test the
-  // actual public interface of this class rather than its implementation
-  // details such as this.
-  static bool ParseAccounts(
-      const base::Value* accounts,
-      IdpNetworkRequestManager::AccountList& account_list);
-
  private:
   void OnWellKnownLoaded(std::unique_ptr<std::string> response_body);
   void OnWellKnownParsed(data_decoder::DataDecoder::ValueOrError result);
@@ -159,7 +155,9 @@
   // URL of the Identity Provider.
   GURL provider_;
 
-  RenderFrameHost* render_frame_host_;
+  url::Origin relying_party_origin_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
 
   FetchWellKnownCallback idp_well_known_callback_;
   SigninRequestCallback signin_request_callback_;
diff --git a/content/browser/webid/idp_network_request_manager_unittest.cc b/content/browser/webid/idp_network_request_manager_unittest.cc
index f17082d..0274e52 100644
--- a/content/browser/webid/idp_network_request_manager_unittest.cc
+++ b/content/browser/webid/idp_network_request_manager_unittest.cc
@@ -6,135 +6,256 @@
 
 #include <array>
 #include <string>
+#include <tuple>
+#include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
+#include "base/test/task_environment.h"
 #include "base/values.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using AccountList = content::IdpNetworkRequestManager::AccountList;
+using AccountsResponse = content::IdpNetworkRequestManager::AccountsResponse;
+using AccountsRequestCallback =
+    content::IdpNetworkRequestManager::AccountsRequestCallback;
 
 namespace content {
 
 namespace {
 
-base::Value CreateTestAccount(const std::string& sub) {
-  base::Value::DictStorage storage;
-  storage.emplace("sub", sub);
-  storage.emplace("email", "email@idp.test");
-  storage.emplace("name", "Ken R. Example");
-  storage.emplace("given_name", "Ken");
-  storage.emplace("picture", "https://idp.test/profile");
+const char kTestIdpUrl[] = "https://idp.test";
+const char kTestRpUrl[] = "https://rp.test";
+const char kTestAccountsEndpoint[] = "https://idp.test/accounts_endpoint";
 
-  return base::Value(storage);
-}
+class IdpNetworkRequestManagerTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    manager_ = std::make_unique<IdpNetworkRequestManager>(
+        GURL(kTestIdpUrl), url::Origin::Create(GURL(kTestRpUrl)),
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            &test_url_loader_factory_));
+  }
 
-TEST(AccountsParseTest, EmptyAccounts) {
-  base::ListValue empty_list;
-  AccountList parsed_accounts;
-  EXPECT_TRUE(
-      IdpNetworkRequestManager::ParseAccounts(&empty_list, parsed_accounts));
-  EXPECT_TRUE(parsed_accounts.empty());
-}
+  void TearDown() override { manager_.reset(); }
 
-TEST(AccountsParseTest, SingleAccount) {
-  base::Value::ListStorage accounts;
-  accounts.emplace_back(CreateTestAccount("1234"));
-  base::Value single_account_value(accounts);
-  AccountList parsed_accounts;
-  EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&single_account_value,
-                                                      parsed_accounts));
-  EXPECT_EQ(1UL, parsed_accounts.size());
-  EXPECT_EQ("1234", parsed_accounts[0].sub);
-}
+  std::tuple<AccountsResponse, AccountList>
+  SendAccountsRequestAndWaitForResponse(const char* test_accounts) {
+    GURL accounts_endpoint(kTestAccountsEndpoint);
+    test_url_loader_factory().AddResponse(accounts_endpoint.spec(),
+                                          test_accounts);
 
-TEST(AccountsParseTest, MultipleAccounts) {
-  base::Value::ListStorage accounts;
-  accounts.emplace_back(CreateTestAccount("1234"));
-  accounts.emplace_back(CreateTestAccount("5678"));
-  base::Value single_account_value(accounts);
-  AccountList parsed_accounts;
-  EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&single_account_value,
-                                                      parsed_accounts));
-  EXPECT_EQ(2UL, parsed_accounts.size());
-  EXPECT_EQ("1234", parsed_accounts[0].sub);
-  EXPECT_EQ("5678", parsed_accounts[1].sub);
-}
-
-TEST(AccountsParseTest, OptionalFields) {
-  auto account = CreateTestAccount("1234");
-  account.RemoveKey("given_name");
-  account.RemoveKey("family_name");
-  account.RemoveKey("picture");
-  // given_name and picture are optional
-  base::Value::ListStorage accounts;
-  accounts.emplace_back(std::move(account));
-  base::Value single_account_value(accounts);
-
-  AccountList parsed_accounts;
-  EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&single_account_value,
-                                                      parsed_accounts));
-  EXPECT_EQ(1UL, parsed_accounts.size());
-  EXPECT_EQ("1234", parsed_accounts[0].sub);
-}
-
-TEST(AccountsParseTest, RequiredFields) {
-  auto TestAccountWithMissingField = [](const std::string& removed_key) {
-    auto account = CreateTestAccount("1234");
-    account.RemoveKey(removed_key);
-    base::Value::ListStorage accounts;
-    accounts.emplace_back(std::move(account));
-    return base::Value(accounts);
-  };
-
-  {
-    auto account_value = TestAccountWithMissingField("sub");
+    base::RunLoop run_loop;
+    AccountsResponse parsed_accounts_response;
     AccountList parsed_accounts;
-    EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&account_value,
-                                                        parsed_accounts));
-    EXPECT_TRUE(parsed_accounts.empty());
+    auto callback = base::BindLambdaForTesting(
+        [&](AccountsResponse response, const AccountList& accounts) {
+          parsed_accounts_response = response;
+          parsed_accounts = accounts;
+          run_loop.Quit();
+        });
+    manager().SendAccountsRequest(accounts_endpoint, std::move(callback));
+    run_loop.Run();
+
+    return {parsed_accounts_response, parsed_accounts};
+  }
+
+  IdpNetworkRequestManager& manager() { return *manager_; }
+
+  network::TestURLLoaderFactory& test_url_loader_factory() {
+    return test_url_loader_factory_;
+  }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  std::unique_ptr<IdpNetworkRequestManager> manager_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
+};
+
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountEmpty) {
+  const auto* test_empty_account_json = R"({
+  "accounts" : []
+  })";
+
+  AccountsResponse accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_empty_account_json);
+
+  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_TRUE(accounts.empty());
+}
+
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountSingle) {
+  const auto* test_single_account_json = R"({
+  "accounts" : [
+    {
+      "sub" : "1234",
+      "email": "ken@idp.test",
+      "name": "Ken R. Example",
+      "given_name": "Ken",
+      "picture": "https://idp.test/profile/1"
+    }
+  ]
+  })";
+
+  AccountsResponse accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_single_account_json);
+
+  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(1UL, accounts.size());
+  EXPECT_EQ("1234", accounts[0].sub);
+}
+
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountMultiple) {
+  const auto* test_accounts_json = R"({
+  "accounts" : [
+    {
+      "sub" : "1234",
+      "email": "ken@idp.test",
+      "name": "Ken R. Example",
+      "given_name": "Ken",
+      "picture": "https://idp.test/profile/1"
+    },
+    {
+      "sub" : "5678",
+      "email": "sam@idp.test",
+      "name": "Sam G. Test",
+      "given_name": "Sam",
+      "picture": "https://idp.test/profile/2"
+    }
+  ]
+  })";
+  AccountsResponse accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_accounts_json);
+
+  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(2UL, accounts.size());
+  EXPECT_EQ("1234", accounts[0].sub);
+  EXPECT_EQ("5678", accounts[1].sub);
+}
+
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountOptionalFields) {
+  // given_name and picture fields are optional
+  const auto* test_accounts_json = R"({
+  "accounts" : [
+    {
+      "sub" : "1234",
+      "email": "ken@idp.test",
+      "name": "Ken R. Example"
+    }
+  ]
+  })";
+
+  AccountsResponse accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_accounts_json);
+
+  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ("1234", accounts[0].sub);
+}
+
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountRequiredFields) {
+  {
+    const auto* test_accounts_missing_sub_json = R"({"accounts" : [{
+      "email": "ken@idp.test",
+      "name": "Ken R. Example"
+    }]})";
+    AccountsResponse accounts_response;
+    AccountList accounts;
+    std::tie(accounts_response, accounts) =
+        SendAccountsRequestAndWaitForResponse(test_accounts_missing_sub_json);
+
+    EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+    EXPECT_TRUE(accounts.empty());
   }
   {
-    auto account_value = TestAccountWithMissingField("email");
-    AccountList parsed_accounts;
-    EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&account_value,
-                                                        parsed_accounts));
-    EXPECT_TRUE(parsed_accounts.empty());
+    const auto* test_accounts_missing_email_json = R"({"accounts" : [{
+      "sub" : "1234",
+      "name": "Ken R. Example"
+    }]})";
+    AccountsResponse accounts_response;
+    AccountList accounts;
+    std::tie(accounts_response, accounts) =
+        SendAccountsRequestAndWaitForResponse(test_accounts_missing_email_json);
+
+    EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+    EXPECT_TRUE(accounts.empty());
   }
   {
-    auto account_value = TestAccountWithMissingField("name");
-    AccountList parsed_accounts;
-    EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&account_value,
-                                                        parsed_accounts));
-    EXPECT_TRUE(parsed_accounts.empty());
+    const auto* test_accounts_missing_name_json = R"({"accounts" : [{
+      "sub" : "1234",
+      "email": "ken@idp.test"
+    }]})";
+    AccountsResponse accounts_response;
+    AccountList accounts;
+    std::tie(accounts_response, accounts) =
+        SendAccountsRequestAndWaitForResponse(test_accounts_missing_name_json);
+
+    EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+    EXPECT_TRUE(accounts.empty());
   }
 }
 
-TEST(AccountsParseTest, Unicode) {
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountUnicode) {
   auto TestAccountWithKeyValue = [](const std::string& key,
                                     const std::string& value) {
-    auto account = CreateTestAccount("1234");
-    account.SetStringKey(key, value);
-    base::Value::ListStorage accounts;
-    accounts.emplace_back(std::move(account));
-    return base::Value(accounts);
+    const auto* json = R"({
+     "accounts" : [
+        {
+          "sub" : "1234",
+          "email": "ken@idp.test",
+          "%s": "%s"
+        }
+      ]
+    })";
+    return base::StringPrintf(json, key.c_str(), value.c_str());
   };
 
   std::array<std::string, 3> test_values{"ascii", "🦖", "مجید"};
 
   for (auto& test_value : test_values) {
-    const auto& account_value = TestAccountWithKeyValue("name", test_value);
-    AccountList parsed_accounts;
-    EXPECT_TRUE(IdpNetworkRequestManager::ParseAccounts(&account_value,
-                                                        parsed_accounts));
-    EXPECT_EQ(1UL, parsed_accounts.size());
-    EXPECT_EQ(test_value, parsed_accounts[0].name);
+    const auto& accounts_json = TestAccountWithKeyValue("name", test_value);
+
+    AccountsResponse accounts_response;
+    AccountList accounts;
+    std::tie(accounts_response, accounts) =
+        SendAccountsRequestAndWaitForResponse(accounts_json.c_str());
+
+    EXPECT_EQ(1UL, accounts.size());
+    EXPECT_EQ(test_value, accounts[0].name);
   }
 }
 
-TEST(AccountsParseTest, InvalidAccounts) {
-  const base::DictionaryValue dictionary_value;
-  AccountList parsed_accounts;
-  EXPECT_FALSE(IdpNetworkRequestManager::ParseAccounts(&dictionary_value,
-                                                       parsed_accounts));
-  EXPECT_TRUE(parsed_accounts.empty());
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountInvalid) {
+  const auto* test_invalid_account_json = "{}";
+
+  AccountsResponse accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_invalid_account_json);
+
+  EXPECT_EQ(AccountsResponse::kInvalidResponseError, accounts_response);
+  EXPECT_TRUE(accounts.empty());
+}
+
+TEST_F(IdpNetworkRequestManagerTest, ParseAccountMalformed) {
+  const auto* test_invalid_account_json = "malformed_json";
+
+  AccountsResponse accounts_response;
+  AccountList accounts;
+  std::tie(accounts_response, accounts) =
+      SendAccountsRequestAndWaitForResponse(test_invalid_account_json);
+
+  EXPECT_EQ(AccountsResponse::kInvalidResponseError, accounts_response);
+  EXPECT_TRUE(accounts.empty());
 }
 
 }  // namespace
diff --git a/content/browser/webid/test/mock_idp_network_request_manager.cc b/content/browser/webid/test/mock_idp_network_request_manager.cc
index f55c541..9c30d82 100644
--- a/content/browser/webid/test/mock_idp_network_request_manager.cc
+++ b/content/browser/webid/test/mock_idp_network_request_manager.cc
@@ -8,8 +8,8 @@
 
 MockIdpNetworkRequestManager::MockIdpNetworkRequestManager(
     const GURL& provider,
-    RenderFrameHost* host)
-    : IdpNetworkRequestManager(provider, host) {}
+    const url::Origin& relying_party)
+    : IdpNetworkRequestManager(provider, relying_party, nullptr) {}
 
 MockIdpNetworkRequestManager::~MockIdpNetworkRequestManager() = default;
 
diff --git a/content/browser/webid/test/mock_idp_network_request_manager.h b/content/browser/webid/test/mock_idp_network_request_manager.h
index 6bedb488..20adf91 100644
--- a/content/browser/webid/test/mock_idp_network_request_manager.h
+++ b/content/browser/webid/test/mock_idp_network_request_manager.h
@@ -12,7 +12,8 @@
 
 class MockIdpNetworkRequestManager : public IdpNetworkRequestManager {
  public:
-  MockIdpNetworkRequestManager(const GURL& provider, RenderFrameHost* host);
+  MockIdpNetworkRequestManager(const GURL& provider,
+                               const url::Origin& relaying_party_origin);
 
   ~MockIdpNetworkRequestManager() override;
 
diff --git a/content/public/renderer/render_view.h b/content/public/renderer/render_view.h
index f3a7d177..f88ac5e 100644
--- a/content/public/renderer/render_view.h
+++ b/content/public/renderer/render_view.h
@@ -9,7 +9,6 @@
 
 #include "build/build_config.h"
 #include "content/common/content_export.h"
-#include "ipc/ipc_sender.h"
 #include "ui/gfx/native_widget_types.h"
 
 namespace blink {
@@ -35,7 +34,7 @@
 // agnostic of frames and document content or structure. For more context,
 // please see https://crbug.com/467770 and
 // https://www.chromium.org/developers/design-documents/site-isolation.
-class CONTENT_EXPORT RenderView : public IPC::Sender {
+class CONTENT_EXPORT RenderView {
  public:
   // Returns the RenderView containing the given WebView.
   static RenderView* FromWebView(blink::WebView* webview);
@@ -57,7 +56,7 @@
   virtual blink::WebView* GetWebView() = 0;
 
  protected:
-  ~RenderView() override {}
+  virtual ~RenderView() {}
 
  private:
   // This interface should only be implemented inside content.
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 386a38dd..b3dfc05 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -124,8 +124,6 @@
     scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
   DCHECK(RenderThread::IsMainThread());
 
-  agent_scheduling_group_.AddRoute(routing_id_, this);
-
   WebFrame* opener_frame = nullptr;
   if (params->opener_frame_token)
     opener_frame = WebFrame::FromFrameToken(params->opener_frame_token.value());
@@ -183,7 +181,6 @@
   DCHECK(destroying_);  // Always deleted through Destroy().
 
   g_routing_id_view_map.Get().erase(routing_id_);
-  agent_scheduling_group_.RemoveRoute(routing_id_);
 
 #ifndef NDEBUG
   // Make sure we are no longer referenced by the ViewMap or RoutingIDViewMap.
@@ -269,12 +266,6 @@
   frames_with_pending_state_.clear();
 }
 
-// IPC::Listener -------------------------------------------------------------
-
-bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) {
-  return false;
-}
-
 // blink::WebViewClient ------------------------------------------------------
 
 // TODO(csharrison): Migrate this method to WebLocalFrameClient /
@@ -540,14 +531,6 @@
 
 // RenderView implementation ---------------------------------------------------
 
-bool RenderViewImpl::Send(IPC::Message* message) {
-  // No messages sent through RenderView come without a routing id, yay. Let's
-  // keep that up.
-  CHECK_NE(message->routing_id(), MSG_ROUTING_NONE);
-
-  return agent_scheduling_group_.Send(message);
-}
-
 RenderFrameImpl* RenderViewImpl::GetMainRenderFrame() {
   return main_render_frame_;
 }
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 1d8ffde..b855cc7 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -81,7 +81,6 @@
 // the owner of it. Thus a tab may have multiple RenderViewImpls, one for the
 // main frame, and one for each other frame tree generated.
 class CONTENT_EXPORT RenderViewImpl : public blink::WebViewClient,
-                                      public IPC::Listener,
                                       public RenderView {
  public:
   // Creates a new RenderView. Note that if the original opener has been closed,
@@ -145,9 +144,6 @@
   // Returns the current instance of blink::RendererPreferences.
   const blink::RendererPreferences& GetRendererPreferences() const;
 
-  // IPC::Listener implementation.
-  bool OnMessageReceived(const IPC::Message& msg) override;
-
   // blink::WebViewClient implementation --------------------------------------
 
   blink::WebView* CreateView(
@@ -170,7 +166,6 @@
 
   // RenderView implementation -------------------------------------------------
 
-  bool Send(IPC::Message* message) override;
   RenderFrameImpl* GetMainRenderFrame() override;
   int GetRoutingID() override;
   blink::WebView* GetWebView() override;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index fc872e6..b7743969 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2566,6 +2566,7 @@
       "//gpu/command_buffer/service:android_texture_owner_test_support",
       "//media/capture/content/android",
       "//media/capture/content/android:screen_capture_java",
+      "//services/data_decoder/public/cpp/android:safe_json_java",
       "//third_party/blink/public/common",
       "//third_party/blink/public/common:font_unique_name_table_proto",
       "//ui/android:android",
diff --git a/content/web_test/browser/web_test_bluetooth_adapter_provider.cc b/content/web_test/browser/web_test_bluetooth_adapter_provider.cc
index 126e70c..aa2bb430 100644
--- a/content/web_test/browser/web_test_bluetooth_adapter_provider.cc
+++ b/content/web_test/browser/web_test_bluetooth_adapter_provider.cc
@@ -1373,7 +1373,11 @@
   uuids.push_back(BluetoothUUID(kGlucoseServiceUUID));
   uuids.push_back(BluetoothUUID(kTxPowerServiceUUID));
 
-  return GetBaseDevice(adapter, "Glucose Device", uuids, makeMACAddress(0x2));
+  auto device =
+      GetBaseDevice(adapter, "Glucose Device", uuids, makeMACAddress(0x2));
+  device->SetManufacturerData({{0x0001, {1, 2}}, {0x0002, {3, 4}}});
+
+  return device;
 }
 
 // static
diff --git a/content/web_test/browser/web_test_bluetooth_adapter_provider.h b/content/web_test/browser/web_test_bluetooth_adapter_provider.h
index 6c16cdd..564891e 100644
--- a/content/web_test/browser/web_test_bluetooth_adapter_provider.h
+++ b/content/web_test/browser/web_test_bluetooth_adapter_provider.h
@@ -555,6 +555,9 @@
   //   - Generic Access (0x1800)
   //   - Glucose UUID (0x1808)
   //   - Tx Power (0x1804)
+  // Manufacturer Data added:
+  //   - 0x0001 : { 1, 2 }
+  //   - 0x0002 : { 3, 4 }
   // Services added:
   // None.
   static std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>>
diff --git a/device/bluetooth/public/mojom/test/fake_bluetooth.mojom b/device/bluetooth/public/mojom/test/fake_bluetooth.mojom
index cd13311..b2d87dd 100644
--- a/device/bluetooth/public/mojom/test/fake_bluetooth.mojom
+++ b/device/bluetooth/public/mojom/test/fake_bluetooth.mojom
@@ -150,6 +150,7 @@
   // the connection alive.
   SimulatePreconnectedPeripheral(string address,
                                  string name,
+                                 map<uint16, array<uint8>> manufacturer_data,
                                  array<UUID> known_service_uuids) => ();
 
   // Simulates an advertisement packet described in |result| being received by
diff --git a/device/bluetooth/test/fake_central.cc b/device/bluetooth/test/fake_central.cc
index 6928cdd..2cb87e2 100644
--- a/device/bluetooth/test/fake_central.cc
+++ b/device/bluetooth/test/fake_central.cc
@@ -45,6 +45,7 @@
 void FakeCentral::SimulatePreconnectedPeripheral(
     const std::string& address,
     const std::string& name,
+    const base::flat_map<uint16_t, std::vector<uint8_t>>& manufacturer_data,
     const std::vector<device::BluetoothUUID>& known_service_uuids,
     SimulatePreconnectedPeripheralCallback callback) {
   FakePeripheral* fake_peripheral = GetFakePeripheral(address);
@@ -57,6 +58,9 @@
 
   fake_peripheral->SetName(name);
   fake_peripheral->SetSystemConnected(true);
+  fake_peripheral->SetManufacturerData(
+      device::BluetoothDevice::ManufacturerDataMap(manufacturer_data.begin(),
+                                                   manufacturer_data.end()));
   fake_peripheral->SetServiceUUIDs(device::BluetoothDevice::UUIDSet(
       known_service_uuids.begin(), known_service_uuids.end()));
 
diff --git a/device/bluetooth/test/fake_central.h b/device/bluetooth/test/fake_central.h
index 03eb7aa7..820c80ff 100644
--- a/device/bluetooth/test/fake_central.h
+++ b/device/bluetooth/test/fake_central.h
@@ -37,6 +37,7 @@
   void SimulatePreconnectedPeripheral(
       const std::string& address,
       const std::string& name,
+      const base::flat_map<uint16_t, std::vector<uint8_t>>& manufacturer_data,
       const std::vector<device::BluetoothUUID>& known_service_uuids,
       SimulatePreconnectedPeripheralCallback callback) override;
   void SimulateAdvertisementReceived(
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index 8c997a26..940c109d 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -54,6 +54,11 @@
   device_uuids_.ReplaceServiceUUIDs(gatt_services);
 }
 
+void FakePeripheral::SetManufacturerData(
+    ManufacturerDataMap manufacturer_data) {
+  manufacturer_data_ = std::move(manufacturer_data);
+}
+
 void FakePeripheral::SetNextGATTConnectionResponse(uint16_t code) {
   DCHECK(!next_connection_response_);
   DCHECK(create_gatt_connection_error_callbacks_.empty());
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index ca936ac3..af6e2589 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -42,6 +42,10 @@
   // BluetoothDevice::GetUUIDs().
   void SetServiceUUIDs(UUIDSet service_uuids);
 
+  // Updates the peripheral's Manufacturer Data that are returned by
+  // BluetoothDevice::GetManufacturerData().
+  void SetManufacturerData(ManufacturerDataMap manufacturer_data);
+
   // If |code| is kHCISuccess calls a pending success callback for
   // CreateGattConnection. Otherwise calls a pending error callback
   // with the ConnectErrorCode corresponding to |code|.
diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h
index 53ea37a..dd224e0 100644
--- a/device/bluetooth/test/mock_bluetooth_device.h
+++ b/device/bluetooth/test/mock_bluetooth_device.h
@@ -151,6 +151,12 @@
 
   void AddUUID(const BluetoothUUID& uuid) { uuids_.insert(uuid); }
 
+  // Updates the device's Manufacturer Data that are returned by
+  // BluetoothDevice::GetManufacturerData().
+  void SetManufacturerData(ManufacturerDataMap manufacturer_data) {
+    manufacturer_data_ = std::move(manufacturer_data);
+  }
+
   // Functions to save and run callbacks from this device. Useful when
   // trying to run callbacks in response to other actions e.g. run a read
   // value callback in response to a connection request.
diff --git a/device/vr/android/arcore/ar_compositor_frame_sink.cc b/device/vr/android/arcore/ar_compositor_frame_sink.cc
index f3c435d..a54b3b09 100644
--- a/device/vr/android/arcore/ar_compositor_frame_sink.cc
+++ b/device/vr/android/arcore/ar_compositor_frame_sink.cc
@@ -357,13 +357,7 @@
   // First the DOM, if it's enabled
   if (should_composite_dom_overlay_) {
     auto dom_surface_id = xr_frame_sink_client_->GetDOMSurface();
-    bool can_composite_dom_overlay =
-        dom_surface_id && dom_surface_id->is_valid();
-    DVLOG(3)
-        << __func__
-        << " Attempting to composite DOMOverlay, can_composite_dom_overlay="
-        << can_composite_dom_overlay;
-    if (can_composite_dom_overlay) {
+    if (dom_surface_id && dom_surface_id->is_valid()) {
       viz::SharedQuadState* dom_quad_state =
           render_pass->CreateAndAppendSharedQuadState();
       dom_quad_state->SetAll(
diff --git a/device/vr/android/arcore/arcore_gl.cc b/device/vr/android/arcore/arcore_gl.cc
index bda6598..6eee07b 100644
--- a/device/vr/android/arcore/arcore_gl.cc
+++ b/device/vr/android/arcore/arcore_gl.cc
@@ -1654,9 +1654,7 @@
     }
 
     // Save the touch point for use in Blink's XR input event deduplication.
-    if (IsFeatureEnabled(device::mojom::XRSessionFeature::DOM_OVERLAY)) {
-      state->overlay_pointer_position = screen_last_touch;
-    }
+    state->overlay_pointer_position = screen_last_touch;
 
     state->description = device::mojom::XRInputSourceDescription::New();
 
diff --git a/ios/chrome/app/app_metrics_app_state_agent.mm b/ios/chrome/app/app_metrics_app_state_agent.mm
index 0c73896..0e6051f 100644
--- a/ios/chrome/app/app_metrics_app_state_agent.mm
+++ b/ios/chrome/app/app_metrics_app_state_agent.mm
@@ -40,11 +40,14 @@
   [sceneState addObserver:self];
 }
 
-- (void)appStateDidExitSafeMode:(AppState*)appState {
-  DCHECK(self.appState.lastTimeInForeground.is_null());
-  // Log session start. This normally happens in
-  // sceneState:transitionedToActivationLevel:, but is skipped in safe mode.
-  [self handleSessionStart];
+- (void)appState:(AppState*)appState
+    didTransitionFromInitStage:(InitStage)previousInitStage {
+  if (previousInitStage == InitStageSafeMode) {
+    // Log session start if the app is already foreground
+    if (self.appState.foregroundScenes.count > 0) {
+      [self handleSessionStart];
+    }
+  }
 }
 
 #pragma mark - SceneStateObserver
@@ -52,8 +55,6 @@
 - (void)sceneState:(SceneState*)sceneState
     transitionedToActivationLevel:(SceneActivationLevel)level {
   if (self.appState.initStage <= InitStageSafeMode) {
-    // Don't log any metrics at safe mode. Wait for the transition out of safe
-    // mode to log session start.
     return;
   }
 
@@ -61,19 +62,17 @@
       self.appState.lastTimeInForeground.is_null()) {
     [self handleSessionStart];
   } else if (level <= SceneActivationLevelBackground) {
-    for (SceneState* scene in self.appState.connectedScenes) {
-      if (scene.activationLevel > SceneActivationLevelBackground) {
-        // One scene has gone background, but at least one other is still
-        // foreground. Consider the session ongoing.
-        return;
-      }
+    // Do not consider the app as brackgrounded when there are still scenes on
+    // the foreground.
+    if (self.appState.foregroundScenes.count > 0) {
+      return;
     }
-
     if (self.appState.lastTimeInForeground.is_null()) {
       // This method will be called multiple times, once per scene, if multiple
       // scenes go background simulatneously (for example, if two windows were
       // in split screen and the user swiped to go home). Only log the session
-      // duration once.
+      // duration once. This also makes sure that the first scene that ramps up
+      // to foreground doesn't end the session.
       return;
     }
 
diff --git a/ios/chrome/app/app_metrics_app_state_agent_unittest.mm b/ios/chrome/app/app_metrics_app_state_agent_unittest.mm
index e3a8d07..ae511d2 100644
--- a/ios/chrome/app/app_metrics_app_state_agent_unittest.mm
+++ b/ios/chrome/app/app_metrics_app_state_agent_unittest.mm
@@ -95,6 +95,14 @@
             browser_state_.get()));
   }
 
+  void SimulateTransitionToCurrentStage() {
+    InitStage previousStage =
+        app_state_.initStage == InitStageStart
+            ? InitStageStart
+            : static_cast<InitStage>(app_state_.initStage - 1);
+    [agent_ appState:app_state_ didTransitionFromInitStage:previousStage];
+  }
+
   AppMetricsAppStateAgent* agent_;
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   FakeAppState* app_state_;
@@ -114,6 +122,8 @@
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_started_count());
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_ended_count());
 
+  SimulateTransitionToCurrentStage();
+
   // Going foreground starts the session.
   scene.activationLevel = SceneActivationLevelForegroundInactive;
   EXPECT_EQ(1, getProfileSessionDurationsService()->session_started_count());
@@ -135,6 +145,8 @@
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_started_count());
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_ended_count());
 
+  SimulateTransitionToCurrentStage();
+
   // One scene is enough to start a session.
   sceneA.activationLevel = SceneActivationLevelForegroundInactive;
   EXPECT_EQ(1, getProfileSessionDurationsService()->session_started_count());
@@ -165,6 +177,8 @@
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_started_count());
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_ended_count());
 
+  SimulateTransitionToCurrentStage();
+
   // Going to background at app start doesn't log anything.
   scene.activationLevel = SceneActivationLevelBackground;
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_started_count());
@@ -177,7 +191,7 @@
 
   // Session starts when safe mode completes.
   app_state_.initStageForTesting = InitStageFinal;
-  [agent_ appStateDidExitSafeMode:app_state_];
+  SimulateTransitionToCurrentStage();
   EXPECT_EQ(1, getProfileSessionDurationsService()->session_started_count());
   EXPECT_EQ(0, getProfileSessionDurationsService()->session_ended_count());
 
diff --git a/ios/chrome/app/application_delegate/app_state.h b/ios/chrome/app/application_delegate/app_state.h
index 49673067..2ba13886 100644
--- a/ios/chrome/app/application_delegate/app_state.h
+++ b/ios/chrome/app/application_delegate/app_state.h
@@ -158,6 +158,10 @@
 // Returns a list of all connected scenes.
 - (NSArray<SceneState*>*)connectedScenes;
 
+// Returns a list of all scenes in the foreground that are not necessarly
+// active.
+- (NSArray<SceneState*>*)foregroundScenes;
+
 // Adds an observer to this app state. The observers will be notified about
 // app state changes per AppStateObserver protocol.
 - (void)addObserver:(id<AppStateObserver>)observer;
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 2c790265..ed927f9 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -629,6 +629,15 @@
   return @[];
 }
 
+- (NSArray<SceneState*>*)foregroundScenes {
+  return [self.connectedScenes
+      filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
+                                                   SceneState* scene,
+                                                   NSDictionary* bindings) {
+        return scene.activationLevel >= SceneActivationLevelForegroundInactive;
+      }]];
+}
+
 - (void)setLastTappedWindow:(UIWindow*)window {
   if (_lastTappedWindow == window) {
     return;
@@ -654,9 +663,6 @@
 }
 
 - (void)initializeUIPostSafeMode {
-  //  Cache the safe mode status which is needed later on to complete the
-  //  post-safemode initialization.
-  BOOL wasInSafeMode = self.inSafeMode;
   // Make sure that safe mode is turned off before moving further with the
   // browser startup.
   self.inSafeMode = NO;
@@ -667,12 +673,6 @@
   DCHECK([self.startupInformation isColdStart]);
   [_browserLauncher startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
 
-  if (wasInSafeMode) {
-    // Complete the transition out of safe mode if the app was really in safe
-    // mode.
-    [self.observers appStateDidExitSafeMode:self];
-  }
-
   if (EnableSyntheticCrashReportsForUte()) {
     // Must be called after sequenced context creation, which happens in
     // startUpBrowserToStage: method called above.
diff --git a/ios/chrome/app/application_delegate/app_state_observer.h b/ios/chrome/app/application_delegate/app_state_observer.h
index 8658b0e..3adecbc 100644
--- a/ios/chrome/app/application_delegate/app_state_observer.h
+++ b/ios/chrome/app/application_delegate/app_state_observer.h
@@ -39,9 +39,6 @@
 - (void)appState:(AppState*)appState
     firstSceneHasInitializedUI:(SceneState*)sceneState;
 
-// Called after the app exits safe mode.
-- (void)appStateDidExitSafeMode:(AppState*)appState;
-
 // Called when |AppState.lastTappedWindow| changes.
 - (void)appState:(AppState*)appState lastTappedWindowChanged:(UIWindow*)window;
 
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 8f7f667a..0e9b5397 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -523,8 +523,7 @@
   id browserLauncherMock = getBrowserLauncherMock();
   [[browserLauncherMock expect] setLaunchOptions:launchOptions];
 
-  // Expected calls on AppState#coordinatorDidExitSafeMode.
-  [[appStateObserverMock expect] appStateDidExitSafeMode:appState];
+  // Expected calls after safe mode.
   [[browserLauncherMock expect]
       startUpBrowserToStage:INITIALIZATION_STAGE_FOREGROUND];
 
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index ea5bd1567..ca7bcb9d 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -734,6 +734,11 @@
      flag_descriptions::kIncognitoBrandConsistencyForIOSName,
      flag_descriptions::kIncognitoBrandConsistencyForIOSDescription,
      flags_ui::kOsIos, FEATURE_VALUE_TYPE(kIncognitoBrandConsistencyForIOS)},
+    {"update-history-entry-points-in-incognito",
+     flag_descriptions::kUpdateHistoryEntryPointsInIncognitoName,
+     flag_descriptions::kUpdateHistoryEntryPointsInIncognitoDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kUpdateHistoryEntryPointsInIncognito)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 5ed1ab32..add5970a 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -434,6 +434,12 @@
     "When enabled, the toolbars and their fullscreen animations will be "
     "managed by the toolbar container coordinator rather than BVC.";
 
+const char kUpdateHistoryEntryPointsInIncognitoName[] =
+    "Update history entry points in Incongito.";
+const char kUpdateHistoryEntryPointsInIncognitoDescription[] =
+    "When enabled, the entry points to history UI from Incognito mode will be "
+    "removed.";
+
 const char kURLBlocklistIOSName[] = "URL Blocklist Policy";
 const char kURLBlocklistIOSDescription[] =
     "When enabled, URLs can be blocked/allowed by the URLBlocklist/URLAllowlist"
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index feb71a42..95f99d17 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -378,6 +378,11 @@
 extern const char kToolbarContainerName[];
 extern const char kToolbarContainerDescription[];
 
+// Title and description for the flag to enable removing any entry points to the
+// history UI from Incognito mode.
+extern const char kUpdateHistoryEntryPointsInIncognitoName[];
+extern const char kUpdateHistoryEntryPointsInIncognitoDescription[];
+
 // Title and description for the flag to enable URLBlocklist/URLAllowlist
 // enterprise policy.
 extern const char kURLBlocklistIOSName[];
diff --git a/ios/chrome/browser/ui/bookmarks/BUILD.gn b/ios/chrome/browser/ui/bookmarks/BUILD.gn
index bfd1ab7..0534159 100644
--- a/ios/chrome/browser/ui/bookmarks/BUILD.gn
+++ b/ios/chrome/browser/ui/bookmarks/BUILD.gn
@@ -228,6 +228,7 @@
   testonly = true
   sources = [
     "bookmark_home_view_controller_unittest.mm",
+    "bookmark_model_bridge_observer_unittest.mm",
     "bookmark_path_cache_unittest.mm",
     "bookmark_utils_ios_unittest.mm",
   ]
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm
index 87a6211..bebb5ab2 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.mm
@@ -62,8 +62,12 @@
     size_t old_index,
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
-  [observer_ bookmarkNodeDeleted:node fromFolder:parent];
-  [observer_ bookmarkNodeChildrenChanged:parent];
+  // Hold a non-weak reference to |observer_|, in case the first event below
+  // destroys |this|.
+  id<BookmarkModelBridgeObserver> observer = observer_;
+
+  [observer bookmarkNodeDeleted:node fromFolder:parent];
+  [observer bookmarkNodeChildrenChanged:parent];
 }
 
 void BookmarkModelBridge::BookmarkNodeChanged(BookmarkModel* model,
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer_unittest.mm b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer_unittest.mm
new file mode 100644
index 0000000..120d6a5
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer_unittest.mm
@@ -0,0 +1,102 @@
+// 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 "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
+
+#include <memory>
+
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "ios/chrome/browser/ui/bookmarks/bookmark_ios_unittest.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@class TestBookmarkModelBridgeObserver;
+
+@interface TestOwner : NSObject {
+ @public
+  std::unique_ptr<bookmarks::BookmarkModelBridge> bridge;
+}
+
+@property(nonatomic, strong) TestBookmarkModelBridgeObserver* observer;
+
+- (void)bookmarkNodeDeleted;
+@end
+
+@implementation TestOwner
+- (void)bookmarkNodeDeleted {
+  bridge.reset();
+  self.observer = nil;
+}
+@end
+
+@interface TestBookmarkModelBridgeObserver
+    : NSObject <BookmarkModelBridgeObserver> {
+  id owner;
+}
+
+- (void)setOwner:(id)newOwner;
+@end
+
+@implementation TestBookmarkModelBridgeObserver
+
+- (void)setOwner:(id)newOwner {
+  owner = newOwner;
+}
+
+#pragma mark - BookmarkModelBridgeObserver
+
+- (void)bookmarkNodeChildrenChanged:
+    (const bookmarks::BookmarkNode*)bookmarkNode {
+}
+
+- (void)bookmarkModelRemovedAllNodes {
+}
+
+- (void)bookmarkModelLoaded {
+}
+
+- (void)bookmarkNodeChanged:(const bookmarks::BookmarkNode*)bookmarkNode {
+}
+
+- (void)bookmarkNode:(const bookmarks::BookmarkNode*)bookmarkNode
+     movedFromParent:(const bookmarks::BookmarkNode*)oldParent
+            toParent:(const bookmarks::BookmarkNode*)newParent {
+}
+
+- (void)bookmarkNodeDeleted:(const bookmarks::BookmarkNode*)node
+                 fromFolder:(const bookmarks::BookmarkNode*)folder {
+  [owner bookmarkNodeDeleted];
+}
+
+@end
+
+namespace bookmarks {
+
+namespace {
+
+using BookmarkModelBridgeObserverTest = BookmarkIOSUnitTest;
+
+TEST_F(BookmarkModelBridgeObserverTest,
+       NotifyBookmarkNodeChildrenChangedDespiteSelfDestruction) {
+  @autoreleasepool {
+    const BookmarkNode* mobile_node = bookmark_model_->mobile_node();
+    const BookmarkNode* folder = AddFolder(mobile_node, @"title");
+
+    TestOwner* owner = [[TestOwner alloc] init];
+    owner.observer = [[TestBookmarkModelBridgeObserver alloc] init];
+    [owner.observer setOwner:owner];
+
+    owner->bridge =
+        std::make_unique<BookmarkModelBridge>(owner.observer, bookmark_model_);
+
+    // Deleting the folder should not cause a crash.
+    bookmark_model_->Remove(folder);
+  }
+}
+
+}  // namespace
+
+}  // namespace bookmarks
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm
index ac1c81f1..17a45a7 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_coordinator.mm
@@ -120,7 +120,13 @@
 }
 
 - (void)infobarBannerWillBeDismissed:(BOOL)userInitiated {
-  // No-op.
+  if (userInitiated) {
+    SceneState* sceneState =
+        SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState();
+    DefaultBrowserSceneAgent* agent =
+        [DefaultBrowserSceneAgent agentFromScene:sceneState];
+    [agent.nonModalScheduler logUserDismissedPromo];
+  }
 }
 
 - (void)infobarWasDismissed {
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
index 51a82bb0..afb832086 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h
@@ -45,6 +45,9 @@
 // Handles the user performing the promo action.
 - (void)logUserPerformedPromoAction;
 
+// Handles the user manually dismissing the promo.
+- (void)logUserDismissedPromo;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_BROWSER_PROMO_NON_MODAL_SCHEDULER_H_
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
index 8f7c50bf..1dd2e05 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.mm
@@ -27,6 +27,14 @@
 // This should allow any initial overlays to be presented first.
 const NSTimeInterval kShowPromoWebpageLoadWaitTime = 3;
 
+// Number of times to show the promo to a user.
+const int kPromoShownTimesLimit = 2;
+
+bool PromoCanBeDisplayed() {
+  return !UserInPromoCooldown() &&
+         UserInteractionWithNonModalPromoCount() < kPromoShownTimesLimit;
+}
+
 }  // namespace
 
 @interface DefaultBrowserPromoNonModalScheduler () <WebStateListObserving,
@@ -95,6 +103,7 @@
 }
 
 - (void)logUserPerformedPromoAction {
+  LogUserInteractionWithNonModalPromo();
   if (NonModalPromosInstructionsEnabled()) {
     id<ApplicationSettingsCommands> handler =
         HandlerForProtocol(self.dispatcher, ApplicationSettingsCommands);
@@ -108,6 +117,10 @@
   }
 }
 
+- (void)logUserDismissedPromo {
+  LogUserInteractionWithNonModalPromo();
+}
+
 - (void)dismissPromoAnimated:(BOOL)animated {
   [self cancelDismissPromoTimer];
   [self.handler dismissDefaultBrowserNonModalPromoAnimated:animated];
@@ -183,7 +196,7 @@
 #pragma mark - Timer Management
 
 - (void)startShowPromoTimer {
-  if (self.promoIsShowing || self.showPromoTimer) {
+  if (!PromoCanBeDisplayed() || self.promoIsShowing || self.showPromoTimer) {
     return;
   }
   self.showPromoTimer =
@@ -200,7 +213,7 @@
 }
 
 - (void)showPromoTimerFinished {
-  if (self.promoIsShowing) {
+  if (!PromoCanBeDisplayed() || self.promoIsShowing) {
     return;
   }
   self.showPromoTimer = nil;
@@ -228,7 +241,10 @@
 
 - (void)dismissPromoTimerFinished {
   self.dismissPromoTimer = nil;
-  [self.handler dismissDefaultBrowserNonModalPromoAnimated:YES];
+  if (self.promoIsShowing) {
+    LogUserInteractionWithNonModalPromo();
+    [self.handler dismissDefaultBrowserNonModalPromoAnimated:YES];
+  }
 }
 
 @end
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_utils.h b/ios/chrome/browser/ui/default_promo/default_browser_utils.h
index da12631..b40547d 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_utils.h
+++ b/ios/chrome/browser/ui/default_promo/default_browser_utils.h
@@ -101,12 +101,19 @@
 // previously. Returns false otherwise.
 bool HasUserInteractedWithTailoredFullscreenPromoBefore();
 
+// Returns the number of times the user has seen and interacted with the
+// non-modal promo before.
+int UserInteractionWithNonModalPromoCount();
+
 // Logs that the user has interacted with the Fullscreen Promo.
 void LogUserInteractionWithFullscreenPromo();
 
 // Logs that the user has interacted with a Tailored Fullscreen Promo.
 void LogUserInteractionWithTailoredFullscreenPromo();
 
+// Logs that the user has interacted with a Non-Modals Promo.
+void LogUserInteractionWithNonModalPromo();
+
 // Returns true if the last URL open is within the time threshold that would
 // indicate Chrome is likely still the default browser. Returns false otherwise.
 bool IsChromeLikelyDefaultBrowser();
@@ -121,8 +128,8 @@
 // true, this type of promo will be ignored.
 DefaultPromoType MostRecentInterestDefaultPromoType(BOOL skipAllTabsPromo);
 
-// Return YES if the user has seen a fullscreen promo recently, and shouldn't
+// Return YES if the user has seen a promo recently, and shouldn't
 // see another one.
-BOOL UserInFullscreenPromoCooldown();
+BOOL UserInPromoCooldown();
 
 #endif  // IOS_CHROME_BROWSER_UI_DEFAULT_PROMO_DEFAULT_BROWSER_UTILS_H_
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_utils.mm b/ios/chrome/browser/ui/default_promo/default_browser_utils.mm
index f8f6f68..dc5cf2b 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_utils.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_utils.mm
@@ -41,8 +41,10 @@
     @"lastSignificantUserEventAllTabs";
 
 // Key for NSUserDefaults containing an NSDate indicating the last time a user
-// interacted with ANY fullscreen promo.
-NSString* const kLastTimeUserInteractedWithFullscreenPromo =
+// interacted with ANY promo. The string value is kept from when the promos
+// first launched to avoid changing the behavior for users that have already
+// seen the promo.
+NSString* const kLastTimeUserInteractedWithPromo =
     @"lastTimeUserInteractedWithFullscreenPromo";
 
 // Key for NSUserDefaults containing a bool indicating if the user has
@@ -55,6 +57,11 @@
 NSString* const kUserHasInteractedWithTailoredFullscreenPromo =
     @"userHasInteractedWithTailoredFullscreenPromo";
 
+// Key for NSUserDefaults containing an int indicating the number of times the
+// user has interacted with a non-modal promo.
+NSString* const kUserInteractedWithNonModalPromoCount =
+    @"userInteractedWithNonModalPromoCount";
+
 NSString* const kRemindMeLaterPromoActionInteraction =
     @"remindMeLaterPromoActionInteraction";
 
@@ -248,11 +255,16 @@
       boolForKey:kUserHasInteractedWithTailoredFullscreenPromo];
 }
 
+int UserInteractionWithNonModalPromoCount() {
+  NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
+  return [standardDefaults integerForKey:kUserInteractedWithNonModalPromoCount];
+}
+
 void LogUserInteractionWithFullscreenPromo() {
   NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
   [standardDefaults setBool:YES forKey:kUserHasInteractedWithFullscreenPromo];
   [standardDefaults setObject:[NSDate date]
-                       forKey:kLastTimeUserInteractedWithFullscreenPromo];
+                       forKey:kLastTimeUserInteractedWithPromo];
 
   if (IsInRemindMeLaterGroup()) {
     // Clear any possible Remind Me Later timestamp saved.
@@ -265,7 +277,17 @@
   [standardDefaults setBool:YES
                      forKey:kUserHasInteractedWithTailoredFullscreenPromo];
   [standardDefaults setObject:[NSDate date]
-                       forKey:kLastTimeUserInteractedWithFullscreenPromo];
+                       forKey:kLastTimeUserInteractedWithPromo];
+}
+
+void LogUserInteractionWithNonModalPromo() {
+  NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
+  int currentInteractionCount =
+      [standardDefaults integerForKey:kUserInteractedWithNonModalPromoCount];
+  [standardDefaults setInteger:currentInteractionCount + 1
+                        forKey:kUserInteractedWithNonModalPromoCount];
+  [standardDefaults setObject:[NSDate date]
+                       forKey:kLastTimeUserInteractedWithPromo];
 }
 
 bool IsChromeLikelyDefaultBrowser() {
@@ -329,10 +351,10 @@
   return mostRecentType;
 }
 
-BOOL UserInFullscreenPromoCooldown() {
+BOOL UserInPromoCooldown() {
   NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
-  NSDate* lastFullscreenInteraction = ObjCCast<NSDate>([standardDefaults
-      objectForKey:kLastTimeUserInteractedWithFullscreenPromo]);
+  NSDate* lastFullscreenInteraction = ObjCCast<NSDate>(
+      [standardDefaults objectForKey:kLastTimeUserInteractedWithPromo]);
   if (lastFullscreenInteraction) {
     NSDate* coolDownDate =
         [NSDate dateWithTimeIntervalSinceNow:-kFullscreenPromoCoolDown];
diff --git a/ios/chrome/browser/ui/default_promo/default_browser_utils_unittest.mm b/ios/chrome/browser/ui/default_promo/default_browser_utils_unittest.mm
index e906a48..390e02a 100644
--- a/ios/chrome/browser/ui/default_promo/default_browser_utils_unittest.mm
+++ b/ios/chrome/browser/ui/default_promo/default_browser_utils_unittest.mm
@@ -123,11 +123,11 @@
     return;
   }
   LogUserInteractionWithFullscreenPromo();
-  EXPECT_TRUE(UserInFullscreenPromoCooldown());
+  EXPECT_TRUE(UserInPromoCooldown());
 
   ClearUserDefaults();
   LogUserInteractionWithTailoredFullscreenPromo();
-  EXPECT_TRUE(UserInFullscreenPromoCooldown());
+  EXPECT_TRUE(UserInPromoCooldown());
 }
 
 // Tests no 2 tailored promos are not shown.
diff --git a/ios/chrome/browser/ui/first_run/signin/BUILD.gn b/ios/chrome/browser/ui/first_run/signin/BUILD.gn
index 0db8c97..145eb124 100644
--- a/ios/chrome/browser/ui/first_run/signin/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/signin/BUILD.gn
@@ -13,6 +13,7 @@
   ]
   deps = [
     ":signin_ui",
+    "//components/prefs",
     "//ios/chrome/browser",
     "//ios/chrome/browser/first_run",
     "//ios/chrome/browser/main:public",
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
index d386f10..6a5971c 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_coordinator.mm
@@ -10,6 +10,7 @@
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #import "ios/chrome/browser/ui/authentication/authentication_flow.h"
 #import "ios/chrome/browser/ui/authentication/signin/add_account_signin/add_account_signin_coordinator.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_utils.h"
 #import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_coordinator.h"
 #import "ios/chrome/browser/ui/authentication/unified_consent/identity_chooser/identity_chooser_coordinator_delegate.h"
 #import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
@@ -68,10 +69,14 @@
 }
 
 - (void)start {
-  // TODO(crbug.com/1189836): Check if sign-in screen need to be shown.
-  // if not:
-  // [self.delegate willFinishPresenting]
-  // if yes:
+  // TODO(crbug.com/1189836): The kSigninAllowed pref should be observed in case
+  // the policy is applied while this screen is presented.
+
+  if (!signin::IsSigninAllowed(self.browser->GetBrowserState()->GetPrefs())) {
+    self.attemptStatus = first_run::SignInAttemptStatus::SKIPPED_BY_POLICY;
+    [self finishPresentingAndSkipRemainingScreens:NO];
+    return;
+  }
   self.hadIdentitiesAtStartup = ios::GetChromeBrowserProvider()
                                     ->GetChromeIdentityService()
                                     ->HasIdentities();
@@ -125,6 +130,10 @@
   }
 }
 
+- (void)didTapSecondaryActionButton {
+  [self finishPresentingAndSkipRemainingScreens:NO];
+}
+
 #pragma mark - IdentityChooserCoordinatorDelegate
 
 - (void)identityChooserCoordinatorDidClose:
diff --git a/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm b/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm
index 958ee76..b470e410 100644
--- a/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm
+++ b/ios/chrome/browser/ui/first_run/signin/signin_screen_mediator.mm
@@ -153,8 +153,6 @@
   } else {
     [self.consumer noIdentityAvailable];
   }
-
-  // TODO(crbug.com/1189836): Update the buttons.
 }
 
 // Callback used when the sign in flow is complete, with |success|.
diff --git a/ios/chrome/browser/ui/history/history_ui_egtest.mm b/ios/chrome/browser/ui/history/history_ui_egtest.mm
index 60ac3973..279b6ca 100644
--- a/ios/chrome/browser/ui/history/history_ui_egtest.mm
+++ b/ios/chrome/browser/ui/history/history_ui_egtest.mm
@@ -461,8 +461,7 @@
       performAction:grey_longPress()];
 
   [ChromeEarlGrey
-      verifyShareActionWithURL:_URL1
-                     pageTitle:[NSString stringWithUTF8String:kTitle1]];
+      verifyShareActionWithPageTitle:[NSString stringWithUTF8String:kTitle1]];
 }
 
 // Tests the Delete context menu action for a History entry.
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 6817b95..df96950 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -378,107 +378,99 @@
       self.sceneState.presentingModalOverlay) {
     return;
   }
-  if (base::ios::IsSceneStartupSupported()) {
-    if (@available(iOS 13, *)) {
-      // Handle URL opening from
-      // |UIWindowSceneDelegate scene:willConnectToSession:options:|.
-      for (UIOpenURLContext* context in self.sceneState.connectionOptions
-               .URLContexts) {
-        URLOpenerParams* params =
-            [[URLOpenerParams alloc] initWithUIOpenURLContext:context];
-        [self openTabFromLaunchWithParams:params
-                       startupInformation:self.sceneState.appState
-                                              .startupInformation
-                                 appState:self.sceneState.appState];
-      }
-      if (self.sceneState.connectionOptions.shortcutItem) {
-        [UserActivityHandler
-            performActionForShortcutItem:self.sceneState.connectionOptions
-                                             .shortcutItem
-                       completionHandler:nil
-                               tabOpener:self
-                   connectionInformation:self
-                      startupInformation:self.sceneState.appState
-                                             .startupInformation
-                       interfaceProvider:self.interfaceProvider];
-      }
 
-      // See if this scene launched as part of a multiwindow URL opening.
-      // If so, load that URL (this also creates a new tab to load the URL
-      // in). No other UI will show in this case.
-      NSUserActivity* activityWithCompletion;
-      for (NSUserActivity* activity in self.sceneState.connectionOptions
-               .userActivities) {
-        if (ActivityIsURLLoad(activity)) {
-          UrlLoadParams params = LoadParamsFromActivity(activity);
-          ApplicationMode mode = params.in_incognito
-                                     ? ApplicationMode::INCOGNITO
-                                     : ApplicationMode::NORMAL;
-          [self openOrReuseTabInMode:mode
-                   withUrlLoadParams:params
-                 tabOpenedCompletion:nil];
-        } else if (ActivityIsTabMove(activity)) {
-          NSString* tabID = GetTabIDFromActivity(activity);
-          MoveTabToBrowser(tabID, self.mainInterface.browser,
-                           /*destination_tab_index=*/0);
-        } else if (!activityWithCompletion) {
-          // Completion involves user interaction.
-          // Only one can be triggered.
-          activityWithCompletion = activity;
-        }
-      }
-      if (activityWithCompletion) {
-        // This function is called when the scene is activated (or unblocked).
-        // Consider the scene as still not active at this point as the handling
-        // of startup parameters is not yet done (and will be later in this
-        // function).
-        [UserActivityHandler
-             continueUserActivity:activityWithCompletion
-              applicationIsActive:NO
-                        tabOpener:self
-            connectionInformation:self
-               startupInformation:self.sceneState.appState.startupInformation
-                     browserState:self.currentInterface.browserState];
-      }
-      self.sceneState.connectionOptions = nil;
+  if (!base::ios::IsSceneStartupSupported()) {
+    return;
+  }
+
+  if (@available(iOS 13, *)) {
+    // Handle URL opening from
+    // |UIWindowSceneDelegate scene:willConnectToSession:options:|.
+    for (UIOpenURLContext* context in self.sceneState.connectionOptions
+             .URLContexts) {
+      URLOpenerParams* params =
+          [[URLOpenerParams alloc] initWithUIOpenURLContext:context];
+      [self openTabFromLaunchWithParams:params
+                     startupInformation:self.sceneState.appState
+                                            .startupInformation
+                               appState:self.sceneState.appState];
     }
-
-    if (self.startupParameters) {
-      if ([self isIncognitoForced]) {
-        [self.startupParameters
-            setUnexpectedMode:!self.startupParameters.launchInIncognito];
-        // When only incognito mode is available.
-        [self.startupParameters setLaunchInIncognito:YES];
-      } else if ([self isIncognitoDisabled]) {
-        [self.startupParameters
-            setUnexpectedMode:self.startupParameters.launchInIncognito];
-        // When incognito mode is disabled.
-        [self.startupParameters setLaunchInIncognito:NO];
-      }
-
+    if (self.sceneState.connectionOptions.shortcutItem) {
       [UserActivityHandler
-          handleStartupParametersWithTabOpener:self
-                         connectionInformation:self
-                            startupInformation:self.sceneState.appState
-                                                   .startupInformation
-                                  browserState:self.currentInterface
-                                                   .browserState];
-
-      // Show a toast if the browser is opened in an unexpected mode.
-      if (self.startupParameters.isUnexpectedMode) {
-        [self showToastWhenOpenExternalIntentInUnexpectedMode];
-      }
+          performActionForShortcutItem:self.sceneState.connectionOptions
+                                           .shortcutItem
+                     completionHandler:nil
+                             tabOpener:self
+                 connectionInformation:self
+                    startupInformation:self.sceneState.appState
+                                           .startupInformation
+                     interfaceProvider:self.interfaceProvider];
     }
 
-  } else {
-    NSDictionary* launchOptions =
-        self.sceneState.appState.startupInformation.launchOptions;
-    URLOpenerParams* params =
-        [[URLOpenerParams alloc] initWithLaunchOptions:launchOptions];
-    [self
-        openTabFromLaunchWithParams:params
-                 startupInformation:self.sceneState.appState.startupInformation
-                           appState:self.sceneState.appState];
+    // See if this scene launched as part of a multiwindow URL opening.
+    // If so, load that URL (this also creates a new tab to load the URL
+    // in). No other UI will show in this case.
+    NSUserActivity* activityWithCompletion;
+    for (NSUserActivity* activity in self.sceneState.connectionOptions
+             .userActivities) {
+      if (ActivityIsURLLoad(activity)) {
+        UrlLoadParams params = LoadParamsFromActivity(activity);
+        ApplicationMode mode = params.in_incognito ? ApplicationMode::INCOGNITO
+                                                   : ApplicationMode::NORMAL;
+        [self openOrReuseTabInMode:mode
+                 withUrlLoadParams:params
+               tabOpenedCompletion:nil];
+      } else if (ActivityIsTabMove(activity)) {
+        NSString* tabID = GetTabIDFromActivity(activity);
+        MoveTabToBrowser(tabID, self.mainInterface.browser,
+                         /*destination_tab_index=*/0);
+      } else if (!activityWithCompletion) {
+        // Completion involves user interaction.
+        // Only one can be triggered.
+        activityWithCompletion = activity;
+      }
+    }
+    if (activityWithCompletion) {
+      // This function is called when the scene is activated (or unblocked).
+      // Consider the scene as still not active at this point as the handling
+      // of startup parameters is not yet done (and will be later in this
+      // function).
+      [UserActivityHandler
+           continueUserActivity:activityWithCompletion
+            applicationIsActive:NO
+                      tabOpener:self
+          connectionInformation:self
+             startupInformation:self.sceneState.appState.startupInformation
+                   browserState:self.currentInterface.browserState];
+    }
+    self.sceneState.connectionOptions = nil;
+  }
+
+  if (self.startupParameters) {
+    if ([self isIncognitoForced]) {
+      [self.startupParameters
+          setUnexpectedMode:!self.startupParameters.launchInIncognito];
+      // When only incognito mode is available.
+      [self.startupParameters setLaunchInIncognito:YES];
+    } else if ([self isIncognitoDisabled]) {
+      [self.startupParameters
+          setUnexpectedMode:self.startupParameters.launchInIncognito];
+      // When incognito mode is disabled.
+      [self.startupParameters setLaunchInIncognito:NO];
+    }
+
+    [UserActivityHandler
+        handleStartupParametersWithTabOpener:self
+                       connectionInformation:self
+                          startupInformation:self.sceneState.appState
+                                                 .startupInformation
+                                browserState:self.currentInterface
+                                                 .browserState];
+
+    // Show a toast if the browser is opened in an unexpected mode.
+    if (self.startupParameters.isUnexpectedMode) {
+      [self showToastWhenOpenExternalIntentInUnexpectedMode];
+    }
   }
 }
 
@@ -967,7 +959,7 @@
   if (!firstRun && self.sceneState.appState.initStage > InitStageSafeMode &&
       postOpeningAction == NO_ACTION &&
       !self.sceneState.appState.postCrashLaunch &&
-      !IsChromeLikelyDefaultBrowser() && !UserInFullscreenPromoCooldown()) {
+      !IsChromeLikelyDefaultBrowser() && !UserInPromoCooldown()) {
     // Show the Default Browser promo UI if the user's past behavior fits
     // the categorization of potentially interested users or if the user is
     // signed in. Do not show if it is determined that Chrome is already the
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index 5a9a85bf..b4b7af8 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -1117,6 +1117,11 @@
       CreateTableViewItem(IDS_IOS_TOOLS_MENU_SETTINGS, PopupMenuActionSettings,
                           @"popup_menu_settings", kToolsMenuSettingsId);
 
+  if (self.isIncognito &&
+      base::FeatureList::IsEnabled(kUpdateHistoryEntryPointsInIncognito)) {
+    return @[ bookmarks, self.readingListItem, downloadsFolder, settings ];
+  }
+
   return @[
     bookmarks, self.readingListItem, recentTabs, history, downloadsFolder,
     settings
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
index f52df71b..7679eb2 100644
--- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
+++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -1280,8 +1280,7 @@
   [self addURLToReadingList:distillablePageURL];
   LongPressEntry(kDistillableTitle);
 
-  [ChromeEarlGrey verifyShareActionWithURL:distillablePageURL
-                                 pageTitle:kDistillableTitle];
+  [ChromeEarlGrey verifyShareActionWithPageTitle:kDistillableTitle];
 }
 
 // Tests the Delete context menu action for a reading list entry.
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
index 844047a..b6ed001 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -392,9 +392,7 @@
   OpenRecentTabsPanel();
   [self longPressTestURLTab];
 
-  const GURL testPageURL = web::test::HttpServer::MakeUrl(kURLOfTestPage);
-  [ChromeEarlGrey verifyShareActionWithURL:testPageURL
-                                 pageTitle:kTitleOfTestPage];
+  [ChromeEarlGrey verifyShareActionWithPageTitle:kTitleOfTestPage];
 }
 
 #pragma mark Helper Methods
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
index 72f36ac..4c6b87d0 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
@@ -6,9 +6,9 @@
 
 #include "base/check.h"
 #include "base/mac/foundation_util.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/sys_string_conversions.h"
+#include "components/send_tab_to_self/metrics_util.h"
 #include "components/send_tab_to_self/send_tab_to_self_model.h"
 #include "components/send_tab_to_self/target_device_info.h"
 #import "ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_image_detail_text_item.h"
@@ -37,21 +37,6 @@
 NSString* const kSendTabToSelfModalSendButton =
     @"kSendTabToSelfModalSendButton";
 
-// Per histograms.xml this records whether the user has clicked the item when it
-// is shown.
-const char kClickResultHistogramName[] = "SendTabToSelf.ShareMenu.ClickResult";
-
-// TODO(crbug.com/970886): Move to a directory accessible on all platforms.
-// State of the send tab to self option in the context menu.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class SendTabToSelfClickResult {
-  kShowItem = 0,
-  kClickItem = 1,
-  kShowDeviceList = 2,
-  kMaxValue = kShowDeviceList,
-};
-
 }  // namespace
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -222,8 +207,8 @@
 #pragma mark - Helpers
 
 - (void)sendTabWhenPressed:(UIButton*)sender {
-  base::UmaHistogramEnumeration(kClickResultHistogramName,
-                                SendTabToSelfClickResult::kClickItem);
+  send_tab_to_self::RecordDeviceClicked(
+      send_tab_to_self::ShareEntryPoint::kShareMenu);
   [self.delegate sendTabToTargetDeviceCacheGUID:self.selectedItem.cacheGuid
                                targetDeviceName:self.selectedItem.text];
   [self.delegate dismissViewControllerAnimated:YES completion:nil];
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
index 88fdf5e..e3bd4cb 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_egtest.mm
@@ -290,8 +290,7 @@
   [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
 
   [ChromeEarlGrey
-      verifyShareActionWithURL:_URL1
-                     pageTitle:[NSString stringWithUTF8String:kTitle1]];
+      verifyShareActionWithPageTitle:[NSString stringWithUTF8String:kTitle1]];
 }
 
 #pragma mark - Tab Grid Item Context Menu
@@ -312,8 +311,7 @@
   [self longPressTabWithTitle:[NSString stringWithUTF8String:kTitle1]];
 
   [ChromeEarlGrey
-      verifyShareActionWithURL:_URL1
-                     pageTitle:[NSString stringWithUTF8String:kTitle1]];
+      verifyShareActionWithPageTitle:[NSString stringWithUTF8String:kTitle1]];
 }
 
 // Tests the Add to Reading list action on a tab grid item's context menu.
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index c649bc64..8aa1b57 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -73,3 +73,6 @@
 
 const base::Feature kSearchHistoryLinkIOS{"SearchHistoryLinkIOS",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kUpdateHistoryEntryPointsInIncognito{
+    "UpdateHistoryEntryPointsInIncognito", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index 7bfd037..5328622 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -80,4 +80,8 @@
 // Enables the Search History Link in Clear Browsing Data for iOS.
 extern const base::Feature kSearchHistoryLinkIOS;
 
+// Feature flag to enable removing any entry points to the history UI from
+// Incognito mode.
+extern const base::Feature kUpdateHistoryEntryPointsInIncognito;
+
 #endif  // IOS_CHROME_BROWSER_UI_UI_FEATURE_FLAGS_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 4eb35e0..a888941 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -704,10 +704,8 @@
                               useNewString:(BOOL)useNewString;
 
 // Taps on the Share context menu action and validates that the ActivityView
-// was brought up with the correct title in its header. The title starts as the
-// host of the loaded |URL| and is then updated to the page title |pageTitle|.
-- (void)verifyShareActionWithURL:(const GURL&)URL
-                       pageTitle:(NSString*)pageTitle;
+// was brought up with |pageTitle| in its header.
+- (void)verifyShareActionWithPageTitle:(NSString*)pageTitle;
 
 #pragma mark - Unified Consent utilities
 
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 39ecfed9..d8cfdd6 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -1323,13 +1323,11 @@
       assertWithMatcher:grey_notNil()];
 }
 
-- (void)verifyShareActionWithURL:(const GURL&)URL
-                       pageTitle:(NSString*)pageTitle {
+- (void)verifyShareActionWithPageTitle:(NSString*)pageTitle {
   [[EarlGrey selectElementWithMatcher:ShareButton()] performAction:grey_tap()];
 
   // Page title is added asynchronously, so wait for its appearance.
-  NSString* hostString = base::SysUTF8ToNSString(URL.host());
-  [self waitForMatcher:grey_allOf(ActivityViewHeader(hostString, pageTitle),
+  [self waitForMatcher:grey_allOf(ActivityViewHeader(pageTitle),
                                   grey_sufficientlyVisible(), nil)];
 
   // Dismiss the Activity View by tapping outside its bounds.
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.h b/ios/chrome/test/earl_grey/chrome_matchers.h
index 100eb95a..709dbaf 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers.h
@@ -568,7 +568,7 @@
 id<GREYMatcher> ManualFallbackCreditCardTableViewWindowMatcher();
 
 // Returns the matcher for the iOS 13+ Activity View header.
-id<GREYMatcher> ActivityViewHeader(NSString* url_host, NSString* page_title);
+id<GREYMatcher> ActivityViewHeader(NSString* page_title);
 
 // Returns a matcher for the button to trigger password generation on manual
 // fallback.
diff --git a/ios/chrome/test/earl_grey/chrome_matchers.mm b/ios/chrome/test/earl_grey/chrome_matchers.mm
index fb1cf413c..4af257e 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers.mm
@@ -715,9 +715,8 @@
       manualFallbackCreditCardTableViewWindowMatcher];
 }
 
-id<GREYMatcher> ActivityViewHeader(NSString* url_host, NSString* page_title) {
-  return [ChromeMatchersAppInterface activityViewHeaderWithURLHost:url_host
-                                                             title:page_title];
+id<GREYMatcher> ActivityViewHeader(NSString* page_title) {
+  return [ChromeMatchersAppInterface activityViewHeaderWithTitle:page_title];
 }
 
 id<GREYMatcher> ManualFallbackSuggestPasswordMatcher() {
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
index cf32ca7..a0452d0 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.h
@@ -559,8 +559,7 @@
 + (id<GREYMatcher>)manualFallbackCreditCardTableViewWindowMatcher;
 
 // Returns the matcher for the Activity View header.
-+ (id<GREYMatcher>)activityViewHeaderWithURLHost:(NSString*)host
-                                           title:(NSString*)pageTitle;
++ (id<GREYMatcher>)activityViewHeaderWithTitle:(NSString*)pageTitle;
 
 // Returns a matcher for the button to trigger password generation on manual
 // fallback.
diff --git a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
index 3e508d70..6efcf73 100644
--- a/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_matchers_app_interface.mm
@@ -1074,19 +1074,12 @@
   return grey_allOf(classMatcher, parentMatcher, nil);
 }
 
-+ (id<GREYMatcher>)activityViewHeaderWithURLHost:(NSString*)host
-                                           title:(NSString*)pageTitle {
-  return grey_allOf(
-      // The title of the activity view starts as the URL, then asynchronously
-      // changes to the page title. Sometimes, the activity view fails to update
-      // the text to the page title, causing test flake. Allow matcher to pass
-      // with either value for the activity view title.
-      grey_anyOf(grey_accessibilityLabel(host),
-                 grey_accessibilityLabel(pageTitle), nil),
-      grey_ancestor(
-          grey_allOf(grey_accessibilityTrait(UIAccessibilityTraitHeader),
-                     grey_kindOfClassName(@"LPLinkView"), nil)),
-      nil);
++ (id<GREYMatcher>)activityViewHeaderWithTitle:(NSString*)pageTitle {
+  return grey_allOf(grey_accessibilityLabel(pageTitle),
+                    grey_ancestor(grey_allOf(
+                        grey_accessibilityTrait(UIAccessibilityTraitHeader),
+                        grey_kindOfClassName(@"LPLinkView"), nil)),
+                    nil);
 }
 
 + (id<GREYMatcher>)manualFallbackSuggestPasswordMatcher {
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
index 3354b5b09..6792f13 100644
--- a/net/dns/context_host_resolver_unittest.cc
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -76,7 +76,9 @@
     dns_client->set_ignore_system_config_changes(true);
     dns_client_ = dns_client.get();
     manager_->SetDnsClientForTesting(std::move(dns_client));
-    manager_->SetInsecureDnsClientEnabled(true);
+    manager_->SetInsecureDnsClientEnabled(
+        /*enabled=*/true,
+        /*additional_dns_types_enabled=*/true);
 
     // Ensure DnsClient is fully usable.
     EXPECT_TRUE(dns_client_->CanUseInsecureDnsTransactions());
diff --git a/net/dns/dns_client.cc b/net/dns/dns_client.cc
index dfb6407..5401176 100644
--- a/net/dns/dns_client.cc
+++ b/net/dns/dns_client.cc
@@ -105,8 +105,18 @@
            !config->unhandled_options && !config->dns_over_tls_active;
   }
 
-  void SetInsecureEnabled(bool enabled) override {
+  bool CanQueryAdditionalTypesViaInsecureDns() const override {
+    // Only useful information if insecure DNS is usable, so expect this to
+    // never be called if that is not the case.
+    DCHECK(CanUseInsecureDnsTransactions());
+
+    return can_query_additional_types_via_insecure_;
+  }
+
+  void SetInsecureEnabled(bool enabled,
+                          bool additional_types_enabled) override {
     insecure_enabled_ = enabled;
+    can_query_additional_types_via_insecure_ = additional_types_enabled;
   }
 
   bool FallbackFromSecureTransactionPreferred(
@@ -256,6 +266,7 @@
   }
 
   bool insecure_enabled_ = false;
+  bool can_query_additional_types_via_insecure_ = false;
   int insecure_fallback_failures_ = 0;
 
   base::Optional<DnsConfig> system_config_;
diff --git a/net/dns/dns_client.h b/net/dns/dns_client.h
index a0ad590..5ad6fe29b 100644
--- a/net/dns/dns_client.h
+++ b/net/dns/dns_client.h
@@ -42,7 +42,9 @@
   // transactions. If false, insecure transactions should not be created. Will
   // always be false unless SetInsecureEnabled(true) has been called.
   virtual bool CanUseInsecureDnsTransactions() const = 0;
-  virtual void SetInsecureEnabled(bool enabled) = 0;
+  virtual bool CanQueryAdditionalTypesViaInsecureDns() const = 0;
+  virtual void SetInsecureEnabled(bool enabled,
+                                  bool additional_types_enabled) = 0;
 
   // When true, DoH should not be used in AUTOMATIC mode since no DoH servers
   // have a successful probe state.
diff --git a/net/dns/dns_client_unittest.cc b/net/dns/dns_client_unittest.cc
index 85a3a0a..80922bb 100644
--- a/net/dns/dns_client_unittest.cc
+++ b/net/dns/dns_client_unittest.cc
@@ -80,7 +80,8 @@
 };
 
 TEST_F(DnsClientTest, NoConfig) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
 
   EXPECT_FALSE(client_->CanUseSecureDnsTransactions());
   EXPECT_TRUE(
@@ -95,7 +96,8 @@
 }
 
 TEST_F(DnsClientTest, InvalidConfig) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
   client_->SetSystemConfig(DnsConfig());
 
   EXPECT_FALSE(client_->CanUseSecureDnsTransactions());
@@ -111,13 +113,15 @@
 }
 
 TEST_F(DnsClientTest, CanUseSecureDnsTransactions_NoDohServers) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
   client_->SetSystemConfig(BasicValidConfig());
 
   EXPECT_FALSE(client_->CanUseSecureDnsTransactions());
   EXPECT_TRUE(
       client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
   EXPECT_TRUE(client_->CanUseInsecureDnsTransactions());
+  EXPECT_TRUE(client_->CanQueryAdditionalTypesViaInsecureDns());
   EXPECT_FALSE(client_->FallbackFromInsecureTransactionPreferred());
 
   EXPECT_THAT(client_->GetEffectiveConfig(),
@@ -128,7 +132,8 @@
 }
 
 TEST_F(DnsClientTest, InsecureNotEnabled) {
-  client_->SetInsecureEnabled(false);
+  client_->SetInsecureEnabled(/*enabled=*/false,
+                              /*additional_types_enabled=*/false);
   client_->SetSystemConfig(ValidConfigWithDoh(false /* doh_only */));
 
   EXPECT_TRUE(client_->CanUseSecureDnsTransactions());
@@ -145,8 +150,22 @@
             ValidConfigWithDoh(false /* doh_only */));
 }
 
+TEST_F(DnsClientTest, RespectsAdditionalTypesDisabled) {
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/false);
+  client_->SetSystemConfig(BasicValidConfig());
+
+  EXPECT_FALSE(client_->CanUseSecureDnsTransactions());
+  EXPECT_TRUE(
+      client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
+  EXPECT_TRUE(client_->CanUseInsecureDnsTransactions());
+  EXPECT_FALSE(client_->CanQueryAdditionalTypesViaInsecureDns());
+  EXPECT_FALSE(client_->FallbackFromInsecureTransactionPreferred());
+}
+
 TEST_F(DnsClientTest, UnhandledOptions) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
   DnsConfig config = ValidConfigWithDoh(false /* doh_only */);
   config.unhandled_options = true;
   client_->SetSystemConfig(config);
@@ -183,7 +202,8 @@
 }
 
 TEST_F(DnsClientTest, DnsOverTlsActive) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
   DnsConfig config = ValidConfigWithDoh(false /* doh_only */);
   config.dns_over_tls_active = true;
   client_->SetSystemConfig(config);
@@ -201,7 +221,8 @@
 }
 
 TEST_F(DnsClientTest, AllAllowed) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
   client_->SetSystemConfig(ValidConfigWithDoh(false /* doh_only */));
   resolve_context_.InvalidateCachesAndPerSessionData(
       client_->GetCurrentSession(), false /* network_change */);
@@ -213,6 +234,7 @@
   EXPECT_FALSE(
       client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
   EXPECT_TRUE(client_->CanUseInsecureDnsTransactions());
+  EXPECT_TRUE(client_->CanQueryAdditionalTypesViaInsecureDns());
   EXPECT_FALSE(client_->FallbackFromInsecureTransactionPreferred());
 
   EXPECT_THAT(client_->GetEffectiveConfig(),
@@ -224,7 +246,8 @@
 }
 
 TEST_F(DnsClientTest, FallbackFromInsecureTransactionPreferred_Failures) {
-  client_->SetInsecureEnabled(true);
+  client_->SetInsecureEnabled(/*enabled=*/true,
+                              /*additional_types_enabled=*/true);
   client_->SetSystemConfig(ValidConfigWithDoh(false /* doh_only */));
 
   for (int i = 0; i < DnsClient::kMaxInsecureFallbackFailures; ++i) {
@@ -232,6 +255,7 @@
     EXPECT_TRUE(
         client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
     EXPECT_TRUE(client_->CanUseInsecureDnsTransactions());
+    EXPECT_TRUE(client_->CanQueryAdditionalTypesViaInsecureDns());
     EXPECT_FALSE(client_->FallbackFromInsecureTransactionPreferred());
 
     client_->IncrementInsecureFallbackFailures();
@@ -241,6 +265,7 @@
   EXPECT_TRUE(
       client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
   EXPECT_TRUE(client_->CanUseInsecureDnsTransactions());
+  EXPECT_TRUE(client_->CanQueryAdditionalTypesViaInsecureDns());
   EXPECT_TRUE(client_->FallbackFromInsecureTransactionPreferred());
 
   client_->ClearInsecureFallbackFailures();
@@ -249,6 +274,7 @@
   EXPECT_TRUE(
       client_->FallbackFromSecureTransactionPreferred(&resolve_context_));
   EXPECT_TRUE(client_->CanUseInsecureDnsTransactions());
+  EXPECT_TRUE(client_->CanQueryAdditionalTypesViaInsecureDns());
   EXPECT_FALSE(client_->FallbackFromInsecureTransactionPreferred());
 }
 
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 7eeda50..1af39bd 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -608,8 +608,15 @@
          !config->dns_over_tls_active;
 }
 
-void MockDnsClient::SetInsecureEnabled(bool enabled) {
+bool MockDnsClient::CanQueryAdditionalTypesViaInsecureDns() const {
+  DCHECK(CanUseInsecureDnsTransactions());
+  return additional_types_enabled_;
+}
+
+void MockDnsClient::SetInsecureEnabled(bool enabled,
+                                       bool additional_types_enabled) {
   insecure_enabled_ = enabled;
+  additional_types_enabled_ = additional_types_enabled;
 }
 
 bool MockDnsClient::FallbackFromSecureTransactionPreferred(
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index b69418ed..40a2cb44 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -382,7 +382,8 @@
   // DnsClient interface:
   bool CanUseSecureDnsTransactions() const override;
   bool CanUseInsecureDnsTransactions() const override;
-  void SetInsecureEnabled(bool enabled) override;
+  bool CanQueryAdditionalTypesViaInsecureDns() const override;
+  void SetInsecureEnabled(bool enabled, bool additional_types_enabled) override;
   bool FallbackFromSecureTransactionPreferred(
       ResolveContext* resolve_context) const override;
   bool FallbackFromInsecureTransactionPreferred() const override;
@@ -425,6 +426,7 @@
   scoped_refptr<DnsSession> BuildSession();
 
   bool insecure_enabled_ = false;
+  bool additional_types_enabled_ = false;
   int fallback_failures_ = 0;
   int max_fallback_failures_ = DnsClient::kMaxInsecureFallbackFailures;
   bool ignore_system_config_changes_ = false;
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h
index 4d7eab2..944fd64 100644
--- a/net/dns/host_resolver.h
+++ b/net/dns/host_resolver.h
@@ -174,6 +174,10 @@
     // SetInsecureDnsClientEnabled() for details.
     bool insecure_dns_client_enabled = false;
 
+    // Initial setting for whether additional DNS types (e.g. HTTPS) may be
+    // queried when using the built-in resolver for insecure DNS.
+    bool additional_types_via_insecure_dns_enabled = true;
+
     // Initial configuration overrides for the built-in asynchronous DnsClient.
     // See HostResolverManager::SetDnsConfigOverrides() for details.
     DnsConfigOverrides dns_config_overrides;
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 45045af..e1eeb39 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -86,6 +86,7 @@
 #include "net/dns/httpssvc_metrics.h"
 #include "net/dns/mdns_client.h"
 #include "net/dns/public/dns_protocol.h"
+#include "net/dns/public/dns_query_type.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/dns/record_parsed.h"
 #include "net/dns/resolve_context.h"
@@ -1150,13 +1151,16 @@
       transactions_needed_.push(DnsQueryType::A);
       transactions_needed_.push(DnsQueryType::AAAA);
 
-      // Queue up an INTEGRITY query if we are allowed to.
+      // Queue up an INTEGRITY/HTTPS query if we are allowed to.
       const bool is_httpssvc_experiment_domain =
           httpssvc_domain_cache_.IsExperimental(hostname);
       const bool is_httpssvc_control_domain =
           httpssvc_domain_cache_.IsControl(hostname);
+      const bool can_query_via_insecure =
+          !secure_ && features::kDnsHttpssvcEnableQueryOverInsecure.Get() &&
+          client_->CanQueryAdditionalTypesViaInsecureDns();
       if (base::FeatureList::IsEnabled(features::kDnsHttpssvc) &&
-          (secure_ || features::kDnsHttpssvcEnableQueryOverInsecure.Get()) &&
+          (secure_ || can_query_via_insecure) &&
           (is_httpssvc_experiment_domain || is_httpssvc_control_domain)) {
         httpssvc_metrics_.emplace(
             is_httpssvc_experiment_domain /* expect_intact */);
@@ -1192,6 +1196,9 @@
     DnsQueryType type = transactions_needed_.front();
     transactions_needed_.pop();
 
+    DCHECK(type != DnsQueryType::HTTPS || secure_ ||
+           client_->CanQueryAdditionalTypesViaInsecureDns());
+
     // Record how long this transaction has been waiting to be created.
     base::TimeDelta time_queued = tick_clock_->NowTicks() - task_start_time_;
     UMA_HISTOGRAM_LONG_TIMES_100("Net.DNS.JobQueueTime.PerTransaction",
@@ -2541,7 +2548,9 @@
 
 #if defined(ENABLE_BUILT_IN_DNS)
   dns_client_ = DnsClient::CreateClient(net_log_);
-  dns_client_->SetInsecureEnabled(options.insecure_dns_client_enabled);
+  dns_client_->SetInsecureEnabled(
+      options.insecure_dns_client_enabled,
+      options.additional_types_via_insecure_dns_enabled);
   dns_client_->SetConfigOverrides(options.dns_config_overrides);
 #else
   DCHECK(options.dns_config_overrides == DnsConfigOverrides());
@@ -2614,17 +2623,30 @@
   return listener;
 }
 
-void HostResolverManager::SetInsecureDnsClientEnabled(bool enabled) {
+void HostResolverManager::SetInsecureDnsClientEnabled(
+    bool enabled,
+    bool additional_dns_types_enabled) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   if (!dns_client_)
     return;
 
   bool enabled_before = dns_client_->CanUseInsecureDnsTransactions();
-  dns_client_->SetInsecureEnabled(enabled);
+  bool additional_types_before =
+      enabled_before && dns_client_->CanQueryAdditionalTypesViaInsecureDns();
+  dns_client_->SetInsecureEnabled(enabled, additional_dns_types_enabled);
 
-  if (dns_client_->CanUseInsecureDnsTransactions() != enabled_before)
+  // Abort current tasks if `CanUseInsecureDnsTransactions()` changes or if
+  // insecure transactions are enabled and
+  // `CanQueryAdditionalTypesViaInsecureDns()` changes. Changes to allowing
+  // additional types don't matter if insecure transactions are completely
+  // disabled.
+  if (dns_client_->CanUseInsecureDnsTransactions() != enabled_before ||
+      (dns_client_->CanUseInsecureDnsTransactions() &&
+       dns_client_->CanQueryAdditionalTypesViaInsecureDns() !=
+           additional_types_before)) {
     AbortInsecureDnsTasks(ERR_NETWORK_CHANGED, false /* fallback_only */);
+  }
 }
 
 base::Value HostResolverManager::GetDnsConfigAsValue() const {
@@ -3247,7 +3269,9 @@
         if (dns_client_ && dns_client_->GetEffectiveConfig()) {
           bool insecure_allowed =
               dns_client_->CanUseInsecureDnsTransactions() &&
-              !dns_client_->FallbackFromInsecureTransactionPreferred();
+              !dns_client_->FallbackFromInsecureTransactionPreferred() &&
+              (IsAddressType(dns_query_type) ||
+               dns_client_->CanQueryAdditionalTypesViaInsecureDns());
           PushDnsTasks(proc_task_allowed, *out_effective_secure_dns_mode,
                        insecure_allowed, allow_cache, prioritize_local_lookups,
                        resolve_context, out_tasks);
@@ -3268,10 +3292,14 @@
       break;
     case HostResolverSource::DNS:
       if (dns_client_ && dns_client_->GetEffectiveConfig()) {
+        bool insecure_allowed =
+            dns_client_->CanUseInsecureDnsTransactions() &&
+            (IsAddressType(dns_query_type) ||
+             dns_client_->CanQueryAdditionalTypesViaInsecureDns());
         PushDnsTasks(false /* proc_task_allowed */,
-                     *out_effective_secure_dns_mode,
-                     dns_client_->CanUseInsecureDnsTransactions(), allow_cache,
-                     prioritize_local_lookups, resolve_context, out_tasks);
+                     *out_effective_secure_dns_mode, insecure_allowed,
+                     allow_cache, prioritize_local_lookups, resolve_context,
+                     out_tasks);
       }
       break;
     case HostResolverSource::MULTICAST_DNS:
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 573033e..4236235 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -169,7 +169,8 @@
   // DnsConfig, a new config is fetched from NetworkChangeNotifier.
   //
   // Setting to |true| has no effect if |ENABLE_BUILT_IN_DNS| not defined.
-  virtual void SetInsecureDnsClientEnabled(bool enabled);
+  virtual void SetInsecureDnsClientEnabled(bool enabled,
+                                           bool additional_dns_types_enabled);
 
   base::Value GetDnsConfigAsValue() const;
 
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 5e82854..7918bc3 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -59,7 +59,9 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/dns/mock_mdns_client.h"
 #include "net/dns/mock_mdns_socket_factory.h"
+#include "net/dns/public/dns_config_overrides.h"
 #include "net/dns/public/dns_over_https_server_config.h"
+#include "net/dns/public/dns_query_type.h"
 #include "net/dns/public/resolve_error_info.h"
 #include "net/dns/resolve_context.h"
 #include "net/dns/test_dns_config_service.h"
@@ -3853,6 +3855,7 @@
     HostResolver::ManagerOptions options =
         HostResolverManagerTest::DefaultOptions();
     options.insecure_dns_client_enabled = true;
+    options.additional_types_via_insecure_dns_enabled = true;
     return options;
   }
 
@@ -3867,7 +3870,9 @@
         std::make_unique<MockDnsClient>(DnsConfig(), CreateDefaultDnsRules());
     dns_client_ = dns_client.get();
     resolver_->SetDnsClientForTesting(std::move(dns_client));
-    resolver_->SetInsecureDnsClientEnabled(options.insecure_dns_client_enabled);
+    resolver_->SetInsecureDnsClientEnabled(
+        options.insecure_dns_client_enabled,
+        options.additional_types_via_insecure_dns_enabled);
     resolver_->set_proc_params_for_test(params);
 
     resolver_->RegisterResolveContext(resolve_context_.get());
@@ -3882,7 +3887,9 @@
         std::make_unique<MockDnsClient>(DnsConfig(), std::move(rules));
     dns_client_ = dns_client.get();
     resolver_->SetDnsClientForTesting(std::move(dns_client));
-    resolver_->SetInsecureDnsClientEnabled(true);
+    resolver_->SetInsecureDnsClientEnabled(
+        /*enabled=*/true,
+        /*additional_dns_types_enabled=*/true);
     if (!config.Equals(DnsConfig()))
       ChangeDnsConfig(config);
   }
@@ -4117,7 +4124,9 @@
   proc_->AddRuleForAllFamilies("nx_succeed", "192.168.2.47");
   proc_->SignalMultiple(1u);
 
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled*/ false);
   ResolveHostResponseHelper response_proc(resolver_->CreateRequest(
       HostPortPair("nx_succeed", 1212), NetworkIsolationKey(),
       NetLogWithSource(), base::nullopt, resolve_context_.get(),
@@ -4126,7 +4135,8 @@
   EXPECT_THAT(response_proc.request()->GetAddressResults().value().endpoints(),
               testing::ElementsAre(CreateExpected("192.168.2.47", 1212)));
 
-  resolver_->SetInsecureDnsClientEnabled(true);
+  resolver_->SetInsecureDnsClientEnabled(/*enabled*/ true,
+                                         /*additional_dns_types_enabled=*/true);
   ResolveHostResponseHelper response_dns_client(resolver_->CreateRequest(
       HostPortPair("ok_fail", 1212), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, resolve_context_.get(), resolve_context_->host_cache()));
@@ -4407,7 +4417,9 @@
 
   // Simulate the case when the preference or policy has disabled the insecure
   // DNS client causing AbortInsecureDnsTasks.
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
 
   // All requests should fallback to proc resolver.
   EXPECT_THAT(response0.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
@@ -4440,7 +4452,9 @@
 
   // Simulate the case when the preference or policy has disabled the insecure
   // DNS client causing AbortInsecureDnsTasks.
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
 
   // No fallback expected.  All requests should fail.
   EXPECT_THAT(response0.result_error(), IsError(ERR_NETWORK_CHANGED));
@@ -4468,7 +4482,9 @@
 
   // Simulate the case when the preference or policy has disabled the insecure
   // DNS client causing AbortInsecureDnsTasks.
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled*/ false);
 
   EXPECT_THAT(response_secure.result_error(), IsOk());
   EXPECT_THAT(
@@ -4635,7 +4651,9 @@
 
 TEST_F(HostResolverManagerDnsTest, SkipHostsWithUpcomingProcTask) {
   // Disable the DnsClient.
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
 
   proc_->AddRuleForAllFamilies(std::string(),
                                std::string());  // Default to failures.
@@ -4959,7 +4977,9 @@
   proc_->AddRuleForAllFamilies(std::string(), std::string());
 
   // Try without DnsClient.
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
   ResolveHostResponseHelper system_response(resolver_->CreateRequest(
       HostPortPair("localhost", 80), NetworkIsolationKey(), NetLogWithSource(),
       base::nullopt, resolve_context_.get(), resolve_context_->host_cache()));
@@ -5758,7 +5778,9 @@
        SecureDnsMode_Automatic_InsecureAsyncDisabled) {
   proc_->AddRuleForAllFamilies("insecure_automatic", "192.168.1.100");
   ChangeDnsConfig(CreateValidDnsConfig());
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
   DnsConfigOverrides overrides;
   overrides.secure_dns_mode = SecureDnsMode::kAutomatic;
   resolver_->SetDnsConfigOverrides(overrides);
@@ -5940,7 +5962,9 @@
 TEST_F(HostResolverManagerDnsTest, SecureDnsMode_Secure_InsecureAsyncDisabled) {
   proc_->AddRuleForAllFamilies("nx_succeed", "192.168.1.100");
   set_allow_fallback_to_proctask(true);
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
 
   ChangeDnsConfig(CreateValidDnsConfig());
   DnsConfigOverrides overrides;
@@ -6448,7 +6472,9 @@
 
   // Clear DnsClient.  The two in-progress jobs should fall back to a ProcTask,
   // and the next one should be started with a ProcTask.
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
 
   // All three in-progress requests should now be running a ProcTask.
   EXPECT_EQ(3u, num_running_dispatcher_jobs());
@@ -6469,7 +6495,9 @@
 // DnsClient disabled should result in an error.
 TEST_F(HostResolverManagerDnsTest, DnsCallsWithDisabledDnsClient) {
   ChangeDnsConfig(CreateValidDnsConfig());
-  resolver_->SetInsecureDnsClientEnabled(false);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/false,
+      /*additional_dns_types_enabled=*/false);
 
   HostResolver::ResolveHostParameters params;
   params.source = HostResolverSource::DNS;
@@ -8373,6 +8401,32 @@
   EXPECT_EQ(resolve_context_->host_cache()->size(), 0u);
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       TxtInsecureQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const std::string kName = "txt.test";
+
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.dns_query_type = DnsQueryType::TXT;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+  // No non-local work is done, so ERR_DNS_CACHE_MISS is the result.
+  EXPECT_THAT(response.result_error(), IsError(ERR_DNS_CACHE_MISS));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
 // Same as TxtQuery except we specify DNS HostResolverSource instead of relying
 // on automatic determination.  Expect same results since DNS should be what we
 // automatically determine, but some slightly different logic paths are
@@ -8676,6 +8730,32 @@
   EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       PtrInsecureQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const std::string kName = "ptr.test";
+
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.dns_query_type = DnsQueryType::PTR;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+  // No non-local work is done, so ERR_DNS_CACHE_MISS is the result.
+  EXPECT_THAT(response.result_error(), IsError(ERR_DNS_CACHE_MISS));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
 // Same as PtrQuery except we specify DNS HostResolverSource instead of relying
 // on automatic determination.  Expect same results since DNS should be what we
 // automatically determine, but some slightly different logic paths are
@@ -8970,6 +9050,32 @@
   EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       SrvInsecureQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const std::string kName = "srv.test";
+
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.dns_query_type = DnsQueryType::SRV;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+  // No non-local work is done, so ERR_DNS_CACHE_MISS is the result.
+  EXPECT_THAT(response.result_error(), IsError(ERR_DNS_CACHE_MISS));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
 // Same as SrvQuery except we specify DNS HostResolverSource instead of relying
 // on automatic determination.  Expect same results since DNS should be what we
 // automatically determine, but some slightly different logic paths are
@@ -9049,6 +9155,101 @@
   EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       HttpsInsecureQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const std::string kName = "https.test";
+
+  ChangeDnsConfig(CreateValidDnsConfig());
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.dns_query_type = DnsQueryType::HTTPS;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+  // No non-local work is done, so ERR_DNS_CACHE_MISS is the result.
+  EXPECT_THAT(response.result_error(), IsError(ERR_DNS_CACHE_MISS));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
+TEST_F(HostResolverManagerDnsTest,
+       HttpsSecureQueryAllowedWhenAdditionalTypesDisallowed) {
+  const std::string kName = "https.test";
+
+  MockDnsClientRuleList rules;
+  std::vector<DnsResourceRecord> records = {
+      BuildTestHttpsAliasRecord(kName, "alias.test")};
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/true,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         kName, dns_protocol::kTypeHttps, records)),
+                     /*delay=*/false);
+
+  CreateResolver();
+  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kSecure;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.dns_query_type = DnsQueryType::HTTPS;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+  // Experimental type, so does not affect overall result.
+  EXPECT_THAT(response.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  // Results never saved when overall result not OK.
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
+TEST_F(HostResolverManagerDnsTest,
+       HttpsFallbackQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const char kName[] = "https.test";
+
+  MockDnsClientRuleList rules;
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/true,
+                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
+                     /*delay=*/false);
+  // No expected HTTPS request in insecure mode.
+
+  CreateResolver();
+  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kAutomatic;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.dns_query_type = DnsQueryType::HTTPS;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsError(ERR_NAME_NOT_RESOLVED));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
 // Test that HTTPS records can be extracted from a response that also contains
 // unrecognized record types.
 TEST_F(HostResolverManagerDnsTest, HttpsQuery_MixedWithUnrecognizedType) {
@@ -9417,6 +9618,36 @@
   EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
 }
 
+// Same as HttpsInsecureQueryDisallowedWhenAdditionalTypesDisallowed except we
+// specify DNS HostResolverSource instead of relying on automatic determination.
+// Expect same results since DNS should be what we automatically determine, but
+// some slightly different logic paths are involved.
+TEST_F(HostResolverManagerDnsTest,
+       HttpsDnsInsecureQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const std::string kName = "https.test";
+
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver::ResolveHostParameters parameters;
+  parameters.source = HostResolverSource::DNS;
+  parameters.dns_query_type = DnsQueryType::HTTPS;
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      parameters, resolve_context_.get(), resolve_context_->host_cache()));
+  // No non-local work is done, so ERR_DNS_CACHE_MISS is the result.
+  EXPECT_THAT(response.result_error(), IsError(ERR_DNS_CACHE_MISS));
+  EXPECT_FALSE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
 TEST_F(HostResolverManagerDnsTest, HttpsInAddressQuery) {
   const char kName[] = "combined.test";
 
@@ -9779,22 +10010,25 @@
               testing::Optional(testing::ElementsAre(true)));
 }
 
-TEST_F(HostResolverManagerDnsTest, HttpsInAddressQuery_Insecure) {
+TEST_F(HostResolverManagerDnsTest,
+       HttpsInInsecureAddressQueryDisallowedWhenInsecureFeatureDisabled) {
   const char kName[] = "combined.test";
 
   base::test::ScopedFeatureList features;
   features.InitAndEnableFeatureWithParameters(
-      features::kDnsHttpssvc, {{"DnsHttpssvcUseHttpssvc", "true"},
-                               {"DnsHttpssvcExperimentDomains", kName}});
+      features::kDnsHttpssvc,
+      {{"DnsHttpssvcUseHttpssvc", "true"},
+       {"DnsHttpssvcExperimentDomains", kName},
+       {"DnsHttpssvcEnableQueryOverInsecure", "false"}});
 
   MockDnsClientRuleList rules;
   // No expected HTTPS request in insecure mode.
-  rules.emplace_back(kName, dns_protocol::kTypeA, false /* secure */,
+  rules.emplace_back(kName, dns_protocol::kTypeA, /*secure=*/false,
                      MockDnsClientRule::Result(MockDnsClientRule::OK),
-                     false /* delay */);
-  rules.emplace_back(kName, dns_protocol::kTypeAAAA, false /* secure */,
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeAAAA, /*secure=*/false,
                      MockDnsClientRule::Result(MockDnsClientRule::OK),
-                     false /* delay */);
+                     /*delay=*/false);
 
   CreateResolver();
   UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
@@ -9813,6 +10047,197 @@
   EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
 }
 
+TEST_F(HostResolverManagerDnsTest,
+       HttpsInInsecureAddressQueryAllowedWhenInsecureFeatureEnabled) {
+  const char kName[] = "combined.test";
+
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeatureWithParameters(
+      features::kDnsHttpssvc, {{"DnsHttpssvcUseHttpssvc", "true"},
+                               {"DnsHttpssvcExperimentDomains", kName},
+                               {"DnsHttpssvcEnableQueryOverInsecure", "true"}});
+
+  MockDnsClientRuleList rules;
+  std::vector<DnsResourceRecord> records = {
+      BuildTestHttpsAliasRecord(kName, "alias.test")};
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/false,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         kName, dns_protocol::kTypeHttps, records)),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeA, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeAAAA, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+
+  CreateResolver();
+  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      /*optional_parameters=*/base::nullopt, resolve_context_.get(),
+      resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_TRUE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_THAT(response.request()->GetExperimentalResultsForTesting(),
+              testing::Optional(testing::ElementsAre(true)));
+}
+
+TEST_F(HostResolverManagerDnsTest,
+       HttpsInInsecureAddressQueryDisallowedWhenAdditionalTypesDisallowed) {
+  const char kName[] = "combined.test";
+
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeatureWithParameters(
+      features::kDnsHttpssvc, {{"DnsHttpssvcUseHttpssvc", "true"},
+                               {"DnsHttpssvcExperimentDomains", kName},
+                               {"DnsHttpssvcEnableQueryOverInsecure", "true"}});
+
+  MockDnsClientRuleList rules;
+  // Use a delayed rule to hang if an unexpected HTTPS query is made. Allows the
+  // test to fail on unexpected query whether or not HTTPS query errors are
+  // ignored.
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
+                     /*delay=*/true);
+  rules.emplace_back(kName, dns_protocol::kTypeA, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeAAAA, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+
+  CreateResolver();
+  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kOff;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      /*optional_parameters=*/base::nullopt, resolve_context_.get(),
+      resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_TRUE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
+TEST_F(HostResolverManagerDnsTest,
+       HttpsInSecureAddressQueryAllowedWhenAdditionalQueriesDisallowed) {
+  const char kName[] = "combined.test";
+
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeatureWithParameters(
+      features::kDnsHttpssvc, {{"DnsHttpssvcUseHttpssvc", "true"},
+                               {"DnsHttpssvcExperimentDomains", kName},
+                               {"DnsHttpssvcEnableQueryOverInsecure", "true"}});
+
+  MockDnsClientRuleList rules;
+  std::vector<DnsResourceRecord> records = {
+      BuildTestHttpsAliasRecord(kName, "alias.test")};
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/true,
+                     MockDnsClientRule::Result(BuildTestDnsResponse(
+                         kName, dns_protocol::kTypeHttps, records)),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeA, /*secure=*/true,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeAAAA, /*secure=*/true,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+
+  CreateResolver();
+  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kSecure;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      /*optional_parameters=*/base::nullopt, resolve_context_.get(),
+      resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_TRUE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_THAT(response.request()->GetExperimentalResultsForTesting(),
+              testing::Optional(testing::ElementsAre(true)));
+}
+
+TEST_F(HostResolverManagerDnsTest,
+       HttpsInAddressFallbackWhenAdditionalQueriesDisallowedIsDisallowed) {
+  const char kName[] = "combined.test";
+
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeatureWithParameters(
+      features::kDnsHttpssvc, {{"DnsHttpssvcUseHttpssvc", "true"},
+                               {"DnsHttpssvcExperimentDomains", kName},
+                               {"DnsHttpssvcEnableQueryOverInsecure", "true"}});
+
+  MockDnsClientRuleList rules;
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/true,
+                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeA, /*secure=*/true,
+                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeAAAA, /*secure=*/true,
+                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
+                     /*delay=*/false);
+  // Use a delayed rule to hang if an unexpected HTTPS query is made. Allows the
+  // test to fail on unexpected query whether or not HTTPS query errors are
+  // ignored.
+  rules.emplace_back(kName, dns_protocol::kTypeHttps, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::FAIL),
+                     /*delay=*/true);
+  rules.emplace_back(kName, dns_protocol::kTypeA, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+  rules.emplace_back(kName, dns_protocol::kTypeAAAA, /*secure=*/false,
+                     MockDnsClientRule::Result(MockDnsClientRule::OK),
+                     /*delay=*/false);
+
+  CreateResolver();
+  UseMockDnsClient(CreateValidDnsConfig(), std::move(rules));
+  DnsConfigOverrides overrides;
+  overrides.secure_dns_mode = SecureDnsMode::kAutomatic;
+  resolver_->SetDnsConfigOverrides(overrides);
+
+  resolver_->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  ResolveHostResponseHelper response(resolver_->CreateRequest(
+      HostPortPair(kName, 108), NetworkIsolationKey(), NetLogWithSource(),
+      /*optional_parameters=*/base::nullopt, resolve_context_.get(),
+      resolve_context_->host_cache()));
+
+  EXPECT_THAT(response.result_error(), IsOk());
+  EXPECT_TRUE(response.request()->GetAddressResults());
+  EXPECT_FALSE(response.request()->GetTextResults());
+  EXPECT_FALSE(response.request()->GetHostnameResults());
+  EXPECT_FALSE(response.request()->GetExperimentalResultsForTesting());
+}
+
 TEST_F(HostResolverManagerDnsTest, HttpsInAddressQuery_ExperimentalTimeout) {
   const char kName[] = "combined.test";
   const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(2);
diff --git a/remoting/host/installer/linux/BUILD.gn b/remoting/host/installer/linux/BUILD.gn
index 397b4d4..22ab53a 100644
--- a/remoting/host/installer/linux/BUILD.gn
+++ b/remoting/host/installer/linux/BUILD.gn
@@ -67,6 +67,7 @@
   ]
 
   deps = [
+    "//remoting/host:remote_open_url",
     "//remoting/host:remoting_me2me_host",
     "//remoting/host:remoting_native_messaging_host",
     "//remoting/host:remoting_native_messaging_manifests",
diff --git a/remoting/host/installer/linux/Makefile b/remoting/host/installer/linux/Makefile
index bbdfa7f..e0468d4 100644
--- a/remoting/host/installer/linux/Makefile
+++ b/remoting/host/installer/linux/Makefile
@@ -27,6 +27,8 @@
 ME2ME_NM_DEBUGFILE = $(ME2ME_NM_PROGNAME).debug
 REMOTE_ASSISTANCE_PROGNAME = $(BUILD_DIR)/remote_assistance_host
 REMOTE_ASSISTANCE_DEBUGFILE = $(REMOTE_ASSISTANCE_PROGNAME).debug
+REMOTE_OPEN_URL_PROGNAME = $(BUILD_DIR)/remote_open_url
+REMOTE_OPEN_URL_DEBUGFILE = $(REMOTE_OPEN_URL_PROGNAME).debug
 
 all:
 
@@ -81,6 +83,11 @@
 	eu-strip -f "$(REMOTE_ASSISTANCE_DEBUGFILE)" \
 	  "$(INSTALL_DIR)/remote-assistance-host"
 
+	install "$(REMOTE_OPEN_URL_PROGNAME)" \
+	  "$(INSTALL_DIR)/remote-open-url"
+	eu-strip -f "$(REMOTE_OPEN_URL_DEBUGFILE)" \
+	  "$(INSTALL_DIR)/remote-open-url"
+
 	install -m 0644 \
 	  "$(BUILD_DIR)/icudtl.dat" "$(INSTALL_DIR)/icudtl.dat"
 
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index fd97518..10ae674 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -58,6 +58,7 @@
       ":remoting_me2me_host_copy_user_session",
       ":remoting_me2me_host_copy_user_session_wrapper",
       ":remoting_native_messaging_host",
+      "//remoting/host:remote_open_url",
       "//remoting/host:remoting_me2me_host",
     ]
   }
diff --git a/services/network/host_resolver.cc b/services/network/host_resolver.cc
index 1711176..0168bde 100644
--- a/services/network/host_resolver.cc
+++ b/services/network/host_resolver.cc
@@ -9,15 +9,20 @@
 #include "base/bind.h"
 #include "base/lazy_instance.h"
 #include "base/optional.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/dns/host_resolver.h"
 #include "net/dns/host_resolver_source.h"
+#include "net/dns/public/secure_dns_mode.h"
+#include "net/dns/public/secure_dns_policy.h"
 #include "net/log/net_log.h"
 #include "net/net_buildflags.h"
 #include "services/network/host_resolver_mdns_listener.h"
 #include "services/network/public/cpp/host_resolver_mojom_traits.h"
+#include "services/network/public/mojom/host_resolver.mojom-shared.h"
+#include "services/network/public/mojom/host_resolver.mojom.h"
 #include "services/network/resolve_host_request.h"
 
 namespace network {
@@ -54,8 +59,15 @@
   parameters.include_canonical_name = mojo_parameters->include_canonical_name;
   parameters.loopback_only = mojo_parameters->loopback_only;
   parameters.is_speculative = mojo_parameters->is_speculative;
-  parameters.secure_dns_mode_override = mojo::FromOptionalSecureDnsMode(
-      mojo_parameters->secure_dns_mode_override);
+
+  // TODO(crbug.com/1200908): Pass the SecureDnsPolicy through unmodified.
+  net::SecureDnsPolicy secure_dns_policy;
+  mojo::EnumTraits<mojom::SecureDnsPolicy, net::SecureDnsPolicy>::FromMojom(
+      mojo_parameters->secure_dns_policy, &secure_dns_policy);
+
+  if (secure_dns_policy == net::SecureDnsPolicy::kDisable) {
+    parameters.secure_dns_mode_override = net::SecureDnsMode::kOff;
+  }
   return parameters;
 }
 }  // namespace
diff --git a/services/network/host_resolver_unittest.cc b/services/network/host_resolver_unittest.cc
index 370e39c0..4f592c68 100644
--- a/services/network/host_resolver_unittest.cc
+++ b/services/network/host_resolver_unittest.cc
@@ -33,6 +33,7 @@
 #include "net/dns/public/secure_dns_mode.h"
 #include "net/log/net_log.h"
 #include "net/net_buildflags.h"
+#include "net/test/gtest_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -715,8 +716,8 @@
 
   mojom::ResolveHostParametersPtr optional_parameters =
       mojom::ResolveHostParameters::New();
-  optional_parameters->secure_dns_mode_override =
-      network::mojom::OptionalSecureDnsMode::SECURE;
+  optional_parameters->secure_dns_policy =
+      network::mojom::SecureDnsPolicy::DISABLE;
 
   base::RunLoop run_loop;
   mojo::PendingRemote<mojom::ResolveHostClient> pending_response_client;
@@ -730,7 +731,7 @@
   EXPECT_EQ(net::OK, response_client.result_error());
   EXPECT_THAT(response_client.result_addresses().value().endpoints(),
               testing::ElementsAre(CreateExpectedEndPoint("127.0.0.1", 80)));
-  EXPECT_EQ(net::SecureDnsMode::kSecure,
+  EXPECT_EQ(net::SecureDnsMode::kOff,
             inner_resolver->last_secure_dns_mode_override().value());
 }
 
@@ -1292,7 +1293,9 @@
       net::HostResolver::CreateStandaloneContextResolver(net::NetLog::Get());
   inner_resolver->GetManagerForTesting()->SetDnsClientForTesting(
       std::move(dns_client));
-  inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(true);
+  inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/true);
 
   HostResolver resolver(inner_resolver.get(), net::NetLog::Get());
 
@@ -1331,7 +1334,9 @@
       net::HostResolver::CreateStandaloneContextResolver(net::NetLog::Get());
   inner_resolver->GetManagerForTesting()->SetDnsClientForTesting(
       std::move(dns_client));
-  inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(true);
+  inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/true);
 
   HostResolver resolver(inner_resolver.get(), net::NetLog::Get());
 
@@ -1357,6 +1362,40 @@
   EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
 }
 
+TEST_F(HostResolverTest, RespectsDisablingAdditionalQueryTypes) {
+  net::MockDnsClientRuleList rules;
+  auto dns_client = std::make_unique<net::MockDnsClient>(CreateValidDnsConfig(),
+                                                         std::move(rules));
+  dns_client->set_ignore_system_config_changes(true);
+
+  std::unique_ptr<net::ContextHostResolver> inner_resolver =
+      net::HostResolver::CreateStandaloneContextResolver(net::NetLog::Get());
+  inner_resolver->GetManagerForTesting()->SetDnsClientForTesting(
+      std::move(dns_client));
+  inner_resolver->GetManagerForTesting()->SetInsecureDnsClientEnabled(
+      /*enabled=*/true,
+      /*additional_dns_types_enabled=*/false);
+
+  HostResolver resolver(inner_resolver.get(), net::NetLog::Get());
+
+  base::RunLoop run_loop;
+  mojom::ResolveHostParametersPtr optional_parameters =
+      mojom::ResolveHostParameters::New();
+  optional_parameters->dns_query_type = net::DnsQueryType::PTR;
+  mojo::PendingRemote<mojom::ResolveHostClient> pending_response_client;
+  TestResolveHostClient response_client(&pending_response_client, &run_loop);
+
+  resolver.ResolveHost(
+      net::HostPortPair("example.com", 160), net::NetworkIsolationKey(),
+      std::move(optional_parameters), std::move(pending_response_client));
+  run_loop.Run();
+
+  // No queries made, so result is `ERR_DNS_CACHE_MISS`.
+  EXPECT_THAT(response_client.result_error(),
+              net::test::IsError(net::ERR_DNS_CACHE_MISS));
+  EXPECT_EQ(0u, resolver.GetNumOutstandingRequestsForTesting());
+}
+
 #if BUILDFLAG(ENABLE_MDNS)
 TEST_F(HostResolverTest, MdnsListener_AddressResult) {
   auto inner_resolver = std::make_unique<net::MockHostResolver>();
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index eeadeb1..482d6022 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1397,6 +1397,8 @@
     // now, much easier to create entirely separate net::HostResolver instances.
     net::HostResolver::ManagerOptions options;
     options.insecure_dns_client_enabled = true;
+    // Assume additional types are unnecessary for these special cases.
+    options.additional_types_via_insecure_dns_enabled = false;
     options.dns_config_overrides = config_overrides.value();
     private_internal_resolver =
         network_service_->host_resolver_factory()->CreateStandaloneResolver(
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 9684d3d..e64e7ec 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -3812,7 +3812,8 @@
       false /* delay */);
   auto mock_dns_client = std::make_unique<net::MockDnsClient>(
       base_configuration, std::move(rules));
-  mock_dns_client->SetInsecureEnabled(true);
+  mock_dns_client->SetInsecureEnabled(/*enabled=*/true,
+                                      /*additional_types_enabled=*/false);
   mock_dns_client->set_ignore_system_config_changes(true);
   auto* mock_dns_client_ptr = mock_dns_client.get();
   internal_resolver->GetManagerForTesting()->SetDnsClientForTesting(
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 92d2597..28cf047 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -481,8 +481,10 @@
 
   // Enable or disable the insecure part of DnsClient. "DnsClient" is the class
   // that implements the stub resolver.
+  // TODO(crbug.com/1203427): Pass the additional query types param through Mojo
+  // from the browser code.
   host_resolver_manager_->SetInsecureDnsClientEnabled(
-      insecure_dns_client_enabled);
+      insecure_dns_client_enabled, true);
 
   // Configure DNS over HTTPS.
   net::DnsConfigOverrides overrides;
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.cc b/services/network/public/cpp/host_resolver_mojom_traits.cc
index 37b8a86b..cec8c59 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.cc
+++ b/services/network/public/cpp/host_resolver_mojom_traits.cc
@@ -6,6 +6,8 @@
 
 #include "mojo/public/cpp/base/time_mojom_traits.h"
 #include "net/dns/public/dns_over_https_server_config.h"
+#include "net/dns/public/secure_dns_mode.h"
+#include "net/dns/public/secure_dns_policy.h"
 #include "services/network/public/cpp/ip_address_mojom_traits.h"
 #include "services/network/public/cpp/ip_endpoint_mojom_traits.h"
 #include "services/network/public/mojom/host_resolver.mojom.h"
@@ -337,8 +339,6 @@
     case net::SecureDnsMode::kSecure:
       return network::mojom::SecureDnsMode::SECURE;
   }
-  NOTREACHED();
-  return network::mojom::SecureDnsMode::OFF;
 }
 
 // static
@@ -356,7 +356,31 @@
       *out = net::SecureDnsMode::kSecure;
       return true;
   }
-  return false;
+}
+
+// static
+network::mojom::SecureDnsPolicy
+EnumTraits<network::mojom::SecureDnsPolicy, net::SecureDnsPolicy>::ToMojom(
+    net::SecureDnsPolicy secure_dns_mode) {
+  switch (secure_dns_mode) {
+    case net::SecureDnsPolicy::kAllow:
+      return network::mojom::SecureDnsPolicy::ALLOW;
+    case net::SecureDnsPolicy::kDisable:
+      return network::mojom::SecureDnsPolicy::DISABLE;
+  }
+}
+
+// static
+bool EnumTraits<network::mojom::SecureDnsPolicy, net::SecureDnsPolicy>::
+    FromMojom(network::mojom::SecureDnsPolicy in, net::SecureDnsPolicy* out) {
+  switch (in) {
+    case network::mojom::SecureDnsPolicy::ALLOW:
+      *out = net::SecureDnsPolicy::kAllow;
+      return true;
+    case network::mojom::SecureDnsPolicy::DISABLE:
+      *out = net::SecureDnsPolicy::kDisable;
+      return true;
+  }
 }
 
 // static
diff --git a/services/network/public/cpp/host_resolver_mojom_traits.h b/services/network/public/cpp/host_resolver_mojom_traits.h
index 4b3a9d9..4aed6e74 100644
--- a/services/network/public/cpp/host_resolver_mojom_traits.h
+++ b/services/network/public/cpp/host_resolver_mojom_traits.h
@@ -22,6 +22,7 @@
 #include "net/dns/public/dns_config_overrides.h"
 #include "net/dns/public/dns_query_type.h"
 #include "net/dns/public/secure_dns_mode.h"
+#include "net/dns/public/secure_dns_policy.h"
 #include "services/network/public/mojom/host_resolver.mojom-forward.h"
 #include "services/network/public/mojom/host_resolver.mojom-shared.h"
 
@@ -123,6 +124,14 @@
 };
 
 template <>
+struct EnumTraits<network::mojom::SecureDnsPolicy, net::SecureDnsPolicy> {
+  static network::mojom::SecureDnsPolicy ToMojom(
+      net::SecureDnsPolicy secure_dns_mode);
+  static bool FromMojom(network::mojom::SecureDnsPolicy in,
+                        net::SecureDnsPolicy* out);
+};
+
+template <>
 class StructTraits<network::mojom::ResolveErrorInfoDataView,
                    net::ResolveErrorInfo> {
  public:
diff --git a/services/network/public/mojom/host_resolver.mojom b/services/network/public/mojom/host_resolver.mojom
index 19da4d5..f5231d8 100644
--- a/services/network/public/mojom/host_resolver.mojom
+++ b/services/network/public/mojom/host_resolver.mojom
@@ -40,6 +40,12 @@
   SECURE,
 };
 
+// This enum corresponds to net::SecureDnsPolicy
+enum SecureDnsPolicy {
+  ALLOW,
+  DISABLE,
+};
+
 // Overridable DNS configuration values for host resolution. All fields default
 // to a non-overriding state where the relevant value will be used from system
 // DNS configuration.
@@ -83,8 +89,9 @@
   // (https://tools.ietf.org/id/draft-ietf-doh-dns-over-https-12.txt).
   array<DnsOverHttpsServer>? dns_over_https_servers;
 
-  // The default SecureDnsMode to use when resolving queries. It can be
-  // for individual requests such as requests to resolve a DoH server hostname.
+  // The SecureDnsMode to use when resolving queries. If set, it overrides
+  // the resolver's current configured mode, and can in turn be overridden by
+  // ResolveHostParameters.secure_dns_policy.
   OptionalSecureDnsMode secure_dns_mode = OptionalSecureDnsMode.NO_OVERRIDE;
 
   // Whether automatic upgrade to DNS over HTTPS servers is permitted.
@@ -244,9 +251,8 @@
   // ResolveHostClient::OnComplete will not receive any addresses.
   bool is_speculative = false;
 
-  // Set to override the resolver's default secure dns mode for this request.
-  OptionalSecureDnsMode secure_dns_mode_override =
-      OptionalSecureDnsMode.NO_OVERRIDE;
+  // Controls the resolver's Secure DNS behavior for this request.
+  SecureDnsPolicy secure_dns_policy = SecureDnsPolicy.ALLOW;
 };
 
 // Response interface used to receive notifications from
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
index 52b2c6f..e83cb35 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_negative.filter
@@ -25,6 +25,7 @@
 -All/WizardControllerDeviceStateExplicitRequirementTest.*
 -All/WizardControllerUpdateAfterCompletedOobeTest.*
 -AppDownloadingScreenTest.*
+-AutoLaunchedKioskEphemeralUsersTest.*
 -ArcTermsOfServiceScreenTest.*
 -AssistantOptInFlowTest.*
 -AutoEnrollmentLocalPolicyServer.*
diff --git a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
index af6f465..8d3dc99e4 100644
--- a/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
+++ b/testing/buildbot/filters/chromeos.msan.browser_tests.oobe_positive.filter
@@ -25,6 +25,7 @@
 All/WizardControllerDeviceStateExplicitRequirementTest.*
 All/WizardControllerUpdateAfterCompletedOobeTest.*
 AppDownloadingScreenTest.*
+AutoLaunchedKioskEphemeralUsersTest.*
 ArcTermsOfServiceScreenTest.*
 AssistantOptInFlowTest.*
 AutoEnrollmentLocalPolicyServer.*
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a2a3177..b73b2cdf 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -876,6 +876,21 @@
             ]
         }
     ],
+    "AutofillDownstreamCvcPromptUseGooglePayLogo": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "AutofillDownstreamCvcPromptUseGooglePayLogo",
+                    "enable_features": [
+                        "AutofillDownstreamCvcPromptUseGooglePayLogo"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillEnableAugmentedPhoneCountryCode": [
         {
             "platforms": [
@@ -3919,6 +3934,21 @@
             ]
         }
     ],
+    "IOSUMABackgroundSessions": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "IOSUMABackgroundSessions"
+                    ]
+                }
+            ]
+        }
+    ],
     "IdentifiabilityStudy": [
         {
             "platforms": [
@@ -6826,6 +6856,23 @@
             ]
         }
     ],
+    "SignInProfileCreationEnterprise": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SignInProfileCreationEnterprise"
+                    ]
+                }
+            ]
+        }
+    ],
     "SimplifiedUrlDisplay": [
         {
             "platforms": [
@@ -8286,6 +8333,26 @@
             ]
         }
     ],
+    "WebRTC-PreStreamDecoders": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "ios",
+                "android",
+                "android_weblayer",
+                "android_webview"
+            ],
+            "experiments": [
+                {
+                    "name": "max:0,_V1"
+                }
+            ]
+        }
+    ],
     "WebRTC-TaskQueuePacer": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/CMake/AbseilHelpers.cmake b/third_party/abseil-cpp/CMake/AbseilHelpers.cmake
index 1f75439..1a80b5b4 100644
--- a/third_party/abseil-cpp/CMake/AbseilHelpers.cmake
+++ b/third_party/abseil-cpp/CMake/AbseilHelpers.cmake
@@ -141,7 +141,8 @@
   endif()
 
   # Generate a pkg-config file for every library:
-  if(_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
+  if((_build_type STREQUAL "static" OR _build_type STREQUAL "shared")
+     AND ABSL_ENABLE_INSTALL)
     if(NOT ABSL_CC_LIB_TESTONLY)
       if(absl_VERSION)
         set(PC_VERSION "${absl_VERSION}")
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index 57d8d56b..2ec53aa 100644
--- a/third_party/abseil-cpp/README.chromium
+++ b/third_party/abseil-cpp/README.chromium
@@ -4,7 +4,7 @@
 License: Apache 2.0
 License File: LICENSE
 Version: 0
-Revision: bcc11a8918f8cc9b43c9a0dc5da7b52d48452bd3
+Revision: a9831f1cbf93fb18dd951453635f488037454ce9
 Security Critical: yes
 
 Description:
diff --git a/third_party/abseil-cpp/absl/base/attributes.h b/third_party/abseil-cpp/absl/base/attributes.h
index d710f28..5213955 100644
--- a/third_party/abseil-cpp/absl/base/attributes.h
+++ b/third_party/abseil-cpp/absl/base/attributes.h
@@ -599,31 +599,24 @@
 //    case 42:
 //      ...
 //
-// Notes: when compiled with clang in C++11 mode, the ABSL_FALLTHROUGH_INTENDED
-// macro is expanded to the [[clang::fallthrough]] attribute, which is analysed
-// when  performing switch labels fall-through diagnostic
-// (`-Wimplicit-fallthrough`). See clang documentation on language extensions
-// for details:
+// Notes: When supported, GCC and Clang can issue a warning on switch labels
+// with unannotated fallthrough using the warning `-Wimplicit-fallthrough`. See
+// clang documentation on language extensions for details:
 // https://clang.llvm.org/docs/AttributeReference.html#fallthrough-clang-fallthrough
 //
-// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro
-// has no effect on diagnostics. In any case this macro has no effect on runtime
+// When used with unsupported compilers, the ABSL_FALLTHROUGH_INTENDED macro has
+// no effect on diagnostics. In any case this macro has no effect on runtime
 // behavior and performance of code.
 
 #ifdef ABSL_FALLTHROUGH_INTENDED
 #error "ABSL_FALLTHROUGH_INTENDED should not be defined."
-#endif
-
-// TODO(zhangxy): Use c++17 standard [[fallthrough]] macro, when supported.
-#if defined(__clang__) && defined(__has_warning)
-#if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
+#elif ABSL_HAVE_CPP_ATTRIBUTE(fallthrough)
+#define ABSL_FALLTHROUGH_INTENDED [[fallthrough]]
+#elif ABSL_HAVE_CPP_ATTRIBUTE(clang::fallthrough)
 #define ABSL_FALLTHROUGH_INTENDED [[clang::fallthrough]]
-#endif
-#elif ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(7, 0)
+#elif ABSL_HAVE_CPP_ATTRIBUTE(gnu::fallthrough)
 #define ABSL_FALLTHROUGH_INTENDED [[gnu::fallthrough]]
-#endif
-
-#ifndef ABSL_FALLTHROUGH_INTENDED
+#else
 #define ABSL_FALLTHROUGH_INTENDED \
   do {                            \
   } while (0)
diff --git a/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc b/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc
index e458a795..689e597 100644
--- a/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc
+++ b/third_party/abseil-cpp/absl/debugging/failure_signal_handler.cc
@@ -136,7 +136,8 @@
 #else
   const size_t page_mask = sysconf(_SC_PAGESIZE) - 1;
 #endif
-  size_t stack_size = (std::max(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
+  size_t stack_size =
+      (std::max<size_t>(SIGSTKSZ, 65536) + page_mask) & ~page_mask;
 #if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
     defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
   // Account for sanitizer instrumentation requiring additional stack space.
diff --git a/third_party/abseil-cpp/absl/memory/memory.h b/third_party/abseil-cpp/absl/memory/memory.h
index 2b5ff623..d6332606 100644
--- a/third_party/abseil-cpp/absl/memory/memory.h
+++ b/third_party/abseil-cpp/absl/memory/memory.h
@@ -420,7 +420,7 @@
 //
 // A C++11 compatible implementation of C++17's std::allocator_traits.
 //
-#if __cplusplus >= 201703L
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
 using std::allocator_traits;
 #else  // __cplusplus >= 201703L
 template <typename Alloc>
diff --git a/third_party/abseil-cpp/absl/meta/type_traits.h b/third_party/abseil-cpp/absl/meta/type_traits.h
index b5427a4..e7c1239 100644
--- a/third_party/abseil-cpp/absl/meta/type_traits.h
+++ b/third_party/abseil-cpp/absl/meta/type_traits.h
@@ -634,7 +634,7 @@
 
 namespace type_traits_internal {
 
-#if __cplusplus >= 201703L
+#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
 // std::result_of is deprecated (C++17) or removed (C++20)
 template<typename> struct result_of;
 template<typename F, typename... Args>
diff --git a/third_party/abseil-cpp/absl/strings/BUILD.bazel b/third_party/abseil-cpp/absl/strings/BUILD.bazel
index 68c71f0..15277de 100644
--- a/third_party/abseil-cpp/absl/strings/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/strings/BUILD.bazel
@@ -529,6 +529,7 @@
         ":cord",
         ":cord_internal",
         ":strings",
+        "//absl/base:config",
     ],
 )
 
@@ -544,6 +545,7 @@
         ":cordz_sample_token",
         ":cordz_statistics",
         ":cordz_update_tracker",
+        ":strings",
         "//absl/base:config",
         "//absl/base:core_headers",
         "@com_google_googletest//:gtest",
diff --git a/third_party/abseil-cpp/absl/strings/BUILD.gn b/third_party/abseil-cpp/absl/strings/BUILD.gn
index 435f4a90..ff1a9046 100644
--- a/third_party/abseil-cpp/absl/strings/BUILD.gn
+++ b/third_party/abseil-cpp/absl/strings/BUILD.gn
@@ -352,6 +352,7 @@
     ":cord",
     ":cord_internal",
     ":strings",
+    "//third_party/abseil-cpp/absl/base:config",
   ]
 }
 
@@ -365,6 +366,7 @@
     ":cordz_sample_token",
     ":cordz_statistics",
     ":cordz_update_tracker",
+    ":strings",
     "//third_party/abseil-cpp/absl/base:config",
     "//third_party/abseil-cpp/absl/base:core_headers",
     "//third_party/googletest:gtest",
diff --git a/third_party/abseil-cpp/absl/strings/CMakeLists.txt b/third_party/abseil-cpp/absl/strings/CMakeLists.txt
index 80ae2a6..1750f7a 100644
--- a/third_party/abseil-cpp/absl/strings/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/strings/CMakeLists.txt
@@ -830,6 +830,7 @@
   COPTS
     ${ABSL_TEST_COPTS}
   DEPS
+    absl::config
     absl::cord
     absl::cord_internal
     absl::strings
@@ -852,6 +853,7 @@
     absl::cordz_statistics
     absl::cordz_update_tracker
     absl::core_headers
+    absl::strings
   TESTONLY
 )
 
diff --git a/third_party/abseil-cpp/absl/strings/cord.cc b/third_party/abseil-cpp/absl/strings/cord.cc
index 15d1733..1c2ff9f2 100644
--- a/third_party/abseil-cpp/absl/strings/cord.cc
+++ b/third_party/abseil-cpp/absl/strings/cord.cc
@@ -56,6 +56,7 @@
 using ::absl::cord_internal::CordRepFlat;
 using ::absl::cord_internal::CordRepRing;
 using ::absl::cord_internal::CordRepSubstring;
+using ::absl::cord_internal::CordzUpdateTracker;
 using ::absl::cord_internal::InlineData;
 using ::absl::cord_internal::kMaxFlatLength;
 using ::absl::cord_internal::kMinFlatLength;
@@ -281,6 +282,35 @@
   }
 }
 
+// Creates a CordRep from the provided string. If the string is large enough,
+// and not wasteful, we move the string into an external cord rep, preserving
+// the already allocated string contents.
+// Requires the provided string length to be larger than `kMaxInline`.
+static CordRep* CordRepFromString(std::string&& src) {
+  assert(src.length() > cord_internal::kMaxInline);
+  if (
+      // String is short: copy data to avoid external block overhead.
+      src.size() <= kMaxBytesToCopy ||
+      // String is wasteful: copy data to avoid pinning too much unused memory.
+      src.size() < src.capacity() / 2
+  ) {
+    return NewTree(src.data(), src.size(), 0);
+  }
+
+  struct StringReleaser {
+    void operator()(absl::string_view /* data */) {}
+    std::string data;
+  };
+  const absl::string_view original_data = src;
+  auto* rep =
+      static_cast<::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>(
+          absl::cord_internal::NewExternalRep(original_data,
+                                              StringReleaser{std::move(src)}));
+  // Moving src may have invalidated its data pointer, so adjust it.
+  rep->base = rep->template get<0>().data.data();
+  return rep;
+}
+
 // --------------------------------------------------------------------
 // Cord::InlineRep functions
 
@@ -486,17 +516,17 @@
     return true;
   }
   if (rep->tag == EXTERNAL) {
-    *total_mem_usage += sizeof(CordRepConcat) + rep->length;
+    // We don't know anything about the embedded / bound data, but we can safely
+    // assume it is 'at least' a word / pointer to data. In the future we may
+    // choose to use the 'data' byte as a tag to identify the types of some
+    // well-known externals, such as a std::string instance.
+    *total_mem_usage +=
+        sizeof(cord_internal::CordRepExternalImpl<intptr_t>) + rep->length;
     return true;
   }
   return false;
 }
 
-void Cord::InlineRep::UpdateCordzStatisticsSlow() {
-  CordRep* tree = as_tree();
-  data_.cordz_info()->RecordMetrics(tree->length);
-}
-
 void Cord::InlineRep::AssignSlow(const Cord::InlineRep& src) {
   assert(&src != this);
   assert(is_tree() || src.is_tree());
@@ -525,42 +555,24 @@
 // --------------------------------------------------------------------
 // Constructors and destructors
 
-Cord::Cord(absl::string_view src) : contents_(InlineData::kDefaultInit) {
+Cord::Cord(absl::string_view src, MethodIdentifier method)
+    : contents_(InlineData::kDefaultInit) {
   const size_t n = src.size();
   if (n <= InlineRep::kMaxInline) {
     contents_.set_data(src.data(), n, true);
   } else {
     CordRep* rep = NewTree(src.data(), n, 0);
-    contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString);
+    contents_.EmplaceTree(rep, method);
   }
 }
 
 template <typename T, Cord::EnableIfString<T>>
-Cord::Cord(T&& src) {
-  if (
-      // String is short: copy data to avoid external block overhead.
-      src.size() <= kMaxBytesToCopy ||
-      // String is wasteful: copy data to avoid pinning too much unused memory.
-      src.size() < src.capacity() / 2
-  ) {
-    if (src.size() <= InlineRep::kMaxInline) {
-      contents_.set_data(src.data(), src.size(), false);
-    } else {
-      contents_.set_tree(NewTree(src.data(), src.size(), 0));
-    }
+Cord::Cord(T&& src) : contents_(InlineData::kDefaultInit) {
+  if (src.size() <= InlineRep::kMaxInline) {
+    contents_.set_data(src.data(), src.size(), true);
   } else {
-    struct StringReleaser {
-      void operator()(absl::string_view /* data */) {}
-      std::string data;
-    };
-    const absl::string_view original_data = src;
-    auto* rep = static_cast<
-        ::absl::cord_internal::CordRepExternalImpl<StringReleaser>*>(
-        absl::cord_internal::NewExternalRep(
-            original_data, StringReleaser{std::forward<T>(src)}));
-    // Moving src may have invalidated its data pointer, so adjust it.
-    rep->base = rep->template get<0>().data.data();
-    contents_.set_tree(rep);
+    CordRep* rep = CordRepFromString(std::forward<T>(src));
+    contents_.EmplaceTree(rep, CordzUpdateTracker::kConstructorString);
   }
 }
 
@@ -583,42 +595,53 @@
   }
 }
 
+Cord& Cord::AssignLargeString(std::string&& src) {
+  auto constexpr method = CordzUpdateTracker::kAssignString;
+  assert(src.size() > kMaxBytesToCopy);
+  CordRep* rep = CordRepFromString(std::move(src));
+  if (CordRep* tree = contents_.tree()) {
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    contents_.SetTree(rep, scope);
+    CordRep::Unref(tree);
+  } else {
+    contents_.EmplaceTree(rep, method);
+  }
+  return *this;
+}
+
 Cord& Cord::operator=(absl::string_view src) {
+  auto constexpr method = CordzUpdateTracker::kAssignString;
   const char* data = src.data();
   size_t length = src.size();
   CordRep* tree = contents_.tree();
   if (length <= InlineRep::kMaxInline) {
-    // Embed into this->contents_
-    if (tree) CordzInfo::MaybeUntrackCord(contents_.cordz_info());
+    // Embed into this->contents_, which is somewhat subtle:
+    // - MaybeUntrackCord must be called before Unref(tree).
+    // - MaybeUntrackCord must be called before set_data() clobbers cordz_info.
+    // - set_data() must be called before Unref(tree) as it may reference tree.
+    if (tree != nullptr) CordzInfo::MaybeUntrackCord(contents_.cordz_info());
     contents_.set_data(data, length, true);
-    if (tree) CordRep::Unref(tree);
+    if (tree != nullptr) CordRep::Unref(tree);
     return *this;
   }
-  if (tree != nullptr && tree->tag >= FLAT &&
-      tree->flat()->Capacity() >= length && tree->refcount.IsOne()) {
-    // Copy in place if the existing FLAT node is reusable.
-    memmove(tree->flat()->Data(), data, length);
-    tree->length = length;
-    VerifyTree(tree);
-    return *this;
-  }
-  contents_.set_tree(NewTree(data, length, 0));
-  if (tree) CordRep::Unref(tree);
-  return *this;
-}
-
-template <typename T, Cord::EnableIfString<T>>
-Cord& Cord::operator=(T&& src) {
-  if (src.size() <= kMaxBytesToCopy) {
-    *this = absl::string_view(src);
+  if (tree != nullptr) {
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    if (tree->tag >= FLAT && tree->flat()->Capacity() >= length &&
+        tree->refcount.IsOne()) {
+      // Copy in place if the existing FLAT node is reusable.
+      memmove(tree->flat()->Data(), data, length);
+      tree->length = length;
+      VerifyTree(tree);
+      return *this;
+    }
+    contents_.SetTree(NewTree(data, length, 0), scope);
+    CordRep::Unref(tree);
   } else {
-    *this = Cord(std::forward<T>(src));
+    contents_.EmplaceTree(NewTree(data, length, 0), method);
   }
   return *this;
 }
 
-template Cord& Cord::operator=(std::string&& src);
-
 // TODO(sanjay): Move to Cord::InlineRep section of file.  For now,
 // we keep it here to make diffs easier.
 void Cord::InlineRep::AppendArray(absl::string_view src,
@@ -644,10 +667,8 @@
       return;
     }
 
-    // It is possible that src.data() == data_, but when we transition from an
-    // InlineRep to a tree we need to assign data_ = root via set_tree. To
-    // avoid corrupting the source data before we copy it, delay calling
-    // set_tree until after we've copied data.
+    // Note: we don't concern ourselves if src aliases data stored in the
+    // inlined data of 'this',  as we update the InlineData only at the end.
     // We are going from an inline size to beyond inline size. Make the new size
     // either double the inlined size, or the added size + 10%.
     const size_t size1 = inline_length * 2 + src.size();
@@ -753,7 +774,8 @@
   if (src.size() <= kMaxBytesToCopy) {
     Append(absl::string_view(src));
   } else {
-    Append(Cord(std::forward<T>(src)));
+    CordRep* rep = CordRepFromString(std::forward<T>(src));
+    contents_.AppendTree(rep, CordzUpdateTracker::kAppendString);
   }
 }
 
@@ -795,7 +817,8 @@
   if (src.size() <= kMaxBytesToCopy) {
     Prepend(absl::string_view(src));
   } else {
-    Prepend(Cord(std::forward<T>(src)));
+    CordRep* rep = CordRepFromString(std::forward<T>(src));
+    contents_.PrependTree(rep, CordzUpdateTracker::kPrependString);
   }
 }
 
@@ -893,12 +916,17 @@
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     contents_.remove_prefix(n);
-  } else if (tree->tag == RING) {
-    contents_.replace_tree(CordRepRing::RemovePrefix(tree->ring(), n));
   } else {
-    CordRep* newrep = RemovePrefixFrom(tree, n);
-    CordRep::Unref(tree);
-    contents_.replace_tree(VerifyTree(newrep));
+    auto constexpr method = CordzUpdateTracker::kRemovePrefix;
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    if (tree->tag == RING) {
+      tree = CordRepRing::RemovePrefix(tree->ring(), n);
+    } else {
+      CordRep* newrep = RemovePrefixFrom(tree, n);
+      CordRep::Unref(tree);
+      tree = VerifyTree(newrep);
+    }
+    contents_.SetTreeOrEmpty(tree, scope);
   }
 }
 
@@ -909,12 +937,17 @@
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     contents_.reduce_size(n);
-  } else if (tree->tag == RING) {
-    contents_.replace_tree(CordRepRing::RemoveSuffix(tree->ring(), n));
   } else {
-    CordRep* newrep = RemoveSuffixFrom(tree, n);
-    CordRep::Unref(tree);
-    contents_.replace_tree(VerifyTree(newrep));
+    auto constexpr method = CordzUpdateTracker::kRemoveSuffix;
+    CordzUpdateScope scope(contents_.cordz_info(), method);
+    if (tree->tag == RING) {
+      tree = CordRepRing::RemoveSuffix(tree->ring(), n);
+    } else {
+      CordRep* newrep = RemoveSuffixFrom(tree, n);
+      CordRep::Unref(tree);
+      tree = VerifyTree(newrep);
+    }
+    contents_.SetTreeOrEmpty(tree, scope);
   }
 }
 
@@ -974,17 +1007,20 @@
   size_t length = size();
   if (pos > length) pos = length;
   if (new_size > length - pos) new_size = length - pos;
+  if (new_size == 0) return sub_cord;
+
   CordRep* tree = contents_.tree();
   if (tree == nullptr) {
     // sub_cord is newly constructed, no need to re-zero-out the tail of
     // contents_ memory.
     sub_cord.contents_.set_data(contents_.data() + pos, new_size, false);
-  } else if (new_size == 0) {
-    // We want to return empty subcord, so nothing to do.
-  } else if (new_size <= InlineRep::kMaxInline) {
+    return sub_cord;
+  }
+
+  if (new_size <= InlineRep::kMaxInline) {
+    char* dest = sub_cord.contents_.data_.as_chars();
     Cord::ChunkIterator it = chunk_begin();
     it.AdvanceBytes(pos);
-    char* dest = sub_cord.contents_.data_.as_chars();
     size_t remaining_size = new_size;
     while (remaining_size > it->size()) {
       cord_internal::SmallMemmove(dest, it->data(), it->size());
@@ -994,12 +1030,17 @@
     }
     cord_internal::SmallMemmove(dest, it->data(), remaining_size);
     sub_cord.contents_.set_inline_size(new_size);
-  } else if (tree->tag == RING) {
-    tree = CordRepRing::SubRing(CordRep::Ref(tree)->ring(), pos, new_size);
-    sub_cord.contents_.set_tree(tree);
-  } else {
-    sub_cord.contents_.set_tree(NewSubRange(tree, pos, new_size));
+    return sub_cord;
   }
+
+  if (tree->tag == RING) {
+    CordRepRing* ring = CordRep::Ref(tree)->ring();
+    tree = CordRepRing::SubRing(ring, pos, new_size);
+  } else {
+    tree = NewSubRange(tree, pos, new_size);
+  }
+  sub_cord.contents_.EmplaceTree(tree, contents_.data_,
+                                 CordzUpdateTracker::kSubCord);
   return sub_cord;
 }
 
@@ -1441,6 +1482,7 @@
   ABSL_HARDENING_ASSERT(bytes_remaining_ >= n &&
                         "Attempted to iterate past `end()`");
   Cord subcord;
+  auto constexpr method = CordzUpdateTracker::kCordReader;
 
   if (n <= InlineRep::kMaxInline) {
     // Range to read fits in inline data. Flatten it.
@@ -1463,11 +1505,12 @@
   if (ring_reader_) {
     size_t chunk_size = current_chunk_.size();
     if (n <= chunk_size && n <= kMaxBytesToCopy) {
-      subcord = Cord(current_chunk_.substr(0, n));
+      subcord = Cord(current_chunk_.substr(0, n), method);
     } else {
       auto* ring = CordRep::Ref(ring_reader_.ring())->ring();
       size_t offset = ring_reader_.length() - bytes_remaining_;
-      subcord.contents_.set_tree(CordRepRing::SubRing(ring, offset, n));
+      CordRep* rep = CordRepRing::SubRing(ring, offset, n);
+      subcord.contents_.EmplaceTree(rep, method);
     }
     if (n < chunk_size) {
       bytes_remaining_ -= n;
@@ -1486,7 +1529,7 @@
     const char* data = subnode->tag == EXTERNAL ? subnode->external()->base
                                                 : subnode->flat()->Data();
     subnode = NewSubstring(subnode, current_chunk_.data() - data, n);
-    subcord.contents_.set_tree(VerifyTree(subnode));
+    subcord.contents_.EmplaceTree(VerifyTree(subnode), method);
     RemoveChunkPrefix(n);
     return subcord;
   }
@@ -1529,7 +1572,7 @@
   if (node == nullptr) {
     // We have reached the end of the Cord.
     assert(bytes_remaining_ == 0);
-    subcord.contents_.set_tree(VerifyTree(subnode));
+    subcord.contents_.EmplaceTree(VerifyTree(subnode), method);
     return subcord;
   }
 
@@ -1569,7 +1612,7 @@
   current_chunk_ = absl::string_view(data + offset + n, length - n);
   current_leaf_ = node;
   bytes_remaining_ -= n;
-  subcord.contents_.set_tree(VerifyTree(subnode));
+  subcord.contents_.EmplaceTree(VerifyTree(subnode), method);
   return subcord;
 }
 
@@ -1676,6 +1719,7 @@
 }
 
 absl::string_view Cord::FlattenSlowPath() {
+  assert(contents_.is_tree());
   size_t total_size = size();
   CordRep* new_rep;
   char* new_buffer;
@@ -1696,10 +1740,9 @@
                                             s.size());
         });
   }
-  if (CordRep* tree = contents_.tree()) {
-    CordRep::Unref(tree);
-  }
-  contents_.set_tree(new_rep);
+  CordzUpdateScope scope(contents_.cordz_info(), CordzUpdateTracker::kFlatten);
+  CordRep::Unref(contents_.as_tree());
+  contents_.SetTree(new_rep, scope);
   return absl::string_view(new_buffer, total_size);
 }
 
diff --git a/third_party/abseil-cpp/absl/strings/cord.h b/third_party/abseil-cpp/absl/strings/cord.h
index d5a13b34..8abc474 100644
--- a/third_party/abseil-cpp/absl/strings/cord.h
+++ b/third_party/abseil-cpp/absl/strings/cord.h
@@ -678,6 +678,10 @@
   using InlineData = cord_internal::InlineData;
   using MethodIdentifier = CordzUpdateTracker::MethodIdentifier;
 
+  // Creates a cord instance with `method` representing the originating
+  // public API call causing the cord to be created.
+  explicit Cord(absl::string_view src, MethodIdentifier method);
+
   friend class CordTestPeer;
   friend bool operator==(const Cord& lhs, const Cord& rhs);
   friend bool operator==(const Cord& lhs, absl::string_view rhs);
@@ -721,11 +725,6 @@
     // Returns nullptr if holding bytes
     absl::cord_internal::CordRep* tree() const;
     absl::cord_internal::CordRep* as_tree() const;
-    // Discards old pointer, if any
-    void set_tree(absl::cord_internal::CordRep* rep);
-    // Replaces a tree with a new root. This is faster than set_tree, but it
-    // should only be used when it's clear that the old rep was a tree.
-    void replace_tree(absl::cord_internal::CordRep* rep);
     // Returns non-null iff was holding a pointer
     absl::cord_internal::CordRep* clear();
     // Converts to pointer if necessary.
@@ -745,12 +744,21 @@
     // the CordzInfo instance is updated to reference the new `rep` value.
     void SetTree(CordRep* rep, const CordzUpdateScope& scope);
 
+    // Identical to SetTree(), except that `rep` is allowed to be null, in
+    // which case the current instance is reset to an empty value.
+    void SetTreeOrEmpty(CordRep* rep, const CordzUpdateScope& scope);
+
     // Sets the tree value for this instance, and randomly samples this cord.
     // This function disregards existing contents in `data_`, and should be
     // called when a Cord is 'promoted' from an 'uninitialized' or 'inlined'
     // value to a non-inlined (tree / ring) value.
     void EmplaceTree(CordRep* rep, MethodIdentifier method);
 
+    // Identical to EmplaceTree, except that it copies the parent stack from
+    // the provided `parent` data if the parent is sampled.
+    void EmplaceTree(CordRep* rep, const InlineData& parent,
+                     MethodIdentifier method);
+
     // Commits the change of a newly created, or updated `rep` root value into
     // this cord. `old_rep` indicates the old (inlined or tree) value of the
     // cord, and determines if the commit invokes SetTree() or EmplaceTree().
@@ -818,11 +826,6 @@
     // Resets the current cordz_info to null / empty.
     void clear_cordz_info() { data_.clear_cordz_info(); }
 
-    // Updates the cordz statistics. info may be nullptr if the CordzInfo object
-    // is unknown.
-    void UpdateCordzStatistics();
-    void UpdateCordzStatisticsSlow();
-
    private:
     friend class Cord;
 
@@ -879,6 +882,10 @@
   template <typename C>
   void AppendImpl(C&& src);
 
+  // Assigns the value in 'src' to this instance, 'stealing' its contents.
+  // Requires src.length() > kMaxBytesToCopy.
+  Cord& AssignLargeString(std::string&& src);
+
   // Helper for AbslHashValue().
   template <typename H>
   H HashFragmented(H hash_state) const {
@@ -981,8 +988,11 @@
 template <typename Releaser>
 Cord MakeCordFromExternal(absl::string_view data, Releaser&& releaser) {
   Cord cord;
-  cord.contents_.set_tree(::absl::cord_internal::NewExternalRep(
-      data, std::forward<Releaser>(releaser)));
+  if (auto* rep = ::absl::cord_internal::NewExternalRep(
+          data, std::forward<Releaser>(releaser))) {
+    cord.contents_.EmplaceTree(rep,
+                               Cord::MethodIdentifier::kMakeCordFromExternal);
+  }
   return cord;
 }
 
@@ -1071,6 +1081,12 @@
   CordzInfo::MaybeTrackCord(data_, method);
 }
 
+inline void Cord::InlineRep::EmplaceTree(CordRep* rep, const InlineData& parent,
+                                         MethodIdentifier method) {
+  data_.make_tree(rep);
+  CordzInfo::MaybeTrackCord(data_, parent, method);
+}
+
 inline void Cord::InlineRep::SetTree(CordRep* rep,
                                      const CordzUpdateScope& scope) {
   assert(rep);
@@ -1079,6 +1095,17 @@
   scope.SetCordRep(rep);
 }
 
+inline void Cord::InlineRep::SetTreeOrEmpty(CordRep* rep,
+                                            const CordzUpdateScope& scope) {
+  assert(data_.is_tree());
+  if (rep) {
+    data_.set_tree(rep);
+  } else {
+    data_ = {};
+  }
+  scope.SetCordRep(rep);
+}
+
 inline void Cord::InlineRep::CommitTree(const CordRep* old_rep, CordRep* rep,
                                         const CordzUpdateScope& scope,
                                         MethodIdentifier method) {
@@ -1089,36 +1116,6 @@
   }
 }
 
-inline void Cord::InlineRep::set_tree(absl::cord_internal::CordRep* rep) {
-  if (rep == nullptr) {
-    if (data_.is_tree()) {
-      CordzInfo::MaybeUntrackCord(data_.cordz_info());
-    }
-    ResetToEmpty();
-  } else {
-    if (data_.is_tree()) {
-      // `data_` already holds a 'tree' value and an optional cordz_info value.
-      // Replace the tree value only, leaving the cordz_info value unchanged.
-      data_.set_tree(rep);
-    } else {
-      // `data_` contains inlined data: initialize data_ to tree value `rep`.
-      data_.make_tree(rep);
-      CordzInfo::MaybeTrackCord(data_, CordzUpdateTracker::kUnknown);
-    }
-    UpdateCordzStatistics();
-  }
-}
-
-inline void Cord::InlineRep::replace_tree(absl::cord_internal::CordRep* rep) {
-  ABSL_ASSERT(is_tree());
-  if (ABSL_PREDICT_FALSE(rep == nullptr)) {
-    set_tree(rep);
-    return;
-  }
-  data_.set_tree(rep);
-  UpdateCordzStatistics();
-}
-
 inline absl::cord_internal::CordRep* Cord::InlineRep::clear() {
   if (is_tree()) {
     CordzInfo::MaybeUntrackCord(cordz_info());
@@ -1135,13 +1132,11 @@
   cord_internal::SmallMemmove(dst, data_.as_chars(), n);
 }
 
-inline void Cord::InlineRep::UpdateCordzStatistics() {
-  if (ABSL_PREDICT_TRUE(!is_profiled())) return;
-  UpdateCordzStatisticsSlow();
-}
-
 constexpr inline Cord::Cord() noexcept {}
 
+inline Cord::Cord(absl::string_view src)
+    : Cord(src, CordzUpdateTracker::kConstructorString) {}
+
 template <typename T>
 constexpr Cord::Cord(strings_internal::StringConstant<T>)
     : contents_(strings_internal::StringConstant<T>::value.size() <=
@@ -1157,6 +1152,15 @@
   return *this;
 }
 
+template <typename T, Cord::EnableIfString<T>>
+Cord& Cord::operator=(T&& src) {
+  if (src.size() <= cord_internal::kMaxBytesToCopy) {
+    return operator=(absl::string_view(src));
+  } else {
+    return AssignLargeString(std::forward<T>(src));
+  }
+}
+
 inline Cord::Cord(const Cord& src) : contents_(src.contents_) {}
 
 inline Cord::Cord(Cord&& src) noexcept : contents_(std::move(src.contents_)) {}
@@ -1171,7 +1175,6 @@
 }
 
 extern template Cord::Cord(std::string&& src);
-extern template Cord& Cord::operator=(std::string&& src);
 
 inline size_t Cord::size() const {
   // Length is 1st field in str.rep_
diff --git a/third_party/abseil-cpp/absl/strings/cord_ring_test.cc b/third_party/abseil-cpp/absl/strings/cord_ring_test.cc
index 2943cf3..cc8fbaf 100644
--- a/third_party/abseil-cpp/absl/strings/cord_ring_test.cc
+++ b/third_party/abseil-cpp/absl/strings/cord_ring_test.cc
@@ -98,15 +98,22 @@
 // Matcher validating when mutable copies are required / performed.
 MATCHER_P2(EqIfPrivate, param, rep,
            absl::StrCat("Equal 0x", absl::Hex(rep), " if private")) {
-  return param.refcount_is_one ? arg == rep : arg != rep;
+  return param.refcount_is_one ? arg == rep : true;
 }
 
 // Matcher validating when mutable copies are required / performed.
 MATCHER_P2(EqIfPrivateAndCapacity, param, rep,
            absl::StrCat("Equal 0x", absl::Hex(rep),
                         " if private and capacity")) {
-  return (param.refcount_is_one && param.with_capacity) ? arg == rep
-                                                        : arg != rep;
+  return (param.refcount_is_one && param.with_capacity) ? arg == rep : true;
+}
+
+// Matcher validating a shared ring was re-allocated. Should only be used for
+// tests doing exactly one update as subsequent updates could return the
+// original (freed and re-used) pointer.
+MATCHER_P2(NeIfShared, param, rep,
+           absl::StrCat("Not equal 0x", absl::Hex(rep), " if shared")) {
+  return param.refcount_is_one ? true : arg != rep;
 }
 
 MATCHER_P2(EqIfInputPrivate, param, rep, "Equal if input is private") {
@@ -518,6 +525,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Create(ring));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
 }
 
@@ -655,6 +663,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, MakeFlat(str2)));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
 }
@@ -666,6 +675,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, MakeFlat(str2)));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1));
 }
@@ -677,6 +687,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(ToFlats(result), ElementsAre(str1, str2));
 }
@@ -689,6 +700,7 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
 }
 
 TEST_P(CordRingBuildTest, AppendStringHavingPartialExtra) {
@@ -710,6 +722,7 @@
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result->length, Eq(str1.size() + str2.size()));
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   if (GetParam().refcount_is_one) {
     EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str1, str1a), str2a));
   } else {
@@ -725,6 +738,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(4 + str2.size()));
   if (GetParam().refcount_is_one) {
     EXPECT_THAT(ToFlats(result), ElementsAre(StrCat("1234", str2)));
@@ -758,6 +772,7 @@
     CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, str2));
     ASSERT_THAT(result, IsValidRingBuffer());
     EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+    EXPECT_THAT(result, NeIfShared(GetParam(), ring));
     EXPECT_THAT(result->length, Eq(4 + str2.size()));
     EXPECT_THAT(ToFlats(result), ElementsAre("1234", str2));
 
@@ -802,6 +817,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, str2));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(result->length, Eq(4 + str2.size()));
   if (GetParam().refcount_is_one) {
     EXPECT_THAT(ToFlats(result), ElementsAre(StrCat(str2, "1234")));
@@ -833,6 +849,7 @@
     ASSERT_THAT(result, IsValidRingBuffer());
     EXPECT_THAT(result->length, Eq(str1a.size() + str2.size()));
     EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+    EXPECT_THAT(result, NeIfShared(GetParam(), ring));
     EXPECT_THAT(ToFlats(result), ElementsAre(str2, str1a));
     CordRep::Unref(shared_type == 1 ? flat1 : flat);
   }
@@ -920,6 +937,7 @@
       result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
       ASSERT_THAT(result, IsValidRingBuffer());
       ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+      ASSERT_THAT(result, NeIfShared(GetParam(), ring));
       ASSERT_THAT(ToString(result), Eq(all.substr(offset, len)));
     }
   }
@@ -945,6 +963,7 @@
       result = NeedsUnref(CordRepRing::SubRing(ring, offset, len));
       ASSERT_THAT(result, IsValidRingBuffer());
       ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+      ASSERT_THAT(result, NeIfShared(GetParam(), ring));
       auto str = ToString(result);
       ASSERT_THAT(str, SizeIs(len));
       ASSERT_THAT(str, Eq(all.substr(offset, len)));
@@ -966,6 +985,7 @@
     result = NeedsUnref(CordRepRing::RemovePrefix(ring, len));
     ASSERT_THAT(result, IsValidRingBuffer());
     EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+    ASSERT_THAT(result, NeIfShared(GetParam(), ring));
     EXPECT_THAT(ToString(result), Eq(all.substr(len)));
   }
 }
@@ -996,8 +1016,9 @@
     ring = RefIfShared(FromFlats(flats, composition));
     result = NeedsUnref(CordRepRing::RemoveSuffix(ring, len));
     ASSERT_THAT(result, IsValidRingBuffer());
-    EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
-    EXPECT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
+    ASSERT_THAT(result, EqIfPrivate(GetParam(), ring));
+    ASSERT_THAT(result, NeIfShared(GetParam(), ring));
+    ASSERT_THAT(ToString(result), Eq(all.substr(0, all.size() - len)));
   }
 }
 
@@ -1010,6 +1031,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, child));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivate(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
 }
 
@@ -1023,6 +1045,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "brown ", "fox ", "jumps ",
                                            "over ", "the ", "lazy ", "dog"));
 }
@@ -1037,6 +1060,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("Head", "umps ", "over ", "the ", "lazy ", "dog"));
 }
@@ -1051,6 +1075,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
                                            "fox ", "jumps ", "over ", "the "));
 }
@@ -1065,6 +1090,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "The ", "quick ", "brown ",
                                            "fox ", "jumps ", "ov"));
 }
@@ -1079,6 +1105,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("Head", "ck ", "brown ", "fox ", "jum"));
 }
@@ -1093,6 +1120,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Head", "row"));
 }
 
@@ -1110,6 +1138,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Append(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("Prepend", "Head", "row"));
 }
 
@@ -1123,6 +1152,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, child));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAreArray(kFoxFlats));
 }
 
@@ -1136,6 +1166,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("brown ", "fox ", "jumps ", "over ",
                                            "the ", "lazy ", "dog", "Tail"));
 }
@@ -1149,6 +1180,7 @@
   CordRep* stripped = RefIfInputSharedIndirect(RemovePrefix(21, child));
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("umps ", "over ", "the ", "lazy ", "dog", "Tail"));
 }
@@ -1163,6 +1195,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
                                            "jumps ", "over ", "the ", "Tail"));
 }
@@ -1177,6 +1210,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("The ", "quick ", "brown ", "fox ",
                                            "jumps ", "ov", "Tail"));
 }
@@ -1192,6 +1226,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result),
               ElementsAre("ck ", "brown ", "fox ", "jum", "Tail"));
 }
@@ -1206,6 +1241,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("row", "Tail"));
 }
 
@@ -1222,6 +1258,7 @@
   CordRepRing* result = NeedsUnref(CordRepRing::Prepend(ring, stripped));
   ASSERT_THAT(result, IsValidRingBuffer());
   EXPECT_THAT(result, EqIfPrivateAndCapacity(GetParam(), ring));
+  EXPECT_THAT(result, NeIfShared(GetParam(), ring));
   EXPECT_THAT(ToFlats(result), ElementsAre("row", "Prepend", "Tail"));
 }
 
diff --git a/third_party/abseil-cpp/absl/strings/cord_test.cc b/third_party/abseil-cpp/absl/strings/cord_test.cc
index 74a9086..14eca15 100644
--- a/third_party/abseil-cpp/absl/strings/cord_test.cc
+++ b/third_party/abseil-cpp/absl/strings/cord_test.cc
@@ -190,14 +190,15 @@
   }
 
   static Cord MakeSubstring(Cord src, size_t offset, size_t length) {
-    Cord cord = src;
-    ABSL_RAW_CHECK(cord.contents_.is_tree(), "Can not be inlined");
+    ABSL_RAW_CHECK(src.contents_.is_tree(), "Can not be inlined");
+    Cord cord;
     auto* rep = new cord_internal::CordRepSubstring;
     rep->tag = cord_internal::SUBSTRING;
-    rep->child = cord.contents_.tree();
+    rep->child = cord_internal::CordRep::Ref(src.contents_.tree());
     rep->start = offset;
     rep->length = length;
-    cord.contents_.replace_tree(rep);
+    cord.contents_.EmplaceTree(rep,
+                               cord_internal::CordzUpdateTracker::kSubCord);
     return cord;
   }
 };
diff --git a/third_party/abseil-cpp/absl/strings/cord_test_helpers.h b/third_party/abseil-cpp/absl/strings/cord_test_helpers.h
index 6ccccc51..31a1dc8 100644
--- a/third_party/abseil-cpp/absl/strings/cord_test_helpers.h
+++ b/third_party/abseil-cpp/absl/strings/cord_test_helpers.h
@@ -19,7 +19,9 @@
 
 #include <cstdint>
 #include <iostream>
+#include <string>
 
+#include "absl/base/config.h"
 #include "absl/strings/cord.h"
 #include "absl/strings/internal/cord_internal.h"
 #include "absl/strings/string_view.h"
@@ -29,10 +31,27 @@
 
 // Cord sizes relevant for testing
 enum class TestCordSize {
+  // An empty value
   kEmpty = 0,
+
+  // An inlined string value
   kInlined = cord_internal::kMaxInline / 2 + 1,
+
+  // 'Well known' SSO lengths (excluding terminating zero).
+  // libstdcxx has a maximum SSO of 15, libc++ has a maximum SSO of 22.
+  kStringSso1 = 15,
+  kStringSso2 = 22,
+
+  // A string value which is too large to fit in inlined data, but small enough
+  // such that Cord prefers copying the value if possible, i.e.: not stealing
+  // std::string inputs, or referencing existing CordReps on Append, etc.
   kSmall = cord_internal::kMaxBytesToCopy / 2 + 1,
+
+  // A string value large enough that Cord prefers to reference or steal from
+  // existing inputs rather than copying contents of the input.
   kMedium = cord_internal::kMaxFlatLength / 2 + 1,
+
+  // A string value large enough to cause it to be stored in mutliple flats.
   kLarge = cord_internal::kMaxFlatLength * 4
 };
 
@@ -45,6 +64,10 @@
       return "Inlined";
     case TestCordSize::kSmall:
       return "Small";
+    case TestCordSize::kStringSso1:
+      return "StringSso1";
+    case TestCordSize::kStringSso2:
+      return "StringSso2";
     case TestCordSize::kMedium:
       return "Medium";
     case TestCordSize::kLarge:
diff --git a/third_party/abseil-cpp/absl/strings/cordz_test.cc b/third_party/abseil-cpp/absl/strings/cordz_test.cc
index b16e968..aeb3d13 100644
--- a/third_party/abseil-cpp/absl/strings/cordz_test.cc
+++ b/third_party/abseil-cpp/absl/strings/cordz_test.cc
@@ -34,6 +34,7 @@
 #ifdef ABSL_INTERNAL_CORDZ_ENABLED
 
 using testing::Eq;
+using testing::AnyOf;
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -52,6 +53,8 @@
 
 namespace {
 
+auto constexpr kMaxInline = cord_internal::kMaxInline;
+
 // Returns a string_view value of the specified length
 // We do this to avoid 'consuming' large strings in Cord by default.
 absl::string_view MakeString(size_t size) {
@@ -82,24 +85,50 @@
   Cord cord_{MakeString(GetParam())};
 };
 
+template <typename T>
+std::string ParamToString(::testing::TestParamInfo<T> param) {
+  return std::string(ToString(param.param));
+}
+
 INSTANTIATE_TEST_SUITE_P(WithParam, CordzUpdateTest,
                          testing::Values(TestCordSize::kEmpty,
                                          TestCordSize::kInlined,
                                          TestCordSize::kLarge),
                          TestParamToString);
 
-TEST(CordzTest, ConstructSmallString) {
+class CordzStringTest : public testing::TestWithParam<TestCordSize> {
+ private:
+  CordzSamplingIntervalHelper sample_every_{1};
+};
+
+INSTANTIATE_TEST_SUITE_P(WithParam, CordzStringTest,
+                         testing::Values(TestCordSize::kInlined,
+                                         TestCordSize::kStringSso1,
+                                         TestCordSize::kStringSso2,
+                                         TestCordSize::kSmall,
+                                         TestCordSize::kLarge),
+                         ParamToString<TestCordSize>);
+
+TEST(CordzTest, ConstructSmallArray) {
   CordzSamplingIntervalHelper sample_every{1};
   Cord cord(MakeString(TestCordSize::kSmall));
   EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
 }
 
-TEST(CordzTest, ConstructLargeString) {
+TEST(CordzTest, ConstructLargeArray) {
   CordzSamplingIntervalHelper sample_every{1};
   Cord cord(MakeString(TestCordSize::kLarge));
   EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
 }
 
+TEST_P(CordzStringTest, ConstructString) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord(std::string(Length(GetParam()), '.'));
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  }
+}
+
 TEST(CordzTest, CopyConstruct) {
   CordzSamplingIntervalHelper sample_every{1};
   Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
@@ -139,6 +168,38 @@
   EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
 }
 
+TEST_P(CordzUpdateTest, AssignSmallArray) {
+  cord() = MakeString(TestCordSize::kSmall);
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(Method::kAssignString));
+}
+
+TEST_P(CordzUpdateTest, AssignInlinedArray) {
+  cord() = MakeString(TestCordSize::kInlined);
+  EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr));
+}
+
+TEST_P(CordzStringTest, AssignStringToInlined) {
+  Cord cord;
+  cord = std::string(Length(GetParam()), '.');
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAssignString));
+  }
+}
+
+TEST_P(CordzStringTest, AssignStringToCord) {
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord = std::string(Length(GetParam()), '.');
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+    EXPECT_THAT(cord, CordzMethodCountEq(Method::kAssignString, 1));
+  }
+}
+
+TEST_P(CordzUpdateTest, AssignInlinedString) {
+  cord() = std::string(Length(TestCordSize::kInlined), '.');
+  EXPECT_THAT(GetCordzInfoForTesting(cord()), Eq(nullptr));
+}
+
 TEST_P(CordzUpdateTest, AppendCord) {
   Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
   cord().Append(src);
@@ -150,12 +211,6 @@
   EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendCord)));
 }
 
-TEST_P(CordzUpdateTest, PrependCord) {
-  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
-  cord().Prepend(src);
-  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord)));
-}
-
 TEST_P(CordzUpdateTest, AppendSmallArray) {
   cord().Append(MakeString(TestCordSize::kSmall));
   EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString)));
@@ -166,6 +221,130 @@
   EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kAppendString)));
 }
 
+TEST_P(CordzStringTest, AppendStringToEmpty) {
+  Cord cord;
+  cord.Append(std::string(Length(GetParam()), '.'));
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString));
+  }
+}
+
+TEST_P(CordzStringTest, AppendStringToInlined) {
+  Cord cord(MakeString(TestCordSize::kInlined));
+  cord.Append(std::string(Length(GetParam()), '.'));
+  if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kAppendString));
+  }
+}
+
+TEST_P(CordzStringTest, AppendStringToCord) {
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord.Append(std::string(Length(GetParam()), '.'));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kAppendString, 1));
+}
+
+TEST(CordzTest, MakeCordFromExternal) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord = MakeCordFromExternal("Hello world", [](absl::string_view) {});
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kMakeCordFromExternal));
+}
+
+TEST(CordzTest, MakeCordFromEmptyExternal) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord cord = MakeCordFromExternal({}, [](absl::string_view) {});
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST_P(CordzUpdateTest, PrependCord) {
+  Cord src = UnsampledCord(MakeString(TestCordSize::kLarge));
+  cord().Prepend(src);
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependCord)));
+}
+
+TEST_P(CordzUpdateTest, PrependSmallArray) {
+  cord().Prepend(MakeString(TestCordSize::kSmall));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString)));
+}
+
+TEST_P(CordzUpdateTest, PrependLargeArray) {
+  cord().Prepend(MakeString(TestCordSize::kLarge));
+  EXPECT_THAT(cord(), HasValidCordzInfoOf(InitialOr(Method::kPrependString)));
+}
+
+TEST_P(CordzStringTest, PrependStringToEmpty) {
+  Cord cord;
+  cord.Prepend(std::string(Length(GetParam()), '.'));
+  if (Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString));
+  }
+}
+
+TEST_P(CordzStringTest, PrependStringToInlined) {
+  Cord cord(MakeString(TestCordSize::kInlined));
+  cord.Prepend(std::string(Length(GetParam()), '.'));
+  if (Length(TestCordSize::kInlined) + Length(GetParam()) > kMaxInline) {
+    EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kPrependString));
+  }
+}
+
+TEST_P(CordzStringTest, PrependStringToCord) {
+  Cord cord(MakeString(TestCordSize::kLarge));
+  cord.Prepend(std::string(Length(GetParam()), '.'));
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kPrependString, 1));
+}
+
+TEST(CordzTest, RemovePrefix) {
+  CordzSamplingIntervalHelper sample_every(1);
+  Cord cord(MakeString(TestCordSize::kLarge));
+
+  // Half the cord
+  cord.RemovePrefix(cord.size() / 2);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 1));
+
+  // TODO(mvels): RemovePrefix does not reset to inlined, except if empty?
+  cord.RemovePrefix(cord.size() - kMaxInline);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemovePrefix, 2));
+
+  cord.RemovePrefix(cord.size());
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST(CordzTest, RemoveSuffix) {
+  CordzSamplingIntervalHelper sample_every(1);
+  Cord cord(MakeString(TestCordSize::kLarge));
+
+  // Half the cord
+  cord.RemoveSuffix(cord.size() / 2);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 1));
+
+  // TODO(mvels): RemoveSuffix does not reset to inlined, except if empty?
+  cord.RemoveSuffix(cord.size() - kMaxInline);
+  EXPECT_THAT(cord, HasValidCordzInfoOf(Method::kConstructorString));
+  EXPECT_THAT(cord, CordzMethodCountEq(Method::kRemoveSuffix, 2));
+
+  cord.RemoveSuffix(cord.size());
+  EXPECT_THAT(GetCordzInfoForTesting(cord), Eq(nullptr));
+}
+
+TEST(CordzTest, SubCord) {
+  CordzSamplingIntervalHelper sample_every{1};
+  Cord src(MakeString(TestCordSize::kLarge));
+
+  Cord cord1 = src.Subcord(10, src.size() / 2);
+  EXPECT_THAT(cord1, HasValidCordzInfoOf(Method::kSubCord));
+
+  Cord cord2 = src.Subcord(10, kMaxInline + 1);
+  EXPECT_THAT(cord2, HasValidCordzInfoOf(Method::kSubCord));
+
+  Cord cord3 = src.Subcord(10, kMaxInline);
+  EXPECT_THAT(GetCordzInfoForTesting(cord3), Eq(nullptr));
+}
+
 }  // namespace
 
 ABSL_NAMESPACE_END
diff --git a/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h b/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
index d9573c9..e410eecf 100644
--- a/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
+++ b/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
@@ -27,6 +27,7 @@
 #include "absl/strings/internal/cordz_sample_token.h"
 #include "absl/strings/internal/cordz_statistics.h"
 #include "absl/strings/internal/cordz_update_tracker.h"
+#include "absl/strings/str_cat.h"
 
 namespace absl {
 ABSL_NAMESPACE_BEGIN
@@ -34,7 +35,7 @@
 // Returns the CordzInfo for the cord, or nullptr if the cord is not sampled.
 inline const cord_internal::CordzInfo* GetCordzInfoForTesting(
     const Cord& cord) {
-  if (cord.size() <= cord_internal::kMaxInline) return nullptr;
+  if (!cord.contents_.is_tree()) return nullptr;
   return cord.contents_.cordz_info();
 }
 
@@ -47,13 +48,11 @@
   return false;
 }
 
-// Matcher on Cord* that verifies all of:
+// Matcher on Cord that verifies all of:
 // - the cord is sampled
 // - the CordzInfo of the cord is listed / discoverable.
 // - the reported CordzStatistics match the cord's actual properties
 // - the cord has an (initial) UpdateTracker count of 1 for `method`
-// This matcher accepts a const Cord* to avoid having the matcher dump
-// copious amounts of cord data on failures.
 MATCHER_P(HasValidCordzInfoOf, method, "CordzInfo matches cord") {
   const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg);
   if (cord_info == nullptr) {
@@ -78,6 +77,24 @@
   return true;
 }
 
+// Matcher on Cord that verifies that the cord is sampled and that the CordzInfo
+// update tracker has 'method' with a call count of 'n'
+MATCHER_P2(CordzMethodCountEq, method, n,
+           absl::StrCat("CordzInfo method count equals ", n)) {
+  const cord_internal::CordzInfo* cord_info = GetCordzInfoForTesting(arg);
+  if (cord_info == nullptr) {
+    *result_listener << "cord is not sampled";
+    return false;
+  }
+  cord_internal::CordzStatistics stat = cord_info->GetCordzStatistics();
+  if (stat.update_tracker.Value(method) != n) {
+    *result_listener << "Expected method count " << n << " for " << method
+                     << ", found " << stat.update_tracker.Value(method);
+    return false;
+  }
+  return true;
+}
+
 // Cordz will only update with a new rate once the previously scheduled event
 // has fired. When we disable Cordz, a long delay takes place where we won't
 // consider profiling new Cords. CordzSampleIntervalHelper will burn through
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc
index 5297ec8..a73fefed 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.cc
@@ -68,11 +68,16 @@
   }
 }
 
+bool CordzHandle::SafeToDelete() const {
+  return is_snapshot_ || queue_->IsEmpty();
+}
+
 void CordzHandle::Delete(CordzHandle* handle) {
+  assert(handle);
   if (handle) {
     handle->ODRCheck();
     Queue* const queue = handle->queue_;
-    if (!handle->is_snapshot_ && !queue->IsEmpty()) {
+    if (!handle->SafeToDelete()) {
       SpinLockHolder lock(&queue->mutex);
       CordzHandle* dq_tail = queue->dq_tail.load(std::memory_order_acquire);
       if (dq_tail != nullptr) {
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h
index 93076cf..5df53c7 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_handle.h
@@ -40,9 +40,20 @@
 
   bool is_snapshot() const { return is_snapshot_; }
 
+  // Returns true if this instance is safe to be deleted because it is either a
+  // snapshot, which is always safe to delete, or not included in the global
+  // delete queue and thus not included in any snapshot.
+  // Callers are responsible for making sure this instance can not be newly
+  // discovered by other threads. For example, CordzInfo instances first de-list
+  // themselves from the global CordzInfo list before determining if they are
+  // safe to be deleted directly.
+  // If SafeToDelete returns false, callers MUST use the Delete() method to
+  // safely queue CordzHandle instances for deletion.
+  bool SafeToDelete() const;
+
   // Deletes the provided instance, or puts it on the delete queue to be deleted
   // once there are no more sample tokens (snapshot) instances potentially
-  // referencing the instance. `handle` may be null.
+  // referencing the instance. `handle` should not be null.
   static void Delete(CordzHandle* handle);
 
   // Returns the current entries in the delete queue in LIFO order.
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc
index c04240e..4eefd72 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_handle_test.cc
@@ -52,6 +52,7 @@
   bool deleted = false;
   auto* handle = new CordzHandleDeleteTracker(&deleted);
   EXPECT_FALSE(handle->is_snapshot());
+  EXPECT_TRUE(handle->SafeToDelete());
   EXPECT_THAT(DeleteQueue(), SizeIs(0));
 
   CordzHandle::Delete(handle);
@@ -62,6 +63,7 @@
 TEST(CordzHandleTest, CordzSnapshotCreateDelete) {
   auto* snapshot = new CordzSnapshot();
   EXPECT_TRUE(snapshot->is_snapshot());
+  EXPECT_TRUE(snapshot->SafeToDelete());
   EXPECT_THAT(DeleteQueue(), ElementsAre(snapshot));
   delete snapshot;
   EXPECT_THAT(DeleteQueue(), SizeIs(0));
@@ -71,10 +73,12 @@
   bool deleted = false;
   auto* snapshot = new CordzSnapshot();
   auto* handle = new CordzHandleDeleteTracker(&deleted);
+  EXPECT_FALSE(handle->SafeToDelete());
 
   CordzHandle::Delete(handle);
   EXPECT_THAT(DeleteQueue(), ElementsAre(handle, snapshot));
   EXPECT_FALSE(deleted);
+  EXPECT_FALSE(handle->SafeToDelete());
 
   delete snapshot;
   EXPECT_THAT(DeleteQueue(), SizeIs(0));
@@ -219,8 +223,8 @@
           if (safe_to_inspect.size() > max_safe_to_inspect) {
             max_safe_to_inspect = safe_to_inspect.size();
           }
+          CordzHandle::Delete(old_handle);
         }
-        CordzHandle::Delete(old_handle);
       }
 
       // Confirm that the test did *something*. This check will be satisfied as
@@ -234,9 +238,10 @@
       // Have each thread attempt to clean up everything. Some thread will be
       // the last to reach this cleanup code, and it will be guaranteed to clean
       // up everything because nothing remains to create new handles.
-      for (size_t i = 0; i < handles.size(); i++) {
-        CordzHandle* handle = handles[i].exchange(nullptr);
-        CordzHandle::Delete(handle);
+      for (auto& h : handles) {
+        if (CordzHandle* handle = h.exchange(nullptr)) {
+          CordzHandle::Delete(handle);
+        }
       }
   });
   }
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc
index 0461ec4..0f657aaf 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info.cc
@@ -126,13 +126,6 @@
 }
 
 void CordzInfo::Untrack() {
-  {
-    // TODO(b/117940323): change this to assuming ownership instead once all
-    // Cord logic is properly keeping `rep_` in sync with the Cord root rep.
-    absl::MutexLock lock(&mutex_);
-    rep_ = nullptr;
-  }
-
   ODRCheck();
   {
     SpinLockHolder l(&list_->mutex);
@@ -154,6 +147,20 @@
       list_->head.store(next, std::memory_order_release);
     }
   }
+
+  // We can no longer be discovered: perform a fast path check if we are not
+  // listed on any delete queue, so we can directly delete this instance.
+  if (SafeToDelete()) {
+    UnsafeSetCordRep(nullptr);
+    delete this;
+    return;
+  }
+
+  // We are likely part of a snapshot, extend the life of the CordRep
+  {
+    absl::MutexLock lock(&mutex_);
+    if (rep_) CordRep::Ref(rep_);
+  }
   CordzHandle::Delete(this);
 }
 
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info.h b/third_party/abseil-cpp/absl/strings/internal/cordz_info.h
index f7682cb..1fe046e 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_info.h
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info.h
@@ -110,7 +110,7 @@
   // Asserts that this CordzInfo instance is locked.
   void AssertHeld() ABSL_ASSERT_EXCLUSIVE_LOCK(mutex_);
 
-  // Updates the `rep' property of this instance. This methods is invoked by
+  // Updates the `rep` property of this instance. This methods is invoked by
   // Cord logic each time the root node of a sampled Cord changes, and before
   // the old root reference count is deleted. This guarantees that collection
   // code can always safely take a reference on the tracked cord.
@@ -119,6 +119,11 @@
   // Cord code is in a state where this can be proven true by the compiler.
   void SetCordRep(CordRep* rep);
 
+  // Returns the current `rep` property of this instance with a reference
+  // added, or null if this instance represents a cord that has since been
+  // deleted or untracked.
+  CordRep* RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_);
+
   // Returns the current value of `rep_` for testing purposes only.
   CordRep* GetCordRepForTesting() const ABSL_NO_THREAD_SAFETY_ANALYSIS {
     return rep_;
@@ -148,6 +153,9 @@
   }
 
  private:
+  using SpinLock = absl::base_internal::SpinLock;
+  using SpinLockHolder = ::absl::base_internal::SpinLockHolder;
+
   // Global cordz info list. CordzInfo stores a pointer to the global list
   // instance to harden against ODR violations.
   struct List {
@@ -155,7 +163,7 @@
         : mutex(absl::kConstInit,
                 absl::base_internal::SCHEDULE_COOPERATIVE_AND_KERNEL) {}
 
-    absl::base_internal::SpinLock mutex;
+    SpinLock mutex;
     std::atomic<CordzInfo*> head ABSL_GUARDED_BY(mutex){nullptr};
   };
 
@@ -165,6 +173,9 @@
                      MethodIdentifier method);
   ~CordzInfo() override;
 
+  // Sets `rep_` without holding a lock.
+  void UnsafeSetCordRep(CordRep* rep) ABSL_NO_THREAD_SAFETY_ANALYSIS;
+
   void Track();
 
   // Returns the parent method from `src`, which is either `parent_method_` or
@@ -244,6 +255,13 @@
   }
 }
 
+inline void CordzInfo::UnsafeSetCordRep(CordRep* rep) { rep_ = rep; }
+
+inline CordRep* CordzInfo::RefCordRep() const ABSL_LOCKS_EXCLUDED(mutex_) {
+  MutexLock lock(&mutex_);
+  return rep_ ? CordRep::Ref(rep_) : nullptr;
+}
+
 }  // namespace cord_internal
 ABSL_NAMESPACE_END
 }  // namespace absl
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc
index 5eaf4b9..5bb23e4 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_info_test.cc
@@ -38,6 +38,7 @@
 using ::testing::Eq;
 using ::testing::HasSubstr;
 using ::testing::Ne;
+using ::testing::SizeIs;
 
 // Used test values
 auto constexpr kUnknownMethod = CordzUpdateTracker::kUnknown;
@@ -78,10 +79,19 @@
   CordzInfo::TrackCord(data.data, kTrackCordMethod);
   CordzInfo* info = data.data.cordz_info();
 
+  info->Untrack();
+  EXPECT_THAT(DeleteQueue(), SizeIs(0));
+}
+
+TEST(CordzInfoTest, UntrackCordWithSnapshot) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
   CordzSnapshot snapshot;
   info->Untrack();
   EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr));
-  EXPECT_THAT(info->GetCordRepForTesting(), Eq(nullptr));
+  EXPECT_THAT(info->GetCordRepForTesting(), Eq(data.rep.rep));
   EXPECT_THAT(DeleteQueue(), ElementsAre(info, &snapshot));
 }
 
@@ -113,6 +123,18 @@
   EXPECT_THAT(CordzInfo::Head(CordzSnapshot()), Eq(nullptr));
 }
 
+TEST(CordzInfoTest, RefCordRep) {
+  TestCordData data;
+  CordzInfo::TrackCord(data.data, kTrackCordMethod);
+  CordzInfo* info = data.data.cordz_info();
+
+  size_t refcount = data.rep.rep->refcount.Get();
+  EXPECT_THAT(info->RefCordRep(), Eq(data.rep.rep));
+  EXPECT_THAT(data.rep.rep->refcount.Get(), Eq(refcount + 1));
+  CordRep::Unref(data.rep.rep);
+  info->Untrack();
+}
+
 #if GTEST_HAS_DEATH_TEST
 
 TEST(CordzInfoTest, SetCordRepRequiresMutex) {
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h
index 741950b..d5b14067 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker.h
@@ -47,8 +47,10 @@
     kClear,
     kConstructorCord,
     kConstructorString,
+    kCordReader,
     kFlatten,
     kGetAppendRegion,
+    kMakeCordFromExternal,
     kMoveAppendCord,
     kMoveAssignCord,
     kMovePrependCord,
diff --git a/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc
index eda662f..fcd17df7 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/cordz_update_tracker_test.cc
@@ -45,8 +45,10 @@
                  Method::kClear,
                  Method::kConstructorCord,
                  Method::kConstructorString,
+                 Method::kCordReader,
                  Method::kFlatten,
                  Method::kGetAppendRegion,
+                 Method::kMakeCordFromExternal,
                  Method::kMoveAppendCord,
                  Method::kMoveAssignCord,
                  Method::kMovePrependCord,
diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h
index 7040c86..3c91be70 100644
--- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h
+++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h
@@ -122,6 +122,14 @@
 StringConvertResult FormatConvertImpl(string_view v,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink);
+#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
+inline StringConvertResult FormatConvertImpl(std::string_view v,
+                                             FormatConversionSpecImpl conv,
+                                             FormatSinkImpl* sink) {
+  return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink);
+}
+#endif  // ABSL_HAVE_STD_STRING_VIEW && !ABSL_USES_STD_STRING_VIEW
+
 ArgConvertResult<FormatConversionCharSetUnion(
     FormatConversionCharSetInternal::s, FormatConversionCharSetInternal::p)>
 FormatConvertImpl(const char* v, const FormatConversionSpecImpl conv,
diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc
index 926283c..91e0360 100644
--- a/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc
@@ -229,6 +229,9 @@
   TestStringConvert(static_cast<const char*>("hello"));
   TestStringConvert(std::string("hello"));
   TestStringConvert(string_view("hello"));
+#if defined(ABSL_HAVE_STD_STRING_VIEW)
+  TestStringConvert(std::string_view("hello"));
+#endif  // ABSL_HAVE_STD_STRING_VIEW
 }
 
 TEST_F(FormatConvertTest, NullString) {
diff --git a/third_party/abseil-cpp/symbols_arm64_dbg.def b/third_party/abseil-cpp/symbols_arm64_dbg.def
index a05d35a0..09f0619 100644
--- a/third_party/abseil-cpp/symbols_arm64_dbg.def
+++ b/third_party/abseil-cpp/symbols_arm64_dbg.def
@@ -118,10 +118,6 @@
     ??$?0PEAVZoneInfoSource@cctz@time_internal@absl@@U__default_init_tag@__1@std@@@?$__compressed_pair@PEAVZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAA@$$QEAPEAVZoneInfoSource@cctz@time_internal@absl@@$$QEAU__default_init_tag@12@@Z
     ??$?0PEAVZoneInfoSource@cctz@time_internal@absl@@X@?$__compressed_pair_elem@PEAVZoneInfoSource@cctz@time_internal@absl@@$0A@$0A@@__1@std@@QEAA@$$QEAPEAVZoneInfoSource@cctz@time_internal@absl@@@Z
     ??$?0U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@X@?$__compressed_pair_elem@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@$00$00@__1@std@@QEAA@$$QEAU?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@12@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$$V$00@?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QEAA@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$CompressedTupleImpl@V?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@U?$integer_sequence@_K$0A@@3@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@Uin_place_t@3@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@3@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@QEAA@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@H@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@Uin_place_t@3@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@3@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$?0USynchEvent@absl@@@Condition@absl@@QEAA@P6A_NPEAUSynchEvent@1@@Z0@Z
     ??$?0Uday_tag@detail@cctz@time_internal@absl@@@?$civil_time@Umonth_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@QEAA@AEBV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@1234@PEAX@Z
     ??$?0Uday_tag@detail@cctz@time_internal@absl@@@?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@QEAA@AEBV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@1234@PEAX@Z
@@ -192,7 +188,6 @@
     ??$?0V?$vector@VFormatArgImpl@str_format_internal@absl@@V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__1@std@@@__1@std@@XV012@@?$Span@$$CBVFormatArgImpl@str_format_internal@absl@@@absl@@QEAA@AEBV?$vector@VFormatArgImpl@str_format_internal@absl@@V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__1@std@@@__1@std@@@Z
     ??$?0VBufferRawSink@str_format_internal@absl@@$0A@@FormatRawSinkImpl@str_format_internal@absl@@QEAA@PEAVBufferRawSink@12@@Z
     ??$?0VFILERawSink@str_format_internal@absl@@$0A@@FormatRawSinkImpl@str_format_internal@absl@@QEAA@PEAVFILERawSink@12@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAAAEAV01@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?8PEAPEAVCordzHandle@cord_internal@absl@@PEAPEAV012@@__1@std@@YA_NAEBV?$__wrap_iter@PEAPEAVCordzHandle@cord_internal@absl@@@01@0@Z
     ??$?8PEAUTransitionType@cctz@time_internal@absl@@PEAU0123@@__1@std@@YA_NAEBV?$__wrap_iter@PEAUTransitionType@cctz@time_internal@absl@@@01@0@Z
     ??$?8PEBUConversionItem@ParsedFormatBase@str_format_internal@absl@@PEBU0123@@__1@std@@YA_NAEBV?$__wrap_iter@PEBUConversionItem@ParsedFormatBase@str_format_internal@absl@@@01@0@Z
@@ -358,12 +353,10 @@
     ??$Init@H@FormatArgImpl@str_format_internal@absl@@AEAAXAEBH@Z
     ??$Initialize@V?$CopyValueAdapter@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@@?$Storage@PEAUCordRep@cord_internal@absl@@$0CP@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAAXV?$CopyValueAdapter@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@12@_K@Z
     ??$Invoke@A6AXXZ$$V@Callable@base_internal@absl@@SAXA6AXXZ@Z
-    ??$Invoke@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@3@@Callable@base_internal@absl@@SAX$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@2@@Z
     ??$InvokeFlush@V?$basic_ostream@DU?$char_traits@D@__1@std@@@__1@std@@@str_format_internal@absl@@YAXPEAV?$basic_ostream@DU?$char_traits@D@__1@std@@@__1@std@@Vstring_view@1@@Z
     ??$InvokeFlush@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@str_format_internal@absl@@YAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ??$InvokeFlush@VBufferRawSink@str_format_internal@absl@@@str_format_internal@absl@@YAXPEAVBufferRawSink@01@Vstring_view@1@@Z
     ??$InvokeFlush@VFILERawSink@str_format_internal@absl@@@str_format_internal@absl@@YAXPEAVFILERawSink@01@Vstring_view@1@@Z
-    ??$InvokeReleaser@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@X@cord_internal@absl@@YAXURank0@01@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@Vstring_view@1@@Z
     ??$LowLevelCallOnce@A6AXXZ$$V@base_internal@absl@@YAXPEAVonce_flag@1@A6AXXZ@Z
     ??$MakeConstSpan@$SQEAX@absl@@YA?AV?$Span@QEAX@0@PEBQEAX_K@Z
     ??$MakeSpan@$SI$0BAA@@absl@@YA?AV?$Span@I@0@AEAY0BAA@I@Z
@@ -373,7 +366,6 @@
     ??$MakeSpan@$SI$0IA@@absl@@YA?AV?$Span@I@0@AEAY0IA@I@Z
     ??$MakeSpan@$SVFormatArgImpl@str_format_internal@absl@@@absl@@YA?AV?$Span@VFormatArgImpl@str_format_internal@absl@@@0@PEAVFormatArgImpl@str_format_internal@0@_K@Z
     ??$Milliseconds@N$0A@@absl@@YA?AVDuration@0@N@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPEAUCordRep@01@Vstring_view@1@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@_KPEAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@_K$0A@$00$01@absl@@U45@@internal_layout@container_internal@absl@@QEBA_KXZ
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@_KPEAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@_K$0A@$00@absl@@U?$integer_sequence@_K$0A@$00$01@5@@internal_layout@container_internal@absl@@QEBA_KXZ
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@_KPEAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@_K$0A@@absl@@U?$integer_sequence@_K$0A@$00@5@@internal_layout@container_internal@absl@@QEBA_KXZ
@@ -700,8 +692,6 @@
     ??$forward@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@__1@std@@YA$$QEAU?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@01@AEAU201@@Z
     ??$forward@UConversionItem@ParsedFormatBase@str_format_internal@absl@@@__1@std@@YA$$QEAUConversionItem@ParsedFormatBase@str_format_internal@absl@@AEAU2345@@Z
     ??$forward@UPayload@status_internal@absl@@@__1@std@@YA$$QEAUPayload@status_internal@absl@@AEAU234@@Z
-    ??$forward@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@__1@std@@YA$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@01@@Z@AEAU2?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@34@QEAA@0@Z@@Z
-    ??$forward@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@absl@@YA$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@0@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAU1?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@20@QEAA@0@Z@@Z
     ??$forward@USubRange@absl@@@__1@std@@YA$$QEAUSubRange@absl@@AEAU23@@Z
     ??$forward@UTransition@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransition@cctz@time_internal@absl@@AEAU2345@@Z
     ??$forward@UTransitionType@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransitionType@cctz@time_internal@absl@@AEAU2345@@Z
@@ -734,7 +724,6 @@
     ??$get@$00@?$CompressedTuple@V?$allocator@USubRange@absl@@@__1@std@@_K@container_internal@absl@@QEGBAAEB_KXZ
     ??$get@$00@?$CompressedTuple@_KV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
     ??$get@$00Vstring_view@absl@@V12@@__1@std@@YAAEBVstring_view@absl@@AEBU?$pair@Vstring_view@absl@@V12@@01@@Z
-    ??$get@$0A@@?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QEGAAAEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@H@__1@std@@PEAH@container_internal@absl@@QEGAAAEAV?$allocator@H@__1@std@@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@H@__1@std@@_K@container_internal@absl@@QEGAAAEAV?$allocator@H@__1@std@@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@PEAPEAUCordRep@cord_internal@absl@@@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
@@ -751,7 +740,6 @@
     ??$get@Vstring_view@absl@@V12@@?$__get_pair@$00@__1@std@@SAAEBVstring_view@absl@@AEBU?$pair@Vstring_view@absl@@V12@@12@@Z
     ??$get@Vstring_view@absl@@V12@@?$__get_pair@$0A@@__1@std@@SAAEBVstring_view@absl@@AEBU?$pair@Vstring_view@absl@@V12@@12@@Z
     ??$invoke@A6AXXZ$$V@base_internal@absl@@YAXA6AXXZ@Z
-    ??$invoke@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@3@@base_internal@absl@@YAX$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@1@@Z
     ??$lower_bound@PEBUTransition@cctz@time_internal@absl@@U1234@UByUnixTime@1234@@__1@std@@YAPEBUTransition@cctz@time_internal@absl@@PEBU2345@0AEBU2345@UByUnixTime@2345@@Z
     ??$make_pair@AEAPEAUCordRep@cord_internal@absl@@AEAPEAU123@@__1@std@@YA?AU?$pair@PEAUCordRep@cord_internal@absl@@PEAU123@@01@AEAPEAUCordRep@cord_internal@absl@@0@Z
     ??$make_unique@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@$$V@__1@std@@YA?AV?$unique_ptr@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@01@XZ
@@ -786,7 +774,6 @@
     ??$move@AEAPEBV?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@YA$$QEAPEBV?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@AEAPEBV23@@Z
     ??$move@AEAUConversionItem@ParsedFormatBase@str_format_internal@absl@@@__1@std@@YA$$QEAUConversionItem@ParsedFormatBase@str_format_internal@absl@@AEAU2345@@Z
     ??$move@AEAUPayload@status_internal@absl@@@__1@std@@YA$$QEAUPayload@status_internal@absl@@AEAU234@@Z
-    ??$move@AEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@__1@std@@YA$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@01@@Z@AEAU2?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@34@QEAA@0@Z@@Z
     ??$move@AEAUSubRange@absl@@@__1@std@@YA$$QEAUSubRange@absl@@AEAU23@@Z
     ??$move@AEAUTransition@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransition@cctz@time_internal@absl@@AEAU2345@@Z
     ??$move@AEAUTransitionType@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransitionType@cctz@time_internal@absl@@AEAU2345@@Z
@@ -1028,9 +1015,9 @@
     ??0Condition@absl@@AEAA@XZ
     ??0Condition@absl@@QEAA@P6A_NPEAX@Z0@Z
     ??0Condition@absl@@QEAA@PEB_N@Z
+    ??0Cord@absl@@AEAA@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0Cord@absl@@QEAA@$$QEAV01@@Z
     ??0Cord@absl@@QEAA@AEBV01@@Z
-    ??0Cord@absl@@QEAA@Vstring_view@1@@Z
     ??0Cord@absl@@QEAA@XZ
     ??0CordForest@absl@@QEAA@_K@Z
     ??0CordRep@cord_internal@absl@@QEAA@XZ
@@ -1089,7 +1076,6 @@
     ??0StatusRep@status_internal@absl@@QEAA@W4StatusCode@2@Vstring_view@2@V?$unique_ptr@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@__1@std@@@Z
     ??0Storage@?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@_KAEBV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@Z
     ??0Streamable@str_format_internal@absl@@QEAA@AEBVUntypedFormatSpecImpl@12@V?$Span@$$CBVFormatArgImpl@str_format_internal@absl@@@2@@Z
-    ??0StringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QEAA@$$QEAU0?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@12@QEAA@0@Z@@Z
     ??0SubRange@absl@@QEAA@PEAUCordRep@cord_internal@1@_K1@Z
     ??0SynchWaitParams@absl@@QEAA@PEBUMuHowS@1@PEBVCondition@1@VKernelTimeout@synchronization_internal@1@PEAVMutex@1@PEAUPerThreadSynch@base_internal@1@PEAU?$atomic@_J@__1@std@@@Z
     ??0Time@absl@@AEAA@VDuration@1@@Z
@@ -1147,9 +1133,6 @@
     ??1?$AllocationTransaction@V?$allocator@PEBUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$AllocationTransaction@V?$allocator@UPayload@status_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$AllocationTransaction@V?$allocator@USubRange@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
-    ??1?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QEAA@XZ
-    ??1?$CompressedTupleImpl@V?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@U?$integer_sequence@_K$0A@@3@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@XZ
-    ??1?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@QEAA@XZ
     ??1?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@XZ
     ??1?$InlinedVector@H$0CP@V?$allocator@H@__1@std@@@absl@@QEAA@XZ
     ??1?$InlinedVector@PEAUCordRep@cord_internal@absl@@$01V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@XZ
@@ -1162,7 +1145,6 @@
     ??1?$Storage@PEAUCordRep@cord_internal@absl@@$0CP@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$Storage@PEBUCordRep@cord_internal@absl@@$0CP@V?$allocator@PEBUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$Storage@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
-    ??1?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@XZ
     ??1?$Storage@USubRange@absl@@$0CP@V?$allocator@USubRange@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$__policy_func@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__1@std@@@__1@std@@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@23@@Z@__function@__1@std@@QEAA@XZ
     ??1?$__policy_func@$$A6AXVstring_view@absl@@AEBVCord@2@@Z@__function@__1@std@@QEAA@XZ
@@ -1234,7 +1216,6 @@
     ??1StatusRep@status_internal@absl@@QEAA@XZ
     ??1Storage@?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@XZ
     ??1Streamable@str_format_internal@absl@@QEAA@XZ
-    ??1StringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QEAA@XZ
     ??1TimeZoneIf@cctz@time_internal@absl@@UEAA@XZ
     ??1TimeZoneInfo@cctz@time_internal@absl@@UEAA@XZ
     ??1TimeZoneLibC@cctz@time_internal@absl@@UEAA@XZ
@@ -1452,7 +1433,6 @@
     ??R<lambda_1>@?0??pop_back@?$InlinedVector@USubRange@absl@@$0CP@V?$allocator@USubRange@absl@@@__1@std@@@absl@@QEAAXXZ@QEBA?A?<auto>@@XZ
     ??R<lambda_1>@?0??remove_prefix@string_view@absl@@QEAAX_K@Z@QEBA?A?<auto>@@XZ
     ??R<lambda_1>@?0??remove_suffix@string_view@absl@@QEAAX_K@Z@QEBA?A?<auto>@@XZ
-    ??R<lambda_1>@?0??replace_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@4@@Z@QEBA?A?<auto>@@XZ
     ??R<lambda_1>@?0??set_inline_size@InlineData@cord_internal@absl@@QEAAX_K@Z@QEBA?A?<auto>@@XZ
     ??R<lambda_2>@?0???$AddRing@$00@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@0_K1@Z@QEBA?A?<auto>@@I@Z
     ??R<lambda_2>@?0???$AddRing@$0A@@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@0_K1@Z@QEBA?A?<auto>@@I@Z
@@ -1480,7 +1460,6 @@
     ??R?$function@$$A6AXVstring_view@absl@@AEBVCord@2@@Z@__1@std@@QEBAXVstring_view@absl@@AEBVCord@4@@Z
     ??RByCivilTime@Transition@cctz@time_internal@absl@@QEBA_NAEBU1234@0@Z
     ??RByUnixTime@Transition@cctz@time_internal@absl@@QEBA_NAEBU1234@0@Z
-    ??RStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QEAAXVstring_view@2@@Z
     ??Sabsl@@YA?AVuint128@0@V10@@Z
     ??Tabsl@@YA?AVuint128@0@V10@0@Z
     ??Uabsl@@YA?AVuint128@0@V10@0@Z
@@ -1582,6 +1561,7 @@
     ?AssertHeld@Mutex@absl@@QEBAXXZ
     ?AssertNotHeld@Mutex@absl@@QEBAXXZ
     ?AssertReaderHeld@Mutex@absl@@QEBAXXZ
+    ?AssignLargeString@Cord@absl@@AEAAAEAV12@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignNext@?$IteratorValueAdapter@V?$allocator@UPayload@status_internal@absl@@@__1@std@@V?$move_iterator@PEAUPayload@status_internal@absl@@@23@@inlined_vector_internal@absl@@QEAAXPEAUPayload@status_internal@3@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AEAAXAEBV123@@Z
     ?At@TimeZone@absl@@QEBA?AUCivilInfo@12@VTime@2@@Z
@@ -1740,6 +1720,7 @@
     ?DumpPCAndFrameSizesAndStackTrace@debugging_internal@absl@@YAXPEAXQEBQEAXQEAHHH_NP6AXPEBD0@Z0@Z
     ?DurationFromTimespec@absl@@YA?AVDuration@1@Utimespec@@@Z
     ?DurationFromTimeval@absl@@YA?AVDuration@1@Utimeval@@@Z
+    ?EmplaceTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@AEBVInlineData@53@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?EmplaceTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?EmptyString@Status@absl@@CAPEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@XZ
     ?EnableDebugLog@CondVar@absl@@QEAAXPEBD@Z
@@ -2203,7 +2184,6 @@
     ?ReaderUnlock@Mutex@absl@@QEAAXXZ
     ?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
     ?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
-    ?RecordMetrics@CordzInfo@cord_internal@absl@@QEAAX_J@Z
     ?Ref@CordRep@cord_internal@absl@@SAPEAU123@PEAU123@@Z
     ?Register@CycleClockSource@base_internal@absl@@CAXP6A_JXZ@Z
     ?Register@HashtablezSampler@container_internal@absl@@QEAAPEAUHashtablezInfo@23@XZ
@@ -2215,7 +2195,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPEBDPEBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPEBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPEBXPEADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPEAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QEAAXXZ
     ?Remove@CondVar@absl@@AEAAXPEAUPerThreadSynch@base_internal@2@@Z
     ?RemoveChunkPrefix@ChunkIterator@Cord@absl@@AEAAX_K@Z
@@ -2240,6 +2219,7 @@
     ?Rethrow@variant_internal@absl@@YAXXZ
     ?RoundUp@cord_internal@absl@@YA_K_K0@Z
     ?RoundUpForTag@cord_internal@absl@@YA_K_K@Z
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
     ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
     ?Seconds@absl@@YA?AVDuration@1@_J@Z
@@ -2275,6 +2255,7 @@
     ?SetToZero@?$BigUnsigned@$03@strings_internal@absl@@QEAAXXZ
     ?SetToZero@?$BigUnsigned@$0FE@@strings_internal@absl@@QEAAXXZ
     ?SetTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@AEBVCordzUpdateScope@53@@Z
+    ?SetTreeOrEmpty@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@AEBVCordzUpdateScope@53@@Z
     ?SetValue@?$Manager@H$01@FormatArgImpl@str_format_internal@absl@@SA?ATData@234@AEBH@Z
     ?SetWidth@FormatConversionSpecImplFriend@str_format_internal@absl@@SAXHPEAVFormatConversionSpecImpl@23@@Z
     ?ShiftLeft@?$BigUnsigned@$03@strings_internal@absl@@QEAAXH@Z
@@ -2452,10 +2433,9 @@
     ?UnrefNonInlined@Status@absl@@CAX_K@Z
     ?UnrefTree@InlineRep@Cord@absl@@AEAAXXZ
     ?Unregister@HashtablezSampler@container_internal@absl@@QEAAXPEAUHashtablezInfo@23@@Z
+    ?UnsafeSetCordRep@CordzInfo@cord_internal@absl@@AEAAXPEAUCordRep@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QEAAXXZ
-    ?UpdateCordzStatistics@InlineRep@Cord@absl@@QEAAXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QEAAXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@HP6AHPEAPEAXH@Z@Z
     ?UsingInlinedStorage@Storage@?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@CA_N_K@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -3144,7 +3124,6 @@
     ?get@?$Storage@PEAPEBUCordRep@cord_internal@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAPEAPEBUCordRep@cord_internal@4@XZ
     ?get@?$Storage@PEAUPayload@status_internal@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAPEAUPayload@status_internal@4@XZ
     ?get@?$Storage@PEAUSubRange@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAPEAUSubRange@4@XZ
-    ?get@?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@4@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@XZ
     ?get@?$Storage@V?$allocator@H@__1@std@@$0A@$00@internal_compressed_tuple@container_internal@absl@@QEGAAAEAV?$allocator@H@__1@std@@XZ
     ?get@?$Storage@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@$00$00@internal_compressed_tuple@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
     ?get@?$Storage@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@$0A@$00@internal_compressed_tuple@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
@@ -3192,7 +3171,6 @@
     ?is_leap_year@impl@detail@cctz@time_internal@absl@@YA_N_J@Z
     ?is_length@ConvTag@str_format_internal@absl@@QEBA_NXZ
     ?is_profiled@InlineData@cord_internal@absl@@QEBA_NXZ
-    ?is_profiled@InlineRep@Cord@absl@@QEBA_NXZ
     ?is_snapshot@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?is_tree@InlineData@cord_internal@absl@@QEBA_NXZ
     ?is_tree@InlineRep@Cord@absl@@QEBA_NXZ
@@ -3318,7 +3296,6 @@
     ?remove_prefix@string_view@absl@@QEAAX_K@Z
     ?remove_suffix@string_view@absl@@QEAAX_K@Z
     ?rend@string_view@absl@@QEBA?AV?$reverse_iterator@PEBD@__1@std@@XZ
-    ?replace_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?reserve@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAX_K@Z
     ?reserve@?$vector@UTransitionType@cctz@time_internal@absl@@V?$allocator@UTransitionType@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAX_K@Z
     ?reserve@?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__1@std@@@__1@std@@QEAAX_K@Z
@@ -3398,7 +3375,6 @@
     ?set_inline_size@InlineData@cord_internal@absl@@QEAAX_K@Z
     ?set_inline_size@InlineRep@Cord@absl@@AEAAX_K@Z
     ?set_tree@InlineData@cord_internal@absl@@QEAAXPEAUCordRep@23@@Z
-    ?set_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?set_value@InputValue@UnboundConversion@str_format_internal@absl@@QEAAXH@Z
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QEBAHXZ
diff --git a/third_party/abseil-cpp/symbols_arm64_rel.def b/third_party/abseil-cpp/symbols_arm64_rel.def
index 73aea3a..011e403 100644
--- a/third_party/abseil-cpp/symbols_arm64_rel.def
+++ b/third_party/abseil-cpp/symbols_arm64_rel.def
@@ -1,6 +1,5 @@
 EXPORTS
     ??$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAAAEAV01@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?MUsecond_tag@detail@cctz@time_internal@absl@@U01234@@detail@cctz@time_internal@absl@@YA_NAEBV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@0@Z
     ??$?RW4LogSeverity@absl@@AEBQEBDHAEAPEBD@?$AtomicHook@P6AXW4LogSeverity@absl@@PEBDHAEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@base_internal@absl@@QEBAX$$QEAW4LogSeverity@2@AEBQEBD$$QEAHAEAPEBD@Z
     ??$AddRing@$00@CordRepRing@cord_internal@absl@@CAPEAV012@PEAV012@0_K1@Z
@@ -62,7 +61,6 @@
     ??$GenericCompare@HVstring_view@absl@@@absl@@YAHAEBVCord@0@AEBVstring_view@0@_K@Z
     ??$GenericCompare@_NVCord@absl@@@absl@@YA_NAEBVCord@0@0_K@Z
     ??$GenericCompare@_NVstring_view@absl@@@absl@@YA_NAEBVCord@0@AEBVstring_view@0@_K@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPEAUCordRep@01@Vstring_view@1@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$ParseFloat@$09@strings_internal@absl@@YA?AUParsedFloat@01@PEBD0W4chars_format@1@@Z
     ??$ParseFloat@$0BA@@strings_internal@absl@@YA?AUParsedFloat@01@PEBD0W4chars_format@1@@Z
     ??$ParseFormatString@UParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@@str_format_internal@absl@@YA_NVstring_view@1@UParsedFormatConsumer@ParsedFormatBase@01@@Z
@@ -108,7 +106,7 @@
     ??0Condition@absl@@AEAA@XZ
     ??0Condition@absl@@QEAA@P6A_NPEAX@Z0@Z
     ??0Condition@absl@@QEAA@PEB_N@Z
-    ??0Cord@absl@@QEAA@Vstring_view@1@@Z
+    ??0Cord@absl@@AEAA@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0CordzHandle@cord_internal@absl@@IEAA@_N@Z
     ??0CordzInfo@cord_internal@absl@@AEAA@PEAUCordRep@12@PEBV012@W4MethodIdentifier@CordzUpdateTracker@12@@Z
     ??0GraphCycles@synchronization_internal@absl@@QEAA@XZ
@@ -225,6 +223,7 @@
     ?AssertHeld@Mutex@absl@@QEBAXXZ
     ?AssertNotHeld@Mutex@absl@@QEBAXXZ
     ?AssertReaderHeld@Mutex@absl@@QEBAXXZ
+    ?AssignLargeString@Cord@absl@@AEAAAEAV12@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AEAAXAEBV123@@Z
     ?At@TimeZone@absl@@QEBA?AUCivilInfo@12@VTime@2@@Z
     ?At@TimeZone@absl@@QEBA?AUTimeInfo@12@V?$civil_time@Usecond_tag@time_internal@absl@@@detail@cctz@time_internal@2@@Z
@@ -600,7 +599,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPEBDPEBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPEBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPEBXPEADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPEAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QEAAXXZ
     ?Remove@CondVar@absl@@AEAAXPEAUPerThreadSynch@base_internal@2@@Z
     ?RemoveEdge@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@0@Z
@@ -613,6 +611,7 @@
     ?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NAEBV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
     ?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
     ?Rethrow@variant_internal@absl@@YAXXZ
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
     ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
     ?Seek@CordRepRingReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
@@ -750,7 +749,6 @@
     ?Unregister@HashtablezSampler@container_internal@absl@@QEAAXPEAUHashtablezInfo@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QEAAXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QEAAXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@HP6AHPEAPEAXH@Z@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ?Utf8SafeCHexEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -854,7 +852,6 @@
     ?safe_strtou32_base@numbers_internal@absl@@YA_NVstring_view@2@PEAIH@Z
     ?safe_strtou64_base@numbers_internal@absl@@YA_NVstring_view@2@PEA_KH@Z
     ?set_cordz_mean_interval@cord_internal@absl@@YAXH@Z
-    ?set_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?shallow_subcords_enabled@cord_internal@absl@@3U?$atomic@_N@__1@std@@A
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QEBAHXZ
diff --git a/third_party/abseil-cpp/symbols_x64_dbg.def b/third_party/abseil-cpp/symbols_x64_dbg.def
index fd92091..9f722fd 100644
--- a/third_party/abseil-cpp/symbols_x64_dbg.def
+++ b/third_party/abseil-cpp/symbols_x64_dbg.def
@@ -118,10 +118,6 @@
     ??$?0PEAVZoneInfoSource@cctz@time_internal@absl@@U__default_init_tag@__1@std@@@?$__compressed_pair@PEAVZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAA@$$QEAPEAVZoneInfoSource@cctz@time_internal@absl@@$$QEAU__default_init_tag@12@@Z
     ??$?0PEAVZoneInfoSource@cctz@time_internal@absl@@X@?$__compressed_pair_elem@PEAVZoneInfoSource@cctz@time_internal@absl@@$0A@$0A@@__1@std@@QEAA@$$QEAPEAVZoneInfoSource@cctz@time_internal@absl@@@Z
     ??$?0U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@X@?$__compressed_pair_elem@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@$00$00@__1@std@@QEAA@$$QEAU?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@12@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$$V$00@?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QEAA@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$CompressedTupleImpl@V?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@U?$integer_sequence@_K$0A@@3@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@Uin_place_t@3@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@3@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@QEAA@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@H@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@Uin_place_t@3@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@3@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$?0USynchEvent@absl@@@Condition@absl@@QEAA@P6A_NPEAUSynchEvent@1@@Z0@Z
     ??$?0Uday_tag@detail@cctz@time_internal@absl@@@?$civil_time@Umonth_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@QEAA@AEBV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@1234@PEAX@Z
     ??$?0Uday_tag@detail@cctz@time_internal@absl@@@?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@QEAA@AEBV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@1234@PEAX@Z
@@ -192,7 +188,6 @@
     ??$?0V?$vector@VFormatArgImpl@str_format_internal@absl@@V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__1@std@@@__1@std@@XV012@@?$Span@$$CBVFormatArgImpl@str_format_internal@absl@@@absl@@QEAA@AEBV?$vector@VFormatArgImpl@str_format_internal@absl@@V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__1@std@@@__1@std@@@Z
     ??$?0VBufferRawSink@str_format_internal@absl@@$0A@@FormatRawSinkImpl@str_format_internal@absl@@QEAA@PEAVBufferRawSink@12@@Z
     ??$?0VFILERawSink@str_format_internal@absl@@$0A@@FormatRawSinkImpl@str_format_internal@absl@@QEAA@PEAVFILERawSink@12@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAAAEAV01@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?8PEAPEAVCordzHandle@cord_internal@absl@@PEAPEAV012@@__1@std@@YA_NAEBV?$__wrap_iter@PEAPEAVCordzHandle@cord_internal@absl@@@01@0@Z
     ??$?8PEAUTransitionType@cctz@time_internal@absl@@PEAU0123@@__1@std@@YA_NAEBV?$__wrap_iter@PEAUTransitionType@cctz@time_internal@absl@@@01@0@Z
     ??$?8PEBUConversionItem@ParsedFormatBase@str_format_internal@absl@@PEBU0123@@__1@std@@YA_NAEBV?$__wrap_iter@PEBUConversionItem@ParsedFormatBase@str_format_internal@absl@@@01@0@Z
@@ -358,12 +353,10 @@
     ??$Init@H@FormatArgImpl@str_format_internal@absl@@AEAAXAEBH@Z
     ??$Initialize@V?$CopyValueAdapter@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@@?$Storage@PEAUCordRep@cord_internal@absl@@$0CP@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAAXV?$CopyValueAdapter@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@12@_K@Z
     ??$Invoke@A6AXXZ$$V@Callable@base_internal@absl@@SAXA6AXXZ@Z
-    ??$Invoke@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@3@@Callable@base_internal@absl@@SAX$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@2@@Z
     ??$InvokeFlush@V?$basic_ostream@DU?$char_traits@D@__1@std@@@__1@std@@@str_format_internal@absl@@YAXPEAV?$basic_ostream@DU?$char_traits@D@__1@std@@@__1@std@@Vstring_view@1@@Z
     ??$InvokeFlush@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@str_format_internal@absl@@YAXPEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ??$InvokeFlush@VBufferRawSink@str_format_internal@absl@@@str_format_internal@absl@@YAXPEAVBufferRawSink@01@Vstring_view@1@@Z
     ??$InvokeFlush@VFILERawSink@str_format_internal@absl@@@str_format_internal@absl@@YAXPEAVFILERawSink@01@Vstring_view@1@@Z
-    ??$InvokeReleaser@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@X@cord_internal@absl@@YAXURank0@01@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@Vstring_view@1@@Z
     ??$LowLevelCallOnce@A6AXXZ$$V@base_internal@absl@@YAXPEAVonce_flag@1@A6AXXZ@Z
     ??$MakeConstSpan@$SQEAX@absl@@YA?AV?$Span@QEAX@0@PEBQEAX_K@Z
     ??$MakeSpan@$SI$0BAA@@absl@@YA?AV?$Span@I@0@AEAY0BAA@I@Z
@@ -373,7 +366,6 @@
     ??$MakeSpan@$SI$0IA@@absl@@YA?AV?$Span@I@0@AEAY0IA@I@Z
     ??$MakeSpan@$SVFormatArgImpl@str_format_internal@absl@@@absl@@YA?AV?$Span@VFormatArgImpl@str_format_internal@absl@@@0@PEAVFormatArgImpl@str_format_internal@0@_K@Z
     ??$Milliseconds@N$0A@@absl@@YA?AVDuration@0@N@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPEAUCordRep@01@Vstring_view@1@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@_KPEAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@_K$0A@$00$01@absl@@U45@@internal_layout@container_internal@absl@@QEBA_KXZ
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@_KPEAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@_K$0A@$00@absl@@U?$integer_sequence@_K$0A@$00$01@5@@internal_layout@container_internal@absl@@QEBA_KXZ
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@_KPEAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@_K$0A@@absl@@U?$integer_sequence@_K$0A@$00@5@@internal_layout@container_internal@absl@@QEBA_KXZ
@@ -702,8 +694,6 @@
     ??$forward@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@__1@std@@YA$$QEAU?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@01@AEAU201@@Z
     ??$forward@UConversionItem@ParsedFormatBase@str_format_internal@absl@@@__1@std@@YA$$QEAUConversionItem@ParsedFormatBase@str_format_internal@absl@@AEAU2345@@Z
     ??$forward@UPayload@status_internal@absl@@@__1@std@@YA$$QEAUPayload@status_internal@absl@@AEAU234@@Z
-    ??$forward@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@__1@std@@YA$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@01@@Z@AEAU2?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@34@QEAA@0@Z@@Z
-    ??$forward@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@absl@@YA$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@0@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAU1?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@20@QEAA@0@Z@@Z
     ??$forward@USubRange@absl@@@__1@std@@YA$$QEAUSubRange@absl@@AEAU23@@Z
     ??$forward@UTransition@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransition@cctz@time_internal@absl@@AEAU2345@@Z
     ??$forward@UTransitionType@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransitionType@cctz@time_internal@absl@@AEAU2345@@Z
@@ -736,7 +726,6 @@
     ??$get@$00@?$CompressedTuple@V?$allocator@USubRange@absl@@@__1@std@@_K@container_internal@absl@@QEGBAAEB_KXZ
     ??$get@$00@?$CompressedTuple@_KV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
     ??$get@$00Vstring_view@absl@@V12@@__1@std@@YAAEBVstring_view@absl@@AEBU?$pair@Vstring_view@absl@@V12@@01@@Z
-    ??$get@$0A@@?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QEGAAAEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@H@__1@std@@PEAH@container_internal@absl@@QEGAAAEAV?$allocator@H@__1@std@@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@H@__1@std@@_K@container_internal@absl@@QEGAAAEAV?$allocator@H@__1@std@@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@PEAPEAUCordRep@cord_internal@absl@@@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
@@ -753,7 +742,6 @@
     ??$get@Vstring_view@absl@@V12@@?$__get_pair@$00@__1@std@@SAAEBVstring_view@absl@@AEBU?$pair@Vstring_view@absl@@V12@@12@@Z
     ??$get@Vstring_view@absl@@V12@@?$__get_pair@$0A@@__1@std@@SAAEBVstring_view@absl@@AEBU?$pair@Vstring_view@absl@@V12@@12@@Z
     ??$invoke@A6AXXZ$$V@base_internal@absl@@YAXA6AXXZ@Z
-    ??$invoke@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@3@@base_internal@absl@@YAX$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AEAVstring_view@1@@Z
     ??$lower_bound@PEBUTransition@cctz@time_internal@absl@@U1234@UByUnixTime@1234@@__1@std@@YAPEBUTransition@cctz@time_internal@absl@@PEBU2345@0AEBU2345@UByUnixTime@2345@@Z
     ??$make_pair@AEAPEAUCordRep@cord_internal@absl@@AEAPEAU123@@__1@std@@YA?AU?$pair@PEAUCordRep@cord_internal@absl@@PEAU123@@01@AEAPEAUCordRep@cord_internal@absl@@0@Z
     ??$make_unique@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@$$V@__1@std@@YA?AV?$unique_ptr@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@01@XZ
@@ -788,7 +776,6 @@
     ??$move@AEAPEBV?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@YA$$QEAPEBV?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@AEAPEBV23@@Z
     ??$move@AEAUConversionItem@ParsedFormatBase@str_format_internal@absl@@@__1@std@@YA$$QEAUConversionItem@ParsedFormatBase@str_format_internal@absl@@AEAU2345@@Z
     ??$move@AEAUPayload@status_internal@absl@@@__1@std@@YA$$QEAUPayload@status_internal@absl@@AEAU234@@Z
-    ??$move@AEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@__1@std@@YA$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@01@@Z@AEAU2?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@34@QEAA@0@Z@@Z
     ??$move@AEAUSubRange@absl@@@__1@std@@YA$$QEAUSubRange@absl@@AEAU23@@Z
     ??$move@AEAUTransition@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransition@cctz@time_internal@absl@@AEAU2345@@Z
     ??$move@AEAUTransitionType@cctz@time_internal@absl@@@__1@std@@YA$$QEAUTransitionType@cctz@time_internal@absl@@AEAU2345@@Z
@@ -1030,9 +1017,9 @@
     ??0Condition@absl@@AEAA@XZ
     ??0Condition@absl@@QEAA@P6A_NPEAX@Z0@Z
     ??0Condition@absl@@QEAA@PEB_N@Z
+    ??0Cord@absl@@AEAA@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0Cord@absl@@QEAA@$$QEAV01@@Z
     ??0Cord@absl@@QEAA@AEBV01@@Z
-    ??0Cord@absl@@QEAA@Vstring_view@1@@Z
     ??0Cord@absl@@QEAA@XZ
     ??0CordForest@absl@@QEAA@_K@Z
     ??0CordRep@cord_internal@absl@@QEAA@XZ
@@ -1091,7 +1078,6 @@
     ??0StatusRep@status_internal@absl@@QEAA@W4StatusCode@2@Vstring_view@2@V?$unique_ptr@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@__1@std@@@Z
     ??0Storage@?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@_KAEBV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@Z
     ??0Streamable@str_format_internal@absl@@QEAA@AEBVUntypedFormatSpecImpl@12@V?$Span@$$CBVFormatArgImpl@str_format_internal@absl@@@2@@Z
-    ??0StringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QEAA@$$QEAU0?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@12@QEAA@0@Z@@Z
     ??0SubRange@absl@@QEAA@PEAUCordRep@cord_internal@1@_K1@Z
     ??0SynchWaitParams@absl@@QEAA@PEBUMuHowS@1@PEBVCondition@1@VKernelTimeout@synchronization_internal@1@PEAVMutex@1@PEAUPerThreadSynch@base_internal@1@PEAU?$atomic@_J@__1@std@@@Z
     ??0Time@absl@@AEAA@VDuration@1@@Z
@@ -1149,9 +1135,6 @@
     ??1?$AllocationTransaction@V?$allocator@PEBUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$AllocationTransaction@V?$allocator@UPayload@status_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$AllocationTransaction@V?$allocator@USubRange@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
-    ??1?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QEAA@XZ
-    ??1?$CompressedTupleImpl@V?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@U?$integer_sequence@_K$0A@@3@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@XZ
-    ??1?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@QEAA@XZ
     ??1?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@XZ
     ??1?$InlinedVector@H$0CP@V?$allocator@H@__1@std@@@absl@@QEAA@XZ
     ??1?$InlinedVector@PEAUCordRep@cord_internal@absl@@$01V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@XZ
@@ -1164,7 +1147,6 @@
     ??1?$Storage@PEAUCordRep@cord_internal@absl@@$0CP@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$Storage@PEBUCordRep@cord_internal@absl@@$0CP@V?$allocator@PEBUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$Storage@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
-    ??1?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QEAA@XZ
     ??1?$Storage@USubRange@absl@@$0CP@V?$allocator@USubRange@absl@@@__1@std@@@inlined_vector_internal@absl@@QEAA@XZ
     ??1?$__policy_func@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__1@std@@@__1@std@@AEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@23@@Z@__function@__1@std@@QEAA@XZ
     ??1?$__policy_func@$$A6AXVstring_view@absl@@AEBVCord@2@@Z@__function@__1@std@@QEAA@XZ
@@ -1236,7 +1218,6 @@
     ??1StatusRep@status_internal@absl@@QEAA@XZ
     ??1Storage@?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QEAA@XZ
     ??1Streamable@str_format_internal@absl@@QEAA@XZ
-    ??1StringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QEAA@XZ
     ??1TimeZoneIf@cctz@time_internal@absl@@UEAA@XZ
     ??1TimeZoneInfo@cctz@time_internal@absl@@UEAA@XZ
     ??1TimeZoneLibC@cctz@time_internal@absl@@UEAA@XZ
@@ -1454,7 +1435,6 @@
     ??R<lambda_1>@?0??pop_back@?$InlinedVector@USubRange@absl@@$0CP@V?$allocator@USubRange@absl@@@__1@std@@@absl@@QEAAXXZ@QEBA?A?<auto>@@XZ
     ??R<lambda_1>@?0??remove_prefix@string_view@absl@@QEAAX_K@Z@QEBA?A?<auto>@@XZ
     ??R<lambda_1>@?0??remove_suffix@string_view@absl@@QEAAX_K@Z@QEBA?A?<auto>@@XZ
-    ??R<lambda_1>@?0??replace_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@4@@Z@QEBA?A?<auto>@@XZ
     ??R<lambda_1>@?0??set_inline_size@InlineData@cord_internal@absl@@QEAAX_K@Z@QEBA?A?<auto>@@XZ
     ??R<lambda_2>@?0???$AddRing@$00@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@0_K1@Z@QEBA?A?<auto>@@I@Z
     ??R<lambda_2>@?0???$AddRing@$0A@@CordRepRing@cord_internal@absl@@CAPEAV123@PEAV123@0_K1@Z@QEBA?A?<auto>@@I@Z
@@ -1482,7 +1462,6 @@
     ??R?$function@$$A6AXVstring_view@absl@@AEBVCord@2@@Z@__1@std@@QEBAXVstring_view@absl@@AEBVCord@4@@Z
     ??RByCivilTime@Transition@cctz@time_internal@absl@@QEBA_NAEBU1234@0@Z
     ??RByUnixTime@Transition@cctz@time_internal@absl@@QEBA_NAEBU1234@0@Z
-    ??RStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QEAAXVstring_view@2@@Z
     ??Sabsl@@YA?AVuint128@0@V10@@Z
     ??Tabsl@@YA?AVuint128@0@V10@0@Z
     ??XDuration@absl@@QEAAAEAV01@N@Z
@@ -1583,6 +1562,7 @@
     ?AssertHeld@Mutex@absl@@QEBAXXZ
     ?AssertNotHeld@Mutex@absl@@QEBAXXZ
     ?AssertReaderHeld@Mutex@absl@@QEBAXXZ
+    ?AssignLargeString@Cord@absl@@AEAAAEAV12@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignNext@?$IteratorValueAdapter@V?$allocator@UPayload@status_internal@absl@@@__1@std@@V?$move_iterator@PEAUPayload@status_internal@absl@@@23@@inlined_vector_internal@absl@@QEAAXPEAUPayload@status_internal@3@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AEAAXAEBV123@@Z
     ?At@TimeZone@absl@@QEBA?AUCivilInfo@12@VTime@2@@Z
@@ -1741,6 +1721,7 @@
     ?DumpPCAndFrameSizesAndStackTrace@debugging_internal@absl@@YAXPEAXQEBQEAXQEAHHH_NP6AXPEBD0@Z0@Z
     ?DurationFromTimespec@absl@@YA?AVDuration@1@Utimespec@@@Z
     ?DurationFromTimeval@absl@@YA?AVDuration@1@Utimeval@@@Z
+    ?EmplaceTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@AEBVInlineData@53@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?EmplaceTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?EmptyString@Status@absl@@CAPEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@XZ
     ?EnableDebugLog@CondVar@absl@@QEAAXPEBD@Z
@@ -2202,7 +2183,6 @@
     ?ReaderUnlock@Mutex@absl@@QEAAXXZ
     ?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPEAX@Z
     ?RecordInsertSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@_K1@Z
-    ?RecordMetrics@CordzInfo@cord_internal@absl@@QEAAX_J@Z
     ?Ref@CordRep@cord_internal@absl@@SAPEAU123@PEAU123@@Z
     ?Register@CycleClockSource@base_internal@absl@@CAXP6A_JXZ@Z
     ?Register@HashtablezSampler@container_internal@absl@@QEAAPEAUHashtablezInfo@23@XZ
@@ -2214,7 +2194,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPEBDPEBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPEBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPEBXPEADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPEAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QEAAXXZ
     ?Remove@CondVar@absl@@AEAAXPEAUPerThreadSynch@base_internal@2@@Z
     ?RemoveChunkPrefix@ChunkIterator@Cord@absl@@AEAAX_K@Z
@@ -2239,6 +2218,7 @@
     ?Rethrow@variant_internal@absl@@YAXXZ
     ?RoundUp@cord_internal@absl@@YA_K_K0@Z
     ?RoundUpForTag@cord_internal@absl@@YA_K_K@Z
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
     ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
     ?Seconds@absl@@YA?AVDuration@1@_J@Z
@@ -2274,6 +2254,7 @@
     ?SetToZero@?$BigUnsigned@$03@strings_internal@absl@@QEAAXXZ
     ?SetToZero@?$BigUnsigned@$0FE@@strings_internal@absl@@QEAAXXZ
     ?SetTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@AEBVCordzUpdateScope@53@@Z
+    ?SetTreeOrEmpty@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@AEBVCordzUpdateScope@53@@Z
     ?SetValue@?$Manager@H$01@FormatArgImpl@str_format_internal@absl@@SA?ATData@234@AEBH@Z
     ?SetWidth@FormatConversionSpecImplFriend@str_format_internal@absl@@SAXHPEAVFormatConversionSpecImpl@23@@Z
     ?ShiftLeft@?$BigUnsigned@$03@strings_internal@absl@@QEAAXH@Z
@@ -2448,10 +2429,9 @@
     ?UnrefNonInlined@Status@absl@@CAX_K@Z
     ?UnrefTree@InlineRep@Cord@absl@@AEAAXXZ
     ?Unregister@HashtablezSampler@container_internal@absl@@QEAAXPEAUHashtablezInfo@23@@Z
+    ?UnsafeSetCordRep@CordzInfo@cord_internal@absl@@AEAAXPEAUCordRep@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QEAAXXZ
-    ?UpdateCordzStatistics@InlineRep@Cord@absl@@QEAAXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QEAAXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@HP6AHPEAPEAXH@Z@Z
     ?UsingInlinedStorage@Storage@?$FixedArray@PEAUCordRep@cord_internal@absl@@$0?0V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@@absl@@CA_N_K@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -3141,7 +3121,6 @@
     ?get@?$Storage@PEAPEBUCordRep@cord_internal@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAPEAPEBUCordRep@cord_internal@4@XZ
     ?get@?$Storage@PEAUPayload@status_internal@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAPEAUPayload@status_internal@4@XZ
     ?get@?$Storage@PEAUSubRange@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAPEAUSubRange@4@XZ
-    ?get@?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QEGAAAEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@4@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@XZ
     ?get@?$Storage@V?$allocator@H@__1@std@@$0A@$00@internal_compressed_tuple@container_internal@absl@@QEGAAAEAV?$allocator@H@__1@std@@XZ
     ?get@?$Storage@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@$00$00@internal_compressed_tuple@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
     ?get@?$Storage@V?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@$0A@$00@internal_compressed_tuple@container_internal@absl@@QEGAAAEAV?$allocator@PEAUCordRep@cord_internal@absl@@@__1@std@@XZ
@@ -3189,7 +3168,6 @@
     ?is_leap_year@impl@detail@cctz@time_internal@absl@@YA_N_J@Z
     ?is_length@ConvTag@str_format_internal@absl@@QEBA_NXZ
     ?is_profiled@InlineData@cord_internal@absl@@QEBA_NXZ
-    ?is_profiled@InlineRep@Cord@absl@@QEBA_NXZ
     ?is_snapshot@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?is_tree@InlineData@cord_internal@absl@@QEBA_NXZ
     ?is_tree@InlineRep@Cord@absl@@QEBA_NXZ
@@ -3315,7 +3293,6 @@
     ?remove_prefix@string_view@absl@@QEAAX_K@Z
     ?remove_suffix@string_view@absl@@QEAAX_K@Z
     ?rend@string_view@absl@@QEBA?AV?$reverse_iterator@PEBD@__1@std@@XZ
-    ?replace_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?reserve@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAX_K@Z
     ?reserve@?$vector@UTransitionType@cctz@time_internal@absl@@V?$allocator@UTransitionType@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAX_K@Z
     ?reserve@?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__1@std@@@__1@std@@QEAAX_K@Z
@@ -3395,7 +3372,6 @@
     ?set_inline_size@InlineData@cord_internal@absl@@QEAAX_K@Z
     ?set_inline_size@InlineRep@Cord@absl@@AEAAX_K@Z
     ?set_tree@InlineData@cord_internal@absl@@QEAAXPEAUCordRep@23@@Z
-    ?set_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?set_value@InputValue@UnboundConversion@str_format_internal@absl@@QEAAXH@Z
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QEBAHXZ
diff --git a/third_party/abseil-cpp/symbols_x64_rel.def b/third_party/abseil-cpp/symbols_x64_rel.def
index a36e6a12..d2820ce 100644
--- a/third_party/abseil-cpp/symbols_x64_rel.def
+++ b/third_party/abseil-cpp/symbols_x64_rel.def
@@ -1,6 +1,5 @@
 EXPORTS
     ??$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAAAEAV01@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?MUsecond_tag@detail@cctz@time_internal@absl@@U01234@@detail@cctz@time_internal@absl@@YA_NAEBV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@0@Z
     ??$?RW4LogSeverity@absl@@AEBQEBDHAEAPEBD@?$AtomicHook@P6AXW4LogSeverity@absl@@PEBDHAEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@base_internal@absl@@QEBAX$$QEAW4LogSeverity@2@AEBQEBD$$QEAHAEAPEBD@Z
     ??$AddRing@$00@CordRepRing@cord_internal@absl@@CAPEAV012@PEAV012@0_K1@Z
@@ -62,7 +61,6 @@
     ??$GenericCompare@HVstring_view@absl@@@absl@@YAHAEBVCord@0@AEBVstring_view@0@_K@Z
     ??$GenericCompare@_NVCord@absl@@@absl@@YA_NAEBVCord@0@0_K@Z
     ??$GenericCompare@_NVstring_view@absl@@@absl@@YA_NAEBVCord@0@AEBVstring_view@0@_K@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPEAUCordRep@01@Vstring_view@1@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$ParseFloat@$09@strings_internal@absl@@YA?AUParsedFloat@01@PEBD0W4chars_format@1@@Z
     ??$ParseFloat@$0BA@@strings_internal@absl@@YA?AUParsedFloat@01@PEBD0W4chars_format@1@@Z
     ??$ParseFormatString@UParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@@str_format_internal@absl@@YA_NVstring_view@1@UParsedFormatConsumer@ParsedFormatBase@01@@Z
@@ -110,7 +108,7 @@
     ??0Condition@absl@@AEAA@XZ
     ??0Condition@absl@@QEAA@P6A_NPEAX@Z0@Z
     ??0Condition@absl@@QEAA@PEB_N@Z
-    ??0Cord@absl@@QEAA@Vstring_view@1@@Z
+    ??0Cord@absl@@AEAA@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0CordzHandle@cord_internal@absl@@IEAA@_N@Z
     ??0CordzInfo@cord_internal@absl@@AEAA@PEAUCordRep@12@PEBV012@W4MethodIdentifier@CordzUpdateTracker@12@@Z
     ??0GraphCycles@synchronization_internal@absl@@QEAA@XZ
@@ -227,6 +225,7 @@
     ?AssertHeld@Mutex@absl@@QEBAXXZ
     ?AssertNotHeld@Mutex@absl@@QEBAXXZ
     ?AssertReaderHeld@Mutex@absl@@QEBAXXZ
+    ?AssignLargeString@Cord@absl@@AEAAAEAV12@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AEAAXAEBV123@@Z
     ?At@TimeZone@absl@@QEBA?AUCivilInfo@12@VTime@2@@Z
     ?At@TimeZone@absl@@QEBA?AUTimeInfo@12@V?$civil_time@Usecond_tag@time_internal@absl@@@detail@cctz@time_internal@2@@Z
@@ -600,7 +599,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPEBDPEBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPEBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPEBXPEADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPEAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QEAAXXZ
     ?Remove@CondVar@absl@@AEAAXPEAUPerThreadSynch@base_internal@2@@Z
     ?RemoveEdge@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@0@Z
@@ -613,6 +611,7 @@
     ?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NAEBV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
     ?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
     ?Rethrow@variant_internal@absl@@YAXXZ
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
     ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
     ?Seek@CordRepRingReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
@@ -750,7 +749,6 @@
     ?Unregister@HashtablezSampler@container_internal@absl@@QEAAXPEAUHashtablezInfo@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QEAAXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QEAAXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@HP6AHPEAPEAXH@Z@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ?Utf8SafeCHexEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -854,7 +852,6 @@
     ?safe_strtou32_base@numbers_internal@absl@@YA_NVstring_view@2@PEAIH@Z
     ?safe_strtou64_base@numbers_internal@absl@@YA_NVstring_view@2@PEA_KH@Z
     ?set_cordz_mean_interval@cord_internal@absl@@YAXH@Z
-    ?set_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?shallow_subcords_enabled@cord_internal@absl@@3U?$atomic@_N@__1@std@@A
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QEBAHXZ
diff --git a/third_party/abseil-cpp/symbols_x64_rel_asan.def b/third_party/abseil-cpp/symbols_x64_rel_asan.def
index e6f752d3..5c25343 100644
--- a/third_party/abseil-cpp/symbols_x64_rel_asan.def
+++ b/third_party/abseil-cpp/symbols_x64_rel_asan.def
@@ -1,6 +1,5 @@
 EXPORTS
     ??$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAAAEAV01@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?MUsecond_tag@detail@cctz@time_internal@absl@@U01234@@detail@cctz@time_internal@absl@@YA_NAEBV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@0@Z
     ??$?RW4LogSeverity@absl@@AEBQEBDHAEAPEBD@?$AtomicHook@P6AXW4LogSeverity@absl@@PEBDHAEBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@base_internal@absl@@QEBAX$$QEAW4LogSeverity@2@AEBQEBD$$QEAHAEAPEBD@Z
     ??$AddRing@$00@CordRepRing@cord_internal@absl@@CAPEAV012@PEAV012@0_K1@Z
@@ -62,7 +61,6 @@
     ??$GenericCompare@HVstring_view@absl@@@absl@@YAHAEBVCord@0@AEBVstring_view@0@_K@Z
     ??$GenericCompare@_NVCord@absl@@@absl@@YA_NAEBVCord@0@0_K@Z
     ??$GenericCompare@_NVstring_view@absl@@@absl@@YA_NAEBVCord@0@AEBVstring_view@0@_K@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPEAUCordRep@01@Vstring_view@1@$$QEAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$ParseFloat@$09@strings_internal@absl@@YA?AUParsedFloat@01@PEBD0W4chars_format@1@@Z
     ??$ParseFloat@$0BA@@strings_internal@absl@@YA?AUParsedFloat@01@PEBD0W4chars_format@1@@Z
     ??$ParseFormatString@UParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@@str_format_internal@absl@@YA_NVstring_view@1@UParsedFormatConsumer@ParsedFormatBase@01@@Z
@@ -114,7 +112,7 @@
     ??0Condition@absl@@AEAA@XZ
     ??0Condition@absl@@QEAA@P6A_NPEAX@Z0@Z
     ??0Condition@absl@@QEAA@PEB_N@Z
-    ??0Cord@absl@@QEAA@Vstring_view@1@@Z
+    ??0Cord@absl@@AEAA@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0CordzHandle@cord_internal@absl@@IEAA@_N@Z
     ??0CordzInfo@cord_internal@absl@@AEAA@PEAUCordRep@12@PEBV012@W4MethodIdentifier@CordzUpdateTracker@12@@Z
     ??0GraphCycles@synchronization_internal@absl@@QEAA@XZ
@@ -240,6 +238,7 @@
     ?AssertHeld@Mutex@absl@@QEBAXXZ
     ?AssertNotHeld@Mutex@absl@@QEBAXXZ
     ?AssertReaderHeld@Mutex@absl@@QEBAXXZ
+    ?AssignLargeString@Cord@absl@@AEAAAEAV12@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AEAAXAEBV123@@Z
     ?At@TimeZone@absl@@QEBA?AUCivilInfo@12@VTime@2@@Z
     ?At@TimeZone@absl@@QEBA?AUTimeInfo@12@V?$civil_time@Usecond_tag@time_internal@absl@@@detail@cctz@time_internal@2@@Z
@@ -614,7 +613,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPEBDPEBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPEBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPEBXPEADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QEAA@$$QEAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPEAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QEAAXXZ
     ?Remove@CondVar@absl@@AEAAXPEAUPerThreadSynch@base_internal@2@@Z
     ?RemoveEdge@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@0@Z
@@ -627,6 +625,7 @@
     ?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AEAA_NAEBV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
     ?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
     ?Rethrow@variant_internal@absl@@YAXXZ
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QEBA_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPEBD_K@Z
     ?SampleSlow@container_internal@absl@@YAPEAUHashtablezInfo@12@PEA_J@Z
     ?Seek@CordRepRingReader@cord_internal@absl@@QEAA?AVstring_view@3@_K@Z
@@ -764,7 +763,6 @@
     ?Unregister@HashtablezSampler@container_internal@absl@@QEAAXPEAUHashtablezInfo@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPEAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QEAAXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QEAAXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QEAAXUGraphId@23@HP6AHPEAPEAXH@Z@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ?Utf8SafeCHexEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -904,7 +902,6 @@
     ?safe_strtou32_base@numbers_internal@absl@@YA_NVstring_view@2@PEAIH@Z
     ?safe_strtou64_base@numbers_internal@absl@@YA_NVstring_view@2@PEA_KH@Z
     ?set_cordz_mean_interval@cord_internal@absl@@YAXH@Z
-    ?set_tree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@@Z
     ?shallow_subcords_enabled@cord_internal@absl@@3U?$atomic@_N@__1@std@@A
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QEAAXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QEBAHXZ
diff --git a/third_party/abseil-cpp/symbols_x86_dbg.def b/third_party/abseil-cpp/symbols_x86_dbg.def
index 75334b42..e1c26cc 100644
--- a/third_party/abseil-cpp/symbols_x86_dbg.def
+++ b/third_party/abseil-cpp/symbols_x86_dbg.def
@@ -118,10 +118,6 @@
     ??$?0PAVZoneInfoSource@cctz@time_internal@absl@@U__default_init_tag@__1@std@@@?$__compressed_pair@PAVZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__1@std@@@__1@std@@QAE@$$QAPAVZoneInfoSource@cctz@time_internal@absl@@$$QAU__default_init_tag@12@@Z
     ??$?0PAVZoneInfoSource@cctz@time_internal@absl@@X@?$__compressed_pair_elem@PAVZoneInfoSource@cctz@time_internal@absl@@$0A@$0A@@__1@std@@QAE@$$QAPAVZoneInfoSource@cctz@time_internal@absl@@@Z
     ??$?0U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@X@?$__compressed_pair_elem@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@$00$00@__1@std@@QAE@$$QAU?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@12@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$$V$00@?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QAE@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$CompressedTupleImpl@V?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@U?$integer_sequence@I$0A@@3@$0A@@internal_compressed_tuple@container_internal@absl@@QAE@Uin_place_t@3@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@3@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@QAE@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@H@Z
-    ??$?0UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QAE@Uin_place_t@3@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@3@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$?0USynchEvent@absl@@@Condition@absl@@QAE@P6A_NPAUSynchEvent@1@@Z0@Z
     ??$?0Uday_tag@detail@cctz@time_internal@absl@@@?$civil_time@Umonth_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@QAE@ABV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@1234@PAX@Z
     ??$?0Uday_tag@detail@cctz@time_internal@absl@@@?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@detail@cctz@time_internal@absl@@QAE@ABV?$civil_time@Uday_tag@detail@cctz@time_internal@absl@@@1234@PAX@Z
@@ -192,7 +188,6 @@
     ??$?0V?$vector@VFormatArgImpl@str_format_internal@absl@@V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__1@std@@@__1@std@@XV012@@?$Span@$$CBVFormatArgImpl@str_format_internal@absl@@@absl@@QAE@ABV?$vector@VFormatArgImpl@str_format_internal@absl@@V?$allocator@VFormatArgImpl@str_format_internal@absl@@@__1@std@@@__1@std@@@Z
     ??$?0VBufferRawSink@str_format_internal@absl@@$0A@@FormatRawSinkImpl@str_format_internal@absl@@QAE@PAVBufferRawSink@12@@Z
     ??$?0VFILERawSink@str_format_internal@absl@@$0A@@FormatRawSinkImpl@str_format_internal@absl@@QAE@PAVFILERawSink@12@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAEAAV01@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?8PAPAVCordzHandle@cord_internal@absl@@PAPAV012@@__1@std@@YA_NABV?$__wrap_iter@PAPAVCordzHandle@cord_internal@absl@@@01@0@Z
     ??$?8PAUTransitionType@cctz@time_internal@absl@@PAU0123@@__1@std@@YA_NABV?$__wrap_iter@PAUTransitionType@cctz@time_internal@absl@@@01@0@Z
     ??$?8PBUConversionItem@ParsedFormatBase@str_format_internal@absl@@PBU0123@@__1@std@@YA_NABV?$__wrap_iter@PBUConversionItem@ParsedFormatBase@str_format_internal@absl@@@01@0@Z
@@ -358,12 +353,10 @@
     ??$Init@H@FormatArgImpl@str_format_internal@absl@@AAEXABH@Z
     ??$Initialize@V?$CopyValueAdapter@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@@?$Storage@PAUCordRep@cord_internal@absl@@$0CP@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QAEXV?$CopyValueAdapter@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@12@I@Z
     ??$Invoke@A6AXXZ$$V@Callable@base_internal@absl@@SAXA6AXXZ@Z
-    ??$Invoke@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AAVstring_view@3@@Callable@base_internal@absl@@SAX$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AAVstring_view@2@@Z
     ??$InvokeFlush@V?$basic_ostream@DU?$char_traits@D@__1@std@@@__1@std@@@str_format_internal@absl@@YAXPAV?$basic_ostream@DU?$char_traits@D@__1@std@@@__1@std@@Vstring_view@1@@Z
     ??$InvokeFlush@V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@str_format_internal@absl@@YAXPAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ??$InvokeFlush@VBufferRawSink@str_format_internal@absl@@@str_format_internal@absl@@YAXPAVBufferRawSink@01@Vstring_view@1@@Z
     ??$InvokeFlush@VFILERawSink@str_format_internal@absl@@@str_format_internal@absl@@YAXPAVFILERawSink@01@Vstring_view@1@@Z
-    ??$InvokeReleaser@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@X@cord_internal@absl@@YAXURank0@01@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@Vstring_view@1@@Z
     ??$LowLevelCallOnce@A6AXXZ$$V@base_internal@absl@@YAXPAVonce_flag@1@A6AXXZ@Z
     ??$MakeConstSpan@$SQAX@absl@@YA?AV?$Span@QAX@0@PBQAXI@Z
     ??$MakeSpan@$SI$0BAA@@absl@@YA?AV?$Span@I@0@AAY0BAA@I@Z
@@ -373,7 +366,6 @@
     ??$MakeSpan@$SI$0IA@@absl@@YA?AV?$Span@I@0@AAY0IA@I@Z
     ??$MakeSpan@$SVFormatArgImpl@str_format_internal@absl@@@absl@@YA?AV?$Span@VFormatArgImpl@str_format_internal@absl@@@0@PAVFormatArgImpl@str_format_internal@0@I@Z
     ??$Milliseconds@N$0A@@absl@@YA?AVDuration@0@N@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPAUCordRep@01@Vstring_view@1@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@IPAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@I$0A@$00$01@absl@@U45@@internal_layout@container_internal@absl@@QBEIXZ
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@IPAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@I$0A@$00@absl@@U?$integer_sequence@I$0A@$00$01@5@@internal_layout@container_internal@absl@@QBEIXZ
     ??$Offset@$00$0A@@?$LayoutImpl@V?$tuple@IPAUCordRep@cord_internal@absl@@I@__1@std@@U?$integer_sequence@I$0A@@absl@@U?$integer_sequence@I$0A@$00@5@@internal_layout@container_internal@absl@@QBEIXZ
@@ -697,8 +689,6 @@
     ??$forward@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@__1@std@@YA$$QAU?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@01@AAU201@@Z
     ??$forward@UConversionItem@ParsedFormatBase@str_format_internal@absl@@@__1@std@@YA$$QAUConversionItem@ParsedFormatBase@str_format_internal@absl@@AAU2345@@Z
     ??$forward@UPayload@status_internal@absl@@@__1@std@@YA$$QAUPayload@status_internal@absl@@AAU234@@Z
-    ??$forward@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@__1@std@@YA$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@01@@Z@AAU2?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@34@QAE@0@Z@@Z
-    ??$forward@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@absl@@YA$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@0@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AAU1?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@20@QAE@0@Z@@Z
     ??$forward@USubRange@absl@@@__1@std@@YA$$QAUSubRange@absl@@AAU23@@Z
     ??$forward@UTransition@cctz@time_internal@absl@@@__1@std@@YA$$QAUTransition@cctz@time_internal@absl@@AAU2345@@Z
     ??$forward@UTransitionType@cctz@time_internal@absl@@@__1@std@@YA$$QAUTransitionType@cctz@time_internal@absl@@AAU2345@@Z
@@ -732,7 +722,6 @@
     ??$get@$00@?$CompressedTuple@V?$allocator@USubRange@absl@@@__1@std@@PAUSubRange@absl@@@container_internal@absl@@QGAEAAPAUSubRange@2@XZ
     ??$get@$00Vstring_view@absl@@V12@@__1@std@@YAABVstring_view@absl@@ABU?$pair@Vstring_view@absl@@V12@@01@@Z
     ??$get@$0A@@?$CompressedTuple@IV?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@container_internal@absl@@QGBEABIXZ
-    ??$get@$0A@@?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QGAEAAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@2@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@H@__1@std@@I@container_internal@absl@@QGAEAAV?$allocator@H@__1@std@@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@H@__1@std@@PAH@container_internal@absl@@QGAEAAV?$allocator@H@__1@std@@XZ
     ??$get@$0A@@?$CompressedTuple@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@I@container_internal@absl@@QGAEAAV?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@XZ
@@ -748,7 +737,6 @@
     ??$get@Vstring_view@absl@@V12@@?$__get_pair@$00@__1@std@@SAABVstring_view@absl@@ABU?$pair@Vstring_view@absl@@V12@@12@@Z
     ??$get@Vstring_view@absl@@V12@@?$__get_pair@$0A@@__1@std@@SAABVstring_view@absl@@ABU?$pair@Vstring_view@absl@@V12@@12@@Z
     ??$invoke@A6AXXZ$$V@base_internal@absl@@YAXA6AXXZ@Z
-    ??$invoke@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AAVstring_view@3@@base_internal@absl@@YAX$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@AAVstring_view@1@@Z
     ??$lower_bound@PBUTransition@cctz@time_internal@absl@@U1234@UByUnixTime@1234@@__1@std@@YAPBUTransition@cctz@time_internal@absl@@PBU2345@0ABU2345@UByUnixTime@2345@@Z
     ??$make_pair@AAPAUCordRep@cord_internal@absl@@AAPAU123@@__1@std@@YA?AU?$pair@PAUCordRep@cord_internal@absl@@PAU123@@01@AAPAUCordRep@cord_internal@absl@@0@Z
     ??$make_unique@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@$$V@__1@std@@YA?AV?$unique_ptr@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@01@XZ
@@ -783,7 +771,6 @@
     ??$move@AAPBV?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@YA$$QAPBV?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@AAPBV23@@Z
     ??$move@AAUConversionItem@ParsedFormatBase@str_format_internal@absl@@@__1@std@@YA$$QAUConversionItem@ParsedFormatBase@str_format_internal@absl@@AAU2345@@Z
     ??$move@AAUPayload@status_internal@absl@@@__1@std@@YA$$QAUPayload@status_internal@absl@@AAU234@@Z
-    ??$move@AAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@__1@std@@YA$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@01@@Z@AAU2?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@34@QAE@0@Z@@Z
     ??$move@AAUSubRange@absl@@@__1@std@@YA$$QAUSubRange@absl@@AAU23@@Z
     ??$move@AAUTransition@cctz@time_internal@absl@@@__1@std@@YA$$QAUTransition@cctz@time_internal@absl@@AAU2345@@Z
     ??$move@AAUTransitionType@cctz@time_internal@absl@@@__1@std@@YA$$QAUTransitionType@cctz@time_internal@absl@@AAU2345@@Z
@@ -1025,9 +1012,9 @@
     ??0Condition@absl@@AAE@XZ
     ??0Condition@absl@@QAE@P6A_NPAX@Z0@Z
     ??0Condition@absl@@QAE@PB_N@Z
+    ??0Cord@absl@@AAE@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0Cord@absl@@QAE@$$QAV01@@Z
     ??0Cord@absl@@QAE@ABV01@@Z
-    ??0Cord@absl@@QAE@Vstring_view@1@@Z
     ??0Cord@absl@@QAE@XZ
     ??0CordForest@absl@@QAE@I@Z
     ??0CordRep@cord_internal@absl@@QAE@XZ
@@ -1086,7 +1073,6 @@
     ??0StatusRep@status_internal@absl@@QAE@W4StatusCode@2@Vstring_view@2@V?$unique_ptr@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@U?$default_delete@V?$InlinedVector@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@absl@@@__1@std@@@__1@std@@@Z
     ??0Storage@?$FixedArray@PAUCordRep@cord_internal@absl@@$0PPPPPPPP@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QAE@IABV?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@Z
     ??0Streamable@str_format_internal@absl@@QAE@ABVUntypedFormatSpecImpl@12@V?$Span@$$CBVFormatArgImpl@str_format_internal@absl@@@2@@Z
-    ??0StringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QAE@$$QAU0?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@12@QAE@0@Z@@Z
     ??0SubRange@absl@@QAE@PAUCordRep@cord_internal@1@II@Z
     ??0SynchWaitParams@absl@@QAE@PBUMuHowS@1@PBVCondition@1@VKernelTimeout@synchronization_internal@1@PAVMutex@1@PAUPerThreadSynch@base_internal@1@PAU?$atomic@H@__1@std@@@Z
     ??0Time@absl@@AAE@VDuration@1@@Z
@@ -1144,9 +1130,6 @@
     ??1?$AllocationTransaction@V?$allocator@PBUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
     ??1?$AllocationTransaction@V?$allocator@UPayload@status_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
     ??1?$AllocationTransaction@V?$allocator@USubRange@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
-    ??1?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@QAE@XZ
-    ??1?$CompressedTupleImpl@V?$CompressedTuple@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@container_internal@absl@@U?$integer_sequence@I$0A@@3@$0A@@internal_compressed_tuple@container_internal@absl@@QAE@XZ
-    ??1?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@QAE@XZ
     ??1?$FixedArray@PAUCordRep@cord_internal@absl@@$0PPPPPPPP@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QAE@XZ
     ??1?$InlinedVector@H$0CP@V?$allocator@H@__1@std@@@absl@@QAE@XZ
     ??1?$InlinedVector@PAUCordRep@cord_internal@absl@@$01V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QAE@XZ
@@ -1159,7 +1142,6 @@
     ??1?$Storage@PAUCordRep@cord_internal@absl@@$0CP@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
     ??1?$Storage@PBUCordRep@cord_internal@absl@@$0CP@V?$allocator@PBUCordRep@cord_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
     ??1?$Storage@UPayload@status_internal@absl@@$00V?$allocator@UPayload@status_internal@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
-    ??1?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QAE@XZ
     ??1?$Storage@USubRange@absl@@$0CP@V?$allocator@USubRange@absl@@@__1@std@@@inlined_vector_internal@absl@@QAE@XZ
     ??1?$__policy_func@$$A6A?AV?$unique_ptr@VZoneInfoSource@cctz@time_internal@absl@@U?$default_delete@VZoneInfoSource@cctz@time_internal@absl@@@__1@std@@@__1@std@@ABV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@23@@Z@__function@__1@std@@QAE@XZ
     ??1?$__policy_func@$$A6AXVstring_view@absl@@ABVCord@2@@Z@__function@__1@std@@QAE@XZ
@@ -1231,7 +1213,6 @@
     ??1StatusRep@status_internal@absl@@QAE@XZ
     ??1Storage@?$FixedArray@PAUCordRep@cord_internal@absl@@$0PPPPPPPP@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@absl@@QAE@XZ
     ??1Streamable@str_format_internal@absl@@QAE@XZ
-    ??1StringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QAE@XZ
     ??1TimeZoneIf@cctz@time_internal@absl@@UAE@XZ
     ??1TimeZoneInfo@cctz@time_internal@absl@@UAE@XZ
     ??1TimeZoneLibC@cctz@time_internal@absl@@UAE@XZ
@@ -1449,7 +1430,6 @@
     ??R<lambda_1>@?0??pop_back@?$InlinedVector@USubRange@absl@@$0CP@V?$allocator@USubRange@absl@@@__1@std@@@absl@@QAEXXZ@QBE?A?<auto>@@XZ
     ??R<lambda_1>@?0??remove_prefix@string_view@absl@@QAEXI@Z@QBE?A?<auto>@@XZ
     ??R<lambda_1>@?0??remove_suffix@string_view@absl@@QAEXI@Z@QBE?A?<auto>@@XZ
-    ??R<lambda_1>@?0??replace_tree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@4@@Z@QBE?A?<auto>@@XZ
     ??R<lambda_1>@?0??set_inline_size@InlineData@cord_internal@absl@@QAEXI@Z@QBE?A?<auto>@@XZ
     ??R<lambda_2>@?0???$AddRing@$00@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@0II@Z@QBE?A?<auto>@@I@Z
     ??R<lambda_2>@?0???$AddRing@$0A@@CordRepRing@cord_internal@absl@@CAPAV123@PAV123@0II@Z@QBE?A?<auto>@@I@Z
@@ -1477,7 +1457,6 @@
     ??R?$function@$$A6AXVstring_view@absl@@ABVCord@2@@Z@__1@std@@QBEXVstring_view@absl@@ABVCord@4@@Z
     ??RByCivilTime@Transition@cctz@time_internal@absl@@QBE_NABU1234@0@Z
     ??RByUnixTime@Transition@cctz@time_internal@absl@@QBE_NABU1234@0@Z
-    ??RStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@QAEXVstring_view@2@@Z
     ??Sabsl@@YA?AVuint128@0@V10@@Z
     ??XDuration@absl@@QAEAAV01@N@Z
     ??XDuration@absl@@QAEAAV01@_J@Z
@@ -1577,6 +1556,7 @@
     ?AssertHeld@Mutex@absl@@QBEXXZ
     ?AssertNotHeld@Mutex@absl@@QBEXXZ
     ?AssertReaderHeld@Mutex@absl@@QBEXXZ
+    ?AssignLargeString@Cord@absl@@AAEAAV12@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignNext@?$IteratorValueAdapter@V?$allocator@UPayload@status_internal@absl@@@__1@std@@V?$move_iterator@PAUPayload@status_internal@absl@@@23@@inlined_vector_internal@absl@@QAEXPAUPayload@status_internal@3@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AAEXABV123@@Z
     ?At@TimeZone@absl@@QBE?AUCivilInfo@12@VTime@2@@Z
@@ -1735,6 +1715,7 @@
     ?DumpPCAndFrameSizesAndStackTrace@debugging_internal@absl@@YAXPAXQBQAXQAHHH_NP6AXPBD0@Z0@Z
     ?DurationFromTimespec@absl@@YA?AVDuration@1@Utimespec@@@Z
     ?DurationFromTimeval@absl@@YA?AVDuration@1@Utimeval@@@Z
+    ?EmplaceTree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@ABVInlineData@53@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?EmplaceTree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?EmptyString@Status@absl@@CAPBV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@XZ
     ?EnableDebugLog@CondVar@absl@@QAEXPBD@Z
@@ -2196,7 +2177,6 @@
     ?ReaderUnlock@Mutex@absl@@QAEXXZ
     ?ReclaimThreadIdentity@synchronization_internal@absl@@YAXPAX@Z
     ?RecordInsertSlow@container_internal@absl@@YAXPAUHashtablezInfo@12@II@Z
-    ?RecordMetrics@CordzInfo@cord_internal@absl@@QAEX_J@Z
     ?Ref@CordRep@cord_internal@absl@@SAPAU123@PAU123@@Z
     ?Register@CycleClockSource@base_internal@absl@@CAXP6A_JXZ@Z
     ?Register@HashtablezSampler@container_internal@absl@@QAEPAUHashtablezInfo@23@XZ
@@ -2208,7 +2188,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPBDPBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPBXPADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QAEXXZ
     ?Remove@CondVar@absl@@AAEXPAUPerThreadSynch@base_internal@2@@Z
     ?RemoveChunkPrefix@ChunkIterator@Cord@absl@@AAEXI@Z
@@ -2233,6 +2212,7 @@
     ?Rethrow@variant_internal@absl@@YAXXZ
     ?RoundUp@cord_internal@absl@@YAIII@Z
     ?RoundUpForTag@cord_internal@absl@@YAII@Z
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QBE_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPBDI@Z
     ?SampleSlow@container_internal@absl@@YAPAUHashtablezInfo@12@PA_J@Z
     ?Seconds@absl@@YA?AVDuration@1@_J@Z
@@ -2268,6 +2248,7 @@
     ?SetToZero@?$BigUnsigned@$03@strings_internal@absl@@QAEXXZ
     ?SetToZero@?$BigUnsigned@$0FE@@strings_internal@absl@@QAEXXZ
     ?SetTree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@ABVCordzUpdateScope@53@@Z
+    ?SetTreeOrEmpty@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@ABVCordzUpdateScope@53@@Z
     ?SetValue@?$Manager@H$01@FormatArgImpl@str_format_internal@absl@@SA?ATData@234@ABH@Z
     ?SetWidth@FormatConversionSpecImplFriend@str_format_internal@absl@@SAXHPAVFormatConversionSpecImpl@23@@Z
     ?ShiftLeft@?$BigUnsigned@$03@strings_internal@absl@@QAEXH@Z
@@ -2442,10 +2423,9 @@
     ?UnrefNonInlined@Status@absl@@CAXI@Z
     ?UnrefTree@InlineRep@Cord@absl@@AAEXXZ
     ?Unregister@HashtablezSampler@container_internal@absl@@QAEXPAUHashtablezInfo@23@@Z
+    ?UnsafeSetCordRep@CordzInfo@cord_internal@absl@@AAEXPAUCordRep@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QAEXXZ
-    ?UpdateCordzStatistics@InlineRep@Cord@absl@@QAEXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QAEXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QAEXUGraphId@23@HP6AHPAPAXH@Z@Z
     ?UsingInlinedStorage@Storage@?$FixedArray@PAUCordRep@cord_internal@absl@@$0PPPPPPPP@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@@absl@@CA_NI@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -3138,7 +3118,6 @@
     ?get@?$Storage@PAPBUCordRep@cord_internal@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QGAEAAPAPBUCordRep@cord_internal@4@XZ
     ?get@?$Storage@PAUPayload@status_internal@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QGAEAAPAUPayload@status_internal@4@XZ
     ?get@?$Storage@PAUSubRange@absl@@$00$0A@@internal_compressed_tuple@container_internal@absl@@QGAEAAPAUSubRange@4@XZ
-    ?get@?$Storage@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@$0A@$0A@@internal_compressed_tuple@container_internal@absl@@QGAEAAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@4@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@XZ
     ?get@?$Storage@V?$allocator@H@__1@std@@$0A@$00@internal_compressed_tuple@container_internal@absl@@QGAEAAV?$allocator@H@__1@std@@XZ
     ?get@?$Storage@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@$00$00@internal_compressed_tuple@container_internal@absl@@QGAEAAV?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@XZ
     ?get@?$Storage@V?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@$0A@$00@internal_compressed_tuple@container_internal@absl@@QGAEAAV?$allocator@PAUCordRep@cord_internal@absl@@@__1@std@@XZ
@@ -3183,7 +3162,6 @@
     ?is_leap_year@impl@detail@cctz@time_internal@absl@@YA_N_J@Z
     ?is_length@ConvTag@str_format_internal@absl@@QBE_NXZ
     ?is_profiled@InlineData@cord_internal@absl@@QBE_NXZ
-    ?is_profiled@InlineRep@Cord@absl@@QBE_NXZ
     ?is_snapshot@CordzHandle@cord_internal@absl@@QBE_NXZ
     ?is_tree@InlineData@cord_internal@absl@@QBE_NXZ
     ?is_tree@InlineRep@Cord@absl@@QBE_NXZ
@@ -3309,7 +3287,6 @@
     ?remove_prefix@string_view@absl@@QAEXI@Z
     ?remove_suffix@string_view@absl@@QAEXI@Z
     ?rend@string_view@absl@@QBE?AV?$reverse_iterator@PBD@__1@std@@XZ
-    ?replace_tree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@@Z
     ?reserve@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QAEXI@Z
     ?reserve@?$vector@UTransitionType@cctz@time_internal@absl@@V?$allocator@UTransitionType@cctz@time_internal@absl@@@__1@std@@@__1@std@@QAEXI@Z
     ?reserve@?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__1@std@@@__1@std@@QAEXI@Z
@@ -3389,7 +3366,6 @@
     ?set_inline_size@InlineData@cord_internal@absl@@QAEXI@Z
     ?set_inline_size@InlineRep@Cord@absl@@AAEXI@Z
     ?set_tree@InlineData@cord_internal@absl@@QAEXPAUCordRep@23@@Z
-    ?set_tree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@@Z
     ?set_value@InputValue@UnboundConversion@str_format_internal@absl@@QAEXH@Z
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QAEXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QBEHXZ
diff --git a/third_party/abseil-cpp/symbols_x86_rel.def b/third_party/abseil-cpp/symbols_x86_rel.def
index e816caa..1c763535 100644
--- a/third_party/abseil-cpp/symbols_x86_rel.def
+++ b/third_party/abseil-cpp/symbols_x86_rel.def
@@ -1,6 +1,5 @@
 EXPORTS
     ??$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
-    ??$?4V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAEAAV01@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ??$?MUsecond_tag@detail@cctz@time_internal@absl@@U01234@@detail@cctz@time_internal@absl@@YA_NABV?$civil_time@Usecond_tag@detail@cctz@time_internal@absl@@@0123@0@Z
     ??$?RW4LogSeverity@absl@@ABQBDHAAPBD@?$AtomicHook@P6AXW4LogSeverity@absl@@PBDHABV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@base_internal@absl@@QBEX$$QAW4LogSeverity@2@ABQBD$$QAHAAPBD@Z
     ??$AddRing@$00@CordRepRing@cord_internal@absl@@CAPAV012@PAV012@0II@Z
@@ -62,7 +61,6 @@
     ??$GenericCompare@HVstring_view@absl@@@absl@@YAHABVCord@0@ABVstring_view@0@I@Z
     ??$GenericCompare@_NVCord@absl@@@absl@@YA_NABVCord@0@0I@Z
     ??$GenericCompare@_NVstring_view@absl@@@absl@@YA_NABVCord@0@ABVstring_view@0@I@Z
-    ??$NewExternalRep@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@YAPAUCordRep@01@Vstring_view@1@$$QAUStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@1@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@Z
     ??$ParseFloat@$09@strings_internal@absl@@YA?AUParsedFloat@01@PBD0W4chars_format@1@@Z
     ??$ParseFloat@$0BA@@strings_internal@absl@@YA?AUParsedFloat@01@PBD0W4chars_format@1@@Z
     ??$ParseFormatString@UParsedFormatConsumer@ParsedFormatBase@str_format_internal@absl@@@str_format_internal@absl@@YA_NVstring_view@1@UParsedFormatConsumer@ParsedFormatBase@01@@Z
@@ -108,7 +106,7 @@
     ??0Condition@absl@@AAE@XZ
     ??0Condition@absl@@QAE@P6A_NPAX@Z0@Z
     ??0Condition@absl@@QAE@PB_N@Z
-    ??0Cord@absl@@QAE@Vstring_view@1@@Z
+    ??0Cord@absl@@AAE@Vstring_view@1@W4MethodIdentifier@CordzUpdateTracker@cord_internal@1@@Z
     ??0CordzHandle@cord_internal@absl@@IAE@_N@Z
     ??0CordzInfo@cord_internal@absl@@AAE@PAUCordRep@12@PBV012@W4MethodIdentifier@CordzUpdateTracker@12@@Z
     ??0GraphCycles@synchronization_internal@absl@@QAE@XZ
@@ -223,6 +221,7 @@
     ?AssertHeld@Mutex@absl@@QBEXXZ
     ?AssertNotHeld@Mutex@absl@@QBEXXZ
     ?AssertReaderHeld@Mutex@absl@@QBEXXZ
+    ?AssignLargeString@Cord@absl@@AAEAAV12@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z
     ?AssignSlow@InlineRep@Cord@absl@@AAEXABV123@@Z
     ?At@TimeZone@absl@@QBE?AUCivilInfo@12@VTime@2@@Z
     ?At@TimeZone@absl@@QBE?AUTimeInfo@12@V?$civil_time@Usecond_tag@time_internal@absl@@@detail@cctz@time_internal@2@@Z
@@ -596,7 +595,6 @@
     ?RegisterMutexTracer@absl@@YAXP6AXPBDPBX_J@Z@Z
     ?RegisterSpinLockProfiler@base_internal@absl@@YAXP6AXPBX_J@Z@Z
     ?RegisterSymbolizer@absl@@YAXP6A_NPBXPADH@Z@Z
-    ?Release@?$CordRepExternalImpl@UStringReleaser@?M@???$?0V?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@$0A@@Cord@absl@@QAE@$$QAV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@@Z@@cord_internal@absl@@SAXPAUCordRepExternal@23@@Z
     ?Release@ReleasableMutexLock@absl@@QAEXXZ
     ?Remove@CondVar@absl@@AAEXPAUPerThreadSynch@base_internal@2@@Z
     ?RemoveEdge@GraphCycles@synchronization_internal@absl@@QAEXUGraphId@23@0@Z
@@ -609,6 +607,7 @@
     ?ResetToBuiltinUTC@TimeZoneInfo@cctz@time_internal@absl@@AAE_NABV?$duration@_JV?$ratio@$00$00@__1@std@@@chrono@__1@std@@@Z
     ?ResourceExhaustedError@absl@@YA?AVStatus@1@Vstring_view@1@@Z
     ?Rethrow@variant_internal@absl@@YAXXZ
+    ?SafeToDelete@CordzHandle@cord_internal@absl@@QBE_NXZ
     ?SafeWriteToStderr@raw_logging_internal@absl@@YAXPBDI@Z
     ?SampleSlow@container_internal@absl@@YAPAUHashtablezInfo@12@PA_J@Z
     ?Seek@CordRepRingReader@cord_internal@absl@@QAE?AVstring_view@3@I@Z
@@ -746,7 +745,6 @@
     ?Unregister@HashtablezSampler@container_internal@absl@@QAEXPAUHashtablezInfo@23@@Z
     ?UnsampleSlow@container_internal@absl@@YAXPAUHashtablezInfo@12@@Z
     ?Untrack@CordzInfo@cord_internal@absl@@QAEXXZ
-    ?UpdateCordzStatisticsSlow@InlineRep@Cord@absl@@QAEXXZ
     ?UpdateStackTrace@GraphCycles@synchronization_internal@absl@@QAEXUGraphId@23@HP6AHPAPAXH@Z@Z
     ?Utf8SafeCEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
     ?Utf8SafeCHexEscape@absl@@YA?AV?$basic_string@DU?$char_traits@D@__1@std@@V?$allocator@D@23@@__1@std@@Vstring_view@1@@Z
@@ -848,7 +846,6 @@
     ?safe_strtou32_base@numbers_internal@absl@@YA_NVstring_view@2@PAIH@Z
     ?safe_strtou64_base@numbers_internal@absl@@YA_NVstring_view@2@PA_KH@Z
     ?set_cordz_mean_interval@cord_internal@absl@@YAXH@Z
-    ?set_tree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@@Z
     ?shallow_subcords_enabled@cord_internal@absl@@3U?$atomic@_N@__1@std@@A
     ?shrink_to_fit@?$vector@UTransition@cctz@time_internal@absl@@V?$allocator@UTransition@cctz@time_internal@absl@@@__1@std@@@__1@std@@QAEXXZ
     ?size@?$BigUnsigned@$03@strings_internal@absl@@QBEHXZ
diff --git a/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom b/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom
index ee8e9dcc..ca007008 100644
--- a/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom
+++ b/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom
@@ -101,10 +101,34 @@
   SCANNING_BLOCKED,
 };
 
+// To match a filter, a Bluetooth device must:
+// - support all the UUIDs in the services list if that member is present,
+// - have a name equal to name if that member is present,
+// - have a name starting with name_prefix if that member is present,
+// - advertise manufacturer data matching all of the values in
+//   manufacturer_data if that member is present.
 struct WebBluetoothLeScanFilter {
   array<bluetooth.mojom.UUID>? services;
   string? name;
   string? name_prefix;
+  map<WebBluetoothCompany, array<WebBluetoothDataFilter>>? manufacturer_data;
+};
+
+// A Bluetooth company identifier is a unique number assigned by the Bluetooth
+// SIG to member companies. This struct is currently required in
+// WebBluetoothLeScanFilter because WTF::HashMap doesn't allow integer keys of 0
+// and -1, 0xffff being -1 in a uint16t. See crbug.com/1204960
+struct WebBluetoothCompany {
+  uint16 id;
+};
+
+// This struct is used as elements of an array to filter Bluetooth device data
+// such as manufacturer data and service data. Bluetooth device data matches
+// if for each bit in mask, the corresponding bit in Bluetooth device data is
+// equal to the corresponding bit in |data|.
+struct WebBluetoothDataFilter {
+  uint8 data;
+  uint8 mask;
 };
 
 struct WebBluetoothRequestDeviceOptions {
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 71e78535..6a1be66 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3204,6 +3204,11 @@
   kCSSFilterColorMatrix = 3889,
   kHTMLFencedFrameElement = 3890,
   kCSSFilterLuminanceToAlpha = 3891,
+  kHandwritingRecognitionCreateRecognizer = 3892,
+  kHandwritingRecognitionQuerySupport = 3893,
+  kHandwritingRecognitionStartDrawing = 3894,
+  kHandwritingRecognitionGetPrediction = 3895,
+  kWebBluetoothManufacturerDataFilter = 3896,
 
   // 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/bindings/core/v8/native_value_traits_impl.h b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
index 2971361..0fc6904 100644
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h
@@ -827,6 +827,85 @@
 struct NativeValueTraits<IDLNullable<IDLPromise>>;
 
 // Sequence types
+
+namespace bindings {
+
+// Fast case: we're iterating over an Array that adheres to
+// %ArrayIteratorPrototype%'s protocol.
+template <typename T>
+typename NativeValueTraits<IDLSequence<T>>::ImplType
+CreateIDLSequenceFromV8Array(v8::Isolate* isolate,
+                             v8::Local<v8::Array> v8_array,
+                             ExceptionState& exception_state) {
+  // https://heycam.github.io/webidl/#create-sequence-from-iterable
+  const uint32_t length = v8_array->Length();
+  if (length > NativeValueTraits<IDLSequence<T>>::ImplType::MaxCapacity()) {
+    exception_state.ThrowRangeError("Array length exceeds supported limit.");
+    return {};
+  }
+
+  typename NativeValueTraits<IDLSequence<T>>::ImplType result;
+  result.ReserveInitialCapacity(length);
+  v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
+  v8::TryCatch try_block(isolate);
+  // Array length may change if array is mutated during iteration.
+  for (uint32_t i = 0; i < v8_array->Length(); ++i) {
+    v8::Local<v8::Value> v8_element;
+    if (!v8_array->Get(current_context, i).ToLocal(&v8_element)) {
+      exception_state.RethrowV8Exception(try_block.Exception());
+      return {};
+    }
+    // 3.4. Initialize Si to the result of converting nextItem to an IDL value
+    //   of type T.
+    auto&& element =
+        NativeValueTraits<T>::NativeValue(isolate, v8_element, exception_state);
+    if (exception_state.HadException())
+      return {};
+    result.push_back(std::move(element));
+  }
+  // 3.2. If next is false, then return an IDL sequence value of type
+  //   sequence<T> of length i, where the value of the element at index j is Sj.
+  return result;
+}
+
+// Slow case: follow WebIDL's "Creating a sequence from an iterable" steps to
+// iterate through each element.
+template <typename T>
+typename NativeValueTraits<IDLSequence<T>>::ImplType
+CreateIDLSequenceFromIterator(v8::Isolate* isolate,
+                              ScriptIterator script_iterator,
+                              ExceptionState& exception_state) {
+  // https://heycam.github.io/webidl/#create-sequence-from-iterable
+  ExecutionContext* execution_context =
+      ToExecutionContext(isolate->GetCurrentContext());
+  typename NativeValueTraits<IDLSequence<T>>::ImplType result;
+  // 3. Repeat:
+  while (script_iterator.Next(execution_context, exception_state)) {
+    // 3.1. Let next be ? IteratorStep(iter).
+    DCHECK(!exception_state.HadException());
+    // 3.3. Let nextItem be ? IteratorValue(next).
+    //
+    // The value should already be non-empty, as guaranteed by the call to
+    // Next() and the |exception_state| check above.
+    v8::Local<v8::Value> v8_element =
+        script_iterator.GetValue().ToLocalChecked();
+    // 3.4. Initialize Si to the result of converting nextItem to an IDL value
+    //   of type T.
+    auto&& element =
+        NativeValueTraits<T>::NativeValue(isolate, v8_element, exception_state);
+    if (exception_state.HadException())
+      return {};
+    result.push_back(std::move(element));
+  }
+  if (exception_state.HadException())
+    return {};
+  // 3.2. If next is false, then return an IDL sequence value of type
+  //   sequence<T> of length i, where the value of the element at index j is Sj.
+  return result;
+}
+
+}  // namespace bindings
+
 template <typename T>
 struct NativeValueTraits<IDLSequence<T>>
     : public NativeValueTraitsBase<IDLSequence<T>> {
@@ -837,6 +916,15 @@
   static ImplType NativeValue(v8::Isolate* isolate,
                               v8::Local<v8::Value> value,
                               ExceptionState& exception_state) {
+    // TODO(https://crbug.com/715122): Checking for IsArray() may not be
+    // enough. Other engines also prefer regular array iteration over a custom
+    // @@iterator when the latter is defined, but it is not clear if this is a
+    // valid optimization.
+    if (value->IsArray()) {
+      return bindings::CreateIDLSequenceFromV8Array<T>(
+          isolate, value.As<v8::Array>(), exception_state);
+    }
+
     // 1. If Type(V) is not Object, throw a TypeError.
     if (!value->IsObject()) {
       exception_state.ThrowTypeError(
@@ -844,36 +932,22 @@
       return ImplType();
     }
 
-    ImplType result;
-    // TODO(https://crbug.com/715122): Checking for IsArray() may not be
-    // enough. Other engines also prefer regular array iteration over a custom
-    // @@iterator when the latter is defined, but it is not clear if this is a
-    // valid optimization.
-    if (value->IsArray()) {
-      ConvertSequenceFast(isolate, value.As<v8::Array>(), exception_state,
-                          result);
-    } else {
-      // 2. Let method be ? GetMethod(V, @@iterator).
-      // 3. If method is undefined, throw a TypeError.
-      // 4. Return the result of creating a sequence from V and method.
-      auto script_iterator = ScriptIterator::FromIterable(
-          isolate, value.As<v8::Object>(), exception_state);
-      if (exception_state.HadException())
-        return ImplType();
-      if (script_iterator.IsNull()) {
-        // A null ScriptIterator with an empty |exception_state| means the
-        // object is lacking a callable @@iterator property.
-        exception_state.ThrowTypeError(
-            "The object must have a callable @@iterator property.");
-        return ImplType();
-      }
-      ConvertSequenceSlow(isolate, std::move(script_iterator), exception_state,
-                          result);
-    }
-
+    // 2. Let method be ? GetMethod(V, @@iterator).
+    // 3. If method is undefined, throw a TypeError.
+    // 4. Return the result of creating a sequence from V and method.
+    auto script_iterator = ScriptIterator::FromIterable(
+        isolate, value.As<v8::Object>(), exception_state);
     if (exception_state.HadException())
       return ImplType();
-    return result;
+    if (script_iterator.IsNull()) {
+      // A null ScriptIterator with an empty |exception_state| means the
+      // object is lacking a callable @@iterator property.
+      exception_state.ThrowTypeError(
+          "The object must have a callable @@iterator property.");
+      return ImplType();
+    }
+    return bindings::CreateIDLSequenceFromIterator<T>(
+        isolate, std::move(script_iterator), exception_state);
   }
 
   // https://heycam.github.io/webidl/#es-sequence
@@ -883,74 +957,8 @@
                               ScriptIterator script_iterator,
                               ExceptionState& exception_state) {
     DCHECK(!script_iterator.IsNull());
-    ImplType result;
-    ConvertSequenceSlow(isolate, std::move(script_iterator), exception_state,
-                        result);
-    return result;
-  }
-
- private:
-  // Fast case: we're interating over an Array that adheres to
-  // %ArrayIteratorPrototype%'s protocol.
-  static void ConvertSequenceFast(v8::Isolate* isolate,
-                                  v8::Local<v8::Array> v8_array,
-                                  ExceptionState& exception_state,
-                                  ImplType& result) {
-    const uint32_t length = v8_array->Length();
-    if (length > ImplType::MaxCapacity()) {
-      exception_state.ThrowRangeError("Array length exceeds supported limit.");
-      return;
-    }
-    result.ReserveInitialCapacity(length);
-    v8::TryCatch block(isolate);
-    // Array length may change if array is mutated during iteration.
-    for (uint32_t i = 0; i < v8_array->Length(); ++i) {
-      v8::Local<v8::Value> element;
-      if (!v8_array->Get(isolate->GetCurrentContext(), i).ToLocal(&element)) {
-        exception_state.RethrowV8Exception(block.Exception());
-        return;
-      }
-      result.push_back(
-          NativeValueTraits<T>::NativeValue(isolate, element, exception_state));
-      if (exception_state.HadException())
-        return;
-    }
-  }
-
-  // Slow case: follow WebIDL's "Creating a sequence from an iterable" steps to
-  // iterate through each element.
-  static void ConvertSequenceSlow(v8::Isolate* isolate,
-                                  ScriptIterator script_iterator,
-                                  ExceptionState& exception_state,
-                                  ImplType& result) {
-    // https://heycam.github.io/webidl/#create-sequence-from-iterable
-    // 2. Initialize i to be 0.
-    // 3. Repeat:
-    ExecutionContext* execution_context =
-        ToExecutionContext(isolate->GetCurrentContext());
-    while (script_iterator.Next(execution_context, exception_state)) {
-      // 3.1. Let next be ? IteratorStep(iter).
-      // 3.2. If next is false, then return an IDL sequence value of type
-      //      sequence<T> of length i, where the value of the element at index
-      //      j is Sj.
-      // 3.3. Let nextItem be ? IteratorValue(next).
-      if (exception_state.HadException())
-        return;
-
-      // The value should already be non-empty, as guaranteed by the call to
-      // Next() and the |exception_state| check above.
-      v8::Local<v8::Value> element =
-          script_iterator.GetValue().ToLocalChecked();
-      DCHECK(!element.IsEmpty());
-
-      // 3.4. Initialize Si to the result of converting nextItem to an IDL
-      //      value of type T.
-      // 3.5. Set i to i + 1.
-      result.push_back(
-          NativeValueTraits<T>::NativeValue(isolate, element, exception_state));
-      if (exception_state.HadException())
-        return;
-    }
+    return bindings::CreateIDLSequenceFromIterator<T>(
+        isolate, std::move(script_iterator), exception_state);
   }
 };
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_iterator.cc b/third_party/blink/renderer/bindings/core/v8/script_iterator.cc
index 6520efd4..18648cb9 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_iterator.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_iterator.cc
@@ -13,68 +13,89 @@
 
 // static
 ScriptIterator ScriptIterator::FromIterable(v8::Isolate* isolate,
-                                            v8::Local<v8::Object> value,
+                                            v8::Local<v8::Object> iterable,
                                             ExceptionState& exception_state) {
-  // First, call the GetMethod(V, @@iterator) abstract ES operation.
-  const v8::Local<v8::Function> iterator_method =
-      GetEsIteratorMethod(isolate, value, exception_state);
-  if (exception_state.HadException())
-    return ScriptIterator();
-  if (iterator_method.IsEmpty())
-    return ScriptIterator();
+  // 7.4.1 GetIterator ( obj [ , hint [ , method ] ] )
+  // https://tc39.es/ecma262/#sec-getiterator
+  v8::TryCatch try_catch(isolate);
+  v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
 
-  // Use the method returned above to invoke the GetIterator(V, sync, method)
-  // abstract ES operation.
-  const v8::Local<v8::Object> iterator =
-      GetEsIteratorWithMethod(isolate, iterator_method, value, exception_state);
-  if (exception_state.HadException())
+  // 3.b. Otherwise, set method to ? GetMethod(obj, @@iterator).
+  v8::Local<v8::Value> method;
+  if (!iterable->Get(current_context, v8::Symbol::GetIterator(isolate))
+           .ToLocal(&method)) {
+    exception_state.RethrowV8Exception(try_catch.Exception());
     return ScriptIterator();
+  }
+  if (method->IsNullOrUndefined()) {
+    // Some algorithms in Web IDL want to change their behavior when `method` is
+    // undefined, so give them a choice.
+    return ScriptIterator();  // Return without an exception.
+  }
+  if (!method->IsFunction()) {
+    exception_state.ThrowTypeError("@@iterator must be a callable.");
+    return ScriptIterator();
+  }
 
-  return ScriptIterator(isolate, iterator);
+  // 4. Let iterator be ? Call(method, obj).
+  v8::Local<v8::Value> iterator;
+  if (!V8ScriptRunner::CallFunction(method.As<v8::Function>(),
+                                    ToExecutionContext(current_context),
+                                    iterable, 0, nullptr, isolate)
+           .ToLocal(&iterator)) {
+    exception_state.RethrowV8Exception(try_catch.Exception());
+    return ScriptIterator();
+  }
+  // 5. If Type(iterator) is not Object, throw a TypeError exception.
+  if (!iterator->IsObject()) {
+    exception_state.ThrowTypeError("Iterator object must be an object.");
+    return ScriptIterator();
+  }
+
+  // 6. Let nextMethod be ? GetV(iterator, "next").
+  v8::Local<v8::Value> next_method;
+  if (!iterator.As<v8::Object>()
+           ->Get(current_context, V8AtomicString(isolate, "next"))
+           .ToLocal(&next_method)) {
+    exception_state.RethrowV8Exception(try_catch.Exception());
+    return ScriptIterator();
+  }
+
+  // 7. Let iteratorRecord be the Record { [[Iterator]]: iterator,
+  //   [[NextMethod]]: nextMethod, [[Done]]: false }.
+  // 8. Return iteratorRecord.
+  return ScriptIterator(isolate, iterator.As<v8::Object>(), next_method);
 }
 
 ScriptIterator::ScriptIterator(v8::Isolate* isolate,
-                               v8::Local<v8::Object> iterator)
+                               v8::Local<v8::Object> iterator,
+                               v8::Local<v8::Value> next_method)
     : isolate_(isolate),
       iterator_(iterator),
-      next_key_(V8AtomicString(isolate, "next")),
+      next_method_(next_method),
       done_key_(V8AtomicString(isolate, "done")),
       value_key_(V8AtomicString(isolate, "value")),
       done_(false) {
-  DCHECK(!iterator.IsEmpty());
+  DCHECK(!IsNull());
 }
 
 bool ScriptIterator::Next(ExecutionContext* execution_context,
                           ExceptionState& exception_state,
-                          v8::Local<v8::Value> next_value) {
+                          v8::Local<v8::Value> value) {
   DCHECK(!IsNull());
 
-  v8::TryCatch try_catch(isolate_);
-  v8::Local<v8::Context> context = isolate_->GetCurrentContext();
-
-  v8::Local<v8::Value> next;
-  if (!iterator_->Get(context, next_key_).ToLocal(&next)) {
-    CHECK(!try_catch.Exception().IsEmpty());
-    exception_state.RethrowV8Exception(try_catch.Exception());
-    done_ = true;
-    return false;
-  }
-  if (!next->IsFunction()) {
+  if (!next_method_->IsFunction()) {
     exception_state.ThrowTypeError("Expected next() function on iterator.");
     done_ = true;
     return false;
   }
 
-  Vector<v8::Local<v8::Value>, 1> argv;
-  if (!next_value.IsEmpty())
-    argv = {next_value};
-
+  v8::TryCatch try_catch(isolate_);
   v8::Local<v8::Value> result;
-  if (!V8ScriptRunner::CallFunction(v8::Local<v8::Function>::Cast(next),
-                                    execution_context, iterator_, argv.size(),
-                                    argv.data(), isolate_)
+  if (!V8ScriptRunner::CallFunction(next_method_.As<v8::Function>(),
+                                    execution_context, iterator_,
+                                    value.IsEmpty() ? 0 : 1, &value, isolate_)
            .ToLocal(&result)) {
-    CHECK(!try_catch.Exception().IsEmpty());
     exception_state.RethrowV8Exception(try_catch.Exception());
     done_ = true;
     return false;
@@ -85,22 +106,22 @@
     done_ = true;
     return false;
   }
-  v8::Local<v8::Object> result_object = v8::Local<v8::Object>::Cast(result);
+  v8::Local<v8::Object> result_object = result.As<v8::Object>();
 
+  v8::Local<v8::Context> context = isolate_->GetCurrentContext();
   value_ = result_object->Get(context, value_key_);
   if (value_.IsEmpty()) {
-    CHECK(!try_catch.Exception().IsEmpty());
-    exception_state.RethrowV8Exception(try_catch.Exception());
-  }
-
-  v8::Local<v8::Value> done;
-  if (!result_object->Get(context, done_key_).ToLocal(&done)) {
-    CHECK(!try_catch.Exception().IsEmpty());
     exception_state.RethrowV8Exception(try_catch.Exception());
     done_ = true;
     return false;
   }
 
+  v8::Local<v8::Value> done;
+  if (!result_object->Get(context, done_key_).ToLocal(&done)) {
+    exception_state.RethrowV8Exception(try_catch.Exception());
+    done_ = true;
+    return false;
+  }
   done_ = done->BooleanValue(isolate_);
   return !done_;
 }
diff --git a/third_party/blink/renderer/bindings/core/v8/script_iterator.h b/third_party/blink/renderer/bindings/core/v8/script_iterator.h
index 0d5e3be..7bbfb65 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_iterator.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_iterator.h
@@ -61,16 +61,9 @@
   // - ScriptIterator can be null even if there is no exception. In this case,
   //   it indicates that the given ES object does not have an @@iterator
   //   property.
-  static ScriptIterator FromIterable(v8::Isolate*,
-                                     v8::Local<v8::Object>,
-                                     ExceptionState&);
-
-  // Constructs a ScriptIterator from an ES object that implements the iterator
-  // protocol: |iterator| is supposed to have a next() method that returns an
-  // object with two properties, "done" and "value".
-  ScriptIterator(v8::Isolate*, v8::Local<v8::Object> iterator);
-
-  ScriptIterator() = default;
+  static ScriptIterator FromIterable(v8::Isolate* isolate,
+                                     v8::Local<v8::Object> iterable,
+                                     ExceptionState& exception_state);
 
   ScriptIterator(ScriptIterator&&) noexcept = default;
   ScriptIterator& operator=(ScriptIterator&&) noexcept = default;
@@ -81,16 +74,25 @@
   bool IsNull() const { return iterator_.IsEmpty(); }
 
   // Returns true if the iterator is still not done.
-  bool Next(ExecutionContext*,
-            ExceptionState&,
-            v8::Local<v8::Value> next_value = v8::Local<v8::Value>());
+  bool Next(ExecutionContext* execution_context,
+            ExceptionState& exception_state,
+            v8::Local<v8::Value> value = v8::Local<v8::Value>());
 
   v8::MaybeLocal<v8::Value> GetValue() { return value_; }
 
  private:
+  // Constructs a ScriptIterator from an ES object that implements the iterator
+  // protocol: |iterator| is supposed to have a next() method that returns an
+  // object with two properties, "done" and "value".
+  ScriptIterator(v8::Isolate*,
+                 v8::Local<v8::Object> iterator,
+                 v8::Local<v8::Value> next_method);
+
+  ScriptIterator() = default;
+
   v8::Isolate* isolate_ = nullptr;
   v8::Local<v8::Object> iterator_;
-  v8::Local<v8::String> next_key_;
+  v8::Local<v8::Value> next_method_;
   v8::Local<v8::String> done_key_;
   v8::Local<v8::String> value_key_;
   bool done_ = true;
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index f96f4e0..03ab4d2 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -651,7 +651,7 @@
     ExecutionContext* context,
     v8::Local<v8::Value> receiver,
     int argc,
-    v8::Local<v8::Value> args[],
+    v8::Local<v8::Value> argv[],
     v8::Isolate* isolate) {
   LocalDOMWindow* window = DynamicTo<LocalDOMWindow>(context);
   LocalFrame* frame = window ? window->GetFrame() : nullptr;
@@ -688,7 +688,7 @@
 
   probe::CallFunction probe(context, function, depth);
   v8::MaybeLocal<v8::Value> result =
-      function->Call(isolate->GetCurrentContext(), receiver, argc, args);
+      function->Call(isolate->GetCurrentContext(), receiver, argc, argv);
   CHECK(!isolate->IsDead());
 
   if (!depth)
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
index 1bf869a..110ad7ad 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.h
@@ -140,7 +140,7 @@
                                                 ExecutionContext*,
                                                 v8::Local<v8::Value> receiver,
                                                 int argc,
-                                                v8::Local<v8::Value> info[],
+                                                v8::Local<v8::Value> argv[],
                                                 v8::Isolate*);
 
   // https://html.spec.whatwg.org/C/#run-a-module-script
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index acfe110..ce85dab 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -175,10 +175,14 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_blob_event_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_advertising_event_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_advertising_event_init.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_data_filter_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_data_filter_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_filter_init.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_filter_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_options.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_options.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_authentication_data.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_cable_registration_data.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 8c28c97..0acd6f72 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -55,10 +55,12 @@
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_advertising_event_init.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_characteristic_properties.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_device.idl",
+          "//third_party/blink/renderer/modules/bluetooth/bluetooth_data_filter_init.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan_filter_init.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan_options.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.idl",
+          "//third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_filter_init.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_characteristic.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_descriptor.idl",
           "//third_party/blink/renderer/modules/bluetooth/bluetooth_remote_gatt_server.idl",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/union.py b/third_party/blink/renderer/bindings/scripts/bind_gen/union.py
index fe36cd3..3e8825d 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/union.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/union.py
@@ -12,6 +12,8 @@
 from .blink_v8_bridge import v8_bridge_class_name
 from .code_node import EmptyNode
 from .code_node import ListNode
+from .code_node import SequenceNode
+from .code_node import SymbolDefinitionNode
 from .code_node import SymbolNode
 from .code_node import SymbolScopeNode
 from .code_node import TextNode
@@ -193,6 +195,7 @@
 
     S = SymbolNode
     T = TextNode
+    F = lambda *args, **kwargs: T(_format(*args, **kwargs))
 
     func_decl = CxxFuncDeclNode(name="Create",
                                 arg_decls=[
@@ -220,9 +223,10 @@
         "exception_state": "exception_state",
     })
 
-    # Create an instance from v8::Value based on the overload resolution
-    # algorithm.
-    # https://heycam.github.io/webidl/#dfn-overload-resolution-algorithm
+    # Create an instance from v8::Value based on the conversion algorithm.
+    #
+    # 3.2.24. Union types
+    # https://heycam.github.io/webidl/#es-union
 
     union_members = cg_context.union_members
     member = None  # Will be a found member in union_members.
@@ -239,7 +243,10 @@
                 return member
         return None
 
-    def dispatch_if(cond_text, value_symbol=None):
+    def dispatch_if(cond_text, value_symbol=None, target_node=body):
+        assert isinstance(cond_text, str) or cond_text is True
+        assert value_symbol is None or isinstance(value_symbol, SymbolNode)
+        assert isinstance(target_node, SequenceNode)
         scope_node = SymbolScopeNode(
             [T("return MakeGarbageCollected<${class_name}>(${blink_value});")])
         if not value_symbol:
@@ -250,17 +257,26 @@
                 error_exit_return_statement="return nullptr;")
         scope_node.register_code_symbol(value_symbol)
         if cond_text is True:
-            body.append(CxxBlockNode(body=scope_node))
+            target_node.append(CxxBlockNode(body=scope_node))
         else:
-            body.append(CxxUnlikelyIfNode(cond=cond_text, body=scope_node))
+            target_node.append(
+                CxxUnlikelyIfNode(cond=cond_text, body=scope_node))
 
-    # 12.3. if V is null or undefined, ...
+    # 2. If the union type includes a nullable type and V is null or undefined,
+    #   ...
     member = find_by_member(lambda m: m.is_null)
     if member:
         dispatch_if("${v8_value}->IsNullOrUndefined()",
                     S("blink_value", "auto&& ${blink_value} = nullptr;"))
 
-    # 12.4. if V is a platform object, ...
+    # 4. If V is null or undefined, then:
+    # 4.1. If types includes a dictionary type, ...
+    member = find_by_type(lambda t: t.is_dictionary)
+    if member:
+        dispatch_if("${v8_value}->IsNullOrUndefined()")
+
+    # 5. If V is a platform object, then:
+    # 5.1. If types includes an interface type that V implements, ...
     interface_members = filter(
         lambda member: member.idl_type and member.idl_type.is_interface,
         union_members)
@@ -278,8 +294,9 @@
             _format("{}::HasInstance(${isolate}, ${v8_value})",
                     v8_bridge_name))
 
-    # 12.5. if Type(V) is Object, V has an [[ArrayBufferData]] internal
-    #   slot, ...
+    # 6. If Type(V) is Object and V has an [[ArrayBufferData]] internal slot,
+    #   then:
+    # 6.1. If types includes ArrayBuffer, ...
     member = find_by_type(lambda t: t.is_array_buffer)
     if member:
         dispatch_if("${v8_value}->IsArrayBuffer() || "
@@ -290,12 +307,16 @@
     if member:
         dispatch_if("${v8_value}->IsArrayBufferView()")
 
-    # 12.6. if Type(V) is Object, V has a [[DataView]] internal slot, ...
+    # 7. If Type(V) is Object and V has a [[DataView]] internal slot, then:
+    # 7.1. If types includes DataView, ...
     member = find_by_type(lambda t: t.is_data_view)
     if member:
         dispatch_if("${v8_value}->IsDataView()")
 
-    # 12.7. if Type(V) is Object, V has a [[TypedArrayName]] internal slot, ...
+    # 8. If Type(V) is Object and V has a [[TypedArrayName]] internal slot,
+    #   then:
+    # 8.1. If types includes a typed array type whose name is the value of V's
+    #   [[TypedArrayName]] internal slot, ...
     typed_array_types = ("Int8Array", "Int16Array", "Int32Array", "Uint8Array",
                          "Uint16Array", "Uint32Array", "Uint8ClampedArray",
                          "Float32Array", "Float64Array")
@@ -304,46 +325,88 @@
         if member:
             dispatch_if(_format("${v8_value}->Is{}()", typed_array_type))
 
-    # 12.8. if IsCallable(V) is true, ...
+    # 9. If IsCallable(V) is true, then:
+    # 9.1. If types includes a callback function type, ...
     member = find_by_type(lambda t: t.is_callback_function)
     if member:
         dispatch_if("${v8_value}->IsFunction()")
 
-    # 12.9. if Type(V) is Object and ... @@iterator ...
+    # 10. If Type(V) is Object, then:
+    # 10.1. If types includes a sequence type, ...
+    # 10.2. If types includes a frozen array type, ...
     member = find_by_type(lambda t: t.is_sequence or t.is_frozen_array)
     if member:
-        dispatch_if("${v8_value}->IsArray() || "  # Excessive optimization
-                    "bindings::IsEsIterableObject"
-                    "(${isolate}, ${v8_value}, ${exception_state})")
-        body.append(
-            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
-                              body=T("return nullptr;")))
+        # TODO(crbug.com/715122): Excessive optimization
+        dispatch_if("${v8_value}->IsArray()")
 
-    # 12.10. if Type(V) is Object and ...
-    member = find_by_type(lambda t: t.is_callback_interface or t.is_dictionary
-                          or t.is_record or t.is_object)
+        # Create an IDL sequence from an iterable object.
+        scope_node = SymbolScopeNode()
+        body.append(
+            CxxUnlikelyIfNode(cond="${v8_value}->IsObject()", body=scope_node))
+        scope_node.extend([
+            T("ScriptIterator script_iterator = ScriptIterator::FromIterable("
+              "${isolate}, ${v8_value}.As<v8::Object>(), "
+              "${exception_state});"),
+            CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
+                              body=T("return nullptr;")),
+        ])
+
+        def blink_value_from_iterator(union_member):
+            def symbol_definition_constructor(symbol_node):
+                node = SymbolDefinitionNode(symbol_node)
+                node.extend([
+                    F(
+                        "auto&& ${blink_value} = "
+                        "bindings::CreateIDLSequenceFromIterator<{}>("
+                        "${isolate}, std::move(script_iterator), "
+                        "${exception_state});",
+                        native_value_tag(
+                            union_member.idl_type.unwrap().element_type)),
+                    CxxUnlikelyIfNode(cond="${exception_state}.HadException()",
+                                      body=T("return nullptr;")),
+                ])
+                return node
+
+            return symbol_definition_constructor
+
+        dispatch_if(
+            "!script_iterator.IsNull()",
+            S("blink_value",
+              definition_constructor=blink_value_from_iterator(member)),
+            target_node=scope_node)
+
+    # 10. If Type(V) is Object, then:
+    # 10.3. If types includes a dictionary type, ...
+    # 10.4. If types includes a record type, ...
+    # 10.5. If types includes a callback interface type, ...
+    # 10.6. If types includes object, ...
+    member = find_by_type(lambda t: t.is_dictionary or t.is_record or t.
+                          is_callback_interface or t.is_object)
     if member:
         dispatch_if("${v8_value}->IsObject()")
 
-    # 12.11. if Type(V) is Boolean and ...
+    # 11. If Type(V) is Boolean, then:
+    # 11.1. If types includes boolean, ...
     member = find_by_type(lambda t: t.is_boolean)
     if member:
         dispatch_if("${v8_value}->IsBoolean()")
 
-    # 12.12. if Type(V) is Number and ...
+    # 12. If Type(V) is Number, then:
+    # 12.1. If types includes a numeric type, ...
     member = find_by_type(lambda t: t.is_numeric)
     if member:
         dispatch_if("${v8_value}->IsNumber()")
 
-    # 12.13. if there is an entry in S that has ... a string type ...
-    # 12.14. if there is an entry in S that has ... a numeric type ...
-    # 12.15. if there is an entry in S that has ... boolean ...
+    # 14. If types includes a string type, ...
+    # 16. If types includes a numeric type, ...
+    # 17. If types includes boolean, ...
     member = (find_by_type(lambda t: t.is_enumeration or t.is_string)
               or find_by_type(lambda t: t.is_numeric)
               or find_by_type(lambda t: t.is_boolean))
     if member:
         dispatch_if(True)
     else:
+        # 19. Throw a TypeError.
         body.append(
             T("${exception_state}.ThrowTypeError("
               "ExceptionMessages::ValueNotOfType("
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index 6852eb9..2caa48e64 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -433,12 +433,12 @@
 }
 
 CSSScrollTimeline* CreateCSSScrollTimeline(
-    Element* element,
+    Document& document,
     CSSScrollTimeline::Options&& options) {
   if (!options.IsValid())
     return nullptr;
-  auto* scroll_timeline = MakeGarbageCollected<CSSScrollTimeline>(
-      &element->GetDocument(), std::move(options));
+  auto* scroll_timeline =
+      MakeGarbageCollected<CSSScrollTimeline>(&document, std::move(options));
   // It's is not allowed for a style resolve to create timelines that
   // needs timing updates (i.e. AnimationTimeline::NeedsAnimationTimingUpdate()
   // must return false). Servicing animations after creation preserves this
@@ -471,7 +471,7 @@
     return nullptr;
   }
   if (rule) {
-    CSSScrollTimeline::Options options(element, *rule);
+    CSSScrollTimeline::Options options(document, *rule);
 
     const AtomicString& name = timeline_name.GetName().GetValue();
     // When multiple animations refer to the same @scroll-timeline, the same
@@ -486,8 +486,11 @@
       if (timeline->Matches(options))
         return existing_timeline;
     }
-    if (auto* timeline = CreateCSSScrollTimeline(element, std::move(options)))
+    if (auto* timeline =
+            CreateCSSScrollTimeline(document, std::move(options))) {
+      document.GetDocumentAnimations().CacheCSSScrollTimeline(*timeline);
       return timeline;
+    }
   }
   return nullptr;
 }
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
index 0ea72a8..5a42582d 100644
--- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
+++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
@@ -204,13 +204,11 @@
 
 }  // anonymous namespace
 
-CSSScrollTimeline::Options::Options(Element* element,
+CSSScrollTimeline::Options::Options(Document& document,
                                     StyleRuleScrollTimeline& rule)
-    : source_(ComputeScrollSource(element->GetDocument(), rule.GetSource())),
+    : source_(ComputeScrollSource(document, rule.GetSource())),
       direction_(ComputeScrollDirection(rule.GetOrientation())),
-      offsets_(ComputeScrollOffsets(element->GetDocument(),
-                                    rule.GetStart(),
-                                    rule.GetEnd())),
+      offsets_(ComputeScrollOffsets(document, rule.GetStart(), rule.GetEnd())),
       time_range_(ComputeTimeRange(rule.GetTimeRange())),
       rule_(&rule) {}
 
@@ -223,7 +221,6 @@
       rule_(options.rule_) {
   DCHECK(options.IsValid());
   DCHECK(rule_);
-  document->GetDocumentAnimations().CacheCSSScrollTimeline(*this);
 }
 
 const AtomicString& CSSScrollTimeline::Name() const {
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h
index ef6811a5..9bc5333 100644
--- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h
+++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h
@@ -23,7 +23,7 @@
     STACK_ALLOCATED();
 
    public:
-    Options(Element*, StyleRuleScrollTimeline&);
+    Options(Document&, StyleRuleScrollTimeline&);
 
     // TODO(crbug.com/1097041): Support 'auto' value.
     bool IsValid() const { return time_range_.has_value(); }
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index fc321b9..42cdbdf 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -1999,7 +1999,8 @@
     // styles.
     GetStyleResolver().PropagateStyleToViewport();
   }
-  GetDocument().GetLayoutView()->UpdateMarkersAndCountersAfterStyleChange();
+  GetDocument().GetLayoutView()->UpdateMarkersAndCountersAfterStyleChange(
+      container.GetLayoutObject());
 }
 
 void StyleEngine::RecalcStyle(StyleRecalcChange change,
diff --git a/third_party/blink/renderer/core/fullscreen/fullscreen.cc b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
index 7360bbee..bd3ca9b 100644
--- a/third_party/blink/renderer/core/fullscreen/fullscreen.cc
+++ b/third_party/blink/renderer/core/fullscreen/fullscreen.cc
@@ -283,25 +283,16 @@
   //
   // The current implementation of WebXR's "dom-overlay" mode internally uses
   // the Fullscreen API to show a single DOM element based on configuration at
-  // XR session start. In addition, for WebXR sessions without "dom-overlay"
-  // the renderer may need to force the page to fullscreen to ensure that
-  // browser UI hides/responds accordingly. In either case, requesting a WebXR
-  // Session does require a user gesture, but it has likely expired by the time
-  // the renderer actually gets the XR session from the device and attempts
-  // to fullscreen the page.
+  // XR session start. The WebXR API doesn't support changing elements during
+  // the session, so to avoid inconsistencies between implementations we need
+  // to block changes via Fullscreen API while the XR session is active, while
+  // still allowing the XR code to set up fullscreen mode on session start.
   if (ScopedAllowFullscreen::FullscreenAllowedReason() ==
-          ScopedAllowFullscreen::kXrOverlay ||
-      ScopedAllowFullscreen::FullscreenAllowedReason() ==
-          ScopedAllowFullscreen::kXrSession) {
-    DVLOG(1) << __func__ << ": allowing fullscreen element setup for XR";
+      ScopedAllowFullscreen::kXrOverlay) {
+    DVLOG(1) << __func__
+             << ": allowing fullscreen element setup for XR DOM overlay";
     return true;
   }
-
-  // The WebXR API doesn't support changing elements during the session if the
-  // dom-overlay feature is in use (indicated by the IsXrOverlay property). To
-  // avoid inconsistencies between implementations we need to block changes via
-  // Fullscreen API while the XR session is active, while still allowing the XR
-  // code to set up fullscreen mode on session start.
   if (document.IsXrOverlay()) {
     DVLOG(1) << __func__
              << ": rejecting change of fullscreen element for XR DOM overlay";
diff --git a/third_party/blink/renderer/core/fullscreen/scoped_allow_fullscreen.h b/third_party/blink/renderer/core/fullscreen/scoped_allow_fullscreen.h
index caf1056..fb2b80f 100644
--- a/third_party/blink/renderer/core/fullscreen/scoped_allow_fullscreen.h
+++ b/third_party/blink/renderer/core/fullscreen/scoped_allow_fullscreen.h
@@ -16,7 +16,7 @@
   STACK_ALLOCATED();
 
  public:
-  enum Reason { kOrientationChange, kXrOverlay, kXrSession };
+  enum Reason { kOrientationChange, kXrOverlay };
 
   static base::Optional<Reason> FullscreenAllowedReason();
   explicit ScopedAllowFullscreen(Reason);
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index db3e0872..85eda5a1 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3461,8 +3461,9 @@
   // If the display-lock blocked child layout, then we don't clear child needs
   // layout bits. However, we can still use the cached result, since we will
   // re-layout when unlocking.
+  bool is_blocked_by_display_lock = ChildLayoutBlockedByDisplayLock();
   bool child_needs_layout_unless_locked =
-      !ChildLayoutBlockedByDisplayLock() &&
+      !is_blocked_by_display_lock &&
       (PosChildNeedsLayout() || NormalChildNeedsLayout());
 
   const NGPhysicalBoxFragment& physical_fragment =
@@ -3636,9 +3637,9 @@
 
   // For example, for elements with a transform change we can re-use the cached
   // result but we still need to recalculate the layout overflow.
-  if (RuntimeEnabledFeatures::LayoutNGLayoutOverflowRecalcEnabled() &&
-      use_layout_cache_slot && NeedsLayoutOverflowRecalc() &&
-      !ChildLayoutBlockedByDisplayLock()) {
+  if (use_layout_cache_slot && !is_blocked_by_display_lock &&
+      NeedsLayoutOverflowRecalc() &&
+      RuntimeEnabledFeatures::LayoutNGLayoutOverflowRecalcEnabled()) {
 #if DCHECK_IS_ON()
     const NGLayoutResult* cloned_cached_layout_result =
         NGLayoutResult::CloneWithPostLayoutFragments(*cached_layout_result);
diff --git a/third_party/blink/renderer/core/layout/layout_view.cc b/third_party/blink/renderer/core/layout/layout_view.cc
index 140b919..63dc0522 100644
--- a/third_party/blink/renderer/core/layout/layout_view.cc
+++ b/third_party/blink/renderer/core/layout/layout_view.cc
@@ -925,17 +925,35 @@
   return CompositingReason::kNone;
 }
 
-void LayoutView::UpdateMarkersAndCountersAfterStyleChange() {
+void LayoutView::UpdateMarkersAndCountersAfterStyleChange(
+    LayoutObject* container) {
   NOT_DESTROYED();
   if (!needs_marker_counter_update_)
     return;
 
+  DCHECK(!container ||
+         (container->View() == this && container->IsDescendantOf(this) &&
+          GetDocument().GetStyleEngine().InContainerQueryStyleRecalc()))
+      << "The container parameter is currently only for scoping updates for "
+         "container query style recalcs";
+
   needs_marker_counter_update_ = false;
   if (!HasLayoutCounters() && !HasLayoutListItems())
     return;
 
-  for (LayoutObject* layout_object = this; layout_object;
-       layout_object = layout_object->NextInPreOrder()) {
+  // For container queries style recalc, we know the counter styles didn't
+  // change outside the container. Hence, we can start the update traversal from
+  // the container.
+  LayoutObject* start = container ? container : this;
+  // Additionally, if the container contains style, we know counters inside the
+  // container cannot affect counters outside the container, which means we can
+  // limit the traversal to the container subtree.
+  LayoutObject* stay_within =
+      container && container->ShouldApplyStyleContainment() ? container
+                                                            : nullptr;
+
+  for (LayoutObject* layout_object = start; layout_object;
+       layout_object = layout_object->NextInPreOrder(stay_within)) {
     if (auto* list_item = DynamicTo<LayoutListItem>(layout_object)) {
       list_item->UpdateCounterStyle();
     } else if (auto* ng_list_item =
diff --git a/third_party/blink/renderer/core/layout/layout_view.h b/third_party/blink/renderer/core/layout/layout_view.h
index 66beab5..c1f912d 100644
--- a/third_party/blink/renderer/core/layout/layout_view.h
+++ b/third_party/blink/renderer/core/layout/layout_view.h
@@ -284,7 +284,11 @@
     NOT_DESTROYED();
     needs_marker_counter_update_ = true;
   }
-  void UpdateMarkersAndCountersAfterStyleChange();
+
+  // Update generated markers and counters after style and layout tree update.
+  // container - The container for container queries, otherwise nullptr.
+  void UpdateMarkersAndCountersAfterStyleChange(
+      LayoutObject* container = nullptr);
 
   bool BackgroundIsKnownToBeOpaqueInRect(
       const PhysicalRect& local_rect) const override;
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
index 5589a3d..3322656 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -100,9 +100,7 @@
       }
     } break;
     case kPercentageGuess: {
-      // Percent columns grow in proportion to difference between their
-      // percentage size and minimum size.
-      // Auto/Fixed columns get min inline size.
+      // Percent columns grow, auto/fixed get min inline size.
       LayoutUnit percent_inline_size_increases =
           guess_size_total_increases[kPercentageGuess];
       LayoutUnit distributable_inline_size =
@@ -296,10 +294,8 @@
           *last_computed_size += rounding_error_inline_size;
         }
       } else if (percent_columns_count > 0) {
-        // All remaining columns are percent.
-        // They grow to max(col minimum, %ge size) + additional size
-        // proportional to column percent.
-        LayoutUnit rounding_error_inline_size = distributable_inline_size;
+        // All remaining columns are percent. Grow them.
+        LayoutUnit rounding_error_inline_size = target_inline_size;
         LayoutUnit* last_computed_size = nullptr;
         LayoutUnit* computed_size = computed_sizes.begin();
         for (const NGTableTypes::Column* column = start_column;
@@ -308,18 +304,13 @@
             continue;
           DCHECK(column->percent);
           last_computed_size = computed_size;
-          LayoutUnit percent_inline_size =
-              column->ResolvePercentInlineSize(target_inline_size);
-          LayoutUnit delta;
-          if (total_percent != 0.0f) {
-            delta = LayoutUnit(distributable_inline_size * *column->percent /
-                               total_percent);
+          if (total_percent > 0.0f) {
+            *computed_size = LayoutUnit(*column->percent / total_percent *
+                                        target_inline_size);
           } else {
-            delta = LayoutUnit(distributable_inline_size.ToFloat() /
-                               percent_columns_count);
+            *computed_size = distributable_inline_size / percent_columns_count;
           }
-          rounding_error_inline_size -= delta;
-          *computed_size = percent_inline_size + delta;
+          rounding_error_inline_size -= *computed_size;
         }
         if (rounding_error_inline_size != LayoutUnit()) {
           DCHECK(last_computed_size);
diff --git a/third_party/blink/renderer/core/paint/paint_timing.cc b/third_party/blink/renderer/core/paint/paint_timing.cc
index b5541a0..5b92ab25 100644
--- a/third_party/blink/renderer/core/paint/paint_timing.cc
+++ b/third_party/blink/renderer/core/paint/paint_timing.cc
@@ -252,7 +252,7 @@
     frame->GetDocument()->Fetcher()->MarkFirstContentfulPaint();
 
   if (frame->GetFrameScheduler())
-    frame->GetFrameScheduler()->OnFirstContentfulPaint();
+    frame->GetFrameScheduler()->OnFirstContentfulPaintInMainFrame();
 
   if (auto* mf_checker = frame->View()->GetMobileFriendlinessChecker())
     mf_checker->NotifyFirstContentfulPaint();
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
index 1f87e85..ec68b5b 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
@@ -30,8 +30,12 @@
     return v8::Local<v8::Object>();
   DCHECK(v8_buffer->IsArrayBuffer());
 
-  v8::Local<v8::Object> wrapper = v8::DataView::New(
-      v8_buffer.As<v8::ArrayBuffer>(), byteOffset(), byteLength());
+  v8::Local<v8::Object> wrapper;
+  {
+    v8::Context::Scope context_scope(creation_context->CreationContext());
+    wrapper = v8::DataView::New(v8_buffer.As<v8::ArrayBuffer>(), byteOffset(),
+                                byteLength());
+  }
 
   return AssociateWithWrapper(isolate, wrapper_type_info, wrapper);
 }
@@ -47,8 +51,12 @@
   }
   DCHECK(v8_buffer->IsArrayBuffer());
 
-  v8::Local<v8::Object> wrapper = v8::DataView::New(
-      v8_buffer.As<v8::ArrayBuffer>(), byteOffset(), byteLength());
+  v8::Local<v8::Object> wrapper;
+  {
+    v8::Context::Scope context_scope(script_state->GetContext());
+    wrapper = v8::DataView::New(v8_buffer.As<v8::ArrayBuffer>(), byteOffset(),
+                                byteLength());
+  }
 
   return AssociateWithWrapper(script_state->GetIsolate(), wrapper_type_info,
                               wrapper);
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
index 66a99d97..a8b3c61 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
@@ -64,12 +64,15 @@
   DCHECK_EQ(IsShared(), v8_buffer->IsSharedArrayBuffer());
 
   v8::Local<v8::Object> wrapper;
-  if (IsShared()) {
-    wrapper = V8TypedArray::New(v8_buffer.As<v8::SharedArrayBuffer>(),
-                                byteOffset(), length());
-  } else {
-    wrapper = V8TypedArray::New(v8_buffer.As<v8::ArrayBuffer>(), byteOffset(),
-                                length());
+  {
+    v8::Context::Scope context_scope(script_state->GetContext());
+    if (IsShared()) {
+      wrapper = V8TypedArray::New(v8_buffer.As<v8::SharedArrayBuffer>(),
+                                  byteOffset(), length());
+    } else {
+      wrapper = V8TypedArray::New(v8_buffer.As<v8::ArrayBuffer>(), byteOffset(),
+                                  length());
+    }
   }
 
   return AssociateWithWrapper(script_state->GetIsolate(), wrapper_type_info,
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
index 7670c51..c31eab74 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
@@ -17,7 +17,9 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_advertising_event_init.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_data_filter_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_le_scan_options.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_bluetooth_manufacturer_data_filter_init.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_request_device_options.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
@@ -36,7 +38,9 @@
 #include "third_party/blink/renderer/modules/bluetooth/bluetooth_uuid.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 
 namespace blink {
 
@@ -71,8 +75,8 @@
     const BluetoothLEScanFilterInit* filter,
     mojom::blink::WebBluetoothLeScanFilterPtr& canonicalized_filter,
     ExceptionState& exception_state) {
-  if (!(filter->hasServices() || filter->hasName() ||
-        filter->hasNamePrefix())) {
+  if (!(filter->hasServices() || filter->hasName() || filter->hasNamePrefix() ||
+        filter->hasManufacturerData())) {
     exception_state.ThrowTypeError(
         "A filter must restrict the devices in some way.");
     return;
@@ -111,16 +115,78 @@
     }
     if (filter->namePrefix().length() == 0) {
       exception_state.ThrowTypeError(
-          "'namePrefix', if present, must me non-empty.");
+          "'namePrefix', if present, must be non-empty.");
       return;
     }
     canonicalized_filter->name_prefix = filter->namePrefix();
   }
+
+  if (filter->hasManufacturerData()) {
+    if (filter->manufacturerData().size() == 0) {
+      exception_state.ThrowTypeError(
+          "'manufacturerData', if present, must be non-empty.");
+      return;
+    }
+    canonicalized_filter->manufacturer_data.emplace();
+    for (const auto& manufacturer_data : filter->manufacturerData()) {
+      DOMArrayPiece mask_buffer = manufacturer_data->mask();
+      DOMArrayPiece data_prefix_buffer = manufacturer_data->dataPrefix();
+      if (manufacturer_data->hasMask()) {
+        if (mask_buffer.IsDetached()) {
+          exception_state.ThrowDOMException(
+              DOMExceptionCode::kInvalidStateError,
+              "'mask' value buffer has been detached.");
+          return;
+        }
+
+        if (!manufacturer_data->hasDataPrefix()) {
+          exception_state.ThrowTypeError(
+              "'dataPrefix' must be non-empty when 'mask' is present.");
+          return;
+        }
+
+        if (data_prefix_buffer.ByteLength() != mask_buffer.ByteLength()) {
+          exception_state.ThrowTypeError(
+              "'mask' size must be equal to 'dataPrefix' size.");
+          return;
+        }
+      }
+
+      Vector<mojom::blink::WebBluetoothDataFilterPtr> data_filters_vector;
+      if (manufacturer_data->hasDataPrefix()) {
+        if (data_prefix_buffer.IsDetached()) {
+          exception_state.ThrowDOMException(
+              DOMExceptionCode::kInvalidStateError,
+              "'dataPrefix' value buffer has been detached.");
+          return;
+        }
+
+        // Iterate by index here since we're iterating through two arrays.
+        for (wtf_size_t i = 0; i < data_prefix_buffer.ByteLength(); ++i) {
+          uint8_t data = data_prefix_buffer.Bytes()[i];
+          uint8_t mask =
+              manufacturer_data->hasMask() ? mask_buffer.Bytes()[i] : 0xff;
+          data_filters_vector.push_back(
+              mojom::blink::WebBluetoothDataFilter::New(data, mask));
+        }
+      }
+
+      auto company = mojom::blink::WebBluetoothCompany::New();
+      company->id = manufacturer_data->companyIdentifier();
+      auto result = canonicalized_filter->manufacturer_data->insert(
+          std::move(company), std::move(data_filters_vector));
+      if (!result.is_new_entry) {
+        exception_state.ThrowTypeError("'companyIdentifier' must be unique.");
+        return;
+      }
+    }
+  }
 }
 
 static void ConvertRequestDeviceOptions(
     const RequestDeviceOptions* options,
     mojom::blink::WebBluetoothRequestDeviceOptionsPtr& result,
+    ExecutionContext* execution_context,
     ExceptionState& exception_state) {
   if (!(options->hasFilters() ^ options->acceptAllDevices())) {
     exception_state.ThrowTypeError(
@@ -148,6 +214,11 @@
       if (exception_state.HadException())
         return;
 
+      if (canonicalized_filter->manufacturer_data) {
+        UseCounter::Count(execution_context,
+                          WebFeature::kWebBluetoothManufacturerDataFilter);
+      }
+
       result->filters->push_back(std::move(canonicalized_filter));
     }
   }
@@ -275,7 +346,8 @@
   // In order to convert the arguments from service names and aliases to just
   // UUIDs, do the following substeps:
   auto device_options = mojom::blink::WebBluetoothRequestDeviceOptions::New();
-  ConvertRequestDeviceOptions(options, device_options, exception_state);
+  ConvertRequestDeviceOptions(options, device_options, GetExecutionContext(),
+                              exception_state);
 
   if (exception_state.HadException())
     return ScriptPromise();
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_data_filter_init.idl b/third_party/blink/renderer/modules/bluetooth/bluetooth_data_filter_init.idl
new file mode 100644
index 0000000..87c30336
--- /dev/null
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_data_filter_init.idl
@@ -0,0 +1,10 @@
+// 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.
+
+// https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothdatafilterinit
+
+dictionary BluetoothDataFilterInit {
+  BufferSource dataPrefix;
+  BufferSource mask;
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan_filter_init.idl b/third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan_filter_init.idl
index ea372ec0..dbd8734 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan_filter_init.idl
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_le_scan_filter_init.idl
@@ -8,5 +8,6 @@
     sequence<BluetoothServiceUUID> services;
     DOMString name;
     DOMString namePrefix;
-    // TODO(crbug.com/707635): Support manufacturerData and serviceData filters.
+    [RuntimeEnabled=WebBluetoothManufacturerDataFilter] sequence<BluetoothManufacturerDataFilterInit> manufacturerData;
+    // TODO(crbug.com/707635): Support serviceData filter.
 };
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_filter_init.idl b/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_filter_init.idl
new file mode 100644
index 0000000..606c40b
--- /dev/null
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_filter_init.idl
@@ -0,0 +1,9 @@
+// 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.
+
+// https://webbluetoothcg.github.io/web-bluetooth/#dictdef-bluetoothmanufacturerdatafilterinit
+
+dictionary BluetoothManufacturerDataFilterInit : BluetoothDataFilterInit {
+  required [EnforceRange] unsigned short companyIdentifier;
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/bluetooth/idls.gni b/third_party/blink/renderer/modules/bluetooth/idls.gni
index e784aae1..30ee000 100644
--- a/third_party/blink/renderer/modules/bluetooth/idls.gni
+++ b/third_party/blink/renderer/modules/bluetooth/idls.gni
@@ -19,8 +19,10 @@
 
 modules_dictionary_idl_files = [
   "bluetooth_advertising_event_init.idl",
+  "bluetooth_data_filter_init.idl",
   "bluetooth_le_scan_filter_init.idl",
   "bluetooth_le_scan_options.idl",
+  "bluetooth_manufacturer_data_filter_init.idl",
   "request_device_options.idl",
   "watch_advertisements_options.idl",
 ]
diff --git a/third_party/blink/renderer/modules/handwriting/handwriting_drawing.idl b/third_party/blink/renderer/modules/handwriting/handwriting_drawing.idl
index a4a0e3e..1a4f505 100644
--- a/third_party/blink/renderer/modules/handwriting/handwriting_drawing.idl
+++ b/third_party/blink/renderer/modules/handwriting/handwriting_drawing.idl
@@ -13,6 +13,6 @@
   void clear();
   sequence<HandwritingStroke> getStrokes();
 
-  [CallWith=ScriptState]
+  [CallWith=ScriptState, MeasureAs=HandwritingRecognitionGetPrediction]
   Promise<sequence<HandwritingPrediction>> getPrediction();
 };
diff --git a/third_party/blink/renderer/modules/handwriting/handwriting_recognizer.idl b/third_party/blink/renderer/modules/handwriting/handwriting_recognizer.idl
index 09bc3d8..12b5ccb 100644
--- a/third_party/blink/renderer/modules/handwriting/handwriting_recognizer.idl
+++ b/third_party/blink/renderer/modules/handwriting/handwriting_recognizer.idl
@@ -9,7 +9,7 @@
   RuntimeEnabled=HandwritingRecognition
 ] interface HandwritingRecognizer {
 
-  [CallWith=ScriptState, RaisesException]
+  [CallWith=ScriptState, RaisesException, MeasureAs=HandwritingRecognitionStartDrawing]
   HandwritingDrawing startDrawing(optional HandwritingHints hints = {});
 
   [RaisesException] void finish();
diff --git a/third_party/blink/renderer/modules/handwriting/navigator_handwriting_recognition_service.idl b/third_party/blink/renderer/modules/handwriting/navigator_handwriting_recognition_service.idl
index a5e0fc4..ff4e433 100644
--- a/third_party/blink/renderer/modules/handwriting/navigator_handwriting_recognition_service.idl
+++ b/third_party/blink/renderer/modules/handwriting/navigator_handwriting_recognition_service.idl
@@ -9,11 +9,11 @@
   ImplementedAs=HandwritingRecognitionService,
   RuntimeEnabled=HandwritingRecognition
 ] partial interface Navigator {
-  [CallWith=ScriptState, RaisesException]
+  [CallWith=ScriptState, RaisesException, MeasureAs=HandwritingRecognitionCreateRecognizer]
   Promise<HandwritingRecognizer>
       createHandwritingRecognizer(HandwritingModelConstraint constraint);
 
-  [CallWith=ScriptState, RaisesException]
+  [CallWith=ScriptState, RaisesException, MeasureAs=HandwritingRecognitionQuerySupport]
   Promise<HandwritingFeatureQueryResult>
       queryHandwritingRecognizerSupport(HandwritingFeatureQuery query);
 };
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
index 1abb606..7612cd53 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_rtp_sender.cc
@@ -353,6 +353,7 @@
     } else if (encoding->scalabilityMode() == "L1T3") {
       webrtc_encoding.num_temporal_layers = 3;
     }
+    webrtc_encoding.scalability_mode = encoding->scalabilityMode().Utf8();
   }
   webrtc_encoding.adaptive_ptime = encoding->adaptivePtime();
   return webrtc_encoding;
@@ -757,12 +758,36 @@
       }
       codec->setSdpFmtpLine(sdp_fmtp_line.c_str());
     }
-    if (rtc_codec.mime_type() == "video/VP8" ||
-        rtc_codec.mime_type() == "video/VP9") {
+    if (rtc_codec.mime_type() == "video/VP8") {
       Vector<String> modes;
       modes.push_back("L1T2");
       modes.push_back("L1T3");
       codec->setScalabilityModes(modes);
+    } else if (rtc_codec.mime_type() == "video/VP9") {
+      auto profile_id = rtc_codec.parameters.find("profile-id");
+      if (profile_id == rtc_codec.parameters.end() ||
+          profile_id->second != "2") {
+        Vector<String> modes;
+        modes.push_back("L1T2");
+        modes.push_back("L1T3");
+        codec->setScalabilityModes(modes);
+      }
+    } else if (rtc_codec.mime_type() == "video/AV1" ||
+               rtc_codec.mime_type() == "video/AV1X") {
+      Vector<String> modes;
+      modes.push_back("L1T2");
+      modes.push_back("L1T3");
+      modes.push_back("L2T1");
+      modes.push_back("L2T1h");
+      modes.push_back("L2T1_KEY");
+      modes.push_back("L2T2");
+      modes.push_back("L2T2_KEY");
+      modes.push_back("L2T2_KEY_SHIFT");
+      modes.push_back("L3T1");
+      modes.push_back("L3T3");
+      modes.push_back("L3T3_KEY");
+      modes.push_back("S2T1");
+      codec->setScalabilityModes(modes);
     }
     codecs.push_back(codec);
   }
diff --git a/third_party/blink/renderer/modules/remote_objects/remote_object_gateway_impl.cc b/third_party/blink/renderer/modules/remote_objects/remote_object_gateway_impl.cc
index ef582af6..4c05e96 100644
--- a/third_party/blink/renderer/modules/remote_objects/remote_object_gateway_impl.cc
+++ b/third_party/blink/renderer/modules/remote_objects/remote_object_gateway_impl.cc
@@ -28,6 +28,8 @@
   ScriptState* script_state = ToScriptStateForMainWorld(GetSupplementable());
   ScriptState::Scope scope(script_state);
   v8::Isolate* isolate = script_state->GetIsolate();
+  v8::MicrotasksScope microtasks_scope(
+      isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
   v8::Local<v8::Context> context = script_state->GetContext();
   if (context.IsEmpty())
     return;
diff --git a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
index fde4fa7..08022833 100644
--- a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.cc
@@ -44,7 +44,6 @@
 
 void XrEnterFullscreenObserver::RequestFullscreen(
     Element* fullscreen_element,
-    bool setup_for_dom_overlay,
     base::OnceCallback<void(bool)> on_completed) {
   DCHECK(!on_completed_);
   DCHECK(fullscreen_element);
@@ -85,16 +84,11 @@
   // immersive session had required a user activation state, but that may have
   // expired by now due to the user taking time to respond to the consent
   // prompt.
-  ScopedAllowFullscreen scope(setup_for_dom_overlay
-                                  ? ScopedAllowFullscreen::kXrOverlay
-                                  : ScopedAllowFullscreen::kXrSession);
+  ScopedAllowFullscreen scope(ScopedAllowFullscreen::kXrOverlay);
 
-  FullscreenRequestType request_type = FullscreenRequestType::kUnprefixed;
-  if (setup_for_dom_overlay) {
-    request_type = request_type | FullscreenRequestType::kForXrOverlay;
-  }
-
-  Fullscreen::RequestFullscreen(*fullscreen_element_, options, request_type);
+  Fullscreen::RequestFullscreen(*fullscreen_element_, options,
+                                FullscreenRequestType::kUnprefixed |
+                                    FullscreenRequestType::kForXrOverlay);
 
   if (!wait_for_fullscreen_change) {
     // Element was already fullscreen, proceed with session creation.
diff --git a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h
index d687dc6..8b8756c0 100644
--- a/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h
+++ b/third_party/blink/renderer/modules/xr/xr_enter_fullscreen_observer.h
@@ -26,7 +26,6 @@
   // Attempt to enter fullscreen with |element| as the root. |on_completed| will
   // be notified with whether or not fullscreen was successfully entered.
   void RequestFullscreen(Element* element,
-                         bool setup_for_dom_overlay,
                          base::OnceCallback<void(bool)> on_completed);
 
   void Trace(Visitor*) const override;
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index eec8382..cf78b72 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -2018,8 +2018,7 @@
     // cross-origin content. If that's the case, the input source is set as
     // invisible, and must not return poses or hit test results.
     bool hide_input_source = false;
-    if (IsFeatureEnabled(device::mojom::XRSessionFeature::DOM_OVERLAY) &&
-        overlay_element_ && input_state->overlay_pointer_position) {
+    if (overlay_element_ && input_state->overlay_pointer_position) {
       input_source->ProcessOverlayHitTest(overlay_element_, input_state);
       if (!stored_input_source && !input_source->IsVisible()) {
         DVLOG(2) << __func__ << ": (new) hidden_input_source";
diff --git a/third_party/blink/renderer/modules/xr/xr_system.cc b/third_party/blink/renderer/modules/xr/xr_system.cc
index 143424a14..7dc5b990 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.cc
+++ b/third_party/blink/renderer/modules/xr/xr_system.cc
@@ -6,7 +6,6 @@
 
 #include <utility>
 
-#include "build/build_config.h"
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
@@ -1117,10 +1116,17 @@
 void XRSystem::DoRequestSession(
     PendingRequestSessionQuery* query,
     device::mojom::blink::XRSessionOptionsPtr session_options) {
-  service_->RequestSession(
-      std::move(session_options),
-      WTF::Bind(&XRSystem::OnRequestSessionReturned, WrapWeakPersistent(this),
-                WrapPersistent(query)));
+  // In DOM overlay mode, there's an additional step before an immersive-ar
+  // session can start, we need to enter fullscreen mode by setting the
+  // appropriate element as fullscreen from the Renderer, then waiting for the
+  // browser side to send an event indicating success or failure.
+  auto callback =
+      query->DOMOverlayElement()
+          ? WTF::Bind(&XRSystem::OnRequestSessionSetupForDomOverlay,
+                      WrapWeakPersistent(this), WrapPersistent(query))
+          : WTF::Bind(&XRSystem::OnRequestSessionReturned,
+                      WrapWeakPersistent(this), WrapPersistent(query));
+  service_->RequestSession(std::move(session_options), std::move(callback));
 }
 
 void XRSystem::RequestInlineSession(PendingRequestSessionQuery* query,
@@ -1449,72 +1455,39 @@
   query->Resolve(supports_session);
 }
 
-void XRSystem::OnRequestSessionReturned(
+void XRSystem::OnRequestSessionSetupForDomOverlay(
     PendingRequestSessionQuery* query,
     device::mojom::blink::RequestSessionResultPtr result) {
-  // If session creation failed, move straight on to processing that.
-  if (!result->is_success()) {
-    FinishSessionCreation(query, std::move(result));
-    return;
+  DCHECK(query->DOMOverlayElement());
+  if (result->is_success()) {
+    // Success. Now request fullscreen mode and continue with
+    // OnRequestSessionReturned once that completes.
+    fullscreen_enter_observer_ =
+        MakeGarbageCollected<XrEnterFullscreenObserver>();
+    fullscreen_enter_observer_->RequestFullscreen(
+        query->DOMOverlayElement(),
+        WTF::Bind(&XRSystem::OnFullscreenConfigured, WrapPersistent(this),
+                  WrapPersistent(query), std::move(result)));
+  } else {
+    // Session request failed, continue processing that normally.
+    OnRequestSessionReturned(query, std::move(result));
   }
-
-  Element* fullscreen_element = nullptr;
-  const auto& enabled_features =
-      result->get_success()->session->enabled_features;
-  if (base::Contains(enabled_features,
-                     device::mojom::XRSessionFeature::DOM_OVERLAY)) {
-    fullscreen_element = query->DOMOverlayElement();
-  }
-
-  // Only setup for dom_overlay if the query actually had a DOMOverlayElement
-  // and the session enabled dom_overlay. (Note that fullscreen_element will be
-  // null if the feature was not enabled).
-  bool setup_for_dom_overlay = !!fullscreen_element;
-
-// On Android, due to the way the device renderer is configured, we always need
-// to enter fullscreen if we're starting an AR session, so if we aren't supposed
-// to enter DOMOverlay, we simply fullscreen the document body.
-#if defined(OS_ANDROID)
-  if (!fullscreen_element &&
-      query->mode() == device::mojom::blink::XRSessionMode::kImmersiveAr) {
-    fullscreen_element = DomWindow()->document()->body();
-  }
-#endif
-
-  // If we don't need to enter fullscreen continue with session setup.
-  if (!fullscreen_element) {
-    FinishSessionCreation(query, std::move(result));
-    return;
-  }
-
-  // At this point, we know that we have an element that we need to make
-  // fullscreen, so we do that before we continue setting up the session.
-  fullscreen_enter_observer_ =
-      MakeGarbageCollected<XrEnterFullscreenObserver>();
-  fullscreen_enter_observer_->RequestFullscreen(
-      fullscreen_element, setup_for_dom_overlay,
-      WTF::Bind(&XRSystem::OnFullscreenConfigured, WrapPersistent(this),
-                WrapPersistent(query), std::move(result)));
 }
 
 void XRSystem::OnFullscreenConfigured(
     PendingRequestSessionQuery* query,
     device::mojom::blink::RequestSessionResultPtr result,
     bool fullscreen_succeeded) {
-  // At this point we no longer need the enter observer, so go ahead and destroy
-  // it.
-  fullscreen_enter_observer_ = nullptr;
-
   if (fullscreen_succeeded) {
-    FinishSessionCreation(query, std::move(result));
+    OnRequestSessionReturned(query, std::move(result));
   } else {
-    FinishSessionCreation(
+    OnRequestSessionReturned(
         query, device::mojom::blink::RequestSessionResult::NewFailureReason(
                    device::mojom::RequestSessionError::FULLSCREEN_ERROR));
   }
 }
 
-void XRSystem::FinishSessionCreation(
+void XRSystem::OnRequestSessionReturned(
     PendingRequestSessionQuery* query,
     device::mojom::blink::RequestSessionResultPtr result) {
   DVLOG(2) << __func__;
@@ -1528,6 +1501,10 @@
     has_outstanding_immersive_request_ = false;
   }
 
+  // Clean up the fullscreen event manager which may have been added for
+  // DOM overlay setup.
+  fullscreen_enter_observer_ = nullptr;
+
   if (!result->is_success()) {
     // |service_| does not support the requested mode. Attempt to create a
     // sensorless session.
@@ -1590,14 +1567,15 @@
       session->OnEnvironmentProviderCreated();
     }
 
-    auto dom_overlay_feature = device::mojom::XRSessionFeature::DOM_OVERLAY;
-    if (query->mode() == device::mojom::blink::XRSessionMode::kImmersiveAr &&
-        query->HasFeature(dom_overlay_feature) &&
-        base::Contains(enabled_features, dom_overlay_feature)) {
-      DCHECK(query->DOMOverlayElement());
-      // The session is using DOM overlay mode. At this point the overlay
-      // element is already in fullscreen mode, and the session can proceed.
-      session->SetDOMOverlayElement(query->DOMOverlayElement());
+    if (query->mode() == device::mojom::blink::XRSessionMode::kImmersiveAr) {
+      DCHECK(DomWindow());
+      if (query->HasFeature(device::mojom::XRSessionFeature::DOM_OVERLAY)) {
+        DCHECK(query->DOMOverlayElement());
+        // The session is using DOM overlay mode. At this point the overlay
+        // element is already in fullscreen mode, and the session can
+        // proceed.
+        session->SetDOMOverlayElement(query->DOMOverlayElement());
+      }
     }
 
     if (query->mode() == device::mojom::blink::XRSessionMode::kImmersiveVr &&
diff --git a/third_party/blink/renderer/modules/xr/xr_system.h b/third_party/blink/renderer/modules/xr/xr_system.h
index d4af864..6f52a7d 100644
--- a/third_party/blink/renderer/modules/xr/xr_system.h
+++ b/third_party/blink/renderer/modules/xr/xr_system.h
@@ -359,14 +359,14 @@
   void DoRequestSession(
       PendingRequestSessionQuery* query,
       device::mojom::blink::XRSessionOptionsPtr session_options);
-  void OnRequestSessionReturned(
+  void OnRequestSessionSetupForDomOverlay(
       PendingRequestSessionQuery*,
       device::mojom::blink::RequestSessionResultPtr result);
   void OnFullscreenConfigured(
       PendingRequestSessionQuery* query,
       device::mojom::blink::RequestSessionResultPtr result,
       bool fullscreen_succeeded);
-  void FinishSessionCreation(
+  void OnRequestSessionReturned(
       PendingRequestSessionQuery*,
       device::mojom::blink::RequestSessionResultPtr result);
   void OnSupportsSessionReturned(PendingSupportsSessionQuery*,
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn
index 233fda4..601e1692 100644
--- a/third_party/blink/renderer/platform/heap/BUILD.gn
+++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -111,8 +111,6 @@
       "v8_wrapper/garbage_collected.h",
       "v8_wrapper/heap.h",
       "v8_wrapper/heap_allocator_impl.h",
-      "v8_wrapper/heap_stats_collector.h",
-      "v8_wrapper/heap_traits.h",
       "v8_wrapper/member.h",
       "v8_wrapper/persistent.h",
       "v8_wrapper/process_heap.h",
@@ -120,7 +118,6 @@
       "v8_wrapper/thread_state.h",
       "v8_wrapper/thread_state_scopes.h",
       "v8_wrapper/trace_traits.h",
-      "v8_wrapper/unified_heap_controller.h",
       "v8_wrapper/unified_heap_marking_visitor.h",
       "v8_wrapper/visitor.h",
       "v8_wrapper/write_barrier.h",
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector.h b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
index 0939d5e..4b7f0657 100644
--- a/third_party/blink/renderer/platform/heap/heap_stats_collector.h
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector.h
@@ -7,10 +7,8 @@
 
 #include "third_party/blink/renderer/platform/wtf/buildflags.h"
 
-#if BUILDFLAG(USE_V8_OILPAN)
-#include "third_party/blink/renderer/platform/heap/v8_wrapper/heap_stats_collector.h"
-#else  // !USE_V8_OILPAN
+#if !BUILDFLAG(USE_V8_OILPAN)
 #include "third_party/blink/renderer/platform/heap/impl/heap_stats_collector.h"
-#endif  // !USE_V8_OILPAN
+#endif  // !BUILDFLAG(USE_V8_OILPAN)
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_STATS_COLLECTOR_H_
diff --git a/third_party/blink/renderer/platform/heap/heap_traits.h b/third_party/blink/renderer/platform/heap/heap_traits.h
index f72489d..cb1f430 100644
--- a/third_party/blink/renderer/platform/heap/heap_traits.h
+++ b/third_party/blink/renderer/platform/heap/heap_traits.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TRAITS_H_
 
 #include <type_traits>
+
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
diff --git a/third_party/blink/renderer/platform/heap/unified_heap_controller.h b/third_party/blink/renderer/platform/heap/unified_heap_controller.h
index 636651e..c1fd663 100644
--- a/third_party/blink/renderer/platform/heap/unified_heap_controller.h
+++ b/third_party/blink/renderer/platform/heap/unified_heap_controller.h
@@ -7,10 +7,8 @@
 
 #include "third_party/blink/renderer/platform/wtf/buildflags.h"
 
-#if BUILDFLAG(USE_V8_OILPAN)
-#include "third_party/blink/renderer/platform/heap/v8_wrapper/unified_heap_controller.h"
-#else  // !USE_V8_OILPAN
+#if !BUILDFLAG(USE_V8_OILPAN)
 #include "third_party/blink/renderer/platform/heap/impl/unified_heap_controller.h"
-#endif  // !USE_V8_OILPAN
+#endif  // !BUILDFLAG(USE_V8_OILPAN)
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_UNIFIED_HEAP_CONTROLLER_H_
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/heap_stats_collector.h b/third_party/blink/renderer/platform/heap/v8_wrapper/heap_stats_collector.h
deleted file mode 100644
index 042d806..0000000
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/heap_stats_collector.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_STATS_COLLECTOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_STATS_COLLECTOR_H_
-
-// TODO(chromium:1056170): Implement wrapper.
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_STATS_COLLECTOR_H_
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/heap_traits.h b/third_party/blink/renderer/platform/heap/v8_wrapper/heap_traits.h
deleted file mode 100644
index 59d9b9e2..0000000
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/heap_traits.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_TRAITS_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_TRAITS_H_
-
-// TODO(chromium:1056170): Implement wrapper.
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_TRAITS_H_
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/unified_heap_controller.h b/third_party/blink/renderer/platform/heap/v8_wrapper/unified_heap_controller.h
deleted file mode 100644
index 36fe078..0000000
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/unified_heap_controller.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_UNIFIED_HEAP_CONTROLLER_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_UNIFIED_HEAP_CONTROLLER_H_
-
-// TODO(chromium:1056170): Implement wrapper.
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_UNIFIED_HEAP_CONTROLLER_H_
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/write_barrier.h b/third_party/blink/renderer/platform/heap/v8_wrapper/write_barrier.h
index 06bf198..4bfa2a3 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/write_barrier.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/write_barrier.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_WRITE_BARRIER_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_WRITE_BARRIER_H_
 
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "v8/include/cppgc/heap-consistency.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index f579035b..8432695 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2261,6 +2261,10 @@
       status: "experimental",
     },
     {
+      name: "WebBluetoothManufacturerDataFilter",
+      status: "experimental",
+    },
+    {
       name: "WebBluetoothRemoteCharacteristicNewWriteValue",
       status: {
         "Android": "stable",
diff --git a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
index 0d8f272..67982ca 100644
--- a/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/dummy_schedulers.cc
@@ -61,7 +61,7 @@
   void DidStartProvisionalLoad(bool is_main_frame) override {}
   void DidCommitProvisionalLoad(bool, FrameScheduler::NavigationType) override {
   }
-  void OnFirstContentfulPaint() override {}
+  void OnFirstContentfulPaintInMainFrame() override {}
   void OnFirstMeaningfulPaint() override {}
   void OnLoad() override {}
   bool IsExemptFromBudgetBasedThrottling() const override { return false; }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 8b83129f..0559e51 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -937,10 +937,10 @@
   return SchedulingLifecycleState::kNotThrottled;
 }
 
-void FrameSchedulerImpl::OnFirstContentfulPaint() {
+void FrameSchedulerImpl::OnFirstContentfulPaintInMainFrame() {
   waiting_for_contentful_paint_ = false;
-  if (GetFrameType() == FrameScheduler::FrameType::kMainFrame)
-    main_thread_scheduler_->OnMainFramePaint();
+  DCHECK_EQ(GetFrameType(), FrameScheduler::FrameType::kMainFrame);
+  main_thread_scheduler_->OnMainFramePaint();
 }
 
 void FrameSchedulerImpl::OnFirstMeaningfulPaint() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
index 6c831164..17a5369f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -122,7 +122,7 @@
       const WTF::String& name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
 
-  void OnFirstContentfulPaint() override;
+  void OnFirstContentfulPaintInMainFrame() override;
   void OnFirstMeaningfulPaint() override;
   void OnLoad() override;
   bool IsWaitingForContentfulPaint() const;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 04f47bd..12c39c5 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -1605,7 +1605,7 @@
       CreateFrameScheduler(page_scheduler_.get(),
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   // Hidden Frame Task Queues.
@@ -1698,7 +1698,7 @@
       CreateFrameScheduler(page_scheduler_.get(),
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   // Sub-Frame Task Queues.
@@ -1794,7 +1794,7 @@
       CreateFrameScheduler(page_scheduler_.get(),
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   // Sub-Frame Task Queues.
@@ -1889,7 +1889,7 @@
       CreateFrameScheduler(page_scheduler_.get(),
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   EXPECT_EQ(LoadingTaskQueue()->GetTaskQueue()->GetQueuePriority(),
@@ -1927,7 +1927,6 @@
 
 TEST_F(LowPriorityThrottleableTaskDuringLoadingExperimentTest,
        MainFrameQueuesPriorities) {
-  frame_scheduler_->OnFirstContentfulPaint();
   frame_scheduler_->OnFirstMeaningfulPaint();
 
   frame_scheduler_ =
@@ -1935,7 +1934,7 @@
                            FrameScheduler::FrameType::kMainFrame);
 
   // Main thread is in the loading use case.
-  frame_scheduler_->OnFirstContentfulPaint();
+  frame_scheduler_->OnFirstContentfulPaintInMainFrame();
 
   // Main Frame Task Queues.
   EXPECT_EQ(LoadingTaskQueue()->GetTaskQueue()->GetQueuePriority(),
@@ -2047,7 +2046,7 @@
       CreateFrameScheduler(page_scheduler_.get(),
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   EXPECT_EQ(LoadingTaskQueue()->GetTaskQueue()->GetQueuePriority(),
@@ -2160,7 +2159,7 @@
       CreateFrameScheduler(page_scheduler_.get(),
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   EXPECT_EQ(LoadingTaskQueue()->GetTaskQueue()->GetQueuePriority(),
@@ -2266,7 +2265,7 @@
             TaskQueue::QueuePriority::kNormalPriority);
 
   // Main thread scheduler is in the loading use case.
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   handle = GetResourceLoadingTaskRunnerHandleImpl();
@@ -2357,7 +2356,7 @@
                            frame_scheduler_delegate_.get(), nullptr,
                            FrameScheduler::FrameType::kMainFrame);
 
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
   ASSERT_EQ(scheduler_->current_use_case(), UseCase::kLoading);
 
   EXPECT_EQ(LoadingTaskQueue()->GetTaskQueue()->GetQueuePriority(),
@@ -2913,7 +2912,7 @@
   EXPECT_CALL(mock_main_thread_scheduler, OnMainFramePaint).Times(2);
 
   main_frame_scheduler->OnFirstMeaningfulPaint();
-  main_frame_scheduler->OnFirstContentfulPaint();
+  main_frame_scheduler->OnFirstContentfulPaintInMainFrame();
 
   main_frame_scheduler = nullptr;
   page_scheduler = nullptr;
@@ -2935,7 +2934,6 @@
   EXPECT_CALL(mock_main_thread_scheduler, OnMainFramePaint).Times(0);
 
   subframe_scheduler->OnFirstMeaningfulPaint();
-  subframe_scheduler->OnFirstContentfulPaint();
 
   subframe_scheduler = nullptr;
   page_scheduler = nullptr;
diff --git a/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h
index dcfe768..ffba694 100644
--- a/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h
@@ -161,8 +161,8 @@
                                         NavigationType navigation_type) = 0;
 
   // Tells the scheduler that the first contentful paint has occurred for this
-  // frame.
-  virtual void OnFirstContentfulPaint() = 0;
+  // frame. Only for main frames.
+  virtual void OnFirstContentfulPaintInMainFrame() = 0;
 
   // Tells the scheduler that the first meaningful paint has occurred for this
   // frame.
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index d8da3f5..58fa590 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -917,7 +917,6 @@
 crbug.com/958381 external/wpt/css/css-tables/tentative/baseline-td.html [ Failure ]
 crbug.com/958381 external/wpt/css/css-tables/tentative/caption.html [ Failure ]
 crbug.com/958381 external/wpt/css/css-tables/tentative/colgroup-col.html [ Failure ]
-crbug.com/958381 external/wpt/css/css-tables/tentative/colspan-redistribution.html [ Failure ]
 crbug.com/174167 external/wpt/css/css-tables/tentative/element-sizing.html [ Failure ]
 crbug.com/958381 external/wpt/css/css-tables/tentative/paint/background-image-column.html [ Failure ]
 crbug.com/958381 external/wpt/css/css-tables/tentative/paint/collapsed-border-large-cell.html [ Failure ]
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 4b659a72..c72f459 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -203,6 +203,9 @@
 # Sheriff 2021-05-03
 crbug.com/1197465 [ Linux ] virtual/scroll-unification/fast/events/mouse-cursor-no-mousemove.html [ Pass Failure ]
 
+# Sheriff 2021-05-05
+crbug.com/1205815 [ Linux ] virtual/shared_array_buffer_on_desktop/http/tests/devtools/sources/snippet-module.js [ Pass Failure ]
+
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 669cc2f..c74ed46 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6039,8 +6039,10 @@
 crbug.com/1048149 [ Mac ] fast/forms/color/color-picker-zoom150-bottom-edge-no-nan.html [ Pass Crash ]
 crbug.com/1048149 crbug.com/1050121 [ Mac ] fast/forms/month/month-picker-appearance-zoom150.html [ Pass Crash ]
 
-# SwANGLE issue
+# SwANGLE issues
 crbug.com/1204234 [ Linux ] css3/blending/background-blend-mode-single-accelerated-element.html [ Failure ]
+crbug.com/1204234 [ Linux ] external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html [ Failure ]
+crbug.com/1204234 [ Linux ] external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https.html [ Failure ]
 
 # Upcoming DevTools change
 crbug.com/1006759 http/tests/devtools/profiler/cpu-profiler-save-load.js [ Pass Failure Timeout ]
@@ -7088,3 +7090,12 @@
 # Browser Infra 2021-05-04
 # Re-enable once all Linux CI/CQ builders migrated to Bionic
 crbug.com/1200134 [ Linux ] fast/gradients/unprefixed-repeating-gradient-color-hint.html [ Pass Failure ]
+
+# Sheriff 2021-05-05
+crbug.com/1205779 [ Win7 ] external/wpt/url/a-element.html [ Failure ]
+crbug.com/1205780 [ Win7 ] external/wpt/css/mediaqueries/test_media_queries.html [ Failure ]
+crbug.com/1205780 [ Mac10.14 ] external/wpt/css/mediaqueries/test_media_queries.html [ Failure ]
+crbug.com/1205780 [ Mac10.15 ] external/wpt/css/mediaqueries/test_media_queries.html [ Failure Pass ]
+crbug.com/1205796 [ Mac10.12 ] external/wpt/html/dom/idlharness.https.html?include=HTML.* [ Failure ]
+crbug.com/1205796 [ Mac10.14 ] external/wpt/html/dom/idlharness.https.html?include=HTML.* [ Failure ]
+crbug.com/1205801 [ Mac10.14 ] external/wpt/css/css-will-change/will-change-fixpos-cb-position-1.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index e5cadba..b24a0ba3 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
@@ -1443,7 +1443,7 @@
      ]
     ],
     "imperative-slot-api-crash.html": [
-     "ca444778e313aaab73d2ba5925b35e201ce110a1",
+     "b029689211f57f355974130fba51d1ea0cebbbcf",
      [
       null,
       {}
@@ -45899,6 +45899,19 @@
         {}
        ]
       ],
+      "mix-blend-mode-rotated-clip.html": [
+       "9e915ac3cca49d127104e43a7fc50fa734eb9da1",
+       [
+        null,
+        [
+         [
+          "/css/compositing/mix-blend-mode/reference/mix-blend-mode-rotated-clip-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "mix-blend-mode-script.html": [
        "1268a8dc3f8242f03007dfc5a5afbd321ee57b7b",
        [
@@ -133108,32 +133121,6 @@
        {}
       ]
      ],
-     "3d-rendering-context-and-abspos.html": [
-      "c5eef46b5e800bcb411b2aed9c65534b8e3e1bc8",
-      [
-       null,
-       [
-        [
-         "/css/css-transforms/reference/green.html",
-         "=="
-        ]
-       ],
-       {}
-      ]
-     ],
-     "3d-rendering-context-and-fixpos.html": [
-      "e763e8b703ca7582fb8756f00c5faff47c7db408",
-      [
-       null,
-       [
-        [
-         "/css/css-transforms/reference/green.html",
-         "=="
-        ]
-       ],
-       {}
-      ]
-     ],
      "3d-rendering-context-and-z-ordering-001.html": [
       "d07c59a5db229cd1ed1ee9a429ea7656cd18d3e5",
       [
@@ -148764,6 +148751,19 @@
        {}
       ]
      ],
+     "will-change-transform-huge-offset-scrolled.html": [
+      "506ac67f0c876740ce8b4529de4abe18a194db05",
+      [
+       null,
+       [
+        [
+         "/css/css-will-change/will-change-transform-huge-offset-scrolled-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "will-change-transform-image.html": [
       "edce6c1f9bfa840004d8e82e6720be9c18bd23b6",
       [
@@ -193256,6 +193256,10 @@
         "275105c5dbb03cf1b82eb058e856d53b129ecbe9",
         []
        ],
+       "mix-blend-mode-rotated-clip-ref.html": [
+        "377ed7c879fd8ec15ff660da617294bc119206e0",
+        []
+       ],
        "mix-blend-mode-script-ref.html": [
         "b18ed6cd3c83d98495ab7c46ccb90fc593c3ad49",
         []
@@ -223210,6 +223214,10 @@
       "edbeaa7b69290ea2514dc85e0e3f47bd8247735e",
       []
      ],
+     "will-change-transform-huge-offset-scrolled-ref.html": [
+      "c2af258e11161654a109d030596cfcca1f96a4d9",
+      []
+     ],
      "will-change-transform-image-ref.html": [
       "d42d5b7fea22d3d2bc2abf782b1efc78f261f69e",
       []
@@ -314513,7 +314521,7 @@
        ]
       ],
       "colspan-redistribution.html": [
-       "e440728e1004bc952a5725bc50206b316a8e7450",
+       "2797a1e6894ed28387af24669e238c743d53787a",
        [
         null,
         {}
@@ -431472,14 +431480,14 @@
      ]
     ],
     "imperative-slot-api-slotchange.html": [
-     "dd9ebbd3f849b3262a01139d6acfbbfc6ade87a6",
+     "1aa6a786760f81fdd37628fe6863717a4cfa7cb8",
      [
       null,
       {}
      ]
     ],
     "imperative-slot-api.html": [
-     "54b22632d11547f33b4d08ef9f51c8ef52380baf",
+     "8133d7e8ebe5e2c90ddd893ffd0b60552ba9c5f1",
      [
       null,
       {}
@@ -450824,7 +450832,7 @@
        ]
       ],
       "audioparam-setValueCurve-exceptions.html": [
-       "37062993f9dc8d90e2d418c842b30e20517bd21d",
+       "ed0c15fb9b3bdf5a9c79e48f97949b21ac05a952",
        [
         null,
         {}
@@ -451921,6 +451929,13 @@
         {}
        ]
       ],
+      "pannernode-setposition-throws.html": [
+       "2053411943719d647ac0f17ac71d0beb742102c9",
+       [
+        null,
+        {}
+       ]
+      ],
       "test-pannernode-automation.html": [
        "ce474b10b5122eaf40b8b6d1af874ad7ec9bff70",
        [
@@ -452458,7 +452473,7 @@
      ]
     ],
     "image-decoder.any.js": [
-     "2bbc6238d0a6bf8454cf693ae3369ee657f0fb48",
+     "1912e63f8c7c06786eb8b368796d65243de02f36",
      [
       "webcodecs/image-decoder.any.html",
       {
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js
new file mode 100644
index 0000000..49cf41c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc =
+    'Manufacturer data mask size must be equal to dataPrefix size.';
+
+bluetooth_test(async (t) => {
+  const companyIdentifier = 0x0001;
+  const dataPrefix = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
+  const mask = new Uint8Array([0xff]);
+
+  await promise_rejects_js(
+      t, TypeError,
+      requestDeviceWithTrustedClick(
+          {filters: [{manufacturerData: [{companyIdentifier, mask}]}]}));
+  await promise_rejects_js(
+      t, TypeError, requestDeviceWithTrustedClick({
+        filters: [{manufacturerData: [{companyIdentifier, dataPrefix, mask}]}]
+      }));
+}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js
new file mode 100644
index 0000000..936ca473
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.js
@@ -0,0 +1,33 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'dataPrefix value buffer must not be detached';
+
+function detachBuffer(buffer) {
+  window.postMessage('', '*', [buffer]);
+}
+
+bluetooth_test(async (t) => {
+  const companyIdentifier = 0x0001;
+
+  const typed_array = Uint8Array.of(1, 2);
+  detachBuffer(typed_array.buffer);
+
+  await promise_rejects_dom(
+      t, 'InvalidStateError', requestDeviceWithTrustedClick({
+        filters:
+            [{manufacturerData: [{companyIdentifier, dataPrefix: typed_array}]}]
+      }));
+
+  const array_buffer = Uint8Array.of(3, 4).buffer;
+  detachBuffer(array_buffer);
+
+  await promise_rejects_dom(
+      t, 'InvalidStateError', requestDeviceWithTrustedClick({
+        filters: [
+          {manufacturerData: [{companyIdentifier, dataPrefix: array_buffer}]}
+        ]
+      }));
+}, test_desc);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js
new file mode 100644
index 0000000..af3118a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.js
@@ -0,0 +1,37 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'requestDevice with empty manufacturerData. ' +
+    'Should reject with TypeError.';
+const test_specs = [
+  {filters: [{manufacturerData: []}]},
+  {filters: [{manufacturerData: [], name: 'Name'}]},
+  {filters: [{manufacturerData: [], services: ['heart_rate']}]},
+  {filters: [{manufacturerData: [], name: 'Name', services: ['heart_rate']}]},
+  {filters: [{manufacturerData: []}], optionalServices: ['heart_rate']}, {
+    filters: [{manufacturerData: [], name: 'Name'}],
+    optionalServices: ['heart_rate']
+  },
+  {
+    filters: [{manufacturerData: [], services: ['heart_rate']}],
+    optionalServices: ['heart_rate']
+  },
+  {
+    filters: [{manufacturerData: [], name: 'Name', services: ['heart_rate']}],
+    optionalServices: ['heart_rate']
+  }
+];
+
+bluetooth_test((t) => {
+  let test_promises = Promise.resolve();
+  test_specs.forEach(args => {
+    test_promises = test_promises.then(
+        () => promise_rejects_js(
+            t, TypeError, requestDeviceWithTrustedClick(args)));
+  });
+  return test_promises;
+}, test_desc);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js
new file mode 100644
index 0000000..18cdbb4b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.js
@@ -0,0 +1,17 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'companyIdentifier must be in the [0, 65535] range';
+
+bluetooth_test(async (t) => {
+  await promise_rejects_js(
+      t, TypeError,
+      requestDeviceWithTrustedClick(
+          {filters: [{manufacturerData: [{companyIdentifier: -1}]}]}));
+  await promise_rejects_js(
+      t, TypeError,
+      requestDeviceWithTrustedClick(
+          {filters: [{manufacturerData: [{companyIdentifier: 65536}]}]}));
+}, test_desc);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js
new file mode 100644
index 0000000..502e2e4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.js
@@ -0,0 +1,36 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'mask value buffer must not be detached';
+
+function detachBuffer(buffer) {
+  window.postMessage('', '*', [buffer]);
+}
+
+bluetooth_test(async (t) => {
+  const companyIdentifier = 0x0001;
+  const dataPrefix = Uint8Array.of(1, 2);
+
+  const typed_array = Uint8Array.of(1, 2);
+  detachBuffer(typed_array.buffer);
+
+  await promise_rejects_dom(
+      t, 'InvalidStateError', requestDeviceWithTrustedClick({
+        filters: [{
+          manufacturerData: [{companyIdentifier, dataPrefix, mask: typed_array}]
+        }]
+      }));
+
+  const array_buffer = Uint8Array.of(3, 4).buffer;
+  detachBuffer(array_buffer);
+
+  await promise_rejects_dom(
+      t, 'InvalidStateError', requestDeviceWithTrustedClick({
+        filters: [{
+          manufacturerData:
+              [{companyIdentifier, dataPrefix, mask: array_buffer}]
+        }]
+      }));
+}, test_desc);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js
new file mode 100644
index 0000000..7416202d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.js
@@ -0,0 +1,25 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Manufacturer data company identifier must be unique.';
+const expected = new TypeError();
+
+let filters = [{
+  manufacturerData: [
+    {
+      companyIdentifier: 0x0001,
+    },
+    {
+      companyIdentifier: 0x0001,
+    }
+  ]
+}];
+
+bluetooth_test(
+    (t) => promise_rejects_js(
+        t, TypeError, requestDeviceWithTrustedClick({filters})),
+    test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html
index 1c42d77..7815a1a 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/filter-matches.https.html
@@ -11,6 +11,7 @@
 let matching_services = [health_thermometer.uuid];
 let matching_name = 'Health Thermometer';
 let matching_namePrefix = 'Health';
+let matching_manufacturerData = [{ companyIdentifier: 0x0001 }];
 
 let test_specs = [
   {
@@ -24,25 +25,50 @@
       name: matching_name,
     }]
   },
-  {filters: [{services: matching_services, namePrefix: matching_namePrefix}]}, {
+  {
+    filters: [{
+      services: matching_services,
+      namePrefix: matching_namePrefix
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
     filters: [{
       name: matching_name,
     }],
     optionalServices: matching_services
   },
   {
-    filters: [{name: matching_name, namePrefix: matching_namePrefix}],
+    filters: [{
+      namePrefix: matching_namePrefix
+    }],
     optionalServices: matching_services
   },
   {
-    filters: [{namePrefix: matching_namePrefix}],
+    filters: [{
+      manufacturerData: matching_manufacturerData
+    }],
+    optionalServices: matching_services
+  },
+  {
+    filters: [{
+      name: matching_name,
+      namePrefix: matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }],
     optionalServices: matching_services
   },
   {
     filters: [{
       services: matching_services,
       name: matching_name,
-      namePrefix: matching_namePrefix
+      namePrefix: matching_namePrefix,
+      manufacturerData: matching_manufacturerData
     }]
   }
 ];
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js
new file mode 100644
index 0000000..548b5d0b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.js
@@ -0,0 +1,141 @@
+// META: script=/resources/testharness.js
+// META: script=/resources/testharnessreport.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/bluetooth/resources/bluetooth-test.js
+// META: script=/bluetooth/resources/bluetooth-fake-devices.js
+'use strict';
+const test_desc = 'Matches a filter when manufacturer data match.';
+
+let test_specs = [
+  {
+    filters: [{
+      manufacturerData: [{
+        companyIdentifier: 0x0001,
+      }],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [{
+        companyIdentifier: 0x0001,
+        dataPrefix: new Uint8Array([0x01]),
+      }],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [{
+        companyIdentifier: 0x0001,
+        dataPrefix: new Uint8Array([0x01]),
+        mask: new Uint8Array([0xff]),
+      }],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [{
+        companyIdentifier: 0x0001,
+        dataPrefix: new Uint8Array([0x01, 0x02]),
+      }],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [{
+        companyIdentifier: 0x0001,
+        dataPrefix: new Uint8Array([0x01, 0x02]),
+        mask: new Uint8Array([0xff, 0x01]),
+      }],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [
+        {
+          companyIdentifier: 0x0001,
+          dataPrefix: new Uint8Array([0x01, 0x02]),
+          mask: new Uint8Array([0xff, 0x01]),
+        },
+        {
+          companyIdentifier: 0x0002,
+        }
+      ],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [
+        {
+          companyIdentifier: 0x0001,
+          dataPrefix: new Uint8Array([0x01, 0x02]),
+          mask: new Uint8Array([0xff, 0x01]),
+        },
+        {
+          companyIdentifier: 0x0002,
+          dataPrefix: new Uint8Array([0x03]),
+        }
+      ],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [
+        {
+          companyIdentifier: 0x0001,
+          dataPrefix: new Uint8Array([0x01, 0x02]),
+          mask: new Uint8Array([0xff, 0x01]),
+        },
+        {
+          companyIdentifier: 0x0002,
+          dataPrefix: new Uint8Array([0x03]),
+          mask: new Uint8Array([0xff]),
+        }
+      ],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [
+        {
+          companyIdentifier: 0x0001,
+          dataPrefix: new Uint8Array([0x01, 0x02]),
+          mask: new Uint8Array([0xff, 0x01]),
+        },
+        {
+          companyIdentifier: 0x0002,
+          dataPrefix: new Uint8Array([0x03, 0x04]),
+        }
+      ],
+    }],
+  },
+  {
+    filters: [{
+      manufacturerData: [
+        {
+          companyIdentifier: 0x0001,
+          dataPrefix: new Uint8Array([0x01, 0x02]),
+          mask: new Uint8Array([0xff, 0x01]),
+        },
+        {
+          companyIdentifier: 0x0002,
+          dataPrefix: new Uint8Array([0x03, 0x04]),
+          mask: new Uint8Array([0xff, 0xff])
+        }
+      ],
+    }],
+  },
+];
+
+bluetooth_test(
+    () => setUpHealthThermometerDevice().then(() => {
+      let test_promises = Promise.resolve();
+      test_specs.forEach(args => {
+        test_promises = test_promises.then(async () => {
+          const device = await requestDeviceWithTrustedClick(args);
+          assert_equals(device.name, 'Health Thermometer');
+        });
+      });
+      return test_promises;
+    }),
+    test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js
index c4d699a..bc142aa4 100644
--- a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js
+++ b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js
@@ -196,8 +196,19 @@
   return [
     {filters: [{services: services}]},
     {filters: [{services: services, name: 'Name'}]},
-    {filters: [{services: services, namePrefix: 'Pre'}]},
-    {filters: [{services: services, name: 'Name', namePrefix: 'Pre'}]},
+    {filters: [{services: services, namePrefix: 'Pre'}]}, {
+      filters: [
+        {services: services, manufacturerData: [{companyIdentifier: 0x0001}]}
+      ]
+    },
+    {
+      filters: [{
+        services: services,
+        name: 'Name',
+        namePrefix: 'Pre',
+        manufacturerData: [{companyIdentifier: 0x0001}]
+      }]
+    },
     {filters: [{services: services}], optionalServices: ['heart_rate']}, {
       filters: [{services: services, name: 'Name'}],
       optionalServices: ['heart_rate']
@@ -207,7 +218,18 @@
       optionalServices: ['heart_rate']
     },
     {
-      filters: [{services: services, name: 'Name', namePrefix: 'Pre'}],
+      filters: [
+        {services: services, manufacturerData: [{companyIdentifier: 0x0001}]}
+      ],
+      optionalServices: ['heart_rate']
+    },
+    {
+      filters: [{
+        services: services,
+        name: 'Name',
+        namePrefix: 'Pre',
+        manufacturerData: [{companyIdentifier: 0x0001}]
+      }],
       optionalServices: ['heart_rate']
     }
   ];
@@ -242,6 +264,7 @@
 /**
  * A dictionary for specifying fake Bluetooth device setup options.
  * @typedef {{address: !string, name: !string,
+ *            manufacturerData: !Object<uint16,Array<uint8>>,
  *            knownServiceUUIDs: !Array<string>, connectable: !boolean,
  *            serviceDiscoveryComplete: !boolean}}
  */
@@ -260,6 +283,7 @@
 const fakeDeviceOptionsDefault = {
   address: '00:00:00:00:00:00',
   name: 'LE Device',
+  manufacturerData: {},
   knownServiceUUIDs: [],
   connectable: false,
   serviceDiscoveryComplete: false,
@@ -327,6 +351,7 @@
       await fake_central.simulatePreconnectedPeripheral({
         address: setupOptions.fakeDeviceOptions.address,
         name: setupOptions.fakeDeviceOptions.name,
+        manufacturerData: setupOptions.fakeDeviceOptions.manufacturerData,
         knownServiceUUIDs: setupOptions.fakeDeviceOptions.knownServiceUUIDs,
       });
 
@@ -360,14 +385,16 @@
 
 /**
  * Deprecated: Use setUpPreconnectedFakeDevice() instead.
- * Simulates a preconnected device with |address|, |name| and
- * |knownServiceUUIDs|. A preconnected device is a device that has been paired
- * with the system previously. This can be done if, for example, the user pairs
- * the device using the OS'es settings.
+ * Simulates a preconnected device with |address|, |name|, |manufacturerData|
+ * and |knownServiceUUIDs|. A preconnected device is a device that has been
+ * paired with the system previously. This can be done if, for example, the user
+ * pairs the device using the OS'es settings.
  * TODO(https://crbug.com/1070816): Remove this method when all uses have been
  * converted to using setUpPreconnectedFakeDevice();
  * @param {string} address The device MAC address.
  * @param {string} name The device name.
+ * @param {Object<uint16,Array<uint8>>} manufacturerData A map of company
+ *     identifier and manufacturer data to set up the fake with.
  * @param {Array<string>} knownServiceUUIDs An array of GATT service UUIDs to
  *     set up the fake with.
  * @returns {Promise<FakePeripheral>} The fake devices are initialized with the
@@ -376,12 +403,14 @@
 async function setUpPreconnectedDevice({
   address = '00:00:00:00:00:00',
   name = 'LE Device',
+  manufacturerData = {},
   knownServiceUUIDs = []
 }) {
   await initializeFakeCentral({state: 'powered-on'});
   return await fake_central.simulatePreconnectedPeripheral({
     address: address,
     name: name,
+    manufacturerData: manufacturerData,
     knownServiceUUIDs: knownServiceUUIDs,
   });
 }
@@ -705,7 +734,7 @@
 /**
  * Returns a FakePeripheral that corresponds to a simulated pre-connected device
  * called 'Health Thermometer'. The device has two known serviceUUIDs:
- * 'generic_access' and 'health_thermometer'.
+ * 'generic_access' and 'health_thermometer' and some fake manufacturer data.
  * @returns {Promise<FakePeripheral>} The device fake initialized as a Health
  *     Thermometer device.
  */
@@ -713,6 +742,7 @@
   return setUpPreconnectedDevice({
     address: '09:09:09:09:09:09',
     name: 'Health Thermometer',
+    manufacturerData: {0x0001: manufacturer1Data, 0x0002: manufacturer2Data},
     knownServiceUUIDs: ['generic_access', 'health_thermometer'],
   });
 }
@@ -1122,11 +1152,13 @@
     fake_central.simulatePreconnectedPeripheral({
       address: '09:09:09:09:09:09',
       name: 'Health Thermometer',
+      manufacturerData: {},
       knownServiceUUIDs: ['generic_access', 'health_thermometer'],
     }),
     fake_central.simulatePreconnectedPeripheral({
       address: '08:08:08:08:08:08',
       name: 'Heart Rate',
+      manufacturerData: {},
       knownServiceUUIDs: ['generic_access', 'heart_rate'],
     })
   ]);
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/colspan-redistribution.html b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/colspan-redistribution.html
index 2797a1e..e440728 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/colspan-redistribution.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/tentative/colspan-redistribution.html
@@ -216,31 +216,29 @@
 </table>
 </div>
 
-<p 12 class="testdesc">Colspan&gt;1:Auto/468 C0:50%/150 C1:30%/150 C2:20/Auto
+<p 12 class="testdesc">Colspan&gt;1:Auto/400 C0:50%/150 C1:30%/150 C2:20/Auto
 This tests conflict resolution where min-width > redistributed min width.
-Colspanned redistribution: distributed 468-8 = 460 over C0/C1
-Colspanned 468px needs to distribute 460px over C0/C1.
-C0 percent size is 50% * 460 = 230
-C1 percent size is 30% * 460 = 138, defaults to min size of 150
-Column 1 size is 230 + 50%/80% * 80 = 280px
-Column 2 size is 150 + 30%/80% * 80 = 180px
-Column 3 remains 20px
-Assignable table inline size
-C1 dominates estimate: 180/30% + 4*8 = 632
-Compute final column widths from assignable table size:
-C0 = 50% of 600 = 300 C1 = 30% of 600 = 180 , C2 gets the remaining 120</p>
-<table data-expected-width="632">
+400-8px distributed max width tries to redistribute as 245|147, but gets constrained to 245|150 in Chrome.
+table width from C0 245/0.5 + 4*8 = 522
+table width from C1 150/0.3 + 4*8 = 532
+C0 = 50% of 500 = 250, C1 = 30% of 500 = 150 , C2 gets the remaining 100</p>
+<p class="error">Chrome/FF/Edge end up with tables of different widths: 532/590/685. Chrome's 2nd span cell seems 'most correct' at its original max width of 150. In FF, extra min-width seems to cause more width to be redistributed. If you hover over 30%/150 cell, its min width will change to 100px, and all browsers will agree.</p>
+<style>
+  .test12:hover {
+    width:100px !important;
+  }
+</style>
+<table data-expected-width="532">
   <tr>
-    <td style="width:50%" data-expected-width="300"><div style="width:150px">50%/150px</div></td>
-    <td style="width:30%" data-expected-width="180"><div style="width:150px">30%/150px</div></td>
-    <td style="width:20px" data-expected-width="120">x</td>
+    <td style="width:50%" data-expected-width="250"><div style="width:150px">50%/150px</div></td>
+    <td style="width:30%" data-expected-width="150"><div class="test12" style="width:150px">30%/150px</div></td>
+    <td style="width:20px" data-expected-width="100">x</td>
   </tr>
   <tr>
-    <td colspan=2 style=""><div style="width:468px">408px min</div></td>
+    <td colspan=2 style=""><div style="width:400px">400px min</div></td>
   </tr>
 </table>
 
-
 <p 13 class="testdesc"> Colspan&gt;1:Auto/400px C0:50%/75px/125px, C1:30%/75px/125px C2:20px/Auto
 Colspan&gt;1 cell distribution over different percentages.
 400-8px min width gets redistributed as 245/147 (no min width limits)
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https-expected.txt b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https-expected.txt
index f698df7..377c729 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https-expected.txt
@@ -1,8 +1,14 @@
 This is a testharness.js-based test.
 PASS Setup
+FAIL Anonymous same-origin iframe is loaded without credentials assert_equals: expected (undefined) undefined but got (string) "same_origin"
+FAIL Anonymous cross-origin iframe is loaded without credentials assert_equals: expected (undefined) undefined but got (string) "cross_origin"
 FAIL same_origin anonymous iframe can't send same_origin credentials assert_equals: expected (undefined) undefined but got (string) "same_origin"
 FAIL same_origin anonymous iframe can't send cross_origin credentials assert_equals: expected (undefined) undefined but got (string) "cross_origin"
 FAIL cross_origin anonymous iframe can't send cross_origin credentials assert_equals: expected (undefined) undefined but got (string) "cross_origin"
 FAIL cross_origin anonymous iframe can't send same_origin credentials assert_equals: expected (undefined) undefined but got (string) "same_origin"
+FAIL same_origin anonymous iframe can't send same_origin credentials on child iframe assert_equals: expected (undefined) undefined but got (string) "same_origin"
+FAIL same_origin anonymous iframe can't send cross_origin credentials on child iframe assert_equals: expected (undefined) undefined but got (string) "cross_origin"
+FAIL cross_origin anonymous iframe can't send cross_origin credentials on child iframe assert_equals: expected (undefined) undefined but got (string) "cross_origin"
+FAIL cross_origin anonymous iframe can't send same_origin credentials on child iframe assert_equals: expected (undefined) undefined but got (string) "same_origin"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https.html
index aa2a383..acd60c5 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/credentialless/full-credentialless/cookie.tentative.https.html
@@ -13,55 +13,120 @@
 const cookie_same_origin = "same_origin";
 const cookie_cross_origin = "cross_origin";
 
-// Fetch a resource, returns the HTTP request cookies.
-const cookieFromResource = async (document_token, resource_origin) => {
-  const resource_token = token();
-  send(document_token, `
-    let img = document.createElement("img");
-    img.src = "${showRequestHeaders(resource_origin, resource_token)}";
-    document.body.appendChild(img);
-  `);
+const cookieFromResource = async resource_token => {
   let headers = JSON.parse(await receive(resource_token));
   return parseCookies(headers)[cookie_key];
 };
 
+// Load an iframe, return the HTTP request cookies.
+const cookieFromIframeNavigationRequest = async (iframe_origin) => {
+  const resource_token = token();
+  let iframe = document.createElement("iframe");
+  iframe.src = `${showRequestHeaders(iframe_origin, resource_token)}`;
+  document.body.appendChild(iframe);
+  return await cookieFromResource(resource_token);
+};
+
+// Load a resource `type` from the iframe with `document_token`,
+// return the HTTP request cookies.
+const cookieFromResourceInIframe =
+    async (document_token, resource_origin, type = "img") => {
+  const resource_token = token();
+  send(document_token, `
+    let el = document.createElement("${type}");
+    el.src = "${showRequestHeaders(resource_origin, resource_token)}";
+    document.body.appendChild(el);
+  `);
+  return await cookieFromResource(resource_token);
+};
+
 promise_test_parallel(async test => {
   await Promise.all([
     setCookie(same_origin, cookie_key, cookie_same_origin),
     setCookie(cross_origin, cookie_key, cookie_cross_origin),
   ]);
 
+  promise_test_parallel(async test => {
+    assert_equals(
+      await cookieFromIframeNavigationRequest(same_origin),
+      undefined
+    );
+  }, "Anonymous same-origin iframe is loaded without credentials");
+
+  promise_test_parallel(async test => {
+    assert_equals(
+      await cookieFromIframeNavigationRequest(cross_origin),
+      undefined
+    );
+  }, "Anonymous cross-origin iframe is loaded without credentials");
+
   let iframe_same_origin = newAnonymousIframe(same_origin);
   let iframe_cross_origin = newAnonymousIframe(cross_origin);
 
   promise_test_parallel(async test => {
     assert_equals(
-      await cookieFromResource(iframe_same_origin, same_origin),
+      await cookieFromResourceInIframe(iframe_same_origin, same_origin),
       undefined
     );
   }, "same_origin anonymous iframe can't send same_origin credentials");
 
   promise_test_parallel(async test => {
     assert_equals(
-      await cookieFromResource(iframe_same_origin, cross_origin),
+      await cookieFromResourceInIframe(iframe_same_origin, cross_origin),
       undefined
     );
   }, "same_origin anonymous iframe can't send cross_origin credentials");
 
   promise_test_parallel(async test => {
     assert_equals(
-      await cookieFromResource(iframe_cross_origin, cross_origin),
+      await cookieFromResourceInIframe(iframe_cross_origin, cross_origin),
       undefined
     );
   }, "cross_origin anonymous iframe can't send cross_origin credentials");
 
   promise_test_parallel(async test => {
     assert_equals(
-      await cookieFromResource(iframe_cross_origin, same_origin),
+      await cookieFromResourceInIframe(iframe_cross_origin, same_origin),
       undefined
     );
   }, "cross_origin anonymous iframe can't send same_origin credentials");
 
+  promise_test_parallel(async test => {
+    assert_equals(
+      await cookieFromResourceInIframe(iframe_same_origin, same_origin,
+                                       "iframe"),
+      undefined
+    );
+  }, "same_origin anonymous iframe can't send same_origin credentials "
+                        + "on child iframe");
+
+  promise_test_parallel(async test => {
+    assert_equals(
+      await cookieFromResourceInIframe(iframe_same_origin, cross_origin,
+                                       "iframe"),
+      undefined
+    );
+  }, "same_origin anonymous iframe can't send cross_origin credentials "
+                        + "on child iframe");
+
+  promise_test_parallel(async test => {
+    assert_equals(
+      await cookieFromResourceInIframe(iframe_cross_origin, cross_origin,
+                                       "iframe"),
+      undefined
+    );
+  }, "cross_origin anonymous iframe can't send cross_origin credentials "
+                        + "on child iframe");
+
+  promise_test_parallel(async test => {
+    assert_equals(
+      await cookieFromResourceInIframe(iframe_cross_origin, same_origin,
+                                       "iframe"),
+      undefined
+    );
+  }, "cross_origin anonymous iframe can't send same_origin credentials "
+                        + "on child iframe");
+
 }, "Setup")
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/fetch-cross-origin-redirect.https.html b/third_party/blink/web_tests/external/wpt/resource-timing/fetch-cross-origin-redirect.https.html
index eab615b..4193422 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/fetch-cross-origin-redirect.https.html
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/fetch-cross-origin-redirect.https.html
@@ -1,65 +1,33 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Test cross-origin fetch redirects have the right values.</title>
+<link rel="author" title="Google" href="http://www.google.com/" />
+<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/common/get-host-info.sub.js"></script>
+<script src="resources/entry-invariants.js"></script>
+<script src="resources/resource-loaders.js"></script>
 <script>
-const run_test = async (t, url, cross_origin) => {
-  // Set up PerformanceObserver
-  const href = new URL(url).href;
-  const setPerformanceObserver = new Promise(resolve => {
-    const po = new PerformanceObserver(resolve);
-    po.observe({type: "resource"});
-  });
-
-  // Fetch the resource
-  await fetch(href, {mode: "no-cors", credentials: "include" });
-
-  // Wait for an entry
-  const timeout = new Promise(resolve => t.step_timeout(resolve, 1000));
-  const list = await Promise.race([setPerformanceObserver, timeout]);
-  assert_equals(typeof(list), "object", "No iframe entry was fired");
-  const entries = list.getEntriesByName(url);
-  assert_equals(entries.length, 1);
-
-  // Test entry values
-  const entry = entries[0];
-  if (cross_origin) {
-    assert_equals(entry.redirectStart, 0, "redirectStart should be 0 in cross-origin redirect.");
-    assert_equals(entry.redirectEnd, 0, "redirectEnd should be 0 in cross-origin redirect.");
-    assert_equals(entry.domainLookupStart, 0, "domainLookupStart should be 0 in cross-origin redirect.");
-    assert_equals(entry.domainLookupEnd, 0, "domainLookupEnd should be 0 in cross-origin redirect.");
-    assert_equals(entry.connectStart, 0, "connectStart should be 0 in cross-origin redirect.");
-    assert_equals(entry.connectEnd, 0, "connectEnd should be 0 in cross-origin redirect.");
-    assert_equals(entry.requestStart, 0, "requestStart should be 0 in cross-origin redirect.");
-    assert_equals(entry.responseStart, 0, "responseStart should be 0 in cross-origin redirect.");
-    assert_equals(entry.secureConnectionStart, 0, "secureConnectionStart should be 0 in cross-origin redirect.");
-  } else {
-    assert_greater_than(entry.redirectStart, 0, "redirectStart should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.redirectEnd, 0, "redirectEnd should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.domainLookupStart, 0, "domainLookupStart should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.domainLookupEnd, 0, "domainLookupEnd should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.connectStart, 0, "connectStart should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.connectEnd, 0, "connectEnd should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.requestStart, 0, "requestStart should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.responseStart, 0, "responseStart should be more than 0 in same-origin redirect.");
-    assert_greater_than(entry.secureConnectionStart, 0, "secureConnectionStart should be more than 0 in same-origin redirect.");
-  }
-  assert_greater_than(entry.fetchStart, 0, "fetchStart should be greater than 0 in redirects.");
-  assert_greater_than(entry.responseEnd, 0, "responseEnd should be greater than 0 in redirects.");
-  assert_greater_than(entry.duration, 0, "duration should be greater than 0 in redirects.");
-  assert_greater_than(entry.responseEnd, entry.fetchStart, "responseEnd should be greater than fetchStart in redirects.");
-}
 
 const {REMOTE_ORIGIN, ORIGIN} = get_host_info();
-const redirect = "/common/redirect.py?location=" + "/resource-timing/resources/green.html";
+const redirect = "/common/redirect.py?" +
+                 "location=/resource-timing/resources/green.html";
 const cross_origin_redirect = REMOTE_ORIGIN + redirect;
 const same_origin_redirect = ORIGIN + redirect;
-promise_test(t => {
-  return run_test(t, cross_origin_redirect, true);
-}, "Test fetch for a cross-origin redirect URL");
-promise_test(t => {
-  return run_test(t, same_origin_redirect, false);
-}, "Test fetch for a same-origin redirect URL");
+
+attribute_test(
+  url => fetch(url, {mode: "no-cors", credentials: "include"}),
+  new URL(cross_origin_redirect).href,
+  invariants.assert_cross_origin_redirected_resource,
+  "Test fetching through a cross-origin redirect URL"
+);
+
+attribute_test(
+  url => fetch(url, {mode: "no-cors", credentials: "include"}),
+  new URL(same_origin_redirect).href,
+  invariants.assert_same_origin_redirected_resource,
+  "Test fetching through a same-origin redirect URL"
+);
+
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/nextHopProtocol-is-tao-protected.https.html b/third_party/blink/web_tests/external/wpt/resource-timing/nextHopProtocol-is-tao-protected.https.html
new file mode 100644
index 0000000..e0e96af
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/nextHopProtocol-is-tao-protected.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>Resource Timing - Check that nextHopProtocol is TAO protected</title>
+<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/entry-invariants.js"></script>
+<script src="resources/resource-loaders.js"></script>
+</head>
+<body>
+<script>
+
+const {HTTPS_REMOTE_ORIGIN} = get_host_info();
+const remote_resources = `${HTTPS_REMOTE_ORIGIN}/resource-timing/resources`;
+
+// Add iframe to remote origin - page without TAO
+attribute_test(
+  load.iframe, `${remote_resources}/green.htm`,
+  entry => assert_equals(entry.nextHopProtocol, "",
+    "nextHopProtocol should be the empty string"),
+  "Add TAO-less iframe from remote origin. Make sure nextHopProtocol is the " +
+  "empty string"
+);
+
+// Add iframe to remote origin - page with TAO
+attribute_test(
+  load.iframe, `${remote_resources}/blank-with-tao.html`,
+  entry => assert_not_equals(entry.nextHopProtocol, "",
+    "nextHopProtocol should not be the empty string"),
+  "Add TAO'd iframe from remote origin. Make sure nextHopProtocol is not " +
+  "the empty string"
+);
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/nextHopProtocol-tao-protected.https.html b/third_party/blink/web_tests/external/wpt/resource-timing/nextHopProtocol-tao-protected.https.html
deleted file mode 100644
index 6011ec2a..0000000
--- a/third_party/blink/web_tests/external/wpt/resource-timing/nextHopProtocol-tao-protected.https.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset="utf-8" />
-<meta name=timeout content=long>
-<title>Resource Timing - Check that nextHopProtocol is TAO protected</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-</head>
-<body>
-<script>
-
-const addFrame = url => {
-  const iframe = document.createElement("iframe");
-  iframe.src = url;
-  document.body.appendChild(iframe);
-};
-
-const host_info = get_host_info();
-
-// Add iframe to remote origin - page without TAO
-promise_test(t => {
-  return new Promise((resolve, reject) => {
-    const observer = new PerformanceObserver(list => {
-      const entries = list.getEntries();
-      for (entry of entries) {
-        if (entry.name.includes("green.html")) {
-          observer.disconnect();
-          // Observe its performance entry to make sure nextHopProtocol is empty
-          if (entry.nextHopProtocol != "") {
-            reject("nextHopProtocol should be the empty string");
-          }
-          resolve();
-        }
-      }
-    });
-    observer.observe({entryTypes: ["resource"]});
-    addFrame(host_info.HTTPS_REMOTE_ORIGIN + "/resource-timing/resources/green.html");
-  });
-}, "Add TAO-less iframe to remote origin. Make sure nextHopProtocol is the empty string");
-
-// Add iframe to remote origin - page with TAO
-promise_test(t => {
-  return new Promise((resolve, reject) => {
-    const observer = new PerformanceObserver(list => {
-      const entries = list.getEntries();
-      for (entry of entries) {
-        if (entry.name.includes("blank-with-tao.html")) {
-          observer.disconnect();
-          // Observe its performance entry to make sure nextHopProtocol is empty
-          if (entry.nextHopProtocol == "") {
-            reject("nextHopProtocol should not be the empty string");
-          }
-          resolve();
-        }
-      }
-    });
-    observer.observe({entryTypes: ["resource"]});
-    addFrame(host_info.HTTPS_REMOTE_ORIGIN + "/resource-timing/resources/blank-with-tao.html");
-  });
-}, "Add TAO iframe to remote origin. Make sure nextHopProtocol is not the empty string");
-</script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js
index ee835c2..ecea5e7 100644
--- a/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js
+++ b/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js
@@ -129,21 +129,23 @@
     this.peripherals_ = new Map();
   }
 
-  // Simulates a peripheral with |address|, |name| and |known_service_uuids|
-  // that has already been connected to the system. If the peripheral existed
-  // already it updates its name and known UUIDs. |known_service_uuids| should
-  // be an array of BluetoothServiceUUIDs
+  // Simulates a peripheral with |address|, |name|, |manufacturerData| and
+  // |known_service_uuids| that has already been connected to the system. If the
+  // peripheral existed already it updates its name, manufacturer data, and
+  // known UUIDs. |known_service_uuids| should be an array of
+  // BluetoothServiceUUIDs
   // https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
   //
   // Platforms offer methods to retrieve devices that have already been
   // connected to the system or weren't connected through the UA e.g. a user
   // connected a peripheral through the system's settings. This method is
   // intended to simulate peripherals that those methods would return.
-  async simulatePreconnectedPeripheral({
-    address, name, knownServiceUUIDs = []}) {
-
+  async simulatePreconnectedPeripheral(
+      {address, name, manufacturerData = {}, knownServiceUUIDs = []}) {
     await this.fake_central_ptr_.simulatePreconnectedPeripheral(
-      address, name, canonicalizeAndConvertToMojoUUID(knownServiceUUIDs));
+        address, name,
+        convertToMojoMap(manufacturerData, Number, true /* isNumberKey */),
+        canonicalizeAndConvertToMojoUUID(knownServiceUUIDs));
 
     return this.fetchOrCreatePeripheral_(address);
   }
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html
index 3706299..ed0c15f 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/audioparam-setValueCurve-exceptions.html
@@ -20,7 +20,6 @@
       let audit = Audit.createTaskRunner();
 
       audit.define('setValueCurve', (task, should) => {
-        let success = true;
         let context =
             new OfflineAudioContext(1, testDurationFrames, sampleRate);
         let g = context.createGain();
@@ -79,6 +78,37 @@
         task.done();
       });
 
+      audit.define('value setter', (task, should) => {
+        let context =
+            new OfflineAudioContext(1, testDurationFrames, sampleRate);
+        let g = context.createGain();
+        let curve = new Float32Array(2);
+
+        // Start time and duration for setValueCurveAtTime
+        let curveStartTime = 0.;
+        let duration = 0.2 * testDurationSec;
+
+        // Some time that is known to be during the setValueCurveTime interval.
+        let automationTime = 0.;
+
+        should(
+            () => {
+              g.gain.setValueCurveAtTime(curve, curveStartTime, duration);
+            },
+            'setValueCurveAtTime(curve, ' + curveStartTime + ', ' + duration +
+                ')')
+            .notThrow();
+
+        should(
+            function() {
+              g.gain.value = 0.;
+            },
+            'value setter')
+            .throw(DOMException, 'NotSupportedError');
+
+        task.done();
+      });
+
       audit.define('automations', (task, should) => {
         let context =
             new OfflineAudioContext(1, testDurationFrames, sampleRate);
@@ -175,7 +205,6 @@
       audit.define('catch-exception', (task, should) => {
         // Verify that the curve isn't inserted into the time line even if we
         // catch the exception.
-        let success = true;
         let context =
             new OfflineAudioContext(1, testDurationFrames, sampleRate);
         let gain = context.createGain();
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/pannernode-setposition-throws.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/pannernode-setposition-throws.html
new file mode 100644
index 0000000..20534119
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/pannernode-setposition-throws.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test PannerNode.setPosition() throws with parameter out of range of float</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+// https://webaudio.github.io/web-audio-api/#dom-pannernode-setposition
+// setPosition(x, y, z) "is equivalent to setting positionX.value,
+// positionY.value, and positionZ.value directly with the given x, y, and z
+// values, respectively."  setPosition() parameters are double, but the
+// AudioParam value setter has a float parameter, so out of range values
+// throw.
+const FLT_MAX = 3.40282e+38;
+let panner;
+setup(() => {
+  const ctx = new OfflineAudioContext({length: 1, sampleRate: 24000});
+  panner = ctx.createPanner();
+});
+test(() => {
+  assert_throws_js(TypeError, () => panner.setPosition(2 * FLT_MAX, 0, 0));
+}, "setPosition x");
+test(() => {
+  assert_throws_js(TypeError, () => panner.setPosition(0, -2 * FLT_MAX, 0));
+}, "setPosition y");
+test(() => {
+  assert_throws_js(TypeError, () => panner.setPosition(0, 0, 2 * FLT_MAX));
+}, "setPosition z");
+test(() => {
+  assert_throws_js(TypeError, () => panner.setOrientation(-2 * FLT_MAX, 0, 0));
+}, "setOrientation x");
+test(() => {
+  assert_throws_js(TypeError, () => panner.setOrientation(0, 2 * FLT_MAX, 0));
+}, "setOrientation y");
+test(() => {
+  assert_throws_js(TypeError, () => panner.setOrientation(0, 0, -2 * FLT_MAX));
+}, "setOrientation z");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-svc/RTCRtpParameters-scalability.html b/third_party/blink/web_tests/external/wpt/webrtc-svc/RTCRtpParameters-scalability.html
index 98a8b3d..3a732f0 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-svc/RTCRtpParameters-scalability.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc-svc/RTCRtpParameters-scalability.html
@@ -5,6 +5,7 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/webrtc/dictionary-helper.js"></script>
 <script src="/webrtc/RTCRtpParameters-helper.js"></script>
+<script src="/webrtc/RTCPeerConnection-helper.js"></script>
 <script>
   'use strict';
 
@@ -19,6 +20,36 @@
     return encodings[0];
   }
 
+  const capabilities = RTCRtpSender.getCapabilities('video');
+  let index = 0;
+  for (const codec of capabilities.codecs) {
+    if ('scalabilityModes' in codec && codec.scalabilityModes.length > 0) {
+      for (const scalabilityMode of codec.scalabilityModes) {
+        promise_test(async t => {
+          const v = document.createElement('video');
+          v.autoplay = true;
+          const pc1 = new RTCPeerConnection();
+          const pc2 = new RTCPeerConnection();
+          t.add_cleanup(() => pc1.close());
+          t.add_cleanup(() => pc2.close());
+          const stream1 = await getNoiseStream({ video: { signal: 100 , width: 60, height: 60} });
+          const [track1] = stream1.getTracks();
+          t.add_cleanup(() => track1.stop());
+          const transceiver = pc1.addTransceiver(track1, {
+            sendEncodings: [{ scalabilityMode: scalabilityMode }],
+          });
+          transceiver.setCodecPreferences([codec]);
+          const haveTrackEvent = new Promise(r => pc2.ontrack = r);
+          exchangeIceCandidates(pc1, pc2);
+          await exchangeOfferAnswer(pc1, pc2);
+          v.srcObject = new MediaStream([(await haveTrackEvent).track]);
+          await new Promise(r => v.onloadedmetadata = r);
+          await detectSignal(t, v, 100);
+        }, `[${index++}] ${codec.mimeType} - ${scalabilityMode} should produce valid video content`);
+      }
+    }
+  }
+
   promise_test(async t => {
     const pc = new RTCPeerConnection();
     t.add_cleanup(() => pc.close());
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt
deleted file mode 100644
index 845f5cf..0000000
--- a/third_party/blink/web_tests/platform/linux/external/wpt/webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-Harness Error. harness_status.status = 1 , harness_status.message = Unhandled rejection: Failed to execute 'end' on 'XRSession': XRSession has already ended.
-FAIL Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl Failed to execute 'getReflectionCubeMap' on 'XRWebGLBinding': WebGL contexts must have the OES_texture_half_float extension enabled prior to calling getReflectionCubeMap with a format of "rgba16f". This restriction does not apply to WebGL 2.0 contexts.
-PASS Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl2
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt
deleted file mode 100644
index 5c38daf1..0000000
--- a/third_party/blink/web_tests/platform/linux/external/wpt/webxr/xrWebGLLayer_opaque_framebuffer_stencil.https-expected.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-This is a testharness.js-based test.
-FAIL Ensure that the framebuffer given by the WebGL layer works with stencil for non-immersive - webgl promise_test: Unhandled rejection with value: "Failed, readPixels (1) didn't work, gl error = 1282, pixel = 30 30 30"
-FAIL Ensure that the framebuffer given by the WebGL layer works with stencil for non-immersive - webgl2 promise_test: Unhandled rejection with value: "Failed, readPixels (1) didn't work, gl error = 1282, pixel = 30 30 30"
-FAIL Ensure that the framebuffer given by the WebGL layer works with stencil for immersive - webgl promise_test: Unhandled rejection with value: "Failed, readPixels (1) didn't work, gl error = 1282, pixel = 30 30 30"
-FAIL Ensure that the framebuffer given by the WebGL layer works with stencil for immersive - webgl2 promise_test: Unhandled rejection with value: "Failed, readPixels (1) didn't work, gl error = 1282, pixel = 30 30 30"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_auto_per-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_auto_per-expected.png
index ce66397..088a44b9 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_auto_per-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_auto_per-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_fix_per-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_fix_per-expected.png
index e164a92..e4d4e6e 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_fix_per-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla/core/col_widths_fix_per-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index 08611ed8..a47cc3cd 100644
--- a/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/linux/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/plz-service-worker/external/wpt/service-workers/service-worker/getregistrations.https-expected.txt b/third_party/blink/web_tests/platform/linux/virtual/plz-service-worker/external/wpt/service-workers/service-worker/getregistrations.https-expected.txt
deleted file mode 100644
index e34401fc..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/plz-service-worker/external/wpt/service-workers/service-worker/getregistrations.https-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS registrations are not returned following unregister
-PASS Register then getRegistrations
-PASS Register multiple times then getRegistrations
-PASS Register then Unregister then getRegistrations
-PASS Register then Unregister with controlled frame then getRegistrations
-PASS getRegistrations promise resolves only with same origin registrations.
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/fetch/api/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/fetch/api/idlharness.any.sharedworker-expected.txt
deleted file mode 100644
index 3a6843f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/fetch/api/idlharness.any.sharedworker-expected.txt
+++ /dev/null
@@ -1,142 +0,0 @@
-This is a testharness.js-based test.
-Found 138 tests; 136 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
-PASS Partial interface mixin WindowOrWorkerGlobalScope: member names are unique
-PASS Partial interface Window: member names are unique
-PASS Request includes Body: member names are unique
-PASS Response includes Body: member names are unique
-PASS Window includes GlobalEventHandlers: member names are unique
-PASS Window includes WindowEventHandlers: member names are unique
-PASS Window includes WindowOrWorkerGlobalScope: member names are unique
-PASS WorkerGlobalScope includes WindowOrWorkerGlobalScope: member names are unique
-PASS Window includes AnimationFrameProvider: member names are unique
-PASS Window includes WindowSessionStorage: member names are unique
-PASS Window includes WindowLocalStorage: member names are unique
-PASS Headers interface: existence and properties of interface object
-PASS Headers interface object length
-PASS Headers interface object name
-PASS Headers interface: existence and properties of interface prototype object
-PASS Headers interface: existence and properties of interface prototype object's "constructor" property
-PASS Headers interface: existence and properties of interface prototype object's @@unscopables property
-PASS Headers interface: operation append(ByteString, ByteString)
-PASS Headers interface: operation delete(ByteString)
-PASS Headers interface: operation get(ByteString)
-PASS Headers interface: operation has(ByteString)
-PASS Headers interface: operation set(ByteString, ByteString)
-PASS Headers interface: iterable<ByteString, ByteString>
-PASS Headers must be primary interface of new Headers()
-PASS Stringification of new Headers()
-PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
-PASS Headers interface: calling append(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "delete(ByteString)" with the proper type
-PASS Headers interface: calling delete(ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "get(ByteString)" with the proper type
-PASS Headers interface: calling get(ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "has(ByteString)" with the proper type
-PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
-PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Request interface: existence and properties of interface object
-PASS Request interface object length
-PASS Request interface object name
-PASS Request interface: existence and properties of interface prototype object
-PASS Request interface: existence and properties of interface prototype object's "constructor" property
-PASS Request interface: existence and properties of interface prototype object's @@unscopables property
-PASS Request interface: attribute method
-PASS Request interface: attribute url
-PASS Request interface: attribute headers
-PASS Request interface: attribute destination
-PASS Request interface: attribute referrer
-PASS Request interface: attribute referrerPolicy
-PASS Request interface: attribute mode
-PASS Request interface: attribute credentials
-PASS Request interface: attribute cache
-PASS Request interface: attribute redirect
-PASS Request interface: attribute integrity
-PASS Request interface: attribute keepalive
-FAIL Request interface: attribute isReloadNavigation assert_true: The prototype object must have a property "isReloadNavigation" expected true got false
-PASS Request interface: attribute isHistoryNavigation
-PASS Request interface: attribute signal
-PASS Request interface: operation clone()
-PASS Request interface: attribute body
-PASS Request interface: attribute bodyUsed
-PASS Request interface: operation arrayBuffer()
-PASS Request interface: operation blob()
-PASS Request interface: operation formData()
-PASS Request interface: operation json()
-PASS Request interface: operation text()
-PASS Request must be primary interface of new Request('about:blank')
-PASS Stringification of new Request('about:blank')
-PASS Request interface: new Request('about:blank') must inherit property "method" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "url" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "headers" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "destination" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "referrer" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "referrerPolicy" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "mode" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "credentials" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "cache" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "redirect" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "integrity" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type
-FAIL Request interface: new Request('about:blank') must inherit property "isReloadNavigation" with the proper type assert_inherits: property "isReloadNavigation" not found in prototype chain
-PASS Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "signal" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "clone()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "body" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "bodyUsed" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "arrayBuffer()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "blob()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "formData()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "json()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "text()" with the proper type
-PASS Response interface: existence and properties of interface object
-PASS Response interface object length
-PASS Response interface object name
-PASS Response interface: existence and properties of interface prototype object
-PASS Response interface: existence and properties of interface prototype object's "constructor" property
-PASS Response interface: existence and properties of interface prototype object's @@unscopables property
-PASS Response interface: operation error()
-PASS Response interface: operation redirect(USVString, optional unsigned short)
-PASS Response interface: attribute type
-PASS Response interface: attribute url
-PASS Response interface: attribute redirected
-PASS Response interface: attribute status
-PASS Response interface: attribute ok
-PASS Response interface: attribute statusText
-PASS Response interface: attribute headers
-PASS Response interface: operation clone()
-PASS Response interface: attribute body
-PASS Response interface: attribute bodyUsed
-PASS Response interface: operation arrayBuffer()
-PASS Response interface: operation blob()
-PASS Response interface: operation formData()
-PASS Response interface: operation json()
-PASS Response interface: operation text()
-PASS Response must be primary interface of new Response()
-PASS Stringification of new Response()
-PASS Response interface: new Response() must inherit property "error()" with the proper type
-PASS Response interface: new Response() must inherit property "redirect(USVString, optional unsigned short)" with the proper type
-PASS Response interface: calling redirect(USVString, optional unsigned short) on new Response() with too few arguments must throw TypeError
-PASS Response interface: new Response() must inherit property "type" with the proper type
-PASS Response interface: new Response() must inherit property "url" with the proper type
-PASS Response interface: new Response() must inherit property "redirected" with the proper type
-PASS Response interface: new Response() must inherit property "status" with the proper type
-PASS Response interface: new Response() must inherit property "ok" with the proper type
-PASS Response interface: new Response() must inherit property "statusText" with the proper type
-PASS Response interface: new Response() must inherit property "headers" with the proper type
-PASS Response interface: new Response() must inherit property "clone()" with the proper type
-PASS Response interface: new Response() must inherit property "body" with the proper type
-PASS Response interface: new Response() must inherit property "bodyUsed" with the proper type
-PASS Response interface: new Response() must inherit property "arrayBuffer()" with the proper type
-PASS Response interface: new Response() must inherit property "blob()" with the proper type
-PASS Response interface: new Response() must inherit property "formData()" with the proper type
-PASS Response interface: new Response() must inherit property "json()" with the proper type
-PASS Response interface: new Response() must inherit property "text()" with the proper type
-PASS WorkerGlobalScope interface: operation fetch(RequestInfo, optional RequestInit)
-PASS WorkerGlobalScope interface: self must inherit property "fetch(RequestInfo, optional RequestInit)" with the proper type
-PASS WorkerGlobalScope interface: calling fetch(RequestInfo, optional RequestInit) on self with too few arguments must throw TypeError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/gamepad/idlharness.https.window-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/gamepad/idlharness.https.window-expected.txt
deleted file mode 100644
index 1d8a0ea4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/gamepad/idlharness.https.window-expected.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-This is a testharness.js-based test.
-Found 73 tests; 66 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface Navigator: original interface defined
-PASS Partial interface Navigator: valid exposure set
-PASS Partial interface Navigator: member names are unique
-PASS Partial interface mixin WindowEventHandlers: original interface mixin defined
-PASS Partial interface mixin WindowEventHandlers: member names are unique
-PASS Partial interface mixin NavigatorID: member names are unique
-PASS Partial interface HTMLBodyElement: member names are unique
-PASS Partial interface Window: member names are unique
-PASS HTMLElement includes GlobalEventHandlers: member names are unique
-PASS HTMLElement includes DocumentAndElementEventHandlers: member names are unique
-PASS HTMLElement includes ElementContentEditable: member names are unique
-PASS HTMLElement includes HTMLOrSVGElement: member names are unique
-PASS HTMLBodyElement includes WindowEventHandlers: member names are unique
-PASS Window includes GlobalEventHandlers: member names are unique
-PASS Window includes WindowEventHandlers: member names are unique
-PASS Window includes WindowOrWorkerGlobalScope: member names are unique
-PASS Navigator includes NavigatorID: member names are unique
-PASS Navigator includes NavigatorLanguage: member names are unique
-PASS Navigator includes NavigatorOnLine: member names are unique
-PASS Navigator includes NavigatorContentUtils: member names are unique
-PASS Navigator includes NavigatorCookies: member names are unique
-PASS Navigator includes NavigatorPlugins: member names are unique
-PASS Navigator includes NavigatorConcurrentHardware: member names are unique
-PASS Window includes AnimationFrameProvider: member names are unique
-PASS Window includes WindowSessionStorage: member names are unique
-PASS Window includes WindowLocalStorage: member names are unique
-PASS HTMLFrameSetElement includes WindowEventHandlers: member names are unique
-PASS Element includes ParentNode: member names are unique
-PASS Element includes NonDocumentTypeChildNode: member names are unique
-PASS Element includes ChildNode: member names are unique
-PASS Element includes Slottable: member names are unique
-PASS Gamepad interface: existence and properties of interface object
-PASS Gamepad interface object length
-PASS Gamepad interface object name
-PASS Gamepad interface: existence and properties of interface prototype object
-PASS Gamepad interface: existence and properties of interface prototype object's "constructor" property
-PASS Gamepad interface: existence and properties of interface prototype object's @@unscopables property
-PASS Gamepad interface: attribute id
-PASS Gamepad interface: attribute index
-PASS Gamepad interface: attribute connected
-PASS Gamepad interface: attribute timestamp
-PASS Gamepad interface: attribute mapping
-PASS Gamepad interface: attribute axes
-PASS Gamepad interface: attribute buttons
-PASS GamepadButton interface: existence and properties of interface object
-PASS GamepadButton interface object length
-PASS GamepadButton interface object name
-PASS GamepadButton interface: existence and properties of interface prototype object
-PASS GamepadButton interface: existence and properties of interface prototype object's "constructor" property
-PASS GamepadButton interface: existence and properties of interface prototype object's @@unscopables property
-PASS GamepadButton interface: attribute pressed
-PASS GamepadButton interface: attribute touched
-PASS GamepadButton interface: attribute value
-PASS GamepadEvent interface: existence and properties of interface object
-FAIL GamepadEvent interface object length assert_equals: wrong value for GamepadEvent.length expected 2 but got 1
-PASS GamepadEvent interface object name
-PASS GamepadEvent interface: existence and properties of interface prototype object
-PASS GamepadEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS GamepadEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS GamepadEvent interface: attribute gamepad
-PASS GamepadEvent must be primary interface of new GamepadEvent("gamepad")
-PASS Stringification of new GamepadEvent("gamepad")
-PASS GamepadEvent interface: new GamepadEvent("gamepad") must inherit property "gamepad" with the proper type
-FAIL HTMLBodyElement interface: attribute ongamepadconnected assert_true: The prototype object must have a property "ongamepadconnected" expected true got false
-FAIL HTMLBodyElement interface: attribute ongamepaddisconnected assert_true: The prototype object must have a property "ongamepaddisconnected" expected true got false
-FAIL Window interface: attribute ongamepadconnected assert_own_property: The global object must have a property "ongamepadconnected" expected property "ongamepadconnected" missing
-FAIL Window interface: attribute ongamepaddisconnected assert_own_property: The global object must have a property "ongamepaddisconnected" expected property "ongamepaddisconnected" missing
-PASS Navigator interface: operation getGamepads()
-PASS Navigator interface: navigator must inherit property "getGamepads()" with the proper type
-FAIL HTMLFrameSetElement interface: attribute ongamepadconnected assert_true: The prototype object must have a property "ongamepadconnected" expected true got false
-FAIL HTMLFrameSetElement interface: attribute ongamepaddisconnected assert_true: The prototype object must have a property "ongamepaddisconnected" expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/storage/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/storage/idlharness.https.any.worker-expected.txt
deleted file mode 100644
index ea67f14d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.12/external/wpt/storage/idlharness.https.any.worker-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface mixin NavigatorID: member names are unique
-PASS Navigator includes NavigatorStorage: member names are unique
-PASS WorkerNavigator includes NavigatorStorage: member names are unique
-PASS Navigator includes NavigatorID: member names are unique
-PASS Navigator includes NavigatorLanguage: member names are unique
-PASS Navigator includes NavigatorOnLine: member names are unique
-PASS Navigator includes NavigatorContentUtils: member names are unique
-PASS Navigator includes NavigatorCookies: member names are unique
-PASS Navigator includes NavigatorPlugins: member names are unique
-PASS Navigator includes NavigatorConcurrentHardware: member names are unique
-PASS WorkerNavigator includes NavigatorID: member names are unique
-PASS WorkerNavigator includes NavigatorLanguage: member names are unique
-PASS WorkerNavigator includes NavigatorOnLine: member names are unique
-PASS WorkerNavigator includes NavigatorConcurrentHardware: member names are unique
-FAIL StorageManager interface: existence and properties of interface object assert_equals: prototype of self's property "StorageManager" is not Function.prototype expected function "function () { [native code] }" but got function "function EventTarget() { [native code] }"
-PASS StorageManager interface object length
-PASS StorageManager interface object name
-FAIL StorageManager interface: existence and properties of interface prototype object assert_equals: prototype of StorageManager.prototype is not Object.prototype expected object "[object Object]" but got object "[object EventTarget]"
-PASS StorageManager interface: existence and properties of interface prototype object's "constructor" property
-PASS StorageManager interface: existence and properties of interface prototype object's @@unscopables property
-PASS StorageManager interface: operation persisted()
-PASS StorageManager interface: member persist
-PASS StorageManager interface: operation estimate()
-PASS StorageManager must be primary interface of navigator.storage
-PASS Stringification of navigator.storage
-PASS StorageManager interface: navigator.storage must inherit property "persisted()" with the proper type
-PASS StorageManager interface: navigator.storage must not have property "persist"
-PASS StorageManager interface: navigator.storage must inherit property "estimate()" with the proper type
-PASS WorkerNavigator interface: attribute storage
-PASS WorkerNavigator interface: navigator must inherit property "storage" with the proper type
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/fetch/api/idlharness.any.sharedworker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/fetch/api/idlharness.any.sharedworker-expected.txt
deleted file mode 100644
index 3a6843f..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/fetch/api/idlharness.any.sharedworker-expected.txt
+++ /dev/null
@@ -1,142 +0,0 @@
-This is a testharness.js-based test.
-Found 138 tests; 136 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface mixin WindowOrWorkerGlobalScope: original interface mixin defined
-PASS Partial interface mixin WindowOrWorkerGlobalScope: member names are unique
-PASS Partial interface Window: member names are unique
-PASS Request includes Body: member names are unique
-PASS Response includes Body: member names are unique
-PASS Window includes GlobalEventHandlers: member names are unique
-PASS Window includes WindowEventHandlers: member names are unique
-PASS Window includes WindowOrWorkerGlobalScope: member names are unique
-PASS WorkerGlobalScope includes WindowOrWorkerGlobalScope: member names are unique
-PASS Window includes AnimationFrameProvider: member names are unique
-PASS Window includes WindowSessionStorage: member names are unique
-PASS Window includes WindowLocalStorage: member names are unique
-PASS Headers interface: existence and properties of interface object
-PASS Headers interface object length
-PASS Headers interface object name
-PASS Headers interface: existence and properties of interface prototype object
-PASS Headers interface: existence and properties of interface prototype object's "constructor" property
-PASS Headers interface: existence and properties of interface prototype object's @@unscopables property
-PASS Headers interface: operation append(ByteString, ByteString)
-PASS Headers interface: operation delete(ByteString)
-PASS Headers interface: operation get(ByteString)
-PASS Headers interface: operation has(ByteString)
-PASS Headers interface: operation set(ByteString, ByteString)
-PASS Headers interface: iterable<ByteString, ByteString>
-PASS Headers must be primary interface of new Headers()
-PASS Stringification of new Headers()
-PASS Headers interface: new Headers() must inherit property "append(ByteString, ByteString)" with the proper type
-PASS Headers interface: calling append(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "delete(ByteString)" with the proper type
-PASS Headers interface: calling delete(ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "get(ByteString)" with the proper type
-PASS Headers interface: calling get(ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "has(ByteString)" with the proper type
-PASS Headers interface: calling has(ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Headers interface: new Headers() must inherit property "set(ByteString, ByteString)" with the proper type
-PASS Headers interface: calling set(ByteString, ByteString) on new Headers() with too few arguments must throw TypeError
-PASS Request interface: existence and properties of interface object
-PASS Request interface object length
-PASS Request interface object name
-PASS Request interface: existence and properties of interface prototype object
-PASS Request interface: existence and properties of interface prototype object's "constructor" property
-PASS Request interface: existence and properties of interface prototype object's @@unscopables property
-PASS Request interface: attribute method
-PASS Request interface: attribute url
-PASS Request interface: attribute headers
-PASS Request interface: attribute destination
-PASS Request interface: attribute referrer
-PASS Request interface: attribute referrerPolicy
-PASS Request interface: attribute mode
-PASS Request interface: attribute credentials
-PASS Request interface: attribute cache
-PASS Request interface: attribute redirect
-PASS Request interface: attribute integrity
-PASS Request interface: attribute keepalive
-FAIL Request interface: attribute isReloadNavigation assert_true: The prototype object must have a property "isReloadNavigation" expected true got false
-PASS Request interface: attribute isHistoryNavigation
-PASS Request interface: attribute signal
-PASS Request interface: operation clone()
-PASS Request interface: attribute body
-PASS Request interface: attribute bodyUsed
-PASS Request interface: operation arrayBuffer()
-PASS Request interface: operation blob()
-PASS Request interface: operation formData()
-PASS Request interface: operation json()
-PASS Request interface: operation text()
-PASS Request must be primary interface of new Request('about:blank')
-PASS Stringification of new Request('about:blank')
-PASS Request interface: new Request('about:blank') must inherit property "method" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "url" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "headers" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "destination" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "referrer" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "referrerPolicy" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "mode" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "credentials" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "cache" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "redirect" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "integrity" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "keepalive" with the proper type
-FAIL Request interface: new Request('about:blank') must inherit property "isReloadNavigation" with the proper type assert_inherits: property "isReloadNavigation" not found in prototype chain
-PASS Request interface: new Request('about:blank') must inherit property "isHistoryNavigation" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "signal" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "clone()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "body" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "bodyUsed" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "arrayBuffer()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "blob()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "formData()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "json()" with the proper type
-PASS Request interface: new Request('about:blank') must inherit property "text()" with the proper type
-PASS Response interface: existence and properties of interface object
-PASS Response interface object length
-PASS Response interface object name
-PASS Response interface: existence and properties of interface prototype object
-PASS Response interface: existence and properties of interface prototype object's "constructor" property
-PASS Response interface: existence and properties of interface prototype object's @@unscopables property
-PASS Response interface: operation error()
-PASS Response interface: operation redirect(USVString, optional unsigned short)
-PASS Response interface: attribute type
-PASS Response interface: attribute url
-PASS Response interface: attribute redirected
-PASS Response interface: attribute status
-PASS Response interface: attribute ok
-PASS Response interface: attribute statusText
-PASS Response interface: attribute headers
-PASS Response interface: operation clone()
-PASS Response interface: attribute body
-PASS Response interface: attribute bodyUsed
-PASS Response interface: operation arrayBuffer()
-PASS Response interface: operation blob()
-PASS Response interface: operation formData()
-PASS Response interface: operation json()
-PASS Response interface: operation text()
-PASS Response must be primary interface of new Response()
-PASS Stringification of new Response()
-PASS Response interface: new Response() must inherit property "error()" with the proper type
-PASS Response interface: new Response() must inherit property "redirect(USVString, optional unsigned short)" with the proper type
-PASS Response interface: calling redirect(USVString, optional unsigned short) on new Response() with too few arguments must throw TypeError
-PASS Response interface: new Response() must inherit property "type" with the proper type
-PASS Response interface: new Response() must inherit property "url" with the proper type
-PASS Response interface: new Response() must inherit property "redirected" with the proper type
-PASS Response interface: new Response() must inherit property "status" with the proper type
-PASS Response interface: new Response() must inherit property "ok" with the proper type
-PASS Response interface: new Response() must inherit property "statusText" with the proper type
-PASS Response interface: new Response() must inherit property "headers" with the proper type
-PASS Response interface: new Response() must inherit property "clone()" with the proper type
-PASS Response interface: new Response() must inherit property "body" with the proper type
-PASS Response interface: new Response() must inherit property "bodyUsed" with the proper type
-PASS Response interface: new Response() must inherit property "arrayBuffer()" with the proper type
-PASS Response interface: new Response() must inherit property "blob()" with the proper type
-PASS Response interface: new Response() must inherit property "formData()" with the proper type
-PASS Response interface: new Response() must inherit property "json()" with the proper type
-PASS Response interface: new Response() must inherit property "text()" with the proper type
-PASS WorkerGlobalScope interface: operation fetch(RequestInfo, optional RequestInit)
-PASS WorkerGlobalScope interface: self must inherit property "fetch(RequestInfo, optional RequestInit)" with the proper type
-PASS WorkerGlobalScope interface: calling fetch(RequestInfo, optional RequestInit) on self with too few arguments must throw TypeError
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/gamepad/idlharness.https.window-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/gamepad/idlharness.https.window-expected.txt
deleted file mode 100644
index 1d8a0ea4..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/gamepad/idlharness.https.window-expected.txt
+++ /dev/null
@@ -1,77 +0,0 @@
-This is a testharness.js-based test.
-Found 73 tests; 66 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface Navigator: original interface defined
-PASS Partial interface Navigator: valid exposure set
-PASS Partial interface Navigator: member names are unique
-PASS Partial interface mixin WindowEventHandlers: original interface mixin defined
-PASS Partial interface mixin WindowEventHandlers: member names are unique
-PASS Partial interface mixin NavigatorID: member names are unique
-PASS Partial interface HTMLBodyElement: member names are unique
-PASS Partial interface Window: member names are unique
-PASS HTMLElement includes GlobalEventHandlers: member names are unique
-PASS HTMLElement includes DocumentAndElementEventHandlers: member names are unique
-PASS HTMLElement includes ElementContentEditable: member names are unique
-PASS HTMLElement includes HTMLOrSVGElement: member names are unique
-PASS HTMLBodyElement includes WindowEventHandlers: member names are unique
-PASS Window includes GlobalEventHandlers: member names are unique
-PASS Window includes WindowEventHandlers: member names are unique
-PASS Window includes WindowOrWorkerGlobalScope: member names are unique
-PASS Navigator includes NavigatorID: member names are unique
-PASS Navigator includes NavigatorLanguage: member names are unique
-PASS Navigator includes NavigatorOnLine: member names are unique
-PASS Navigator includes NavigatorContentUtils: member names are unique
-PASS Navigator includes NavigatorCookies: member names are unique
-PASS Navigator includes NavigatorPlugins: member names are unique
-PASS Navigator includes NavigatorConcurrentHardware: member names are unique
-PASS Window includes AnimationFrameProvider: member names are unique
-PASS Window includes WindowSessionStorage: member names are unique
-PASS Window includes WindowLocalStorage: member names are unique
-PASS HTMLFrameSetElement includes WindowEventHandlers: member names are unique
-PASS Element includes ParentNode: member names are unique
-PASS Element includes NonDocumentTypeChildNode: member names are unique
-PASS Element includes ChildNode: member names are unique
-PASS Element includes Slottable: member names are unique
-PASS Gamepad interface: existence and properties of interface object
-PASS Gamepad interface object length
-PASS Gamepad interface object name
-PASS Gamepad interface: existence and properties of interface prototype object
-PASS Gamepad interface: existence and properties of interface prototype object's "constructor" property
-PASS Gamepad interface: existence and properties of interface prototype object's @@unscopables property
-PASS Gamepad interface: attribute id
-PASS Gamepad interface: attribute index
-PASS Gamepad interface: attribute connected
-PASS Gamepad interface: attribute timestamp
-PASS Gamepad interface: attribute mapping
-PASS Gamepad interface: attribute axes
-PASS Gamepad interface: attribute buttons
-PASS GamepadButton interface: existence and properties of interface object
-PASS GamepadButton interface object length
-PASS GamepadButton interface object name
-PASS GamepadButton interface: existence and properties of interface prototype object
-PASS GamepadButton interface: existence and properties of interface prototype object's "constructor" property
-PASS GamepadButton interface: existence and properties of interface prototype object's @@unscopables property
-PASS GamepadButton interface: attribute pressed
-PASS GamepadButton interface: attribute touched
-PASS GamepadButton interface: attribute value
-PASS GamepadEvent interface: existence and properties of interface object
-FAIL GamepadEvent interface object length assert_equals: wrong value for GamepadEvent.length expected 2 but got 1
-PASS GamepadEvent interface object name
-PASS GamepadEvent interface: existence and properties of interface prototype object
-PASS GamepadEvent interface: existence and properties of interface prototype object's "constructor" property
-PASS GamepadEvent interface: existence and properties of interface prototype object's @@unscopables property
-PASS GamepadEvent interface: attribute gamepad
-PASS GamepadEvent must be primary interface of new GamepadEvent("gamepad")
-PASS Stringification of new GamepadEvent("gamepad")
-PASS GamepadEvent interface: new GamepadEvent("gamepad") must inherit property "gamepad" with the proper type
-FAIL HTMLBodyElement interface: attribute ongamepadconnected assert_true: The prototype object must have a property "ongamepadconnected" expected true got false
-FAIL HTMLBodyElement interface: attribute ongamepaddisconnected assert_true: The prototype object must have a property "ongamepaddisconnected" expected true got false
-FAIL Window interface: attribute ongamepadconnected assert_own_property: The global object must have a property "ongamepadconnected" expected property "ongamepadconnected" missing
-FAIL Window interface: attribute ongamepaddisconnected assert_own_property: The global object must have a property "ongamepaddisconnected" expected property "ongamepaddisconnected" missing
-PASS Navigator interface: operation getGamepads()
-PASS Navigator interface: navigator must inherit property "getGamepads()" with the proper type
-FAIL HTMLFrameSetElement interface: attribute ongamepadconnected assert_true: The prototype object must have a property "ongamepadconnected" expected true got false
-FAIL HTMLFrameSetElement interface: attribute ongamepaddisconnected assert_true: The prototype object must have a property "ongamepaddisconnected" expected true got false
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/input-device-capabilities/idlharness.window-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/input-device-capabilities/idlharness.window-expected.txt
deleted file mode 100644
index a164b00..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/input-device-capabilities/idlharness.window-expected.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface UIEvent: original interface defined
-PASS Partial interface UIEvent: member names are unique
-PASS Partial dictionary UIEventInit: original dictionary defined
-PASS Partial dictionary UIEventInit: member names are unique
-PASS Partial interface UIEvent[2]: member names are unique
-PASS Partial interface UIEvent[3]: member names are unique
-PASS Partial dictionary UIEventInit[2]: member names are unique
-PASS InputDeviceCapabilities interface: existence and properties of interface object
-PASS InputDeviceCapabilities interface object length
-PASS InputDeviceCapabilities interface object name
-PASS InputDeviceCapabilities interface: existence and properties of interface prototype object
-PASS InputDeviceCapabilities interface: existence and properties of interface prototype object's "constructor" property
-PASS InputDeviceCapabilities interface: existence and properties of interface prototype object's @@unscopables property
-PASS InputDeviceCapabilities interface: attribute firesTouchEvents
-FAIL InputDeviceCapabilities interface: attribute pointerMovementScrolls assert_true: The prototype object must have a property "pointerMovementScrolls" expected true got false
-PASS InputDeviceCapabilities must be primary interface of new InputDeviceCapabilities
-PASS Stringification of new InputDeviceCapabilities
-PASS InputDeviceCapabilities interface: new InputDeviceCapabilities must inherit property "firesTouchEvents" with the proper type
-FAIL InputDeviceCapabilities interface: new InputDeviceCapabilities must inherit property "pointerMovementScrolls" with the proper type assert_inherits: property "pointerMovementScrolls" not found in prototype chain
-PASS UIEvent interface: attribute sourceCapabilities
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/periodic-background-sync/idlharness.https.any-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/periodic-background-sync/idlharness.https.any-expected.txt
deleted file mode 100644
index edc336c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/periodic-background-sync/idlharness.https.any-expected.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface ServiceWorkerGlobalScope: original interface defined
-PASS Partial interface ServiceWorkerGlobalScope: member names are unique
-PASS Partial interface ServiceWorkerRegistration: original interface defined
-PASS Partial interface ServiceWorkerRegistration: valid exposure set
-PASS Partial interface ServiceWorkerRegistration: member names are unique
-PASS WorkerGlobalScope includes WindowOrWorkerGlobalScope: member names are unique
-PASS PeriodicSyncManager interface: existence and properties of interface object
-PASS PeriodicSyncManager interface object length
-PASS PeriodicSyncManager interface object name
-PASS PeriodicSyncManager interface: existence and properties of interface prototype object
-PASS PeriodicSyncManager interface: existence and properties of interface prototype object's "constructor" property
-PASS PeriodicSyncManager interface: existence and properties of interface prototype object's @@unscopables property
-PASS PeriodicSyncManager interface: operation register(DOMString, optional BackgroundSyncOptions)
-PASS PeriodicSyncManager interface: operation getTags()
-PASS PeriodicSyncManager interface: operation unregister(DOMString)
-FAIL PeriodicSyncManager must be primary interface of registration.periodicSync assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-FAIL Stringification of registration.periodicSync assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-FAIL PeriodicSyncManager interface: registration.periodicSync must inherit property "register(DOMString, optional BackgroundSyncOptions)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-FAIL PeriodicSyncManager interface: calling register(DOMString, optional BackgroundSyncOptions) on registration.periodicSync with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-FAIL PeriodicSyncManager interface: registration.periodicSync must inherit property "getTags()" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-FAIL PeriodicSyncManager interface: registration.periodicSync must inherit property "unregister(DOMString)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-FAIL PeriodicSyncManager interface: calling unregister(DOMString) on registration.periodicSync with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-PASS PeriodicSyncEvent interface: existence and properties of interface object
-FAIL PeriodicSyncEvent must be primary interface of new PeriodicSyncEvent("tag") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: PeriodicSyncEvent is not defined"
-FAIL Stringification of new PeriodicSyncEvent("tag") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: PeriodicSyncEvent is not defined"
-FAIL PeriodicSyncEvent interface: new PeriodicSyncEvent("tag") must not have property "undefined" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: PeriodicSyncEvent is not defined"
-FAIL PeriodicSyncEvent interface: new PeriodicSyncEvent("tag") must not have property "tag" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: PeriodicSyncEvent is not defined"
-PASS ServiceWorkerRegistration interface: attribute periodicSync
-FAIL ServiceWorkerRegistration interface: registration must inherit property "periodicSync" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: registration is not defined"
-PASS ServiceWorkerGlobalScope interface: self must not have property "onperiodicsync"
-FAIL ServiceWorkerGlobalScope interface: onperiodicsync must not have property "onperiodicsync" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: onperiodicsync is not defined"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/storage/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/storage/idlharness.https.any.worker-expected.txt
deleted file mode 100644
index ea67f14d..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/external/wpt/storage/idlharness.https.any.worker-expected.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface mixin NavigatorID: member names are unique
-PASS Navigator includes NavigatorStorage: member names are unique
-PASS WorkerNavigator includes NavigatorStorage: member names are unique
-PASS Navigator includes NavigatorID: member names are unique
-PASS Navigator includes NavigatorLanguage: member names are unique
-PASS Navigator includes NavigatorOnLine: member names are unique
-PASS Navigator includes NavigatorContentUtils: member names are unique
-PASS Navigator includes NavigatorCookies: member names are unique
-PASS Navigator includes NavigatorPlugins: member names are unique
-PASS Navigator includes NavigatorConcurrentHardware: member names are unique
-PASS WorkerNavigator includes NavigatorID: member names are unique
-PASS WorkerNavigator includes NavigatorLanguage: member names are unique
-PASS WorkerNavigator includes NavigatorOnLine: member names are unique
-PASS WorkerNavigator includes NavigatorConcurrentHardware: member names are unique
-FAIL StorageManager interface: existence and properties of interface object assert_equals: prototype of self's property "StorageManager" is not Function.prototype expected function "function () { [native code] }" but got function "function EventTarget() { [native code] }"
-PASS StorageManager interface object length
-PASS StorageManager interface object name
-FAIL StorageManager interface: existence and properties of interface prototype object assert_equals: prototype of StorageManager.prototype is not Object.prototype expected object "[object Object]" but got object "[object EventTarget]"
-PASS StorageManager interface: existence and properties of interface prototype object's "constructor" property
-PASS StorageManager interface: existence and properties of interface prototype object's @@unscopables property
-PASS StorageManager interface: operation persisted()
-PASS StorageManager interface: member persist
-PASS StorageManager interface: operation estimate()
-PASS StorageManager must be primary interface of navigator.storage
-PASS Stringification of navigator.storage
-PASS StorageManager interface: navigator.storage must inherit property "persisted()" with the proper type
-PASS StorageManager interface: navigator.storage must not have property "persist"
-PASS StorageManager interface: navigator.storage must inherit property "estimate()" with the proper type
-PASS WorkerNavigator interface: attribute storage
-PASS WorkerNavigator interface: navigator must inherit property "storage" with the proper type
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/fetch-event.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/fetch-event.https-expected.txt
deleted file mode 100644
index 3fe3911..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/fetch-event.https-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-This is a testharness.js-based test.
-PASS global setup
-PASS Service Worker headers in the request of a fetch event
-PASS Service Worker responds to fetch event with string
-PASS Service Worker responds to fetch event using request fragment with string
-PASS Service Worker responds to fetch event with blob body
-PASS Service Worker responds to fetch event with the referrer URL
-PASS Service Worker responds to fetch event with an existing client id
-PASS Service Worker responds to fetch event with the correct resulting client id
-PASS Service Worker does not respond to fetch event
-PASS Service Worker responds to fetch event with null response body
-PASS Service Worker fetches other file in fetch event
-PASS Service Worker responds to fetch event with POST form
-PASS Service Worker falls back to network in fetch event with POST form
-PASS Multiple calls of respondWith must throw InvalidStateErrors
-PASS Service Worker event.respondWith must set the used flag
-PASS Service Worker should expose FetchEvent URL fragments.
-PASS Service Worker responds to fetch event with the correct cache types
-PASS Service Worker should intercept EventSource
-FAIL Service Worker responds to fetch event with the correct integrity_metadata assert_equals: integrity expected "gs0nqru8KbsrIt5YToQqS9fYao4GQJXtcId610g7cCU=" but got ""
-PASS FetchEvent#body is a string
-FAIL FetchEvent#body is a ReadableStream promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
-PASS FetchEvent#body is a string and is passed to network fallback
-FAIL FetchEvent#body is a ReadableStream and is passed to network fallback promise_test: Unhandled rejection with value: object "TypeError: Failed to fetch"
-PASS FetchEvent#body is a none Uint8Array ReadableStream and is passed to a service worker
-PASS FetchEvent#body is a string, used and passed to network fallback
-PASS FetchEvent#body is a ReadableStream, used and passed to network fallback
-PASS FetchEvent#body is a string, cloned and passed to network fallback
-PASS FetchEvent#body is a ReadableStream, cloned and passed to network fallback
-PASS FetchEvent#body is a blob
-PASS FetchEvent#body is a blob and is passed to network fallback
-PASS Service Worker responds to fetch event with the correct keepalive value
-FAIL FetchEvent#request.isReloadNavigation is true (location.reload()) assert_equals: expected "method = GET, isReloadNavigation = false" but got "method = GET, isReloadNavigation = undefined"
-FAIL FetchEvent#request.isReloadNavigation is true (history.go(0)) assert_equals: expected "method = GET, isReloadNavigation = false" but got "method = GET, isReloadNavigation = undefined"
-FAIL FetchEvent#request.isReloadNavigation is true (POST + location.reload()) assert_equals: expected "method = GET, isReloadNavigation = false" but got "method = GET, isReloadNavigation = undefined"
-FAIL FetchEvent#request.isReloadNavigation is true (with history traversal) assert_equals: expected "method = GET, isReloadNavigation = false" but got "method = GET, isReloadNavigation = undefined"
-PASS FetchEvent#request.isHistoryNavigation is true (with history.go(-1))
-PASS FetchEvent#request.isHistoryNavigation is true (with history.go(1))
-PASS FetchEvent#request.isHistoryNavigation is false (with history.go(0))
-PASS FetchEvent#request.isHistoryNavigation is false (with location.reload)
-PASS FetchEvent#request.isHistoryNavigation is true (with history.go(-2))
-PASS FetchEvent#request.isHistoryNavigation is true (with history.go(2))
-PASS FetchEvent#request.isHistoryNavigation is true (POST + history.go(-1))
-PASS XHR upload progress events for response coming from SW
-PASS XHR upload progress events for network fallback
-PASS Fetch with POST with text on sw 421 response should not be retried.
-PASS restore global state
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/threaded/external/wpt/animation-worklet/idlharness.any.worker-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/virtual/threaded/external/wpt/animation-worklet/idlharness.any.worker-expected.txt
deleted file mode 100644
index 0a15a780..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.13/virtual/threaded/external/wpt/animation-worklet/idlharness.any.worker-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-This is a testharness.js-based test.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial namespace CSS: original namespace defined
-PASS Partial namespace CSS: valid exposure set
-PASS Partial namespace CSS: member names are unique
-PASS AnimationWorkletGlobalScope interface: existence and properties of interface object
-PASS WorkletAnimationEffect interface: existence and properties of interface object
-PASS WorkletAnimation interface: existence and properties of interface object
-FAIL WorkletAnimation must be primary interface of new WorkletAnimation("name") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WorkletAnimation is not defined"
-FAIL Stringification of new WorkletAnimation("name") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WorkletAnimation is not defined"
-FAIL WorkletAnimation interface: new WorkletAnimation("name") must not have property "undefined" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WorkletAnimation is not defined"
-FAIL WorkletAnimation interface: new WorkletAnimation("name") must not have property "animatorName" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WorkletAnimation is not defined"
-PASS WorkletGroupEffect interface: existence and properties of interface object
-FAIL CSS namespace: operation escape(CSSOMString) Cannot read property 'hasOwnProperty' of undefined
-FAIL CSS namespace: attribute animationWorklet Cannot read property 'hasOwnProperty' of undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla/core/col_widths_auto_per-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla/core/col_widths_auto_per-expected.png
deleted file mode 100644
index a27738cb9..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla/core/col_widths_auto_per-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla/core/col_widths_fix_per-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla/core/col_widths_fix_per-expected.png
deleted file mode 100644
index 0630f43c..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla/core/col_widths_fix_per-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla_expected_failures/bugs/bug17826-expected.png
deleted file mode 100644
index 33317a1..0000000
--- a/third_party/blink/web_tests/platform/mac-mac10.15/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_auto_per-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_auto_per-expected.png
index 9a6749d..17dd5db 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_auto_per-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_auto_per-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_fix_per-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_fix_per-expected.png
index b89fe1c..bc69e128 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_fix_per-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla/core/col_widths_fix_per-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
index 30b31bd..7e7b317 100644
--- a/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
+++ b/third_party/blink/web_tests/platform/win/tables/mozilla_expected_failures/bugs/bug17826-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png b/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png
index d46fdd23..5fb151c 100644
--- a/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png
+++ b/third_party/blink/web_tests/virtual/jxl-enabled/images/jxl/jxl-images-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/bluetooth/requestDevice/filter-does-not-match.https.html b/third_party/blink/web_tests/wpt_internal/bluetooth/requestDevice/filter-does-not-match.https.html
index fc3ae3e..44f12e8 100644
--- a/third_party/blink/web_tests/wpt_internal/bluetooth/requestDevice/filter-does-not-match.https.html
+++ b/third_party/blink/web_tests/wpt_internal/bluetooth/requestDevice/filter-does-not-match.https.html
@@ -9,59 +9,124 @@
 let matching_services = [heart_rate.uuid];
 let matching_name = 'Heart Rate Device';
 let matching_namePrefix = 'Heart';
+let matching_manufacturerData = [{ companyIdentifier: 0x0001 }];
 
 let non_matching_services = ['battery_service'];
 let non_matching_name = 'Some Device';
 let non_matching_namePrefix = 'Some';
+let non_matching_manufacturerData = [{ companyIdentifier: 0x000f }];
 
 let test_specs = [
   {
     filters: [{
       services: non_matching_services,
       name: non_matching_name,
-      namePrefix: non_matching_namePrefix
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
     }]
   },
   {
     filters: [{
       services: matching_services,
       name: non_matching_name,
-      namePrefix: non_matching_namePrefix
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
     }]
   },
   {
     filters: [{
       services: non_matching_services,
       name: matching_name,
-      namePrefix: non_matching_namePrefix
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
     }]
   },
   {
     filters: [{
       services: matching_services,
       name: matching_name,
-      namePrefix: non_matching_namePrefix
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
     }]
   },
   {
     filters: [{
       services: non_matching_services,
       name: non_matching_name,
-      namePrefix: matching_namePrefix
+      namePrefix: matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
     }]
   },
   {
     filters: [{
       services: matching_services,
       name: non_matching_name,
-      namePrefix: matching_namePrefix
+      namePrefix: matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
     }]
   },
   {
     filters: [{
       services: non_matching_services,
       name: matching_name,
-      namePrefix: matching_namePrefix
+      namePrefix: matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: non_matching_name,
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: non_matching_name,
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: matching_name,
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: non_matching_name,
+      namePrefix: matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: matching_name,
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: matching_services,
+      name: non_matching_name,
+      namePrefix: matching_namePrefix,
+      manufacturerData: matching_manufacturerData
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      name: matching_name,
+      namePrefix: matching_namePrefix,
+      manufacturerData: matching_manufacturerData
     }]
   },
   {
@@ -76,6 +141,29 @@
     }]
   },
   {
+    filters: [{
+      manufacturerData: non_matching_manufacturerData,
+    }]
+  },
+  {
+    filters: [{
+      services: non_matching_services,
+      manufacturerData: non_matching_manufacturerData,
+    }]
+  },
+  {
+    filters: [{
+      name: non_matching_name,
+      manufacturerData: non_matching_manufacturerData,
+    }]
+  },
+  {
+    filters: [{
+      namePrefix: non_matching_namePrefix,
+      manufacturerData: non_matching_manufacturerData,
+    }]
+  },
+  {
     filters:
         [{services: non_matching_services, namePrefix: non_matching_namePrefix}]
   },
diff --git a/third_party/blink/web_tests/wpt_internal/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.https.html b/third_party/blink/web_tests/wpt_internal/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.https.html
index e3d1eeb..ca2d7c1b 100644
--- a/third_party/blink/web_tests/wpt_internal/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.https.html
+++ b/third_party/blink/web_tests/wpt_internal/bluetooth/requestLEScan/attempt-to-connect-after-scan.https.https.html
@@ -19,6 +19,7 @@
   const fake_device = await fake_central.simulatePreconnectedPeripheral({
     address: fake_device_address,
     name: 'Some Device',
+    manufacturerData: {},
     knownServiceUUIDs: [],
   });
 
diff --git a/third_party/libjxl/README.chromium b/third_party/libjxl/README.chromium
index f2dcfd3..fb382bd 100644
--- a/third_party/libjxl/README.chromium
+++ b/third_party/libjxl/README.chromium
@@ -2,8 +2,8 @@
 Short Name: libjxl
 URL: https://gitlab.com/wg1/jpeg-xl
 Version: 0
-Date: 2021-04-29
-Revision: e5ce94456581d43f8a52c8100c726a0d079f65e7
+Date: 2021-05-04
+Revision: 9a8f5195e4d1c45112fd65f184ebe115f4163ba2
 License: Apache 2.0
 Security Critical: yes
 CPEPrefix: unknown
diff --git a/tools/autotest.py b/tools/autotest.py
index 6372616..90294ffd 100755
--- a/tools/autotest.py
+++ b/tools/autotest.py
@@ -347,13 +347,18 @@
   return (test_targets, used_cache)
 
 
-def RunTestTargets(out_dir, targets, gtest_filter, extra_args, dry_run):
+def RunTestTargets(out_dir, targets, gtest_filter, extra_args, dry_run,
+                   no_try_android_wrappers):
+
   for target in targets:
+
     # Look for the Android wrapper script first.
     path = os.path.join(out_dir, 'bin', f'run_{target}')
-    if not os.path.isfile(path):
-      # Otherwise, use the Desktop target which is an executable.
+    if no_try_android_wrappers or not os.path.isfile(path):
+      # If the wrapper is not found or disabled use the Desktop target
+      # which is an executable.
       path = os.path.join(out_dir, target)
+
     cmd = [path, f'--gtest_filter={gtest_filter}'] + extra_args
     print('Running test: ' + ' '.join(cmd))
     if not dry_run:
@@ -411,6 +416,10 @@
       '-n',
       action='store_true',
       help='Print ninja and test run commands without executing them.')
+  parser.add_argument(
+      '--no-try-android-wrappers',
+      action='store_true',
+      help='Do not try to use Android test wrappers to run tests.')
   parser.add_argument('file',
                       metavar='FILE_NAME',
                       help='test suite file (eg. FooTest.java)')
@@ -461,7 +470,8 @@
   else:  # cache still valid, quit if the build failed
     if not build_ok: sys.exit(1)
 
-  RunTestTargets(out_dir, targets, gtest_filter, _extras, args.dry_run)
+  RunTestTargets(out_dir, targets, gtest_filter, _extras, args.dry_run,
+                 args.no_try_android_wrappers)
 
 
 if __name__ == '__main__':
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4ddc2202..404e888 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -5048,6 +5048,8 @@
   <int value="23" label="Disabled proactive help setting"/>
   <int value="24" label="Base64 decoding error"/>
   <int value="25" label="The user rejected the bottom sheet onboarding"/>
+  <int value="26" label="CCT -&gt; tab not supported"/>
+  <int value="27" label="Canceled"/>
 </enum>
 
 <enum name="AutofillAssistantLiteScriptOnboarding">
@@ -32374,6 +32376,11 @@
   <int value="3889" label="CSSFilterColorMatrix"/>
   <int value="3890" label="HTMLFencedFrameElement"/>
   <int value="3891" label="CSSFilterLuminanceToAlpha"/>
+  <int value="3892" label="HandwritingRecognitionCreateRecognizer"/>
+  <int value="3893" label="HandwritingRecognitionQuerySupport"/>
+  <int value="3894" label="HandwritingRecognitionStartDrawing"/>
+  <int value="3895" label="HandwritingRecognitionGetPrediction"/>
+  <int value="3896" label="WebBluetoothManufacturerDataFilter"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -70476,9 +70483,17 @@
 </enum>
 
 <enum name="SendTabToSelfClickResult">
-  <int value="0" label="SendTabToSelf entry point is shown. (Obsolete)"/>
+  <int value="0" label="SendTabToSelf entry point is shown.">
+    <obsolete>
+      Removed on 06/2020.
+    </obsolete>
+  </int>
   <int value="1" label="SendTabToSelf target device is clicked."/>
-  <int value="2" label="SendTabToSelf device list is shown. (Obsolete)"/>
+  <int value="2" label="SendTabToSelf device list is shown.">
+    <obsolete>
+      Removed on 06/2020.
+    </obsolete>
+  </int>
 </enum>
 
 <enum name="SendTabToSelfNotification">
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index dd25941..7413c97 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -17070,6 +17070,8 @@
   <owner>jeffreycohen@chromium.org</owner>
   <owner>tgupta@chromium.org</owner>
   <owner>chrome-sharing-core@google.com</owner>
+  <suffix name="AndroidShareSheet"
+      label="Option to show in the android share sheet"/>
   <suffix name="ContentMenu" label="Option shown in the content context menu"/>
   <suffix name="LinkMenu" label="Option shown in the link context menu"/>
   <suffix name="OmniboxIcon" label="Icon shown in the omnibox"/>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 2f5ce77c..79cce27f 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -5185,6 +5185,9 @@
 
 <histogram name="EphemeralTab.BottomSheet.CloseReason"
     enum="BottomSheet.StateChangeReason" expires_after="M92">
+  <obsolete>
+    Deprecated as of 05/2021
+  </obsolete>
   <owner>donnd@chromium.org</owner>
   <owner>jinsukkim@chromium.org</owner>
   <summary>
@@ -5195,6 +5198,9 @@
 
 <histogram name="EphemeralTab.CloseReason"
     enum="OverlayPanel.StateChangeReason" expires_after="M92">
+  <obsolete>
+    Deprecated as of 05/2021
+  </obsolete>
   <owner>donnd@chromium.org</owner>
   <owner>jinsukkim@chromium.org</owner>
   <summary>
@@ -13828,21 +13834,22 @@
   <summary>The time spent before the screen locker is ready.</summary>
 </histogram>
 
-<histogram name="SendTabToSelf.AndroidShareSheet.ClickResult"
-    enum="SendTabToSelfClickResult" expires_after="M97">
-  <owner>jeffreycohen@chromium.org</owner>
-  <owner>tgupta@chromium.org</owner>
-  <owner>chrome-sharing-core@google.com</owner>
-  <summary>Tracks the user flow for sending a tab for SendTabToSelf.</summary>
-</histogram>
-
 <histogram base="true" name="SendTabToSelf.ClickResult"
     enum="SendTabToSelfClickResult" expires_after="M97">
   <owner>jeffreycohen@chromium.org</owner>
   <owner>tgupta@chromium.org</owner>
   <owner>chrome-sharing-core@google.com</owner>
   <summary>
-    Record whether the user has clicked the item when it is shown.
+    Records user flow for SendTabToSelf feature.
+
+    Only the &quot;SendTabToSelf target device is clicked&quot; bucket is
+    recorded today, causing sharing of a tab with the tapped device. Until M84
+    other buckets were recorded. Because each bucket represents a different
+    aspect of the flow, it's important to look at &quot;bucket count&quot;, not
+    &quot;bucket proportion&quot;.
+
+    On iOS this was not recorded between M85 and M90, inclusive (see
+    crbug.com/1204944).
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/histograms_xml/password/histograms.xml b/tools/metrics/histograms/histograms_xml/password/histograms.xml
index 10daab22..f412591 100644
--- a/tools/metrics/histograms/histograms_xml/password/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/password/histograms.xml
@@ -520,54 +520,65 @@
 </histogram>
 
 <histogram name="PasswordManager.AffiliationBackend.FetchSize" units="facets"
-    expires_after="M90">
-  <owner>engedy@chromium.org</owner>
+    expires_after="M96">
+  <owner>vsemeniuk@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     The number of facets for which affiliation information was requested in a
-    network fetch. Recorded for each network fetch.
+    network fetch. Recorded for each network fetch. Warning: this histogram was
+    expired from M90 to M92; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.AffiliationBackend.FirstFetchDelay" units="ms"
-    expires_after="M90">
-  <owner>engedy@chromium.org</owner>
+    expires_after="M96">
+  <owner>vsemeniuk@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     The time elapsed between creation of the AffiliationBackend and the first
-    time it needed to issue a network fetch.
+    time it needed to issue a network fetch. Warning: this histogram was expired
+    from M90 to M92; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.AffiliationBackend.SubsequentFetchDelay"
-    units="ms" expires_after="M90">
-  <owner>engedy@chromium.org</owner>
+    units="ms" expires_after="M96">
+  <owner>vsemeniuk@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     The elapsed time between subsequent network fetches. Recorded whenever the
     AffiliationBackend initiated a network fetch, regardless of success or
-    failure.
+    failure. Warning: this histogram was expired from M90 to M92; data may be
+    missing.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.AffiliationFetcher.FetchErrorCode"
-    enum="NetErrorCodes" expires_after="M90">
-  <owner>engedy@chromium.org</owner>
+    enum="NetErrorCodes" expires_after="M96">
+  <owner>vsemeniuk@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     The network error code, as reported by the underlying URLFetcher. Recorded
     only for each network fetch that failed due to network/server errors.
+    Warning: this histogram was expired from M90 to M92; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.AffiliationFetcher.FetchHttpResponseCode"
-    enum="HttpResponseCode" expires_after="M90">
-  <owner>engedy@chromium.org</owner>
+    enum="HttpResponseCode" expires_after="M96">
+  <owner>vsemeniuk@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     The HTTP response code, as reported by the underlying URLFetcher. Recorded
     only for each network fetch that failed due to network/server errors.
+    Warning: this histogram was expired from M90 to M92; data may be missing.
   </summary>
 </histogram>
 
 <histogram name="PasswordManager.AffiliationFetcher.FetchResult"
-    enum="AffiliationFetchResult" expires_after="2021-09-19">
-  <owner>engedy@chromium.org</owner>
+    enum="AffiliationFetchResult" expires_after="M96">
+  <owner>vsemeniuk@google.com</owner>
+  <owner>vasilii@chromium.org</owner>
   <summary>
     Whether the network fetch succeeded, failed due to network/server errors, or
     contained malformed data. Recorded for each network fetch.
diff --git a/tools/metrics/histograms/update_permissions_policy_enum.py b/tools/metrics/histograms/update_permissions_policy_enum.py
index cec76f06..ba84a1d 100755
--- a/tools/metrics/histograms/update_permissions_policy_enum.py
+++ b/tools/metrics/histograms/update_permissions_policy_enum.py
@@ -25,7 +25,7 @@
                 'permissions_policy_feature.mojom'
   UpdateHistogramEnum(histogram_enum_name='FeaturePolicyFeature',
                       source_enum_path=source_file,
-                      start_marker='^enum FeaturePolicyFeature {',
+                      start_marker='^enum PermissionsPolicyFeature {',
                       end_marker='^};',
                       strip_k_prefix=True,
                       calling_script=os.path.basename(__file__))
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index c9067c4e..c0116170 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -69,6 +69,11 @@
 # Benchmark: blink_perf.shadow_dom
 crbug.com/702319 [ android-nexus-5x ] blink_perf.shadow_dom/* [ Skip ]
 
+# Benchmark: desktop_ui
+crbug.com/1205850 [ win ] desktop_ui/tab_search:measure_memory* [ Skip ]
+crbug.com/1205850 [ mac ] desktop_ui/tab_search:measure_memory* [ Skip ]
+crbug.com/1205850 [ linux ] desktop_ui/tab_search:measure_memory* [ Skip ]
+
 # Benchmark: dromaeo
 crbug.com/1050065 [ android-pixel-2 ] dromaeo/http://dromaeo.com?dom-modify [ Skip ]
 
@@ -109,6 +114,12 @@
 crbug.com/879833 loading.desktop/Walgreens_warm [ Skip ]
 crbug.com/921428 [ chromeos ] loading.desktop/TheVerge_cold [ Skip ]
 crbug.com/921428 [ chromeos ] loading.desktop/TheVerge_warm [ Skip ]
+crbug.com/1205852 [ linux ] loading.desktop/Economist_cold [ Skip ]
+crbug.com/1205852 [ linux ] loading.desktop/Economist_warm [ Skip ]
+crbug.com/1205852 [ mac ] loading.desktop/Economist_cold [ Skip ]
+crbug.com/1205852 [ mac ] loading.desktop/Economist_warm [ Skip ]
+crbug.com/1205852 [ win ] loading.desktop/Economist_cold [ Skip ]
+crbug.com/1205852 [ win ] loading.desktop/Economist_warm [ Skip ]
 
 # Benchmark: loading.mobile
 crbug.com/656861 loading.mobile/G1 [ Skip ]
diff --git a/ui/file_manager/file_manager/foreground/js/BUILD.gn b/ui/file_manager/file_manager/foreground/js/BUILD.gn
index f73e737f..bf24a86 100644
--- a/ui/file_manager/file_manager/foreground/js/BUILD.gn
+++ b/ui/file_manager/file_manager/foreground/js/BUILD.gn
@@ -1319,7 +1319,6 @@
   deps = [
     "//ui/file_manager/file_manager/common/js:volume_manager_types",
     "//ui/webui/resources/js:assert",
-    "//ui/webui/resources/js:load_time_data",
   ]
 }
 
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index fc21d51e..37ba7a5 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1918,12 +1918,6 @@
   canExecute(event, fileManager) {
     const command = event.command;
 
-    if (!HoldingSpaceUtil.isFeatureEnabled()) {
-      event.canExecute = false;
-      command.setHidden(true);
-      return;
-    }
-
     const allowedVolumeTypes = HoldingSpaceUtil.getAllowedVolumeTypes();
     const currentRootType = fileManager.directoryModel.getCurrentRootType();
     if (!util.isRecentRootType(currentRootType)) {
diff --git a/ui/file_manager/file_manager/foreground/js/holding_space_util.js b/ui/file_manager/file_manager/foreground/js/holding_space_util.js
index fe2a5c0..303b3106 100644
--- a/ui/file_manager/file_manager/foreground/js/holding_space_util.js
+++ b/ui/file_manager/file_manager/foreground/js/holding_space_util.js
@@ -9,7 +9,6 @@
 // clang-format off
 // #import {VolumeManagerCommon} from '../../common/js/volume_manager_types.m.js';
 // #import {metrics} from '../../common/js/metrics.m.js';
-// #import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 // #import {xfm} from '../../common/js/xfm.m.js';
 // clang-format on
 
@@ -34,12 +33,6 @@
     return 'holdingSpaceTimeOfFirstWelcomeBannerShow';
   }
 
-  /** @return {boolean} */
-  static isFeatureEnabled() {
-    return loadTimeData.valueExists('HOLDING_SPACE_ENABLED') &&
-        loadTimeData.getBoolean('HOLDING_SPACE_ENABLED');
-  }
-
   /**
    * Returns the volume types for which the holding space feature is allowed.
    * @return {!Array<?VolumeManagerCommon.VolumeType>}
diff --git a/ui/file_manager/file_manager/foreground/js/ui/banners.js b/ui/file_manager/file_manager/foreground/js/ui/banners.js
index 3fbce87..fb64624 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/banners.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/banners.js
@@ -366,7 +366,6 @@
    * @private
    */
   prepareAndShowHoldingSpaceWelcomeBanner_() {
-    assert(HoldingSpaceUtil.isFeatureEnabled());
     this.showHoldingSpaceWelcomeBanner_(true);
 
     // Do not recreate the banner.
@@ -411,6 +410,7 @@
 
     const buttonGroup = util.createChild(wrapper, 'button-group', 'div');
     const dismiss = util.createChild(buttonGroup, 'text-button', 'cr-button');
+    dismiss.id = 'holding-space-welcome-dismiss';
     dismiss.setAttribute('aria-label', str('HOLDING_SPACE_WELCOME_DISMISS'));
     dismiss.textContent = str('HOLDING_SPACE_WELCOME_DISMISS');
     dismiss.tabIndex = 0;
@@ -623,7 +623,6 @@
    * @private
    */
   closeHoldingSpaceWelcomeBanner_() {
-    assert(HoldingSpaceUtil.isFeatureEnabled());
     this.cleanupHoldingSpaceWelcomeBanner_();
 
     // Stop showing the welcome banner.
@@ -685,10 +684,6 @@
    * @private
    */
   async maybeShowHoldingSpaceWelcomeBanner_() {
-    if (!HoldingSpaceUtil.isFeatureEnabled()) {
-      return;
-    }
-
     await this.ready_;
 
     if (!this.showWelcome_) {
@@ -921,8 +916,6 @@
    * @private
    */
   showHoldingSpaceWelcomeBanner_(show) {
-    assert(HoldingSpaceUtil.isFeatureEnabled());
-
     const /** boolean */ hidden = !show;
     if (this.holdingSpaceWelcomeBanner_.hasAttribute('hidden') == hidden) {
       return;
@@ -1098,7 +1091,6 @@
    * @private
    */
   cleanupHoldingSpaceWelcomeBanner_() {
-    assert(HoldingSpaceUtil.isFeatureEnabled());
     this.showHoldingSpaceWelcomeBanner_(false);
   }
 
diff --git a/ui/file_manager/integration_tests/file_manager/holding_space.js b/ui/file_manager/integration_tests/file_manager/holding_space.js
index 3c6ef61..6b04b66 100644
--- a/ui/file_manager/integration_tests/file_manager/holding_space.js
+++ b/ui/file_manager/integration_tests/file_manager/holding_space.js
@@ -4,22 +4,10 @@
 'use strict';
 
 /**
- * Tests that the holding space welcome banner is hidden when the feature is
- * disabled.
+ * Tests that the holding space welcome banner appears and that it can be
+ * dismissed.
  */
-testcase.holdingSpaceWelcomeBannerWithFeatureDisabled = async () => {
-  // Open Files app on Downloads.
-  const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
-
-  // Check: the holding space welcome banner should be hidden.
-  await remoteCall.waitForElement(appId, '.holding-space-welcome[hidden]');
-};
-
-/**
- * Tests that the holding space welcome banner appears when the feature is
- * enabled and that it can be dismissed.
- */
-testcase.holdingSpaceWelcomeBannerWithFeatureEnabled = async () => {
+testcase.holdingSpaceWelcomeBanner = async () => {
   // Open Files app on Downloads.
   const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
 
diff --git a/ui/file_manager/integration_tests/file_manager/tab_index.js b/ui/file_manager/integration_tests/file_manager/tab_index.js
index 8728b1f..3ad61e4 100644
--- a/ui/file_manager/integration_tests/file_manager/tab_index.js
+++ b/ui/file_manager/integration_tests/file_manager/tab_index.js
@@ -91,6 +91,8 @@
       await remoteCall.checkNextTabFocus(appId, 'sort-button'));
   chrome.test.assertTrue(
       await remoteCall.checkNextTabFocus(appId, 'gear-button'));
+  chrome.test.assertTrue(await remoteCall.checkNextTabFocus(
+      appId, 'holding-space-welcome-dismiss'));
   chrome.test.assertTrue(
       await remoteCall.checkNextTabFocus(appId, 'file-list'));
 };
diff --git a/ui/gfx/font_render_params_linux.cc b/ui/gfx/font_render_params_linux.cc
index 3ab8b2e..7e42a2e 100644
--- a/ui/gfx/font_render_params_linux.cc
+++ b/ui/gfx/font_render_params_linux.cc
@@ -12,7 +12,6 @@
 
 #include "base/command_line.h"
 #include "base/containers/mru_cache.h"
-#include "base/hash/hash.h"
 #include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -90,7 +89,7 @@
 
 // Keyed by hashes of FontRenderParamQuery structs from
 // HashFontRenderParamsQuery().
-typedef base::MRUCache<uint32_t, QueryResult> Cache;
+typedef base::HashingMRUCache<std::string, QueryResult> Cache;
 
 // A cache and the lock that must be held while accessing it.
 // GetFontRenderParams() is called by both the UI thread and the sandbox IPC
@@ -173,14 +172,12 @@
   return true;
 }
 
-// Serialize |query| into a string and hash it to a value suitable for use as a
-// cache key.
-uint32_t HashFontRenderParamsQuery(const FontRenderParamsQuery& query) {
-  return base::Hash(base::StringPrintf(
+// Serialize |query| into a string value suitable for use as a cache key.
+std::string GetFontRenderParamsQueryKey(const FontRenderParamsQuery& query) {
+  return base::StringPrintf(
       "%d|%d|%d|%d|%s|%f", query.pixel_size, query.point_size, query.style,
       static_cast<int>(query.weight),
-      base::JoinString(query.families, ",").c_str(),
-      query.device_scale_factor));
+      base::JoinString(query.families, ",").c_str(), query.device_scale_factor);
 }
 
 }  // namespace
@@ -193,15 +190,15 @@
   if (actual_query.device_scale_factor == 0)
     actual_query.device_scale_factor = device_scale_factor_;
 
-  const uint32_t hash = HashFontRenderParamsQuery(actual_query);
+  std::string query_key = GetFontRenderParamsQueryKey(actual_query);
   SynchronizedCache* synchronized_cache = g_synchronized_cache.Pointer();
 
   {
     // Try to find a cached result so Fontconfig doesn't need to be queried.
     base::AutoLock lock(synchronized_cache->lock);
-    Cache::const_iterator it = synchronized_cache->cache.Get(hash);
+    Cache::const_iterator it = synchronized_cache->cache.Get(query_key);
     if (it != synchronized_cache->cache.end()) {
-      DVLOG(1) << "Returning cached params for " << hash;
+      DVLOG(1) << "Returning cached params for " << query_key;
       const QueryResult& result = it->second;
       if (family_out)
         *family_out = result.family;
@@ -209,7 +206,7 @@
     }
   }
 
-  DVLOG(1) << "Computing params for " << hash;
+  DVLOG(1) << "Computing params for " << query_key;
   if (family_out)
     family_out->clear();
 
@@ -251,7 +248,8 @@
     // Store the result. It's fine if this overwrites a result that was cached
     // by a different thread in the meantime; the values should be identical.
     base::AutoLock lock(synchronized_cache->lock);
-    synchronized_cache->cache.Put(hash,
+    synchronized_cache->cache.Put(
+        query_key,
         QueryResult(params, family_out ? *family_out : std::string()));
   }
 
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc
index 2d7686ef..92edb57 100644
--- a/ui/ozone/platform/wayland/host/wayland_connection.cc
+++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -83,6 +83,7 @@
 // value.
 constexpr uint32_t kMinWlDrmVersion = 2;
 constexpr uint32_t kMinWlOutputVersion = 2;
+constexpr uint32_t kMinZwpPointerGesturesVersion = 1;
 
 // gtk_shell1 exposes request_focus() since version 3.  Below that, it is not
 // interesting for us, although it provides some shell integration that might be
@@ -578,7 +579,8 @@
     connection->zaura_shell_ =
         std::make_unique<WaylandZAuraShell>(zaura_shell.release(), connection);
   } else if (!connection->wayland_zwp_pointer_gestures_ &&
-             (strcmp(interface, "zwp_pointer_gestures_v1") == 0)) {
+             strcmp(interface, "zwp_pointer_gestures_v1") == 0 &&
+             version >= kMinZwpPointerGesturesVersion) {
     auto zwp_pointer_gestures_v1 =
         wl::Bind<struct zwp_pointer_gestures_v1>(registry, name, version);
     if (!zwp_pointer_gestures_v1) {
diff --git a/weblayer/browser/tab_impl.cc b/weblayer/browser/tab_impl.cc
index a3dd04e..27fd82b 100644
--- a/weblayer/browser/tab_impl.cc
+++ b/weblayer/browser/tab_impl.cc
@@ -7,7 +7,6 @@
 #include <cmath>
 
 #include "base/auto_reset.h"
-#include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/guid.h"
 #include "base/logging.h"
@@ -16,7 +15,6 @@
 #include "base/time/default_tick_clock.h"
 #include "cc/layers/layer.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
-#include "components/autofill/core/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/autofill_provider.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/blocked_content/popup_blocker.h"
@@ -288,14 +286,6 @@
           web_contents,
           SubresourceFilterProfileContextFactory::GetForBrowserContext(
               web_contents->GetBrowserContext()),
-  // Infobars are supported only on Android in WebLayer. This is not a
-  // problem as the subresource filter shows the infobar only on Android
-  // as well.
-#if defined(OS_ANDROID)
-          infobars::ContentInfoBarManager::FromWebContents(web_contents),
-#else
-          nullptr,
-#endif
           GetDatabaseManagerFromSafeBrowsingService(), dealer);
 }
 
@@ -1417,8 +1407,7 @@
   autofill::ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
       web_contents, AutofillClientImpl::FromWebContents(web_contents),
       i18n::GetApplicationLocale(), enable_autofill_download_manager,
-      base::BindRepeating(&autofill::AndroidAutofillManager::Create,
-                          autofill_provider_.get()));
+      autofill_provider_.get());
 }
 
 find_in_page::FindTabHelper* TabImpl::GetFindTabHelper() {