diff --git a/DEPS b/DEPS
index 4b3c188..c6d90a6 100644
--- a/DEPS
+++ b/DEPS
@@ -209,11 +209,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '115645ee9b1bacc0ae6767e67e81e2f96c14774a',
+  'skia_revision': 'b44d6f40999c563949671c19acdee6899e1d1ab5',
   # 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': '90948ff16986234c86a25cb4d3e5643693faea9d',
+  'v8_revision': 'fbc3cced2dbb3b66a9d5d061319090f0908f5e17',
   # 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.
@@ -225,7 +225,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '8b09c1078a2b0f37dcb7e83ec91c8b7538905b86',
+  'swiftshader_revision': 'e260190edbd2984a045ac5990b3d31429d45d27f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # 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': '09a458f94e1d514a64048cd698e8485809885230',
+  'dawn_revision': 'a91b3f938b3cdd0a50018ef5140b84c7149ebade',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -395,7 +395,7 @@
   'libcxx_revision':       '8fa87946779682841e21e2da977eccfb6cb3bded',
 
   # GN CIPD package version.
-  'gn_version': 'git_revision:7b23bd91f996722e5e55e0ccf0c80c0a984efca5',
+  'gn_version': 'git_revision:281c994dc67772cc2a6f5c5fa1c4bafb9dcb733e',
 }
 
 # Only these hosts are allowed for dependencies in this DEPS file.
@@ -573,7 +573,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '377a73f6a8715d403fd4923612289fae6dbad665',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '7fa200758dbe180e449ae8600865432641d1d6e5',
       'condition': 'checkout_ios',
   },
 
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '360f98195601ea865b59c5e499aed66e78d3ecc4',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b865acd3f2d8cd305f87215154351620354d075f',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1338,7 +1338,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '78e179f910b7cb65044a25f0723a0b263d010974',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '5dfd4bdd5b10dc62ae2c6d021573bb889efaf056',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1416,7 +1416,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'UJM-8JuP3RR55BXmMADjW1ym0ak_RyS4QYOpqVcZp9gC'
+              'version': 'H-kH9WxQErL_AR-Nu_ZL8hbu1D-rZmdQQUaYZYm3AOUC'
           },
       ],
       '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' + '@' + '793bac569fdf1be16cbf24d7871d20d00bbec81b',
+    Var('webrtc_git') + '/src.git' + '@' + '0133a64d2aed45d399be9b0a6b86faa642c91330',
 
   '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@3b957b128c903fff268e1889d8d0ed4566179f32',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@a5e9183f294bf87bfb9d3e9cd9ee5171ea99298b',
     'condition': 'checkout_src_internal',
   },
 
@@ -1632,7 +1632,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '03OKSqR1LvRGvcwRQ7t7Xhelgv0ZuOElJ5B_96MqzLIC',
+        'version': 'ji5QCbNg1UP9xidlR09UsND7KdkU2ttnsrWtSzHDKt4C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4248,20 +4248,6 @@
                 'src/third_party/arcore-android-sdk/test-apks/update.py',
     ],
   },
-  # Download common ink resources for chromeos.
-  {
-    'name': 'ink-build',
-    'pattern': '.',
-    'condition': 'checkout_chromeos',
-    'action': [ 'python',
-                'src/third_party/depot_tools/download_from_google_storage.py',
-                '--no_resume',
-                '--no_auth',
-                '--num_threads=4',
-                '--bucket', 'chromium-ink',
-                '-d', 'src/third_party/ink/build',
-    ],
-  },
   {
     'name': 'subresource-filter-ruleset',
     'pattern': '.',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index d7c50e9..ad12c07 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -316,7 +316,6 @@
     "capture_mode/video_file_handler.h",
     "capture_mode/video_recording_watcher.cc",
     "capture_mode/video_recording_watcher.h",
-    "capture_mode/view_with_ink_drop.h",
     "child_accounts/parent_access_controller_impl.cc",
     "child_accounts/parent_access_controller_impl.h",
     "clipboard/clipboard_history.cc",
diff --git a/ash/accessibility/chromevox/touch_exploration_manager.cc b/ash/accessibility/chromevox/touch_exploration_manager.cc
index 5b839880..fc6f910 100644
--- a/ash/accessibility/chromevox/touch_exploration_manager.cc
+++ b/ash/accessibility/chromevox/touch_exploration_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ash/accessibility/chromevox//touch_exploration_manager.h"
+#include "ash/accessibility/chromevox/touch_exploration_manager.h"
 
 #include <memory>
 #include <vector>
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 5b27c0e..71b2e5ea 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -23,6 +23,7 @@
 #include "ash/public/cpp/app_list/app_list_config.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/app_list/app_list_switches.h"
+#include "base/bind.h"
 #include "base/check_op.h"
 #include "base/notreached.h"
 #include "base/numerics/ranges.h"
@@ -426,9 +427,9 @@
   // state.
   expand_arrow_view_->SetFocusBehavior(
       expand_arrow_enabled ? FocusBehavior::ALWAYS : FocusBehavior::NEVER);
-  expand_arrow_view_->SetInkDropMode(
-      expand_arrow_enabled ? views::InkDropHostView::InkDropMode::ON
-                           : views::InkDropHostView::InkDropMode::OFF);
+  expand_arrow_view_->ink_drop()->SetMode(
+      expand_arrow_enabled ? views::InkDropHost::InkDropMode::ON
+                           : views::InkDropHost::InkDropMode::OFF);
 
   // Allow ChromeVox to focus the expand arrow only when peeking launcher.
   expand_arrow_view_->GetViewAccessibility().OverrideIsIgnored(
diff --git a/ash/app_list/views/expand_arrow_view.cc b/ash/app_list/views/expand_arrow_view.cc
index 2010e0f..1b27af3 100644
--- a/ash/app_list/views/expand_arrow_view.cc
+++ b/ash/app_list/views/expand_arrow_view.cc
@@ -141,18 +141,18 @@
   // TODO(pbos): Replace ::OnPaint focus painting with FocusRing +
   // HighlightPathGenerator usage.
   SetInstallFocusRingOnFocus(false);
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   views::HighlightPathGenerator::Install(
       this, std::make_unique<ExpandArrowHighlightPathGenerator>());
-  views::InkDrop::UseInkDropWithoutAutoHighlight(this,
+  views::InkDrop::UseInkDropWithoutAutoHighlight(ink_drop(),
                                                  /*highlight_on_hover=*/false);
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
         const AppListColorProvider* color_provider =
             AppListColorProvider::Get();
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), host->GetLocalBounds().InsetsFrom(GetCircleBounds()),
-            host->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
             color_provider->GetRippleAttributesBaseColor(),
             color_provider->GetRippleAttributesInkDropOpacity());
       },
@@ -367,7 +367,8 @@
   button_pressed_ = true;
   ResetHintingAnimation();
   TransitToFullscreenAllAppsState();
-  GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
+  ink_drop()->GetInkDrop()->AnimateToState(
+      views::InkDropState::ACTION_TRIGGERED);
 }
 
 void ExpandArrowView::TransitToFullscreenAllAppsState() {
diff --git a/ash/app_list/views/page_switcher.cc b/ash/app_list/views/page_switcher.cc
index ebed5131..7921e31 100644
--- a/ash/app_list/views/page_switcher.cc
+++ b/ash/app_list/views/page_switcher.cc
@@ -55,9 +55,9 @@
       : is_root_app_grid_page_switcher_(is_root_app_grid_page_switcher),
         background_color_(background_color) {
     SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
-    SetInkDropMode(InkDropMode::ON);
-    views::InkDrop::UseInkDropForFloodFillRipple(this);
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+    views::InkDrop::UseInkDropForFloodFillRipple(ink_drop());
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](PageSwitcherButton* host) {
           const AppListColorProvider* const color_provider =
               AppListColorProvider::Get();
@@ -71,7 +71,7 @@
           return highlight;
         },
         this));
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](PageSwitcherButton* host) -> std::unique_ptr<views::InkDropRipple> {
           const gfx::Point center = host->GetLocalBounds().CenterPoint();
           const int max_radius =
@@ -84,7 +84,7 @@
               AppListColorProvider::Get();
           return std::make_unique<views::FloodFillInkDropRipple>(
               host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-              host->GetInkDropCenterBasedOnLastEvent(),
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
               color_provider->GetRippleAttributesBaseColor(
                   host->background_color_),
               color_provider->GetRippleAttributesInkDropOpacity(
@@ -128,7 +128,8 @@
   // views::Button:
   void NotifyClick(const ui::Event& event) override {
     Button::NotifyClick(event);
-    GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
+    ink_drop()->GetInkDrop()->AnimateToState(
+        views::InkDropState::ACTION_TRIGGERED);
   }
 
  private:
diff --git a/ash/app_list/views/privacy_info_view.cc b/ash/app_list/views/privacy_info_view.cc
index d2284a753..fe07c68 100644
--- a/ash/app_list/views/privacy_info_view.cc
+++ b/ash/app_list/views/privacy_info_view.cc
@@ -297,13 +297,13 @@
   close_button->SizeToPreferredSize();
 
   // Ink ripple.
-  close_button->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  close_button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   constexpr SkColor kInkDropBaseColor = gfx::kGoogleGrey900;
   constexpr float kInkDropVisibleOpacity = 0.06f;
   constexpr float kInkDropHighlightOpacity = 0.08f;
-  close_button->SetInkDropVisibleOpacity(kInkDropVisibleOpacity);
-  close_button->SetInkDropHighlightOpacity(kInkDropHighlightOpacity);
-  close_button->SetInkDropBaseColor(kInkDropBaseColor);
+  close_button->ink_drop()->SetVisibleOpacity(kInkDropVisibleOpacity);
+  close_button->ink_drop()->SetHighlightOpacity(kInkDropHighlightOpacity);
+  close_button->ink_drop()->SetBaseColor(kInkDropBaseColor);
   close_button->SetHasInkDropActionOnClick(true);
   views::InstallCircleHighlightPathGenerator(close_button.get());
   close_button_ = AddChildView(std::move(close_button));
diff --git a/ash/app_list/views/search_result_actions_view.cc b/ash/app_list/views/search_result_actions_view.cc
index b16dc9a..030576d 100644
--- a/ash/app_list/views/search_result_actions_view.cc
+++ b/ash/app_list/views/search_result_actions_view.cc
@@ -77,8 +77,8 @@
   // Avoid drawing default dashed focus and draw customized focus in
   // OnPaintBackground();
   SetFocusPainter(nullptr);
-  SetInkDropMode(InkDropMode::ON);
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](SearchResultImageButton* host) {
         const AppListColorProvider* const color_provider =
             AppListColorProvider::Get();
@@ -91,7 +91,7 @@
         return highlight;
       },
       this));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](SearchResultImageButton* host)
           -> std::unique_ptr<views::InkDropRipple> {
         const gfx::Point center = host->GetLocalBounds().CenterPoint();
@@ -103,7 +103,7 @@
         const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-            host->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
             color_provider->GetRippleAttributesBaseColor(bg_color),
             color_provider->GetRippleAttributesInkDropOpacity(bg_color));
       },
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.cc b/ash/app_list/views/search_result_suggestion_chip_view.cc
index 3411fbed..a4b4c6b 100644
--- a/ash/app_list/views/search_result_suggestion_chip_view.cc
+++ b/ash/app_list/views/search_result_suggestion_chip_view.cc
@@ -63,11 +63,11 @@
   SetInstallFocusRingOnFocus(true);
   focus_ring()->SetColor(AppListColorProvider::Get()->GetFocusRingColor());
 
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   views::InstallPillHighlightPathGenerator(this);
-  views::InkDrop::UseInkDropWithoutAutoHighlight(this,
+  views::InkDrop::UseInkDropWithoutAutoHighlight(ink_drop(),
                                                  /*highlight_on_hover=*/false);
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
         const gfx::Point center = host->GetLocalBounds().CenterPoint();
         const int ripple_radius = host->width() / 2;
@@ -79,7 +79,7 @@
         const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-            host->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
             color_provider->GetRippleAttributesBaseColor(bg_color),
             color_provider->GetRippleAttributesInkDropOpacity(bg_color));
       },
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 3ee7160..2d12621 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -3536,7 +3536,9 @@
       <message name="IDS_ASH_SHORTCUT_DEPRECATION_FKEY" desc="Message telling user to use search/launcher + top row key instead of Search + a digit key to emulate a F-Key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
         The <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> + Number keyboard shortcut has changed. To use F-Keys, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + a key on the top row.
       </message>
-
+      <message name="IDS_ASH_SHORTCUT_DEPRECATION_SEARCH_PERIOD_INSERT" desc="Message telling user to use search/launcher + shift + backspace instead of search/launcher + period to emulate the delete key. The name of the launcher key varies by device and may be either Search or Launcher depending on the glyph on the keyboard.">
+        The <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> + Period keyboard shortcut has changed. To use the Insert key, press the <ph name="LAUNCHER_KEY_NAME">$1<ex>Launcher</ex></ph> key + Shift + Backspace.
+      </message>
 
       <!-- PciePeripheral notifications -->
       <message name="IDS_ASH_PCIE_PERIPHERAL_NOTIFICATION_PERFORMANCE_LIMITED_TITLE" desc="Notification title to indicate to users that their peripheral device's performance may be limited.">
diff --git a/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_SEARCH_PERIOD_INSERT.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_SEARCH_PERIOD_INSERT.png.sha1
new file mode 100644
index 0000000..24914481
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SHORTCUT_DEPRECATION_SEARCH_PERIOD_INSERT.png.sha1
@@ -0,0 +1 @@
+d70908f78c51486cccfe93523e02c699fee4ca3b
\ No newline at end of file
diff --git a/ash/assistant/ui/base/assistant_button.cc b/ash/assistant/ui/base/assistant_button.cc
index de4063a2..fbee20c 100644
--- a/ash/assistant/ui/base/assistant_button.cc
+++ b/ash/assistant/ui/base/assistant_button.cc
@@ -53,26 +53,27 @@
   SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
 
   // Ink drop.
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
-  SetInkDropBaseColor(kInkDropBaseColor);
-  SetInkDropVisibleOpacity(kInkDropVisibleOpacity);
+  ink_drop()->SetBaseColor(kInkDropBaseColor);
+  ink_drop()->SetVisibleOpacity(kInkDropVisibleOpacity);
   views::InstallCircleHighlightPathGenerator(this, gfx::Insets(kInkDropInset));
-  views::InkDrop::UseInkDropForFloodFillRipple(this);
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  views::InkDrop::UseInkDropForFloodFillRipple(ink_drop());
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](InkDropHostView* host) {
         auto highlight = std::make_unique<views::InkDropHighlight>(
-            gfx::SizeF(host->size()), host->GetInkDropBaseColor());
+            gfx::SizeF(host->size()), host->ink_drop()->GetBaseColor());
         highlight->set_visible_opacity(kInkDropHighlightOpacity);
         return highlight;
       },
       this));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), gfx::Insets(kInkDropInset),
-            host->GetInkDropCenterBasedOnLastEvent(),
-            host->GetInkDropBaseColor(), host->GetInkDropVisibleOpacity());
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetBaseColor(),
+            host->ink_drop()->GetVisibleOpacity());
       },
       this));
 }
@@ -109,7 +110,7 @@
   // Note that the current assumption is that button bounds are square.
   DCHECK_EQ(width(), height());
   SetFocusPainter(views::Painter::CreateSolidRoundRectPainter(
-      SkColorSetA(GetInkDropBaseColor(), 0xff * kInkDropHighlightOpacity),
+      SkColorSetA(ink_drop()->GetBaseColor(), 0xff * kInkDropHighlightOpacity),
       width() / 2 - kInkDropInset, gfx::Insets(kInkDropInset)));
 }
 
diff --git a/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc b/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc
index 3253c5c..c9c3e9f 100644
--- a/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_onboarding_suggestion_view.cc
@@ -135,11 +135,11 @@
   focus_ring()->SetColor(gfx::kGoogleBlue300);
 
   // Ink Drop.
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
-  SetInkDropBaseColor(GetForegroundColor(index_));
-  SetInkDropVisibleOpacity(kInkDropVisibleOpacity);
-  SetInkDropHighlightOpacity(kInkDropHighlightOpacity);
+  ink_drop()->SetBaseColor(GetForegroundColor(index_));
+  ink_drop()->SetVisibleOpacity(kInkDropVisibleOpacity);
+  ink_drop()->SetHighlightOpacity(kInkDropHighlightOpacity);
 
   // Installing this highlight path generator will set the desired shape for
   // both ink drop effects as well as our focus ring.
diff --git a/ash/capture_mode/capture_label_view.cc b/ash/capture_mode/capture_label_view.cc
index 00c53d9..7d2030c 100644
--- a/ash/capture_mode/capture_label_view.cc
+++ b/ash/capture_mode/capture_label_view.cc
@@ -11,6 +11,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/bind.h"
 #include "base/i18n/number_formatting.h"
 #include "base/task_runner.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -161,11 +162,12 @@
   label_button_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
   label_button_->SetNotifyEnterExitOnChild(true);
 
-  label_button_->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  label_button_->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   const auto ripple_attributes =
       color_provider->GetRippleAttributes(background_color);
-  label_button_->SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
-  label_button_->SetInkDropBaseColor(ripple_attributes.base_color);
+  label_button_->ink_drop()->SetVisibleOpacity(
+      ripple_attributes.inkdrop_opacity);
+  label_button_->ink_drop()->SetBaseColor(ripple_attributes.base_color);
   label_button_->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
 
   label_ = AddChildView(std::make_unique<views::Label>(std::u16string()));
diff --git a/ash/capture_mode/capture_mode_button.cc b/ash/capture_mode/capture_mode_button.cc
index 7e82451..ab72d582 100644
--- a/ash/capture_mode/capture_mode_button.cc
+++ b/ash/capture_mode/capture_mode_button.cc
@@ -4,39 +4,66 @@
 
 #include "ash/capture_mode/capture_mode_button.h"
 
+#include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/style/ash_color_provider.h"
+#include "base/bind.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/animation/ink_drop.h"
+#include "ui/views/animation/ink_drop_highlight.h"
+#include "ui/views/animation/ink_drop_host_view.h"
 #include "ui/views/controls/highlight_path_generator.h"
 
 namespace ash {
 
 CaptureModeButton::CaptureModeButton(views::Button::PressedCallback callback,
                                      const gfx::VectorIcon& icon)
-    : ViewWithInkDrop(callback) {
-  SetPreferredSize(capture_mode::kButtonSize);
-  SetBorder(views::CreateEmptyBorder(capture_mode::kButtonPadding));
-  auto* color_provider = AshColorProvider::Get();
-  const SkColor normal_color = color_provider->GetContentLayerColor(
+    : views::ImageButton(callback) {
+  ConfigureButton(this, focus_ring());
+  const SkColor normal_color = AshColorProvider::Get()->GetContentLayerColor(
       AshColorProvider::ContentLayerType::kButtonIconColor);
   SetImage(views::Button::STATE_NORMAL,
            gfx::CreateVectorIcon(icon, normal_color));
-  SetImageHorizontalAlignment(ALIGN_CENTER);
-  SetImageVerticalAlignment(ALIGN_MIDDLE);
-  GetViewAccessibility().OverrideIsLeaf(true);
 
   // TODO(afakhry): Fix this.
   GetViewAccessibility().OverrideName(GetClassName());
+}
 
-  SetInstallFocusRingOnFocus(true);
-  focus_ring()->SetColor(color_provider->GetControlsLayerColor(
+// static
+void CaptureModeButton::ConfigureButton(views::ImageButton* button,
+                                        views::FocusRing* focus_ring) {
+  button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  button->SetHasInkDropActionOnClick(true);
+  button->ink_drop()->SetVisibleOpacity(capture_mode::kInkDropVisibleOpacity);
+  views::InkDrop::UseInkDropForFloodFillRipple(button->ink_drop(),
+                                               /*highlight_on_hover=*/false,
+                                               /*highlight_on_focus=*/false);
+  button->ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
+      [](views::Button* host) {
+        auto highlight = std::make_unique<views::InkDropHighlight>(
+            gfx::SizeF(host->size()), host->ink_drop()->GetBaseColor());
+        highlight->set_visible_opacity(
+            capture_mode::kInkDropHighlightVisibleOpacity);
+        return highlight;
+      },
+      button));
+  button->ink_drop()->SetBaseColor(capture_mode::kInkDropBaseColor);
+
+  button->SetImageHorizontalAlignment(ALIGN_CENTER);
+  button->SetImageVerticalAlignment(ALIGN_MIDDLE);
+  button->SetPreferredSize(capture_mode::kButtonSize);
+  button->SetBorder(views::CreateEmptyBorder(capture_mode::kButtonPadding));
+  button->GetViewAccessibility().OverrideIsLeaf(true);
+
+  button->SetInstallFocusRingOnFocus(true);
+  focus_ring->SetColor(AshColorProvider::Get()->GetControlsLayerColor(
       AshColorProvider::ControlsLayerType::kFocusRingColor));
-  focus_ring()->SetPathGenerator(
+  focus_ring->SetPathGenerator(
       std::make_unique<views::CircleHighlightPathGenerator>(
           capture_mode::kButtonPadding));
-  views::InstallCircleHighlightPathGenerator(this,
+  views::InstallCircleHighlightPathGenerator(button,
                                              capture_mode::kButtonPadding);
 }
 
@@ -44,7 +71,7 @@
   return this;
 }
 
-BEGIN_METADATA(CaptureModeButton, ViewWithInkDrop<views::ImageButton>)
+BEGIN_METADATA(CaptureModeButton, views::ImageButton)
 END_METADATA
 
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_button.h b/ash/capture_mode/capture_mode_button.h
index d11266a..fa5ad5c 100644
--- a/ash/capture_mode/capture_mode_button.h
+++ b/ash/capture_mode/capture_mode_button.h
@@ -7,20 +7,22 @@
 
 #include "ash/ash_export.h"
 #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
-#include "ash/capture_mode/view_with_ink_drop.h"
 #include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/image_button.h"
 
 namespace gfx {
 struct VectorIcon;
 }  // namespace gfx
 
+namespace views {
+class FocusRing;
+}  // namespace views
+
 namespace ash {
 
 // A view that shows a button which is part of the CaptureBarView.
 class ASH_EXPORT CaptureModeButton
-    : public ViewWithInkDrop<views::ImageButton>,
+    : public views::ImageButton,
       public CaptureModeSessionFocusCycler::HighlightableView {
  public:
   METADATA_HEADER(CaptureModeButton);
@@ -31,6 +33,11 @@
   CaptureModeButton& operator=(const CaptureModeButton&) = delete;
   ~CaptureModeButton() override = default;
 
+  // Common configuration for CaptureModeButton and CaptureModeToggleButton,
+  // such as InkDrop, preferred size, border, etc.
+  static void ConfigureButton(views::ImageButton* button,
+                              views::FocusRing* focus_ring);
+
   // CaptureModeSessionFocusCycler::HighlightableView:
   views::View* GetView() override;
 };
diff --git a/ash/capture_mode/capture_mode_source_view.cc b/ash/capture_mode/capture_mode_source_view.cc
index 19cecc3..97feb3d 100644
--- a/ash/capture_mode/capture_mode_source_view.cc
+++ b/ash/capture_mode/capture_mode_source_view.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/capture_mode/capture_mode_metrics.h"
 #include "ash/capture_mode/capture_mode_toggle_button.h"
diff --git a/ash/capture_mode/capture_mode_toggle_button.cc b/ash/capture_mode/capture_mode_toggle_button.cc
index 07b8b54f..863ebe73 100644
--- a/ash/capture_mode/capture_mode_toggle_button.cc
+++ b/ash/capture_mode/capture_mode_toggle_button.cc
@@ -4,6 +4,8 @@
 
 #include "ash/capture_mode/capture_mode_toggle_button.h"
 
+#include "ash/capture_mode/capture_mode_button.h"
+#include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/style/ash_color_provider.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -17,25 +19,11 @@
 CaptureModeToggleButton::CaptureModeToggleButton(
     views::Button::PressedCallback callback,
     const gfx::VectorIcon& icon)
-    : ViewWithInkDrop(callback) {
-  SetPreferredSize(capture_mode::kButtonSize);
-  SetBorder(views::CreateEmptyBorder(capture_mode::kButtonPadding));
-  SetImageHorizontalAlignment(ALIGN_CENTER);
-  SetImageVerticalAlignment(ALIGN_MIDDLE);
-  GetViewAccessibility().OverrideIsLeaf(true);
-
-  SetInstallFocusRingOnFocus(true);
-  const auto* color_provider = AshColorProvider::Get();
-  focus_ring()->SetColor(color_provider->GetControlsLayerColor(
-      AshColorProvider::ControlsLayerType::kFocusRingColor));
-  focus_ring()->SetPathGenerator(
-      std::make_unique<views::CircleHighlightPathGenerator>(
-          capture_mode::kButtonPadding));
-  views::InstallCircleHighlightPathGenerator(this,
-                                             capture_mode::kButtonPadding);
+    : views::ToggleImageButton(callback) {
+  CaptureModeButton::ConfigureButton(this, focus_ring());
 
   SetIcon(icon);
-  toggled_background_color_ = color_provider->GetControlsLayerColor(
+  toggled_background_color_ = AshColorProvider::Get()->GetControlsLayerColor(
       AshColorProvider::ControlsLayerType::kControlBackgroundColorActive);
 }
 
@@ -83,8 +71,7 @@
   SetToggledImage(views::Button::STATE_NORMAL, &toggled_icon);
 }
 
-BEGIN_METADATA(CaptureModeToggleButton,
-               ViewWithInkDrop<views::ToggleImageButton>)
+BEGIN_METADATA(CaptureModeToggleButton, views::ToggleImageButton)
 END_METADATA
 
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_toggle_button.h b/ash/capture_mode/capture_mode_toggle_button.h
index 02c57d2..7c42cde9 100644
--- a/ash/capture_mode/capture_mode_toggle_button.h
+++ b/ash/capture_mode/capture_mode_toggle_button.h
@@ -7,7 +7,6 @@
 
 #include "ash/ash_export.h"
 #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
-#include "ash/capture_mode/view_with_ink_drop.h"
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/image_button.h"
@@ -22,7 +21,7 @@
 // toggle between image and video capture, and between fullscreen, window, and
 // region capture sources.
 class ASH_EXPORT CaptureModeToggleButton
-    : public ViewWithInkDrop<views::ToggleImageButton>,
+    : public views::ToggleImageButton,
       public CaptureModeSessionFocusCycler::HighlightableView {
  public:
   METADATA_HEADER(CaptureModeToggleButton);
diff --git a/ash/capture_mode/view_with_ink_drop.h b/ash/capture_mode/view_with_ink_drop.h
deleted file mode 100644
index 5bcc142d..0000000
--- a/ash/capture_mode/view_with_ink_drop.h
+++ /dev/null
@@ -1,61 +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 ASH_CAPTURE_MODE_VIEW_WITH_INK_DROP_H_
-#define ASH_CAPTURE_MODE_VIEW_WITH_INK_DROP_H_
-
-#include <type_traits>
-
-#include "ash/capture_mode/capture_mode_constants.h"
-#include "base/bind.h"
-#include "ui/views/animation/ink_drop_host_view.h"
-#include "ui/views/animation/ink_drop_impl.h"
-
-namespace ash {
-
-// A template class that relieves us from having to rewrite the ink drop boiler-
-// plate code for all the Capture Mode views that will need it. This is used by
-// CaptureModeToggleButton, CaptureModeCloseButton, ... etc.
-// |T| must be a subtype of |views::InkDropHostView|.
-// TODO(pbos): After migrating below to use SetCreateInkDrop* callbacks, replace
-// this class with a shared ConfigureInkDrop function called from all current
-// ViewWithInkDrop subclasses' constructors).
-template <typename T>
-class ViewWithInkDrop : public T {
- public:
-  static_assert(std::is_base_of<views::InkDropHostView, T>::value,
-                "T must be a subtype of views::InkDropHostView");
-
-  // A constructor that forwards |args| to |T|'s constructor, so |args| are the
-  // exact same as required by |T|'s constructor. It sets up the ink drop on the
-  // view.
-  template <typename... Args>
-  explicit ViewWithInkDrop(Args... args) : T(std::forward<Args>(args)...) {
-    T::SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
-    T::SetHasInkDropActionOnClick(true);
-    T::SetInkDropVisibleOpacity(capture_mode::kInkDropVisibleOpacity);
-    views::InkDrop::UseInkDropForFloodFillRipple(this,
-                                                 /*highlight_on_hover=*/false,
-                                                 /*highlight_on_focus=*/false);
-    T::SetCreateInkDropHighlightCallback(base::BindRepeating(
-        [](views::InkDropHostView* host) {
-          auto highlight = std::make_unique<views::InkDropHighlight>(
-              gfx::SizeF(host->size()), host->GetInkDropBaseColor());
-          highlight->set_visible_opacity(
-              capture_mode::kInkDropHighlightVisibleOpacity);
-          return highlight;
-        },
-        this));
-    // TODO(pbos): See if this is a no-op when replaced with
-    // SetInkDropBaseColor(), i.e. that nothing sets it later.
-    T::SetInkDropBaseColorCallback(
-        base::BindRepeating([]() { return capture_mode::kInkDropBaseColor; }));
-  }
-
-  ~ViewWithInkDrop() override = default;
-};
-
-}  // namespace ash
-
-#endif  // ASH_CAPTURE_MODE_VIEW_WITH_INK_DROP_H_
diff --git a/ash/clipboard/views/clipboard_history_delete_button.cc b/ash/clipboard/views/clipboard_history_delete_button.cc
index c23904e..1f50e79 100644
--- a/ash/clipboard/views/clipboard_history_delete_button.cc
+++ b/ash/clipboard/views/clipboard_history_delete_button.cc
@@ -34,7 +34,7 @@
   SetPreferredSize(gfx::Size(ClipboardHistoryViews::kDeleteButtonSizeDip,
                              ClipboardHistoryViews::kDeleteButtonSizeDip));
   SetVisible(false);
-  SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   ink_drop_container_ =
       AddChildView(std::make_unique<views::InkDropContainerView>());
 
@@ -50,7 +50,7 @@
   views::InstallFixedSizeCircleHighlightPathGenerator(
       this, ClipboardHistoryViews::kDeleteButtonSizeDip / 2);
   views::InkDrop::UseInkDropForFloodFillRipple(
-      this, /*highlight_on_hover=*/false, /*highlight_on_focus=*/true);
+      ink_drop(), /*highlight_on_hover=*/false, /*highlight_on_focus=*/true);
 }
 
 ClipboardHistoryDeleteButton::~ClipboardHistoryDeleteButton() = default;
@@ -82,9 +82,9 @@
 
   const AshColorProvider::RippleAttributes ripple_attributes =
       AshColorProvider::Get()->GetRippleAttributes();
-  SetInkDropBaseColor(ripple_attributes.base_color);
-  SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
-  SetInkDropHighlightOpacity(ripple_attributes.highlight_opacity);
+  ink_drop()->SetBaseColor(ripple_attributes.base_color);
+  ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  ink_drop()->SetHighlightOpacity(ripple_attributes.highlight_opacity);
 }
 
 void ClipboardHistoryDeleteButton::RemoveLayerBeneathView(ui::Layer* layer) {
diff --git a/ash/clipboard/views/clipboard_history_item_view.cc b/ash/clipboard/views/clipboard_history_item_view.cc
index ace01d90..0262b7e 100644
--- a/ash/clipboard/views/clipboard_history_item_view.cc
+++ b/ash/clipboard/views/clipboard_history_item_view.cc
@@ -12,6 +12,7 @@
 #include "ash/clipboard/views/clipboard_history_text_item_view.h"
 #include "ash/clipboard/views/clipboard_history_view_constants.h"
 #include "base/auto_reset.h"
+#include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/accessibility/ax_node_data.h"
@@ -45,7 +46,7 @@
 
   const bool focused =
       (container_->pseudo_focus_ == PseudoFocus::kDeleteButton);
-  delete_button_->GetInkDrop()->SetFocused(focused);
+  delete_button_->ink_drop()->GetInkDrop()->SetFocused(focused);
   if (focused) {
     delete_button_->NotifyAccessibilityEvent(ax::mojom::Event::kHover,
                                              /*send_native_event*/ true);
diff --git a/ash/clipboard/views/clipboard_history_main_button.cc b/ash/clipboard/views/clipboard_history_main_button.cc
index a7d64f25..f4bfc7f 100644
--- a/ash/clipboard/views/clipboard_history_main_button.cc
+++ b/ash/clipboard/views/clipboard_history_main_button.cc
@@ -30,7 +30,7 @@
           base::Unretained(container))),
       container_(container) {
   SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
-  SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetID(ClipboardHistoryUtil::kMainButtonViewID);
 
   // Let the parent handle accessibility features.
@@ -50,7 +50,7 @@
   // Hence, highlighted background is implemented by customizing in
   // `PaintButtonContents()`.
   views::InkDrop::UseInkDropForFloodFillRipple(
-      this, /*highlight_on_hover=*/false,
+      ink_drop(), /*highlight_on_hover=*/false,
       /*highlight_on_focus=*/!focus_ring());
 }
 
@@ -90,8 +90,8 @@
 
   const AshColorProvider::RippleAttributes ripple_attributes =
       AshColorProvider::Get()->GetRippleAttributes();
-  SetInkDropBaseColor(ripple_attributes.base_color);
-  SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  ink_drop()->SetBaseColor(ripple_attributes.base_color);
+  ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
 }
 
 void ClipboardHistoryMainButton::OnGestureEvent(ui::GestureEvent* event) {
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 6cc2b67..06c7df8 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -705,7 +705,7 @@
 
 // Enables the updated cellular activation UI; see go/cros-cellular-design.
 const base::Feature kUpdatedCellularActivationUi{
-    "UpdatedCellularActivationUi", base::FEATURE_DISABLED_BY_DEFAULT};
+    "UpdatedCellularActivationUi", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Uses the same browser sync consent dialog as Windows/Mac/Linux. Allows the
 // user to fully opt-out of browser sync, including marking the IdentityManager
diff --git a/ash/content/scanning/resources/scanning_app.js b/ash/content/scanning/resources/scanning_app.js
index 87f3c03..db155c7 100644
--- a/ash/content/scanning/resources/scanning_app.js
+++ b/ash/content/scanning/resources/scanning_app.js
@@ -282,8 +282,17 @@
       }
     },
 
-    /** @private {?ScanSettings} */
-    savedScanSettings_: Object,
+    /** @private {!ScanSettings} */
+    savedScanSettings_: {
+      type: Object,
+      value() {
+        return {
+          lastUsedScannerName: '',
+          scanToPath: '',
+          scanners: [],
+        };
+      },
+    },
 
     /** @private {string} */
     lastUsedScannerId_: String,
@@ -424,7 +433,8 @@
     this.selectedFileType = ash.scanning.mojom.FileType.kPdf.toString();
 
     this.setAppState_(
-        this.savedScanSettings_ && this.scanAppStickySettingsEnabled_ ?
+        this.scanAppStickySettingsEnabled_ &&
+                this.areSavedScanSettingsAvailable_() ?
             AppState.SETTING_SAVED_SETTINGS :
             AppState.READY);
   },
@@ -481,7 +491,7 @@
     const fileType = fileTypeFromString(this.selectedFileType);
     const colorMode = colorModeFromString(this.selectedColorMode);
     const pageSize = pageSizeFromString(this.selectedPageSize);
-    const resolution = Number(this.selectedResolution)
+    const resolution = Number(this.selectedResolution);
 
     const settings = {
       sourceName: this.selectedSource,
@@ -509,6 +519,9 @@
             /*@type {!{success: boolean}}*/ (response) => {
               this.onStartScanResponse_(response);
             });
+    if (this.scanAppStickySettingsEnabled_) {
+      this.saveScanSettings_();
+    }
 
     const scanJobSettingsForMetrics = {
       sourceType: this.sourceTypeMap_.get(this.selectedSource),
@@ -762,7 +775,7 @@
 
   /** @private */
   setScanSettingsFromSavedSettings_() {
-    if (!this.savedScanSettings_) {
+    if (!this.areSavedScanSettingsAvailable_()) {
       return;
     }
 
@@ -807,8 +820,7 @@
    * @private
    */
   isLastUsedScanner_(scanner) {
-    return this.savedScanSettings_ !== undefined &&
-        this.savedScanSettings_.lastUsedScannerName ===
+    return this.savedScanSettings_.lastUsedScannerName ===
         getScannerDisplayName(scanner);
   },
 
@@ -876,6 +888,53 @@
             });
   },
 
+  /** @private */
+  saveScanSettings_() {
+    assert(this.scanAppStickySettingsEnabled_);
+
+    const scannerName = this.getSelectedScannerDisplayName_();
+    this.savedScanSettings_.lastUsedScannerName = scannerName;
+    this.savedScanSettings_.scanToPath = this.selectedFilePath;
+
+    // Search the scan settings array for the currently selected scanner. If
+    // found, replace it with the new scan settings. If not, add it to the list.
+    const newScannerSetting = this.createScannerSettingForSelectedScanner_();
+    const scannerIndex = this.savedScanSettings_.scanners.findIndex(
+        scanner => scanner.name === scannerName);
+    if (scannerIndex === -1) {
+      this.savedScanSettings_.scanners.push(newScannerSetting);
+    } else {
+      this.savedScanSettings_.scanners[scannerIndex] = newScannerSetting;
+    }
+
+    this.browserProxy_.saveScanSettings(
+        JSON.stringify(this.savedScanSettings_));
+  },
+
+  /**
+   * @return {!ScannerSetting}
+   * @private
+   */
+  createScannerSettingForSelectedScanner_() {
+    return /** @type {!ScannerSetting} */ ({
+      name: this.getSelectedScannerDisplayName_(),
+      lastScanDate: new Date(),
+      sourceName: this.selectedSource,
+      fileType: fileTypeFromString(this.selectedFileType),
+      colorMode: colorModeFromString(this.selectedColorMode),
+      pageSize: pageSizeFromString(this.selectedPageSize),
+      resolutionDpi: Number(this.selectedResolution),
+    });
+  },
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  areSavedScanSettingsAvailable_() {
+    return this.savedScanSettings_.scanners.length !== 0;
+  },
+
   /**
    * @param {string} sourceName
    * @private
diff --git a/ash/events/accessibility_event_rewriter_unittest.cc b/ash/events/accessibility_event_rewriter_unittest.cc
index 709c3fd..0c974a7 100644
--- a/ash/events/accessibility_event_rewriter_unittest.cc
+++ b/ash/events/accessibility_event_rewriter_unittest.cc
@@ -168,7 +168,7 @@
 
   bool NotifyDeprecatedRightClickRewrite() override { return false; }
   bool NotifyDeprecatedFKeyRewrite() override { return false; }
-  bool NotifyDeprecatedAltBasedKeyRewrite(ui::KeyboardCode key_code) override {
+  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
     return false;
   }
 
diff --git a/ash/lock_screen_action/lock_screen_action_background_view.cc b/ash/lock_screen_action/lock_screen_action_background_view.cc
index 0a95a008..d863a37 100644
--- a/ash/lock_screen_action/lock_screen_action_background_view.cc
+++ b/ash/lock_screen_action/lock_screen_action_background_view.cc
@@ -29,18 +29,18 @@
   explicit NoteBackground(views::InkDropObserver* observer)
       : observer_(observer) {
     DCHECK(observer);
-    SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
 
-    SetCreateInkDropCallback(base::BindRepeating(
+    ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
         [](NoteBackground* host) {
           std::unique_ptr<views::InkDrop> ink_drop =
               views::InkDrop::CreateInkDropWithoutAutoHighlight(
-                  host, /*highlight_on_hover=*/false);
+                  host->ink_drop(), /*highlight_on_hover=*/false);
           ink_drop->AddObserver(host->observer_);
           return ink_drop;
         },
         this));
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
           const gfx::Point center = base::i18n::IsRTL()
                                         ? host->GetLocalBounds().origin()
@@ -48,7 +48,7 @@
           auto ink_drop_ripple =
               std::make_unique<views::FloodFillInkDropRipple>(
                   host->size(), gfx::Insets(), center,
-                  host->GetInkDropBaseColor(), 1);
+                  host->ink_drop()->GetBaseColor(), 1);
           ink_drop_ripple->set_use_hide_transform_duration_for_hide_fade_out(
               true);
           ink_drop_ripple->set_duration_factor(1.5);
@@ -57,8 +57,8 @@
         this));
 
     // TODO(pbos): See if this is a no-op when replaced with
-    // SetInkDropBaseColor(), i.e. that nothing sets it later.
-    SetInkDropBaseColorCallback(
+    // ink_drop()->SetBaseColor(), i.e. that nothing sets it later.
+    ink_drop()->SetBaseColorCallback(
         base::BindRepeating([]() { return SK_ColorBLACK; }));
   }
 
@@ -92,14 +92,15 @@
   animation_end_callback_ = std::move(done);
   animating_to_state_ = views::InkDropState::ACTIVATED;
 
-  background_->AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
+  background_->ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                                          nullptr);
 }
 
 void LockScreenActionBackgroundView::AnimateHide(base::OnceClosure done) {
   animation_end_callback_ = std::move(done);
   animating_to_state_ = views::InkDropState::HIDDEN;
 
-  background_->AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
+  background_->ink_drop()->AnimateToState(views::InkDropState::HIDDEN, nullptr);
 }
 
 void LockScreenActionBackgroundView::InkDropAnimationStarted() {}
diff --git a/ash/login/ui/lock_screen_media_controls_view.cc b/ash/login/ui/lock_screen_media_controls_view.cc
index f9bf7cb..7accc4a 100644
--- a/ash/login/ui/lock_screen_media_controls_view.cc
+++ b/ash/login/ui/lock_screen_media_controls_view.cc
@@ -154,12 +154,12 @@
             view,
             this)),
         icon_size_(icon_size) {
-    SetInkDropMode(views::Button::InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](InkDropHostView* host) {
           return std::make_unique<views::InkDropHighlight>(
-              gfx::SizeF(host->size()), host->GetInkDropBaseColor());
+              gfx::SizeF(host->size()), host->ink_drop()->GetBaseColor());
         },
         this));
 
diff --git a/ash/login/ui/login_base_bubble_view.cc b/ash/login/ui/login_base_bubble_view.cc
index 8b091b46..e64deee 100644
--- a/ash/login/ui/login_base_bubble_view.cc
+++ b/ash/login/ui/login_base_bubble_view.cc
@@ -310,10 +310,10 @@
 
 void LoginBaseBubbleView::ScheduleAnimation(bool visible) {
   if (GetBubbleOpener()) {
-    GetBubbleOpener()->AnimateInkDrop(visible
-                                          ? views::InkDropState::ACTIVATED
-                                          : views::InkDropState::DEACTIVATED,
-                                      nullptr /*event*/);
+    GetBubbleOpener()->ink_drop()->AnimateToState(
+        visible ? views::InkDropState::ACTIVATED
+                : views::InkDropState::DEACTIVATED,
+        nullptr /*event*/);
   }
 
   if (layer())
diff --git a/ash/login/ui/login_button.cc b/ash/login/ui/login_button.cc
index a1de38a7..79b3c218 100644
--- a/ash/login/ui/login_button.cc
+++ b/ash/login/ui/login_button.cc
@@ -28,15 +28,15 @@
     : views::ImageButton(std::move(callback)) {
   SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
   SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](InkDropHostView* host) {
         return std::make_unique<views::InkDropHighlight>(
             gfx::SizeF(host->size()), kInkDropHighlightColor);
       },
       this));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](LoginButton* host) -> std::unique_ptr<views::InkDropRipple> {
         const gfx::Point center = host->GetLocalBounds().CenterPoint();
         const int radius = host->GetInkDropRadius();
@@ -45,8 +45,8 @@
 
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-            host->GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor,
-            1.f /*visible_opacity*/);
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
+            kInkDropRippleColor, 1.f /*visible_opacity*/);
       },
       this));
 
diff --git a/ash/login/ui/login_expanded_public_account_view.cc b/ash/login/ui/login_expanded_public_account_view.cc
index cbf0037..b342977 100644
--- a/ash/login/ui/login_expanded_public_account_view.cc
+++ b/ash/login/ui/login_expanded_public_account_view.cc
@@ -128,7 +128,7 @@
     layer()->SetFillsBoundsOpaquely(false);
     SetFocusBehavior(FocusBehavior::ALWAYS);
     SetLayoutManager(std::make_unique<views::FillLayout>());
-    SetInkDropMode(InkDropMode::OFF);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
 
     auto add_horizontal_margin = [&](int width,
                                      views::View* parent) -> views::View* {
diff --git a/ash/login/ui/login_pin_view.cc b/ash/login/ui/login_pin_view.cc
index 9e7e2af2..3561e43 100644
--- a/ash/login/ui/login_pin_view.cc
+++ b/ash/login/ui/login_pin_view.cc
@@ -106,8 +106,8 @@
     // focus painter to paint.
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
-    SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](BasePinButton* host) {
           auto highlight = std::make_unique<views::InkDropHighlight>(
               gfx::SizeF(host->size()),
@@ -116,7 +116,7 @@
           return highlight;
         },
         this));
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](BasePinButton* host) -> std::unique_ptr<views::InkDropRipple> {
           const gfx::Point center = host->GetLocalBounds().CenterPoint();
           const gfx::Rect bounds(center.x() - kInkDropCornerRadiusDp,
@@ -126,7 +126,7 @@
 
           return std::make_unique<views::FloodFillInkDropRipple>(
               host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-              host->GetInkDropCenterBasedOnLastEvent(),
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
               host->palette_.pin_ink_drop_ripple_color,
               /*visible_opacity=*/1.f);
         },
@@ -180,8 +180,8 @@
     if (event)
       event->SetHandled();
 
-    AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED,
-                   ui::LocatedEvent::FromIfValid(event));
+    ink_drop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED,
+                               ui::LocatedEvent::FromIfValid(event));
     SchedulePaint();
 
     // |on_press_| may delete us.
@@ -281,7 +281,7 @@
 
   void OnEnabledChanged() {
     if (!GetEnabled()) {
-      AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+      ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED, nullptr);
       CancelRepeat();
     }
     UpdateImage();
@@ -328,8 +328,8 @@
       if (event)
         event->SetHandled();
 
-      AnimateInkDrop(views::InkDropState::ACTIVATED,
-                     ui::LocatedEvent::FromIfValid(event));
+      ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                                 ui::LocatedEvent::FromIfValid(event));
       SchedulePaint();
 
       return;
@@ -365,7 +365,7 @@
     if (!did_submit && on_press_)
       on_press_.Run();
 
-    AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+    ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED, nullptr);
     SchedulePaint();
   }
 
diff --git a/ash/login/ui/login_user_menu_view_unittest.cc b/ash/login/ui/login_user_menu_view_unittest.cc
index ccff3ef..d868194 100644
--- a/ash/login/ui/login_user_menu_view_unittest.cc
+++ b/ash/login/ui/login_user_menu_view_unittest.cc
@@ -123,9 +123,8 @@
   container->AddChildView(bubble_opener);
   SetWidget(CreateWidgetWithContent(container));
 
-  views::test::InkDropHostViewTestApi ink_drop_api(bubble_opener);
-  EXPECT_EQ(ink_drop_api.ink_drop_mode(),
-            views::InkDropHostView::InkDropMode::ON);
+  views::test::InkDropHostTestApi ink_drop_api(bubble_opener->ink_drop());
+  EXPECT_EQ(ink_drop_api.ink_drop_mode(), views::InkDropHost::InkDropMode::ON);
   EXPECT_TRUE(ink_drop_api.HasInkDrop());
 
   auto* bubble = new LoginUserMenuView(LoginUserInfo(), container /*anchor*/,
diff --git a/ash/login/ui/media_controls_header_view.cc b/ash/login/ui/media_controls_header_view.cc
index 9f3597c0..91c96ca 100644
--- a/ash/login/ui/media_controls_header_view.cc
+++ b/ash/login/ui/media_controls_header_view.cc
@@ -91,7 +91,7 @@
   std::u16string close_button_label(
       l10n_util::GetStringUTF16(IDS_ASH_LOCK_SCREEN_MEDIA_CONTROLS_CLOSE));
   close_button->SetAccessibleName(close_button_label);
-  close_button->SetInkDropBaseColor(
+  close_button->ink_drop()->SetBaseColor(
       color_utils::DeriveDefaultIconColor(gfx::kGoogleGrey700));
   close_button_ = AddChildView(std::move(close_button));
 }
diff --git a/ash/login/ui/system_label_button.cc b/ash/login/ui/system_label_button.cc
index 78f98661..18f610bc 100644
--- a/ash/login/ui/system_label_button.cc
+++ b/ash/login/ui/system_label_button.cc
@@ -58,7 +58,7 @@
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
   SetTextSubpixelRenderingEnabled(false);
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
 
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetInstallFocusRingOnFocus(true);
@@ -128,9 +128,9 @@
       AshColorProvider::Get()->GetBaseLayerColor(kBubbleLayerType));
   const AshColorProvider::RippleAttributes ripple_attributes =
       AshColorProvider::Get()->GetRippleAttributes(effective_background_color);
-  SetInkDropBaseColor(ripple_attributes.base_color);
-  SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
-  SetInkDropHighlightOpacity(ripple_attributes.highlight_opacity);
+  ink_drop()->SetBaseColor(ripple_attributes.base_color);
+  ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  ink_drop()->SetHighlightOpacity(ripple_attributes.highlight_opacity);
 }
 
 }  // namespace ash
diff --git a/ash/projector/ui/projector_button.cc b/ash/projector/ui/projector_button.cc
index f0ec43d3..36d3ce2 100644
--- a/ash/projector/ui/projector_button.cc
+++ b/ash/projector/ui/projector_button.cc
@@ -30,7 +30,7 @@
   views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
                                                 kProjectorButtonSize / 2.f);
 
-  views::InkDrop::UseInkDropForFloodFillRipple(this,
+  views::InkDrop::UseInkDropForFloodFillRipple(ink_drop(),
                                                /*highlight_on_hover=*/true,
                                                /*highlight_on_focus=*/true);
 }
@@ -55,10 +55,10 @@
   // Ink Drop.
   const AshColorProvider::RippleAttributes ripple_attributes =
       AshColorProvider::Get()->GetRippleAttributes();
-  SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
-  SetInkDropBaseColor(ripple_attributes.base_color);
-  SetInkDropHighlightOpacity(ripple_attributes.highlight_opacity);
+  ink_drop()->SetBaseColor(ripple_attributes.base_color);
+  ink_drop()->SetHighlightOpacity(ripple_attributes.highlight_opacity);
 }
 
 }  // namespace ash
diff --git a/ash/public/cpp/holding_space/holding_space_metrics.cc b/ash/public/cpp/holding_space/holding_space_metrics.cc
index 6a5adba6..beff27b6 100644
--- a/ash/public/cpp/holding_space/holding_space_metrics.cc
+++ b/ash/public/cpp/holding_space/holding_space_metrics.cc
@@ -17,14 +17,12 @@
 
 namespace {
 
-// Helpers ---------------------------------------------------------------------
-
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused. Note that 2, 3, and 4 are reserved in
 // case additional special values need to be added in the future.
-constexpr int kEmptyExtension = 0;
-constexpr int kOtherExtension = 1;
-constexpr int kFirstKnownExtension = 5;
+constexpr size_t kEmptyExtension = 0u;
+constexpr size_t kOtherExtension = 1u;
+constexpr size_t kFirstKnownExtension = 5u;
 constexpr std::array<const char*, 72> kKnownExtensions = {
     ".3ga",     ".3gp",  ".aac",        ".alac", ".arw",   ".asf",  ".avi",
     ".bmp",     ".cr2",  ".crdownload", ".crx",  ".csv",   ".dmg",  ".dng",
@@ -38,25 +36,10 @@
     ".txt",     ".wav",  ".webm",       ".webp", ".wma",   ".wmv",  ".xls",
     ".xlsx",    ".zip",
 };
+constexpr size_t kExtensionsSize =
+    kFirstKnownExtension + kKnownExtensions.size();
 
-// Returns the integer representation of the extension for the specified
-// `file_path`. Note that these values are persisted to histograms so should
-// remain unchanged.
-int FilePathToExtension(const base::FilePath& file_path) {
-  if (file_path.empty())
-    return kEmptyExtension;
-
-  const std::string extension = base::ToLowerASCII(file_path.Extension());
-  if (extension.empty())
-    return kEmptyExtension;
-
-  auto* const* it =
-      std::find(kKnownExtensions.begin(), kKnownExtensions.end(), extension);
-  if (it == kKnownExtensions.end())
-    return kOtherExtension;
-
-  return kFirstKnownExtension + std::distance(kKnownExtensions.begin(), it);
-}
+// Helpers ---------------------------------------------------------------------
 
 // Returns the string representation of the specified `action`. Note that these
 // values are persisted to histograms so should remain unchanged.
@@ -106,6 +89,25 @@
 
 }  // namespace
 
+// Utilities -------------------------------------------------------------------
+
+// Note that these values are persisted to histograms so must remain unchanged.
+size_t FilePathToExtension(const base::FilePath& file_path) {
+  if (file_path.empty())
+    return kEmptyExtension;
+
+  const std::string extension = base::ToLowerASCII(file_path.Extension());
+  if (extension.empty())
+    return kEmptyExtension;
+
+  auto* const* it =
+      std::find(kKnownExtensions.begin(), kKnownExtensions.end(), extension);
+  if (it == kKnownExtensions.end())
+    return kOtherExtension;
+
+  return kFirstKnownExtension + std::distance(kKnownExtensions.begin(), it);
+}
+
 // Metrics ---------------------------------------------------------------------
 
 void RecordPodAction(PodAction action) {
@@ -123,7 +125,6 @@
 void RecordItemAction(const std::vector<const HoldingSpaceItem*>& items,
                       ItemAction action) {
   const std::string action_string = ItemActionToString(action);
-  const int extensions_size = kFirstKnownExtension + kKnownExtensions.size();
 
   for (const HoldingSpaceItem* item : items) {
     base::UmaHistogramEnumeration("HoldingSpace.Item.Action.All", action);
@@ -131,7 +132,7 @@
                                   item->type());
     base::UmaHistogramExactLinear(
         "HoldingSpace.Item.Action." + action_string + ".Extension",
-        FilePathToExtension(item->file_path()), extensions_size);
+        FilePathToExtension(item->file_path()), kExtensionsSize);
   }
 }
 
@@ -151,8 +152,12 @@
   }
 }
 
-void RecordItemFailureToLaunch(HoldingSpaceItem::Type type) {
+void RecordItemFailureToLaunch(HoldingSpaceItem::Type type,
+                               const base::FilePath& file_path) {
   base::UmaHistogramEnumeration("HoldingSpace.Item.FailureToLaunch", type);
+  base::UmaHistogramExactLinear("HoldingSpace.Item.FailureToLaunch.Extension",
+                                FilePathToExtension(file_path),
+                                kExtensionsSize);
 }
 
 void RecordTimeFromFirstAvailabilityToFirstAdd(base::TimeDelta time_delta) {
diff --git a/ash/public/cpp/holding_space/holding_space_metrics.h b/ash/public/cpp/holding_space/holding_space_metrics.h
index 512c514..81b4be0c 100644
--- a/ash/public/cpp/holding_space/holding_space_metrics.h
+++ b/ash/public/cpp/holding_space/holding_space_metrics.h
@@ -17,6 +17,9 @@
 namespace ash {
 namespace holding_space_metrics {
 
+// Returns the numeric representation of the extension for `file_path`.
+ASH_PUBLIC_EXPORT size_t FilePathToExtension(const base::FilePath& file_path);
+
 // Enumeration of actions that can be taken on the holding space pod in the
 // shelf. These values are persisted to logs. Entries should not be renumbered
 // and numeric values should never be reused.
@@ -81,8 +84,11 @@
 ASH_PUBLIC_EXPORT void RecordItemCounts(
     const std::vector<const HoldingSpaceItem*>& items);
 
-// Records a failure to launch a holding space item of the specified `type`.
-ASH_PUBLIC_EXPORT void RecordItemFailureToLaunch(HoldingSpaceItem::Type type);
+// Records a failure to launch a holding space item of the specified `type`
+// backed by the file at the specified `file_path`.
+ASH_PUBLIC_EXPORT void RecordItemFailureToLaunch(
+    HoldingSpaceItem::Type type,
+    const base::FilePath& file_path);
 
 // Records time from the first availability of the holding space feature to the
 // first item being added to holding space.
diff --git a/ash/search_box/search_box_view_base.cc b/ash/search_box/search_box_view_base.cc
index 5c7a14e..5cbc0d0 100644
--- a/ash/search_box/search_box_view_base.cc
+++ b/ash/search_box/search_box_view_base.cc
@@ -93,10 +93,10 @@
 
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     // InkDropState will reset after clicking.
     SetHasInkDropActionOnClick(true);
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](InkDropHostView* host) {
           constexpr SkColor ripple_color =
               SkColorSetA(gfx::kGoogleGrey900, 0x12);
@@ -106,7 +106,7 @@
           return highlight;
         },
         this));
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](SearchBoxImageButton* host)
             -> std::unique_ptr<views::InkDropRipple> {
           const gfx::Point center = host->GetLocalBounds().CenterPoint();
@@ -119,7 +119,8 @@
 
           return std::make_unique<views::FloodFillInkDropRipple>(
               host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-              host->GetInkDropCenterBasedOnLastEvent(), ripple_color, 1.0f);
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
+              ripple_color, 1.0f);
         },
         this));
 
diff --git a/ash/shelf/home_button_controller.cc b/ash/shelf/home_button_controller.cc
index 97f0687a..3f0c1d9f 100644
--- a/ash/shelf/home_button_controller.cc
+++ b/ash/shelf/home_button_controller.cc
@@ -76,8 +76,10 @@
         assistant_animation_delay_timer_->Stop();
       }
 
-      if (CanActivate(button_->GetDisplayId()))
-        button_->AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED, event);
+      if (CanActivate(button_->GetDisplayId())) {
+        button_->ink_drop()->AnimateToState(
+            views::InkDropState::ACTION_TRIGGERED, event);
+      }
 
       // After animating the ripple, let the button handle the event.
       return false;
@@ -89,8 +91,10 @@
                            base::Unretained(this)));
       }
 
-      if (CanActivate(button_->GetDisplayId()))
-        button_->AnimateInkDrop(views::InkDropState::ACTION_PENDING, event);
+      if (CanActivate(button_->GetDisplayId())) {
+        button_->ink_drop()->AnimateToState(views::InkDropState::ACTION_PENDING,
+                                            event);
+      }
 
       return false;
     case ui::ET_GESTURE_LONG_PRESS:
@@ -113,7 +117,7 @@
         return false;
 
       // This event happens after the user long presses and lifts the finger.
-      button_->AnimateInkDrop(views::InkDropState::HIDDEN, event);
+      button_->ink_drop()->AnimateToState(views::InkDropState::HIDDEN, event);
 
       // We already handled the long press; consume the long tap to avoid
       // bringing up the context menu again.
@@ -147,7 +151,8 @@
 }
 
 void HomeButtonController::OnTabletModeStarted() {
-  button_->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+  button_->ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED,
+                                      nullptr);
 }
 
 void HomeButtonController::OnAssistantFeatureAllowedChanged(
@@ -174,8 +179,10 @@
 void HomeButtonController::OnAppListShown() {
   // Do not show a highlight in tablet mode, since the home screen view is
   // always open in the background.
-  if (!Shell::Get()->IsInTabletMode())
-    button_->AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
+  if (!Shell::Get()->IsInTabletMode()) {
+    button_->ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                                        nullptr);
+  }
   is_showing_app_list_ = true;
 }
 
@@ -183,10 +190,11 @@
   // If ink drop is not hidden already, snap it to active state, so animation to
   // DEACTIVATED state starts immediately (the animation would otherwise wait
   // for the current animation to finish).
-  views::InkDrop* const ink_drop = button_->GetInkDrop();
+  views::InkDrop* const ink_drop = button_->ink_drop()->GetInkDrop();
   if (ink_drop->GetTargetInkDropState() != views::InkDropState::HIDDEN)
     ink_drop->SnapToActivated();
-  button_->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+  button_->ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED,
+                                      nullptr);
 
   is_showing_app_list_ = false;
 }
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index 2073b8b..f8cf470 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -218,14 +218,14 @@
         this, GetButtonInsets(), ShelfConfig::Get()->control_border_radius());
     focus_ring()->SetColor(ShelfConfig::Get()->shelf_focus_border_color());
     SetFocusPainter(nullptr);
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
     AshColorProvider::RippleAttributes ripple_attributes =
         color_provider->GetRippleAttributes();
-    SetInkDropBaseColor(ripple_attributes.base_color);
-    SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+    ink_drop()->SetBaseColor(ripple_attributes.base_color);
+    ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
     views::InkDrop::UseInkDropWithoutAutoHighlight(
-        this, /*highlight_on_hover=*/false);
+        ink_drop(), /*highlight_on_hover=*/false);
 
     // Layer rendering is required when the shelf background is visible, which
     // happens when the wallpaper is not blurred.
@@ -315,15 +315,15 @@
         this, GetButtonInsets(), ShelfConfig::Get()->control_border_radius());
     focus_ring()->SetColor(ShelfConfig::Get()->shelf_focus_border_color());
     SetFocusPainter(nullptr);
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
     views::InkDrop::UseInkDropWithoutAutoHighlight(
-        this, /*highlight_on_hover=*/false);
+        ink_drop(), /*highlight_on_hover=*/false);
 
     const AshColorProvider::RippleAttributes ripple_attributes =
         AshColorProvider::Get()->GetRippleAttributes();
-    SetInkDropBaseColor(ripple_attributes.base_color);
-    SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+    ink_drop()->SetBaseColor(ripple_attributes.base_color);
+    ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
 
     // Layer rendering is required when the shelf background is visible, which
     // happens when the wallpaper is not blurred.
diff --git a/ash/shelf/scroll_arrow_view.cc b/ash/shelf/scroll_arrow_view.cc
index fe12144..0090966 100644
--- a/ash/shelf/scroll_arrow_view.cc
+++ b/ash/shelf/scroll_arrow_view.cc
@@ -23,14 +23,15 @@
       arrow_type_(arrow_type),
       is_horizontal_alignment_(is_horizontal_alignment) {
   SetHasInkDropActionOnClick(true);
-  SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
 }
 
 ScrollArrowView::~ScrollArrowView() = default;
 
 void ScrollArrowView::NotifyClick(const ui::Event& event) {
   Button::NotifyClick(event);
-  shelf_button_delegate()->ButtonPressed(/*sender=*/this, event, GetInkDrop());
+  shelf_button_delegate()->ButtonPressed(/*sender=*/this, event,
+                                         ink_drop()->GetInkDrop());
 }
 
 void ScrollArrowView::PaintButtonContents(gfx::Canvas* canvas) {
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index 8f2faa3..edf76514 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -16,6 +16,7 @@
 #include "ash/shell.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/numerics/ranges.h"
 #include "ui/base/dragdrop/mojom/drag_drop_types.mojom-shared.h"
@@ -202,7 +203,7 @@
                         shelf,
                         shelf_button_delegate),
         shelf_(shelf) {
-    SetInkDropMode(InkDropMode::OFF);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
     SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
     SetPaintToLayer();
     layer()->SetFillsBoundsOpaquely(false);
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index 039e49f5..bf66962 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -58,10 +58,10 @@
 class InkDropAnimationWaiter : public views::InkDropObserver {
  public:
   explicit InkDropAnimationWaiter(views::Button* button) : button_(button) {
-    button->GetInkDrop()->AddObserver(this);
+    button->ink_drop()->GetInkDrop()->AddObserver(this);
   }
   ~InkDropAnimationWaiter() override {
-    button_->GetInkDrop()->RemoveObserver(this);
+    button_->ink_drop()->GetInkDrop()->RemoveObserver(this);
   }
 
   void Wait() {
@@ -613,7 +613,7 @@
 
   // Activate a shelf icon's ink drop. Verify that no crash happens.
   views::InkDropHostView* icon = test_api_->GetButton(0);
-  auto* ink_drop = icon->GetInkDrop();
+  auto* ink_drop = icon->ink_drop()->GetInkDrop();
   ink_drop->SnapToActivated();
   EXPECT_EQ(views::InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState());
 }
@@ -711,7 +711,7 @@
     waiter.Wait();
   }
   ASSERT_EQ(views::InkDropState::ACTIVATED,
-            icon->GetInkDrop()->GetTargetInkDropState());
+            icon->ink_drop()->GetInkDrop()->GetTargetInkDropState());
 
   // Verify that in clamshell when the ripple ring is activated, the rounded
   // corners should not be applied.
@@ -729,7 +729,7 @@
     waiter.Wait();
   }
   EXPECT_EQ(views::InkDropState::HIDDEN,
-            icon->GetInkDrop()->GetTargetInkDropState());
+            icon->ink_drop()->GetInkDrop()->GetTargetInkDropState());
 
   // Verify that the rounded corners should not be applied when the ripple ring
   // is hidden.
@@ -766,7 +766,7 @@
     waiter.Wait();
   }
   EXPECT_EQ(views::InkDropState::ACTIVATED,
-            icon->GetInkDrop()->GetTargetInkDropState());
+            icon->ink_drop()->GetInkDrop()->GetTargetInkDropState());
 
   // Emulate to remove a shelf icon from context menu.
   shelf_model->RemoveItemAt(index);
diff --git a/ash/shelf/shelf_app_button.cc b/ash/shelf/shelf_app_button.cc
index ad111982..6ed6f3d 100644
--- a/ash/shelf/shelf_app_button.cc
+++ b/ash/shelf/shelf_app_button.cc
@@ -286,17 +286,17 @@
   };
   icon_shadows_.assign(kShadows, kShadows + base::size(kShadows));
 
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](ShelfAppButton* host) -> std::unique_ptr<views::InkDropRipple> {
         const gfx::Rect small_ripple_area = host->CalculateSmallRippleArea();
         const int ripple_size = host->shelf_view_->GetShelfItemRippleSize();
 
         return std::make_unique<views::SquareInkDropRipple>(
             gfx::Size(ripple_size, ripple_size),
-            host->GetInkDropLargeCornerRadius(), small_ripple_area.size(),
-            host->GetInkDropSmallCornerRadius(),
-            small_ripple_area.CenterPoint(), host->GetInkDropBaseColor(),
-            host->GetInkDropVisibleOpacity());
+            host->ink_drop()->GetLargeCornerRadius(), small_ripple_area.size(),
+            host->ink_drop()->GetSmallCornerRadius(),
+            small_ripple_area.CenterPoint(), host->ink_drop()->GetBaseColor(),
+            host->ink_drop()->GetVisibleOpacity());
       },
       this));
 
@@ -319,7 +319,7 @@
     notification_indicator_ = views::DotIndicator::Install(this);
     SetNotificationBadgeColor(kDefaultIndicatorColor);
   }
-  GetInkDrop()->AddObserver(this);
+  ink_drop()->GetInkDrop()->AddObserver(this);
 
   // Do not set a clip, allow the ink drop to burst out.
   views::InstallEmptyHighlightPathGenerator(this);
@@ -334,7 +334,7 @@
 }
 
 ShelfAppButton::~ShelfAppButton() {
-  GetInkDrop()->RemoveObserver(this);
+  ink_drop()->GetInkDrop()->RemoveObserver(this);
 }
 
 void ShelfAppButton::SetShadowedImage(const gfx::ImageSkia& image) {
@@ -428,17 +428,17 @@
 }
 
 views::InkDrop* ShelfAppButton::GetInkDropForTesting() {
-  return GetInkDrop();
+  return ink_drop()->GetInkDrop();
 }
 
 void ShelfAppButton::OnDragStarted(const ui::LocatedEvent* event) {
-  AnimateInkDrop(views::InkDropState::HIDDEN, event);
+  ink_drop()->AnimateToState(views::InkDropState::HIDDEN, event);
 }
 
 void ShelfAppButton::OnMenuClosed() {
   DCHECK_EQ(views::InkDropState::ACTIVATED,
-            GetInkDrop()->GetTargetInkDropState());
-  GetInkDrop()->AnimateToState(views::InkDropState::DEACTIVATED);
+            ink_drop()->GetInkDrop()->GetTargetInkDropState());
+  ink_drop()->GetInkDrop()->AnimateToState(views::InkDropState::DEACTIVATED);
 }
 
 void ShelfAppButton::ShowContextMenu(const gfx::Point& p,
@@ -742,7 +742,8 @@
             base::TimeDelta::FromMilliseconds(kInkDropRippleActivationTimeMs),
             base::BindOnce(&ShelfAppButton::OnRippleTimer,
                            base::Unretained(this)));
-        GetInkDrop()->AnimateToState(views::InkDropState::ACTION_PENDING);
+        ink_drop()->GetInkDrop()->AnimateToState(
+            views::InkDropState::ACTION_PENDING);
         event->SetHandled();
       }
       break;
@@ -753,9 +754,10 @@
       // for this ShelfAppButton, don't deactivate the ink drop.
       if (!(state_ & STATE_DRAGGING) &&
           !shelf_view_->IsShowingMenuForView(this) &&
-          (GetInkDrop()->GetTargetInkDropState() ==
+          (ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
            views::InkDropState::ACTIVATED)) {
-        GetInkDrop()->AnimateToState(views::InkDropState::DEACTIVATED);
+        ink_drop()->GetInkDrop()->AnimateToState(
+            views::InkDropState::DEACTIVATED);
       }
       ClearDragStateOnGestureEnd();
       break;
@@ -767,7 +769,7 @@
         // The drag went to the bezel and is about to be passed to
         // ShelfLayoutManager.
         drag_timer_.Stop();
-        GetInkDrop()->AnimateToState(views::InkDropState::HIDDEN);
+        ink_drop()->GetInkDrop()->AnimateToState(views::InkDropState::HIDDEN);
       }
       break;
     case ui::ET_GESTURE_SCROLL_UPDATE:
@@ -785,12 +787,12 @@
       }
       break;
     case ui::ET_GESTURE_LONG_TAP:
-      GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
+      ink_drop()->GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
       // Handle LONG_TAP to avoid opening the context menu twice.
       event->SetHandled();
       break;
     case ui::ET_GESTURE_TWO_FINGER_TAP:
-      GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
+      ink_drop()->GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
       break;
     default:
       break;
@@ -845,11 +847,11 @@
 }
 
 void ShelfAppButton::OnRippleTimer() {
-  if (GetInkDrop()->GetTargetInkDropState() !=
+  if (ink_drop()->GetInkDrop()->GetTargetInkDropState() !=
       views::InkDropState::ACTION_PENDING) {
     return;
   }
-  GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
+  ink_drop()->GetInkDrop()->AnimateToState(views::InkDropState::ACTIVATED);
 }
 
 gfx::Transform ShelfAppButton::GetScaleTransform(float icon_scale) {
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc
index 74423fa..52b33f3 100644
--- a/ash/shelf/shelf_button.cc
+++ b/ash/shelf/shelf_button.cc
@@ -24,14 +24,14 @@
   SetHideInkDropWhenShowingContextMenu(false);
   const AshColorProvider::RippleAttributes ripple_attributes =
       AshColorProvider::Get()->GetRippleAttributes();
-  SetInkDropBaseColor(ripple_attributes.base_color);
-  SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  ink_drop()->SetBaseColor(ripple_attributes.base_color);
+  ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
   SetFocusBehavior(FocusBehavior::ALWAYS);
-  SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
   SetFocusPainter(views::Painter::CreateSolidFocusPainter(
       ShelfConfig::Get()->shelf_focus_border_color(), kFocusBorderThickness,
       gfx::InsetsF()));
-  views::InkDrop::UseInkDropForSquareRipple(this,
+  views::InkDrop::UseInkDropForSquareRipple(ink_drop(),
                                             /*highlight_on_hover=*/false);
 }
 
@@ -76,7 +76,8 @@
 
   Button::NotifyClick(event);
   if (shelf_button_delegate_)
-    shelf_button_delegate_->ButtonPressed(/*sender=*/this, event, GetInkDrop());
+    shelf_button_delegate_->ButtonPressed(/*sender=*/this, event,
+                                          ink_drop()->GetInkDrop());
 }
 
 }  // namespace ash
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 45917da4..d58220e 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -799,7 +799,7 @@
   const ShelfItem* item = ShelfItemForView(source);
   if (!item_awaiting_response_.IsNull()) {
     if (item && item->id != item_awaiting_response_) {
-      static_cast<views::Button*>(source)->AnimateInkDrop(
+      static_cast<views::Button*>(source)->ink_drop()->AnimateToState(
           views::InkDropState::DEACTIVATED, nullptr);
     }
     return;
@@ -2317,8 +2317,10 @@
   if ((source_type == ui::MenuSourceType::MENU_SOURCE_MOUSE ||
        source_type == ui::MenuSourceType::MENU_SOURCE_KEYBOARD) &&
       item) {
-    static_cast<ShelfAppButton*>(source)->GetInkDrop()->AnimateToState(
-        views::InkDropState::ACTIVATED);
+    static_cast<ShelfAppButton*>(source)
+        ->ink_drop()
+        ->GetInkDrop()
+        ->AnimateToState(views::InkDropState::ACTIVATED);
   }
 
   // Only selected shelf items with context menu opened can be dragged.
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 774801f6..2910485 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -2389,9 +2389,10 @@
     home_button_ = GetPrimaryShelf()->navigation_widget()->GetHomeButton();
 
     auto home_button_ink_drop = std::make_unique<InkDropSpy>(
-        views::InkDrop::CreateInkDropWithoutAutoHighlight(home_button_));
+        views::InkDrop::CreateInkDropWithoutAutoHighlight(
+            home_button_->ink_drop()));
     home_button_ink_drop_ = home_button_ink_drop.get();
-    views::test::InkDropHostViewTestApi(home_button_)
+    views::test::InkDropHostTestApi(home_button_->ink_drop())
         .SetInkDrop(std::move(home_button_ink_drop), false);
   }
 
@@ -2399,13 +2400,13 @@
     browser_button_ = test_api_->GetButton(0);
 
     auto ink_drop_impl = std::make_unique<views::InkDropImpl>(
-        browser_button_, browser_button_->size());
+        browser_button_->ink_drop(), browser_button_->size());
     browser_button_ink_drop_impl_ = ink_drop_impl.get();
 
     auto browser_button_ink_drop =
         std::make_unique<InkDropSpy>(std::move(ink_drop_impl));
     browser_button_ink_drop_ = browser_button_ink_drop.get();
-    views::test::InkDropHostViewTestApi(browser_button_)
+    views::test::InkDropHostTestApi(browser_button_->ink_drop())
         .SetInkDrop(std::move(browser_button_ink_drop));
   }
 
diff --git a/ash/system/holding_space/holding_space_tray.cc b/ash/system/holding_space/holding_space_tray.cc
index 30c46b95..282d4cd 100644
--- a/ash/system/holding_space/holding_space_tray.cc
+++ b/ash/system/holding_space/holding_space_tray.cc
@@ -790,13 +790,14 @@
   const views::InkDropState target_ink_drop_state =
       is_drop_target ? views::InkDropState::ACTION_PENDING
                      : views::InkDropState::HIDDEN;
-  if (GetInkDrop()->GetTargetInkDropState() == target_ink_drop_state)
+  if (ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
+      target_ink_drop_state)
     return;
 
   // Do *not* pass in an event as the origin for the ink drop. Since the user is
   // not directly over this view, it would look strange to give the ink drop an
   // out-of-bounds origin.
-  AnimateInkDrop(target_ink_drop_state, /*event=*/nullptr);
+  ink_drop()->AnimateToState(target_ink_drop_state, /*event=*/nullptr);
 }
 
 void HoldingSpaceTray::SetShouldAnimate(bool should_animate) {
diff --git a/ash/system/holding_space/pinned_files_section.cc b/ash/system/holding_space/pinned_files_section.cc
index 06ec925..4b43846 100644
--- a/ash/system/holding_space/pinned_files_section.cc
+++ b/ash/system/holding_space/pinned_files_section.cc
@@ -18,6 +18,7 @@
 #include "ash/system/holding_space/holding_space_item_chip_view.h"
 #include "ash/system/holding_space/holding_space_item_chips_container.h"
 #include "ash/system/holding_space/holding_space_util.h"
+#include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/layer.h"
@@ -91,8 +92,8 @@
     // Ink drop.
     const AshColorProvider::RippleAttributes ripple_attributes =
         ash_color_provider->GetRippleAttributes();
-    SetInkDropBaseColor(ripple_attributes.base_color);
-    SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+    ink_drop()->SetBaseColor(ripple_attributes.base_color);
+    ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
   }
 
   void Init() {
@@ -103,7 +104,7 @@
     SetID(kHoldingSpaceFilesAppChipId);
 
     // Ink drop.
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
                                                   kFilesAppChipHeight / 2);
 
diff --git a/ash/system/ime_menu/ime_list_view.cc b/ash/system/ime_menu/ime_list_view.cc
index 22239c6..e3888b1d 100644
--- a/ash/system/ime_menu/ime_list_view.cc
+++ b/ash/system/ime_menu/ime_list_view.cc
@@ -19,6 +19,7 @@
 #include "ash/system/tray/tray_detailed_view.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/tray/tri_view.h"
+#include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -57,7 +58,7 @@
       : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
         ime_list_view_(list_view),
         selected_(selected) {
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
 
     TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
     AddChildView(tri_view);
diff --git a/ash/system/locale/locale_detailed_view.cc b/ash/system/locale/locale_detailed_view.cc
index 8d2fa12..b69d792 100644
--- a/ash/system/locale/locale_detailed_view.cc
+++ b/ash/system/locale/locale_detailed_view.cc
@@ -46,7 +46,7 @@
       : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
         locale_detailed_view_(locale_detailed_view),
         checked_(checked) {
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
 
     TriView* tri_view = TrayPopupUtils::CreateDefaultRowView();
     AddChildView(tri_view);
diff --git a/ash/system/message_center/stacked_notification_bar.cc b/ash/system/message_center/stacked_notification_bar.cc
index 254a363e..9b5d4c3 100644
--- a/ash/system/message_center/stacked_notification_bar.cc
+++ b/ash/system/message_center/stacked_notification_bar.cc
@@ -50,11 +50,11 @@
     TrayPopupUtils::ConfigureTrayPopupButton(
         this, TrayPopupInkDropStyle::FILL_BOUNDS, /*highlight_on_hover=*/true,
         /*highlight_on_focus=*/true);
-    // SetCreateInkDropHighlightCallback and SetCreateInkDropRippleCallback are
+    // SetCreateHighlightCallback and SetCreateRippleCallback are
     // explicitly called after ConfigureTrayPopupButton as
     // ConfigureTrayPopupButton configures the InkDrop and these callbacks
     // override that behavior.
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](InkDropHostView* host) {
           auto highlight = std::make_unique<views::InkDropHighlight>(
               gfx::SizeF(host->size()), message_center_style::kInkRippleColor);
@@ -63,10 +63,11 @@
           return highlight;
         },
         this));
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
           return std::make_unique<views::FloodFillInkDropRipple>(
-              host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+              host->size(),
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
               message_center_style::kInkRippleColor,
               message_center_style::kInkRippleOpacity);
         },
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index a46f99f..b953b05 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -72,7 +72,7 @@
 }
 
 void OverviewButtonTray::SnapRippleToActivated() {
-  GetInkDrop()->SnapToActivated();
+  ink_drop()->GetInkDrop()->SnapToActivated();
 }
 
 void OverviewButtonTray::OnGestureEvent(ui::GestureEvent* event) {
@@ -130,7 +130,7 @@
         }
       }
 
-      AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+      ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED, nullptr);
       wm::ActivateWindow(new_active_window);
       last_press_event_time_ = base::nullopt;
       return true;
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc
index 95c422d..3729c906 100644
--- a/ash/system/toast/toast_overlay.cc
+++ b/ash/system/toast/toast_overlay.cc
@@ -131,13 +131,13 @@
  public:
   ToastOverlayButton(PressedCallback callback, const std::u16string& text)
       : views::LabelButton(std::move(callback), text, CONTEXT_TOAST_OVERLAY) {
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](InkDropHostView* host) {
           return std::make_unique<views::InkDropHighlight>(
               gfx::SizeF(host->GetLocalBounds().size()),
-              host->GetInkDropBaseColor());
+              host->ink_drop()->GetBaseColor());
         },
         this));
 
@@ -163,7 +163,7 @@
   void OnThemeChanged() override {
     views::LabelButton::OnThemeChanged();
     const auto* color_provider = AshColorProvider::Get();
-    SetInkDropBaseColor(color_provider->GetRippleAttributes().base_color);
+    ink_drop()->SetBaseColor(color_provider->GetRippleAttributes().base_color);
     SetEnabledTextColors(color_provider->GetContentLayerColor(
         AshColorProvider::ContentLayerType::kButtonLabelColorBlue));
   }
diff --git a/ash/system/tray/actionable_view.cc b/ash/system/tray/actionable_view.cc
index c88a4444..76654bf 100644
--- a/ash/system/tray/actionable_view.cc
+++ b/ash/system/tray/actionable_view.cc
@@ -34,12 +34,12 @@
   SetInstallFocusRingOnFocus(false);
   SetFocusPainter(TrayPopupUtils::CreateFocusPainter());
   TrayPopupUtils::InstallHighlightPathGenerator(this, ink_drop_style_);
-  SetCreateInkDropCallback(base::BindRepeating(
+  ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
       [](InkDropHostView* host) { return TrayPopupUtils::CreateInkDrop(host); },
       this));
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       &TrayPopupUtils::CreateInkDropHighlight, base::Unretained(this)));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](ActionableView* host) {
         return TrayPopupUtils::CreateInkDropRipple(host->ink_drop_style_, host);
       },
@@ -53,9 +53,10 @@
 
 void ActionableView::HandlePerformActionResult(bool action_performed,
                                                const ui::Event& event) {
-  AnimateInkDrop(action_performed ? views::InkDropState::ACTION_TRIGGERED
-                                  : views::InkDropState::HIDDEN,
-                 ui::LocatedEvent::FromIfValid(&event));
+  ink_drop()->AnimateToState(action_performed
+                                 ? views::InkDropState::ACTION_TRIGGERED
+                                 : views::InkDropState::HIDDEN,
+                             ui::LocatedEvent::FromIfValid(&event));
 }
 
 const char* ActionableView::GetClassName() const {
diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc
index 880d2bb..13cd0a35 100644
--- a/ash/system/tray/hover_highlight_view.cc
+++ b/ash/system/tray/hover_highlight_view.cc
@@ -28,7 +28,7 @@
 HoverHighlightView::HoverHighlightView(ViewClickListener* listener)
     : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS), listener_(listener) {
   SetNotifyEnterExitOnChild(true);
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
 }
 
 HoverHighlightView::~HoverHighlightView() = default;
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 5e279ee..fc905f08c 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -216,11 +216,11 @@
   SetNotifyEnterExitOnChild(true);
 
   auto ripple_attributes = AshColorProvider::Get()->GetRippleAttributes();
-  SetInkDropBaseColor(ripple_attributes.base_color);
-  SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  ink_drop()->SetBaseColor(ripple_attributes.base_color);
+  ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
 
-  SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](TrayBackgroundView* host) {
         gfx::Rect bounds = host->GetBackgroundBounds();
         // Currently, we don't handle view resize. To compensate for that,
@@ -241,13 +241,13 @@
         return highlight;
       },
       this));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](TrayBackgroundView* host) -> std::unique_ptr<views::InkDropRipple> {
         const AshColorProvider::RippleAttributes ripple_attributes =
             AshColorProvider::Get()->GetRippleAttributes();
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), host->GetBackgroundInsets(),
-            host->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
             ripple_attributes.base_color, ripple_attributes.inkdrop_opacity);
       },
       this));
@@ -636,9 +636,9 @@
   if (is_active_ == is_active)
     return;
   is_active_ = is_active;
-  AnimateInkDrop(is_active_ ? views::InkDropState::ACTIVATED
-                            : views::InkDropState::DEACTIVATED,
-                 nullptr);
+  ink_drop()->AnimateToState(is_active_ ? views::InkDropState::ACTIVATED
+                                        : views::InkDropState::DEACTIVATED,
+                             nullptr);
 }
 
 views::View* TrayBackgroundView::GetBubbleAnchor() const {
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index 088a39cc..58d713e 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -246,13 +246,13 @@
     bool highlight_on_hover,
     bool highlight_on_focus) {
   button->SetInstallFocusRingOnFocus(true);
-  button->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
+  button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   button->SetHasInkDropActionOnClick(true);
-  button->SetCreateInkDropCallback(base::BindRepeating(
+  button->ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
       &CreateInkDrop, button, highlight_on_hover, highlight_on_focus));
-  button->SetCreateInkDropRippleCallback(
+  button->ink_drop()->SetCreateRippleCallback(
       base::BindRepeating(&CreateInkDropRipple, ink_drop_style, button));
-  button->SetCreateInkDropHighlightCallback(
+  button->ink_drop()->SetCreateHighlightCallback(
       base::BindRepeating(&CreateInkDropHighlight, button));
 }
 
@@ -291,7 +291,7 @@
     bool highlight_on_hover,
     bool highlight_on_focus) {
   return views::InkDrop::CreateInkDropForFloodFillRipple(
-      host, highlight_on_hover, highlight_on_focus);
+      host->ink_drop(), highlight_on_hover, highlight_on_focus);
 }
 
 std::unique_ptr<views::InkDropRipple> TrayPopupUtils::CreateInkDropRipple(
@@ -301,8 +301,8 @@
       AshColorProvider::Get()->GetRippleAttributes();
   return std::make_unique<views::FloodFillInkDropRipple>(
       host->size(), GetInkDropInsets(ink_drop_style),
-      host->GetInkDropCenterBasedOnLastEvent(), ripple_attributes.base_color,
-      ripple_attributes.inkdrop_opacity);
+      host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
+      ripple_attributes.base_color, ripple_attributes.inkdrop_opacity);
 }
 
 std::unique_ptr<views::InkDropHighlight> TrayPopupUtils::CreateInkDropHighlight(
diff --git a/ash/system/unified/page_indicator_view.cc b/ash/system/unified/page_indicator_view.cc
index 391fcb0..e849e59b 100644
--- a/ash/system/unified/page_indicator_view.cc
+++ b/ash/system/unified/page_indicator_view.cc
@@ -52,15 +52,15 @@
             base::Unretained(controller),
             page)) {
     SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     views::InstallFixedSizeCircleHighlightPathGenerator(this, kInkDropRadius);
-    SetCreateInkDropCallback(base::BindRepeating(
+    ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
         [](InkDropHostView* host) {
           return TrayPopupUtils::CreateInkDrop(host,
                                                /*highlight_on_hover=*/true);
         },
         this));
-    SetCreateInkDropHighlightCallback(base::BindRepeating(
+    ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
         [](PageIndicatorButton* host) {
           auto highlight = std::make_unique<views::InkDropHighlight>(
               gfx::SizeF(host->size()), host->ripple_base_color_);
@@ -68,7 +68,7 @@
           return highlight;
         },
         this));
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](PageIndicatorButton* host) -> std::unique_ptr<views::InkDropRipple> {
           gfx::Point center = host->GetLocalBounds().CenterPoint();
           gfx::Rect bounds(center.x() - kInkDropRadius,
@@ -76,7 +76,7 @@
                            2 * kInkDropRadius);
           return std::make_unique<views::FloodFillInkDropRipple>(
               host->size(), host->GetLocalBounds().InsetsFrom(bounds),
-              host->GetInkDropCenterBasedOnLastEvent(),
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
               host->ripple_base_color_, host->inkdrop_opacity_);
         },
         this));
@@ -135,7 +135,8 @@
   // views::Button:
   void NotifyClick(const ui::Event& event) override {
     Button::NotifyClick(event);
-    GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
+    ink_drop()->GetInkDrop()->AnimateToState(
+        views::InkDropState::ACTION_TRIGGERED);
   }
 
  private:
diff --git a/ash/system/unified/unified_system_info_view.cc b/ash/system/unified/unified_system_info_view.cc
index 5c93979..ed3d0cb 100644
--- a/ash/system/unified/unified_system_info_view.cc
+++ b/ash/system/unified/unified_system_info_view.cc
@@ -21,6 +21,7 @@
 #include "ash/system/supervised/supervised_icon_string.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_popup_utils.h"
+#include "base/bind.h"
 #include "base/i18n/time_formatting.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -98,7 +99,7 @@
   Shell::Get()->system_tray_model()->clock()->AddObserver(this);
   SetEnabled(Shell::Get()->system_tray_model()->clock()->IsSettingsAvailable());
   SetInstallFocusRingOnFocus(true);
-  SetInkDropMode(views::InkDropHostView::InkDropMode::OFF);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
 }
 
 DateView::~DateView() {
@@ -286,7 +287,7 @@
       gfx::Size(kUnifiedSystemInfoHeight, kUnifiedSystemInfoHeight));
 
   SetInstallFocusRingOnFocus(true);
-  SetInkDropMode(views::InkDropHostView::InkDropMode::OFF);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
 }
 
 // A view that shows whether the device is enterprise managed or not. It updates
diff --git a/ash/touch/touch_observer_hud_unittest.cc b/ash/touch/touch_observer_hud_unittest.cc
index d8a15bb7..f4a4924 100644
--- a/ash/touch/touch_observer_hud_unittest.cc
+++ b/ash/touch/touch_observer_hud_unittest.cc
@@ -490,8 +490,7 @@
 // Test if the WM sets correct work area under different density.
 TEST_F(TouchHudProjectionTest, TouchMoveRelease) {
   SetupSingleDisplay();
-  EXPECT_NE(static_cast<TouchHudProjection*>(nullptr),
-            GetInternalTouchHudProjection());
+  EXPECT_NE(nullptr, GetInternalTouchHudProjection());
   EXPECT_EQ(0, GetInternalTouchPointsCount());
 
   SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
@@ -508,8 +507,7 @@
 // and touch-cancelled events.
 TEST_F(TouchHudProjectionTest, TouchMoveCancel) {
   SetupSingleDisplay();
-  EXPECT_NE(static_cast<TouchHudProjection*>(NULL),
-            GetInternalTouchHudProjection());
+  EXPECT_NE(nullptr, GetInternalTouchHudProjection());
   EXPECT_EQ(0, GetInternalTouchPointsCount());
 
   SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
@@ -525,8 +523,7 @@
 // Checks projection touch HUD with two simultaneous touches.
 TEST_F(TouchHudProjectionTest, DoubleTouch) {
   SetupSingleDisplay();
-  EXPECT_NE(static_cast<TouchHudProjection*>(NULL),
-            GetInternalTouchHudProjection());
+  EXPECT_NE(nullptr, GetInternalTouchHudProjection());
   EXPECT_EQ(0, GetInternalTouchPointsCount());
 
   SendTouchEventToInternalHud(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), 1);
diff --git a/ash/wm/desks/close_desk_button.cc b/ash/wm/desks/close_desk_button.cc
index 2858ba2b..289f81c 100644
--- a/ash/wm/desks/close_desk_button.cc
+++ b/ash/wm/desks/close_desk_button.cc
@@ -28,18 +28,18 @@
   SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
   SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
 
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
-  views::InkDrop::UseInkDropForFloodFillRipple(this);
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  views::InkDrop::UseInkDropForFloodFillRipple(ink_drop());
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](CloseDeskButton* host) {
         auto highlight = std::make_unique<views::InkDropHighlight>(
-            gfx::SizeF(host->size()), host->GetInkDropBaseColor());
+            gfx::SizeF(host->size()), host->ink_drop()->GetBaseColor());
         highlight->set_visible_opacity(host->highlight_opacity_);
         return highlight;
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](CloseDeskButton* host) { return host->inkdrop_base_color_; }, this));
 
   SetFocusPainter(nullptr);
@@ -65,7 +65,7 @@
       color_provider->GetRippleAttributes(background()->get_color());
   highlight_opacity_ = ripple_attributes.highlight_opacity;
   inkdrop_base_color_ = ripple_attributes.base_color;
-  SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+  ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
 }
 
 bool CloseDeskButton::DoesIntersectRect(const views::View* target,
diff --git a/ash/wm/desks/desk_preview_view.cc b/ash/wm/desks/desk_preview_view.cc
index 8988dac..b32dd01 100644
--- a/ash/wm/desks/desk_preview_view.cc
+++ b/ash/wm/desks/desk_preview_view.cc
@@ -303,7 +303,7 @@
   DCHECK(mini_view_);
 
   SetFocusPainter(nullptr);
-  SetInkDropMode(InkDropMode::OFF);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
   SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
 
   SetPaintToLayer(ui::LAYER_TEXTURED);
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 0813c5b..d45e8c2 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -3590,7 +3590,7 @@
   bool IsSearchKeyAcceleratorReserved() const override { return true; }
   bool NotifyDeprecatedRightClickRewrite() override { return false; }
   bool NotifyDeprecatedFKeyRewrite() override { return false; }
-  bool NotifyDeprecatedAltBasedKeyRewrite(ui::KeyboardCode key_code) override {
+  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
     return false;
   }
 
diff --git a/ash/wm/desks/expanded_state_new_desk_button.cc b/ash/wm/desks/expanded_state_new_desk_button.cc
index 9236a04..9ebc84b 100644
--- a/ash/wm/desks/expanded_state_new_desk_button.cc
+++ b/ash/wm/desks/expanded_state_new_desk_button.cc
@@ -79,7 +79,7 @@
     if (!enabled)
       background_color_ = AshColorProvider::GetDisabledColor(background_color_);
 
-    SetInkDropVisibleOpacity(
+    ink_drop()->SetVisibleOpacity(
         color_provider->GetRippleAttributes(background_color_).inkdrop_opacity);
     SchedulePaint();
   }
diff --git a/ash/wm/desks/zero_state_button.cc b/ash/wm/desks/zero_state_button.cc
index b75734c..f7319a35 100644
--- a/ash/wm/desks/zero_state_button.cc
+++ b/ash/wm/desks/zero_state_button.cc
@@ -56,13 +56,13 @@
 
   // Do not show highlight on hover and focus. Since the button will be painted
   // with a background, see |highlight_on_hover_| for more details.
-  views::InkDrop::UseInkDropForFloodFillRipple(this,
+  views::InkDrop::UseInkDropForFloodFillRipple(ink_drop(),
                                                /*highlight_on_hover=*/false,
                                                /*highlight_on_focus=*/false);
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](DeskButtonBase* host) {
         auto highlight = std::make_unique<views::InkDropHighlight>(
-            gfx::SizeF(host->size()), host->GetInkDropBaseColor());
+            gfx::SizeF(host->size()), host->ink_drop()->GetBaseColor());
         highlight->set_visible_opacity(
             AshColorProvider::Get()
                 ->GetRippleAttributes(host->background_color_)
@@ -70,7 +70,7 @@
         return highlight;
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](DeskButtonBase* host) {
         return AshColorProvider::Get()
             ->GetRippleAttributes(host->background_color_)
@@ -78,7 +78,7 @@
       },
       this));
 
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
   SetFocusPainter(nullptr);
   SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
diff --git a/ash/wm/overview/overview_item_view.cc b/ash/wm/overview/overview_item_view.cc
index 75da1c8..3d4eb76 100644
--- a/ash/wm/overview/overview_item_view.cc
+++ b/ash/wm/overview/overview_item_view.cc
@@ -88,8 +88,8 @@
  public:
   explicit OverviewCloseButton(PressedCallback callback)
       : views::ImageButton(std::move(callback)) {
-    SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
-    views::InkDrop::UseInkDropForFloodFillRipple(this);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
+    views::InkDrop::UseInkDropForFloodFillRipple(ink_drop());
     SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
     SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
     SetMinimumImageSize(gfx::Size(kHeaderHeightDp, kHeaderHeightDp));
@@ -118,8 +118,8 @@
              gfx::CreateVectorIcon(kOverviewWindowCloseIcon, color));
 
     const auto ripple_attributes = color_provider->GetRippleAttributes(color);
-    SetInkDropBaseColor(ripple_attributes.base_color);
-    SetInkDropVisibleOpacity(ripple_attributes.inkdrop_opacity);
+    ink_drop()->SetBaseColor(ripple_attributes.base_color);
+    ink_drop()->SetVisibleOpacity(ripple_attributes.inkdrop_opacity);
   }
 };
 
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index 3fc1425..cd12503 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -950,12 +950,15 @@
   PA_DCHECK(IsValidSlotSpan(slot_span));
   auto* root = FromSlotSpan(slot_span);
 
-  // TODO(bikineev): Change the first condition to LIKELY once PCScan is enabled
-  // by default.
-  if (UNLIKELY(root->IsQuarantineEnabled()) &&
-      LIKELY(!slot_span->bucket->is_direct_mapped())) {
-    PCScan::MoveToQuarantine(ptr, slot_span->bucket->slot_size);
-    return;
+  // TODO(bikineev): Change the condition to LIKELY once PCScan is enabled by
+  // default.
+  if (UNLIKELY(root->IsQuarantineEnabled())) {
+    // PCScan safepoint. Call before potentially scheduling scanning task.
+    PCScan::JoinScanIfNeeded();
+    if (LIKELY(!slot_span->bucket->is_direct_mapped())) {
+      PCScan::MoveToQuarantine(ptr, slot_span->bucket->slot_size);
+      return;
+    }
   }
 
   root->FreeNoHooksImmediate(ptr, slot_span);
diff --git a/base/i18n/break_iterator_unittest.cc b/base/i18n/break_iterator_unittest.cc
index a236451..f91ea69b 100644
--- a/base/i18n/break_iterator_unittest.cc
+++ b/base/i18n/break_iterator_unittest.cc
@@ -95,10 +95,7 @@
 }
 
 TEST(BreakIteratorTest, BreakWordWide32) {
-  // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
-  const char very_wide_char[] = "\xF0\x9D\x92\x9C";
-  const std::u16string str(
-      UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char)));
+  const std::u16string str = u"\U0001d49c a";
   const std::u16string very_wide_word(str.substr(0, 2));
 
   BreakIterator iter(str, BreakIterator::BREAK_WORD);
@@ -120,23 +117,22 @@
 
 TEST(BreakIteratorTest, BreakWordThai) {
   // Terms in Thai, without spaces in between.
-  const char term1[] = "พิมพ์";
-  const char term2[] = "น้อย";
-  const char term3[] = "ลง";
-  const std::u16string str(
-      UTF8ToUTF16(base::JoinString({term1, term2, term3}, "")));
+  const char16_t term1[] = u"พิมพ์";
+  const char16_t term2[] = u"น้อย";
+  const char16_t term3[] = u"ลง";
+  const std::u16string str(base::JoinString({term1, term2, term3}, u""));
 
   BreakIterator iter(str, BreakIterator::BREAK_WORD);
   ASSERT_TRUE(iter.Init());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString());
+  EXPECT_EQ(term1, iter.GetString());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString());
+  EXPECT_EQ(term2, iter.GetString());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term3), iter.GetString());
+  EXPECT_EQ(term3, iter.GetString());
   EXPECT_FALSE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
 }
@@ -149,41 +145,40 @@
 
 TEST(BreakIteratorTest, BreakWordChinese) {
   // Terms in Traditional Chinese, without spaces in between.
-  const char term1[] = "瀏覽";
-  const char term2[] = "速度";
-  const char term3[] = "飛快";
-  const std::u16string str(
-      UTF8ToUTF16(base::JoinString({term1, term2, term3}, "")));
+  const char16_t term1[] = u"瀏覽";
+  const char16_t term2[] = u"速度";
+  const char16_t term3[] = u"飛快";
+  const std::u16string str(base::JoinString({term1, term2, term3}, u""));
 
   BreakIterator iter(str, BreakIterator::BREAK_WORD);
   ASSERT_TRUE(iter.Init());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString());
+  EXPECT_EQ(term1, iter.GetString());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString());
+  EXPECT_EQ(term2, iter.GetString());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term3), iter.GetString());
+  EXPECT_EQ(term3, iter.GetString());
   EXPECT_FALSE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
 }
 
 TEST(BreakIteratorTest, BreakWordJapanese) {
   // Terms in Japanese, without spaces in between.
-  const char term1[] = "モバイル";
-  const char term2[] = "でも";
-  const std::u16string str(UTF8ToUTF16(base::JoinString({term1, term2}, "")));
+  const char16_t term1[] = u"モバイル";
+  const char16_t term2[] = u"でも";
+  const std::u16string str(base::JoinString({term1, term2}, u""));
 
   BreakIterator iter(str, BreakIterator::BREAK_WORD);
   ASSERT_TRUE(iter.Init());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term1), iter.GetString());
+  EXPECT_EQ(term1, iter.GetString());
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(term2), iter.GetString());
+  EXPECT_EQ(term2, iter.GetString());
   EXPECT_FALSE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
 }
@@ -191,21 +186,21 @@
 TEST(BreakIteratorTest, BreakWordChineseEnglish) {
   // Terms in Simplified Chinese mixed with English and wide punctuations.
   std::u16string space(u" ");
-  const char token1[] = "下载";
-  const char token2[] = "Chrome";
-  const char token3[] = "(";
-  const char token4[] = "Mac";
-  const char token5[] = "版";
-  const char token6[] = ")";
-  const std::u16string str(UTF8ToUTF16(base::JoinString(
-      {token1, " ", token2, token3, token4, " ", token5, token6}, "")));
+  const char16_t token1[] = u"下载";
+  const char16_t token2[] = u"Chrome";
+  const char16_t token3[] = u"(";
+  const char16_t token4[] = u"Mac";
+  const char16_t token5[] = u"版";
+  const char16_t token6[] = u")";
+  const std::u16string str(base::JoinString(
+      {token1, u" ", token2, token3, token4, u" ", token5, token6}, u""));
 
   BreakIterator iter(str, BreakIterator::BREAK_WORD);
   ASSERT_TRUE(iter.Init());
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(token1), iter.GetString());
+  EXPECT_EQ(token1, iter.GetString());
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
@@ -213,15 +208,15 @@
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(token2), iter.GetString());
+  EXPECT_EQ(token2, iter.GetString());
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(token3), iter.GetString());
+  EXPECT_EQ(token3, iter.GetString());
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(token4), iter.GetString());
+  EXPECT_EQ(token4, iter.GetString());
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
@@ -229,11 +224,11 @@
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_TRUE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(token5), iter.GetString());
+  EXPECT_EQ(token5, iter.GetString());
 
   EXPECT_TRUE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
-  EXPECT_EQ(UTF8ToUTF16(token6), iter.GetString());
+  EXPECT_EQ(token6, iter.GetString());
 
   EXPECT_FALSE(iter.Advance());
   EXPECT_FALSE(iter.IsWord());
@@ -323,10 +318,7 @@
 }
 
 TEST(BreakIteratorTest, BreakSpaceWide32) {
-  // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
-  const char very_wide_char[] = "\xF0\x9D\x92\x9C";
-  const std::u16string str(
-      UTF8ToUTF16(base::StringPrintf("%s a", very_wide_char)));
+  const std::u16string str = u"\U0001d49c a";
   const std::u16string very_wide_word(str.substr(0, 3));
 
   BreakIterator iter(str, BreakIterator::BREAK_SPACE);
@@ -378,8 +370,8 @@
 
 TEST(BreakIteratorTest, BreakSentence) {
   std::u16string nl(u"\n");
-  std::u16string str(UTF8ToUTF16(
-      "\nFoo bar!\nOne sentence.\n\n\tAnother sentence?One more thing"));
+  std::u16string str(
+      u"\nFoo bar!\nOne sentence.\n\n\tAnother sentence?One more thing");
   BreakIterator iter(str, BreakIterator::BREAK_SENTENCE);
   ASSERT_TRUE(iter.Init());
   EXPECT_TRUE(iter.Advance());
@@ -405,8 +397,8 @@
 }
 
 TEST(BreakIteratorTest, IsSentenceBoundary) {
-  std::u16string str(UTF8ToUTF16(
-      "Foo bar!\nOne sentence.\n\n\tAnother sentence?One more thing"));
+  std::u16string str(
+      u"Foo bar!\nOne sentence.\n\n\tAnother sentence?One more thing");
   BreakIterator iter(str, BreakIterator::BREAK_SENTENCE);
   ASSERT_TRUE(iter.Init());
 
@@ -470,10 +462,7 @@
 }
 
 TEST(BreakIteratorTest, BreakLineWide32) {
-  // U+1D49C MATHEMATICAL SCRIPT CAPITAL A
-  const char very_wide_char[] = "\xF0\x9D\x92\x9C";
-  const std::u16string str(
-      UTF8ToUTF16(base::StringPrintf("%s\na", very_wide_char)));
+  const std::u16string str = u"\U0001d49c\na";
   const std::u16string very_wide_line(str.substr(0, 3));
   BreakIterator iter(str, BreakIterator::BREAK_NEWLINE);
   ASSERT_TRUE(iter.Init());
@@ -490,29 +479,29 @@
 }
 
 TEST(BreakIteratorTest, BreakCharacter) {
-  static const char* kCharacters[] = {
+  static const char16_t* const kCharacters[] = {
       // An English word consisting of four ASCII characters.
-      "w",
-      "o",
-      "r",
-      "d",
-      " ",
+      u"w",
+      u"o",
+      u"r",
+      u"d",
+      u" ",
       // A Hindi word (which means "Hindi") consisting of two Devanagari
       // grapheme clusters.
-      "\u0939\u093F",
-      "\u0928\u094D\u0926\u0940",
-      " ",
+      u"हि",
+      u"न्दी",
+      u" ",
       // A Thai word (which means "feel") consisting of three Thai grapheme
       // clusters.
-      "\u0E23\u0E39\u0E49",
-      "\u0E2A\u0E36",
-      "\u0E01",
-      " ",
+      u"รู้",
+      u"สึ",
+      u"ก",
+      u" ",
   };
   std::vector<std::u16string> characters;
   std::u16string text;
-  for (auto*& i : kCharacters) {
-    characters.push_back(base::UTF8ToUTF16(i));
+  for (const auto* i : kCharacters) {
+    characters.push_back(i);
     text.append(characters.back());
   }
   BreakIterator iter(text, BreakIterator::BREAK_CHARACTER);
diff --git a/base/strings/string_piece_unittest.cc b/base/strings/string_piece_unittest.cc
index 5cb60aa..e7d46413 100644
--- a/base/strings/string_piece_unittest.cc
+++ b/base/strings/string_piece_unittest.cc
@@ -633,12 +633,11 @@
   ASSERT_EQ(f.size(), 6U);
 }
 
-
-
 TEST(StringPiece16Test, CheckConversion) {
-  // Make sure that we can convert from UTF8 to UTF16 and back. We use a two
-  // byte character (G clef) to test this.
-  ASSERT_EQ(UTF16ToUTF8(UTF8ToUTF16("\xf0\x9d\x84\x9e")), "\xf0\x9d\x84\x9e");
+  // Make sure that we can convert from UTF8 to UTF16 and back. We use a
+  // character (G clef) outside the BMP to test this.
+  const char kTest[] = "\U0001D11E";
+  ASSERT_EQ(UTF16ToUTF8(UTF8ToUTF16(kTest)), kTest);
 }
 
 TYPED_TEST(CommonStringPieceTest, CheckConstructors) {
diff --git a/build/toolchain/apple/toolchain.gni b/build/toolchain/apple/toolchain.gni
index 7210cc20f..80fa7b3 100644
--- a/build/toolchain/apple/toolchain.gni
+++ b/build/toolchain/apple/toolchain.gni
@@ -447,9 +447,9 @@
       # hardlink but ensure the file have distinct metadata (thus avoid the
       # error with ditto, see https://crbug.com/1042182).
       if (host_os == "mac") {
-        command = "rm -rf {{output}} && cp -Rc {{source}} {{output}}"
+        command = "rm -rf {{output}} && /bin/cp -Rc {{source}} {{output}}"
       } else {
-        command = "rm -rf {{output}} && cp -Rl {{source}} {{output}}"
+        command = "rm -rf {{output}} && /bin/cp -Rl {{source}} {{output}}"
       }
       description = "COPY_BUNDLE_DATA {{source}} {{output}}"
       pool = "//build/toolchain/apple:bundle_pool($default_toolchain)"
diff --git a/cc/animation/keyframe_effect.cc b/cc/animation/keyframe_effect.cc
index 9b34b0c..92a9472 100644
--- a/cc/animation/keyframe_effect.cc
+++ b/cc/animation/keyframe_effect.cc
@@ -16,9 +16,9 @@
 #include "cc/animation/animation_timeline.h"
 #include "cc/animation/scroll_offset_animation_curve.h"
 #include "cc/trees/property_animation_state.h"
-#include "ui/gfx//transform_operations.h"
 #include "ui/gfx/animation/keyframe/animation_curve.h"
 #include "ui/gfx/animation/keyframe/target_property.h"
+#include "ui/gfx/transform_operations.h"
 
 namespace cc {
 
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 1219a51..7879d56d 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1468,11 +1468,6 @@
     "//chrome/browser/resources:resources",
     "//components/autofill/core/browser:autofill_address_rewriter_resources",
   ]
-
-  if (is_chromeos_ash && !enable_use_media_app_ink) {
-    # TODO(crbug/1150244): Remove when enable_use_media_app_ink is default true.
-    public_deps += [ "//third_party/ink:ink_resources" ]
-  }
 }
 
 if (is_chrome_branded && !is_android) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
index 2822477..a1a1c8c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
@@ -1426,6 +1426,10 @@
             cleanUpPersistentData();
             onStateLoaded();
             mTabLoader = null;
+            RecordHistogram.recordCountHistogram(
+                    "Tabs.Startup.TabCount.Regular", mTabModelSelector.getModel(false).getCount());
+            RecordHistogram.recordCountHistogram(
+                    "Tabs.Startup.TabCount.Incognito", mTabModelSelector.getModel(true).getCount());
             Log.i(TAG,
                     "Loaded tab lists; counts: " + mTabModelSelector.getModel(false).getCount()
                             + "," + mTabModelSelector.getModel(true).getCount());
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index 9b6d999..846fb7af 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -122,6 +122,7 @@
 #define IDC_OFFERS_AND_REWARDS_FOR_PAGE 35026
 #define IDC_WEBAUTHN                    35027
 #define IDC_SHARING_HUB                 35028
+#define IDC_VIRTUAL_CARD_MANUAL_FALLBACK 35029
 
 // Page-manipulation commands that target a specified tab, which may not be the
 // active one.
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index f659159..ab39e8d 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -337,7 +337,7 @@
     Activation code detected
   </message>
   <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_USE_CAMERA_AGAIN" desc="Label for button informing the user to click to use the camera to rescan for QR codes.">
-    use camera again
+    Use camera again
   </message>
   <message name="IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_ERROR" desc="Label informing the user that there was an error detecting activation code.">
     Error detecting code
diff --git a/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_USE_CAMERA_AGAIN.png.sha1 b/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_USE_CAMERA_AGAIN.png.sha1
index bd84a3ad..fad07d1 100644
--- a/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_USE_CAMERA_AGAIN.png.sha1
+++ b/chrome/app/chromeos_strings_grdp/IDS_CELLULAR_SETUP_ESIM_PAGE_SCAN_QR_CODE_USE_CAMERA_AGAIN.png.sha1
@@ -1 +1 @@
-770be185e26dd97739b50680a9ea92f977e7b1e2
\ No newline at end of file
+3ca968f6f61bfaeb331f150257e6adea3c975d30
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 4cd3355..32b8723 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -3126,12 +3126,30 @@
   <message name="IDS_SETTINGS_FACTORY_RESET_WARNING" desc="Warning text in the 'Factory Reset' window">
     A restart is required before your device can be reset with Powerwash. <ph name="LINK_BEGIN">&lt;a&gt;</ph>Learn more<ph name="LINK_END">&lt;/a&gt;</ph>
   </message>
+  <message name="IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_TITLE" desc="Name of the 'Factory reset' window">
+    Remove eSIM profiles before Powerwash
+  </message>
+  <message name="IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING" desc="Warning text in the 'Factory Reset' window informing the user that the eSIM profiles they have installed won't be removed by a factory reset. Contains a url to the mobile data subpage.">
+    Powerwashing your device won't remove your eSIM profiles. Go to <ph name="LINK_BEGIN">&lt;a&gt;</ph>Mobile Settings<ph name="LINK_END">&lt;/a&gt;</ph> to manually remove these profiles.
+  </message>
+  <message name="IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_TITLE" desc="Title for the list showing installed eSIM profiles in the 'Factory Reset' window.">
+    Active profiles
+  </message>
+  <message name="IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_ITEM_TITLE" desc="Label for an eSIM profile shown in the 'Factory Reset' window displaying its name and provider.">
+    <ph name="NETWORK_NAME">$1<ex>My Verizon eSIM</ex></ph> - <ph name="SPAN_START">&lt;span id="providerName"&gt;</ph><ph name="CARRIER_NAME">$2<ex>Verizon Wireless</ex></ph><ph name="SPAN_END">&lt;/span&gt;</ph>
+  </message>
+  <message name="IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_CHECKBOX_LABEL" desc="Label for checkbox in the 'Factory Reset' window that confirms that the user is aware that the eSIM profiles they have installed won't be removed by a factory reset.">
+    I understand that installed eSIM profiles will not be removed by Powerwash
+  </message>
   <message name="IDS_SETTINGS_FACTORY_RESET_BUTTON_LABEL" desc="Label for the factory reset button.">
     Reset
   </message>
   <message name="IDS_SETTINGS_FACTORY_RESET_BUTTON_ROLE" desc="A label to be read aloud by screen readers when the Reset button is focused. This button initiates the device factory reset flow.">
     Reset Button
   </message>
+  <message name="IDS_SETTINGS_FACTORY_CONTINUE_BUTTON_LABEL" desc="Label for the button that navigates from the eSIM warning to the 'Factory Reset' UI.">
+    Continue
+  </message>
 
   <!-- Google Assistant Page (OS Settings)-->
   <message name="IDS_SETTINGS_GOOGLE_ASSISTANT" desc="Name of the settings page for Google Assistant.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_CONTINUE_BUTTON_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_CONTINUE_BUTTON_LABEL.png.sha1
new file mode 100644
index 0000000..1a6d28f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_CONTINUE_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@
+058df431bbd66f8555111b31273a30386c05910c
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_ITEM_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_ITEM_TITLE.png.sha1
new file mode 100644
index 0000000..1a6d28f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_ITEM_TITLE.png.sha1
@@ -0,0 +1 @@
+058df431bbd66f8555111b31273a30386c05910c
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_TITLE.png.sha1
new file mode 100644
index 0000000..1a6d28f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_TITLE.png.sha1
@@ -0,0 +1 @@
+058df431bbd66f8555111b31273a30386c05910c
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING.png.sha1
new file mode 100644
index 0000000..1a6d28f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING.png.sha1
@@ -0,0 +1 @@
+058df431bbd66f8555111b31273a30386c05910c
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_CHECKBOX_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_CHECKBOX_LABEL.png.sha1
new file mode 100644
index 0000000..1a6d28f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_CHECKBOX_LABEL.png.sha1
@@ -0,0 +1 @@
+058df431bbd66f8555111b31273a30386c05910c
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_TITLE.png.sha1
new file mode 100644
index 0000000..1a6d28f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_TITLE.png.sha1
@@ -0,0 +1 @@
+058df431bbd66f8555111b31273a30386c05910c
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 32b17237f..580dd0e 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4688,6 +4688,8 @@
     sources += [
       "apps/app_service/fake_lacros_web_apps_host.cc",
       "apps/app_service/fake_lacros_web_apps_host.h",
+      "apps/app_service/web_apps_publisher_host.cc",
+      "apps/app_service/web_apps_publisher_host.h",
       "chrome_browser_main_parts_lacros.cc",
       "chrome_browser_main_parts_lacros.h",
       "feedback/show_feedback_page_lacros.cc",
@@ -5571,8 +5573,8 @@
     }
     if (is_win || is_mac || is_linux || is_chromeos) {
       sources += [
-        "printing/print_backend_service.cc",
-        "printing/print_backend_service.h",
+        "printing/print_backend_service_manager.cc",
+        "printing/print_backend_service_manager.h",
       ]
       deps += [ "//chrome/services/printing/public/mojom" ]
     }
diff --git a/chrome/browser/apps/app_service/app_service_proxy_desktop.cc b/chrome/browser/apps/app_service/app_service_proxy_desktop.cc
index 62d9c76..6763f22 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_desktop.cc
+++ b/chrome/browser/apps/app_service/app_service_proxy_desktop.cc
@@ -10,10 +10,17 @@
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/browser/apps/app_service/fake_lacros_web_apps_host.h"
+#include "chrome/browser/apps/app_service/web_apps_publisher_host.h"
 #endif
 
 namespace apps {
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// TODO(crbug.com/1144877): Remove after the actual lacros web app host code
+// completed.
+const bool kUseFakeWebAppsHost = false;
+#endif
+
 AppServiceProxy::AppServiceProxy(Profile* profile)
     : AppServiceProxyBase(profile) {
   Initialize();
@@ -35,14 +42,16 @@
   web_apps_ = std::make_unique<WebApps>(app_service_, profile_);
   extension_apps_ = std::make_unique<ExtensionApps>(app_service_, profile_);
 
-// Create a fake lacros web app host in the lacros-chrome for testing lacros
-// web app publishing. This will be removed after the actual lacros web app host
-// code is created.
-// TODO(crbug.com/1144877): Remove after the actual lacros web app host code
-// created.
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  fake_lacros_web_apps_host_ = std::make_unique<FakeLacrosWebAppsHost>();
-  fake_lacros_web_apps_host_->Init();
+  if (kUseFakeWebAppsHost) {
+    // Create a fake lacros web app host in the lacros-chrome for testing lacros
+    // web app publishing. This will be removed after the actual lacros web app
+    // host code is created.
+    fake_lacros_web_apps_host_ = std::make_unique<FakeLacrosWebAppsHost>();
+    fake_lacros_web_apps_host_->Init();
+  } else {
+    web_apps_publisher_host_ = std::make_unique<WebAppsPublisherHost>(profile_);
+  }
 #endif
 
   // Asynchronously add app icon source, so we don't do too much work in the
diff --git a/chrome/browser/apps/app_service/app_service_proxy_desktop.h b/chrome/browser/apps/app_service/app_service_proxy_desktop.h
index bd9028a..d25eae1f 100644
--- a/chrome/browser/apps/app_service/app_service_proxy_desktop.h
+++ b/chrome/browser/apps/app_service/app_service_proxy_desktop.h
@@ -22,6 +22,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 class FakeLacrosWebAppsHost;
+class WebAppsPublisherHost;
 #endif
 
 // Singleton (per Profile) proxy and cache of an App Service's apps in Chrome
@@ -50,7 +51,8 @@
   std::unique_ptr<ExtensionApps> extension_apps_;
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  std::unique_ptr<apps::FakeLacrosWebAppsHost> fake_lacros_web_apps_host_;
+  std::unique_ptr<FakeLacrosWebAppsHost> fake_lacros_web_apps_host_;
+  std::unique_ptr<WebAppsPublisherHost> web_apps_publisher_host_;
 #endif
 
   base::WeakPtrFactory<AppServiceProxy> weak_ptr_factory_{this};
diff --git a/chrome/browser/apps/app_service/publishers/web_apps_base.cc b/chrome/browser/apps/app_service/publishers/web_apps_base.cc
index 24082e3..f47cf92 100644
--- a/chrome/browser/apps/app_service/publishers/web_apps_base.cc
+++ b/chrome/browser/apps/app_service/publishers/web_apps_base.cc
@@ -90,16 +90,8 @@
     return;
   }
 
-  // Construct an App with only the information required to identify an
-  // uninstallation.
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_type = app_type_;
-  app->app_id = web_app->app_id();
-  // TODO(loyso): Plumb uninstall source (reason) here.
-  app->readiness = apps::mojom::Readiness::kUninstalledByUser;
-
-  apps_util::SetWebAppShowInFields(app, web_app);
-  Publish(std::move(app), subscribers_);
+  Publish(apps_util::ConvertUninstalledWebApp(web_app, app_type_),
+          subscribers_);
 }
 
 IconEffects WebAppsBase::GetIconEffects(const web_app::WebApp* web_app) {
diff --git a/chrome/browser/apps/app_service/web_apps_publisher_host.cc b/chrome/browser/apps/app_service/web_apps_publisher_host.cc
new file mode 100644
index 0000000..d9c6ea8
--- /dev/null
+++ b/chrome/browser/apps/app_service/web_apps_publisher_host.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/apps/app_service/web_apps_publisher_host.h"
+
+#include "base/bind.h"
+#include "base/one_shot_event.h"
+#include "chrome/browser/apps/app_service/web_apps_utils.h"
+#include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
+#include "chromeos/lacros/lacros_service.h"
+
+namespace apps {
+
+// static
+crosapi::mojom::AppPublisher* WebAppsPublisherHost::publisher_for_testing_ =
+    nullptr;
+
+WebAppsPublisherHost::WebAppsPublisherHost(Profile* profile)
+    : profile_(profile), provider_(web_app::WebAppProvider::Get(profile)) {
+  // Allow for web app migration tests.
+  if (!provider_->registrar().AsWebAppRegistrar())
+    return;
+
+  provider_->on_registry_ready().Post(
+      FROM_HERE, base::BindOnce(&WebAppsPublisherHost::OnReady,
+                                weak_ptr_factory_.GetWeakPtr()));
+  registrar_observation_.Observe(&registrar());
+}
+
+WebAppsPublisherHost::~WebAppsPublisherHost() = default;
+
+web_app::WebAppRegistrar& WebAppsPublisherHost::registrar() const {
+  return *provider_->registrar().AsWebAppRegistrar();
+}
+
+crosapi::mojom::AppPublisher* WebAppsPublisherHost::GetPublisher() const {
+  if (publisher_for_testing_) {
+    return publisher_for_testing_;
+  }
+  auto* service = chromeos::LacrosService::Get();
+  if (!service) {
+    return nullptr;
+  }
+  if (!service->IsAvailable<crosapi::mojom::AppPublisher>()) {
+    return nullptr;
+  }
+  if (!service->init_params()->web_apps_enabled) {
+    return nullptr;
+  }
+  return service->GetRemote<crosapi::mojom::AppPublisher>().get();
+}
+
+// static
+void WebAppsPublisherHost::SetPublisherForTesting(
+    crosapi::mojom::AppPublisher* publisher) {
+  publisher_for_testing_ = publisher;
+}
+
+void WebAppsPublisherHost::OnReady() {
+  crosapi::mojom::AppPublisher* const publisher = GetPublisher();
+  if (!publisher || !registrar_observation_.IsObserving()) {
+    return;
+  }
+
+  std::vector<apps::mojom::AppPtr> apps;
+  for (const web_app::WebApp& web_app : registrar().GetApps()) {
+    apps.push_back(Convert(&web_app, apps::mojom::Readiness::kReady));
+  }
+  publisher->OnApps(std::move(apps));
+}
+
+// web_app::AppRegistrarObserver:
+void WebAppsPublisherHost::OnWebAppInstalled(const web_app::AppId& app_id) {
+  const web_app::WebApp* web_app = GetWebApp(app_id);
+  if (!web_app) {
+    return;
+  }
+
+  Publish(Convert(web_app, apps::mojom::Readiness::kReady));
+}
+
+void WebAppsPublisherHost::OnWebAppWillBeUninstalled(
+    const web_app::AppId& app_id) {
+  const web_app::WebApp* web_app = GetWebApp(app_id);
+  if (!web_app) {
+    return;
+  }
+
+  Publish(
+      apps_util::ConvertUninstalledWebApp(web_app, apps::mojom::AppType::kWeb));
+}
+
+void WebAppsPublisherHost::OnAppRegistrarDestroyed() {
+  registrar_observation_.Reset();
+}
+
+const web_app::WebApp* WebAppsPublisherHost::GetWebApp(
+    const web_app::AppId& app_id) const {
+  return registrar().GetAppById(app_id);
+}
+
+apps::mojom::AppPtr WebAppsPublisherHost::Convert(
+    const web_app::WebApp* web_app,
+    apps::mojom::Readiness readiness) const {
+  return apps_util::ConvertWebApp(profile_, web_app, apps::mojom::AppType::kWeb,
+                                  readiness);
+}
+
+void WebAppsPublisherHost::Publish(apps::mojom::AppPtr app) {
+  crosapi::mojom::AppPublisher* const publisher = GetPublisher();
+  if (!publisher) {
+    return;
+  }
+
+  std::vector<apps::mojom::AppPtr> apps;
+  apps.push_back(std::move(app));
+  publisher->OnApps(std::move(apps));
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/web_apps_publisher_host.h b/chrome/browser/apps/app_service/web_apps_publisher_host.h
new file mode 100644
index 0000000..8b44d945
--- /dev/null
+++ b/chrome/browser/apps/app_service/web_apps_publisher_host.h
@@ -0,0 +1,81 @@
+// 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_APPS_APP_SERVICE_WEB_APPS_PUBLISHER_HOST_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_WEB_APPS_PUBLISHER_HOST_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/web_applications/components/app_registrar.h"
+#include "chrome/browser/web_applications/components/app_registrar_observer.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chromeos/crosapi/mojom/app_service.mojom.h"
+#include "components/services/app_service/public/mojom/types.mojom.h"
+
+class Profile;
+
+namespace web_app {
+class WebApp;
+class WebAppProvider;
+class WebAppRegistrar;
+}  // namespace web_app
+
+namespace apps {
+
+// This WebAppsPublisherHost observes AppRegistrar on Lacros, and calls
+// WebAppsCrosapi to inform the Ash browser of the current set of web apps.
+class WebAppsPublisherHost : public web_app::AppRegistrarObserver {
+ public:
+  explicit WebAppsPublisherHost(Profile* profile);
+  WebAppsPublisherHost(const WebAppsPublisherHost&) = delete;
+  WebAppsPublisherHost& operator=(const WebAppsPublisherHost&) = delete;
+  ~WebAppsPublisherHost() override;
+
+  web_app::WebAppRegistrar& registrar() const;
+
+  crosapi::mojom::AppPublisher* GetPublisher() const;
+
+  static void SetPublisherForTesting(crosapi::mojom::AppPublisher* publisher);
+
+ private:
+  void OnReady();
+
+  // web_app::AppRegistrarObserver:
+  void OnWebAppInstalled(const web_app::AppId& app_id) override;
+  void OnWebAppWillBeUninstalled(const web_app::AppId& app_id) override;
+  void OnAppRegistrarDestroyed() override;
+
+  // TODO(crbug.com/1194709): override
+  // - OnWebAppLastLaunchTimeChanged
+  // - OnWebAppLocallyInstalledStateChanged
+  // - OnWebAppManifestUpdated
+
+  // TODO(crbug.com/1194709): inherit from content_settings::Observer and
+  // override:
+  // - OnContentSettingChanged
+
+  // TODO(crbug.com/1194709): Add more overrides, guided by WebAppsChromeOs.
+
+  const web_app::WebApp* GetWebApp(const web_app::AppId& app_id) const;
+  apps::mojom::AppPtr Convert(const web_app::WebApp* web_app,
+                              apps::mojom::Readiness readiness) const;
+  void Publish(apps::mojom::AppPtr app);
+
+  static crosapi::mojom::AppPublisher* publisher_for_testing_;
+
+  Profile* const profile_;
+  web_app::WebAppProvider* const provider_;
+
+  base::ScopedObservation<web_app::AppRegistrar, web_app::AppRegistrarObserver>
+      registrar_observation_{this};
+
+  base::WeakPtrFactory<WebAppsPublisherHost> weak_ptr_factory_{this};
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_WEB_APPS_PUBLISHER_HOST_H_
diff --git a/chrome/browser/apps/app_service/web_apps_publisher_host_browsertest.cc b/chrome/browser/apps/app_service/web_apps_publisher_host_browsertest.cc
new file mode 100644
index 0000000..2b9c086
--- /dev/null
+++ b/chrome/browser/apps/app_service/web_apps_publisher_host_browsertest.cc
@@ -0,0 +1,87 @@
+// 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/apps/app_service/web_apps_publisher_host.h"
+
+#include <iterator>
+#include <vector>
+
+#include "base/run_loop.h"
+#include "base/test/bind.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
+#include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/browser/web_applications/components/web_app_id.h"
+#include "chromeos/crosapi/mojom/app_service.mojom.h"
+#include "content/public/test/browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+namespace apps {
+
+class MockAppPublisher : public crosapi::mojom::AppPublisher {
+ public:
+  MockAppPublisher() { run_loop_ = std::make_unique<base::RunLoop>(); }
+  ~MockAppPublisher() override = default;
+
+  void Wait() {
+    run_loop_->Run();
+    run_loop_ = std::make_unique<base::RunLoop>();
+  }
+
+  const std::vector<apps::mojom::AppPtr>& get_deltas() const { return deltas_; }
+
+ private:
+  // crosapi::mojom::AppPublisher:
+  void OnApps(std::vector<apps::mojom::AppPtr> deltas) override {
+    deltas_.insert(deltas_.end(), std::make_move_iterator(deltas.begin()),
+                   std::make_move_iterator(deltas.end()));
+    run_loop_->Quit();
+  }
+
+  std::vector<apps::mojom::AppPtr> deltas_;
+  std::unique_ptr<base::RunLoop> run_loop_;
+};
+
+using WebAppsPublisherHostBrowserTest = web_app::WebAppControllerBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, PublishApps) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  web_app::InstallWebAppFromManifest(
+      browser(), embedded_test_server()->GetURL("/web_apps/basic.html"));
+  web_app::InstallWebAppFromManifest(
+      browser(),
+      embedded_test_server()->GetURL("/web_share_target/charts.html"));
+  MockAppPublisher mock_app_publisher;
+  WebAppsPublisherHost::SetPublisherForTesting(&mock_app_publisher);
+
+  WebAppsPublisherHost web_apps_publisher_host(profile());
+  mock_app_publisher.Wait();
+  EXPECT_EQ(mock_app_publisher.get_deltas().size(), 2U);
+
+  web_app::AppId app_id = web_app::InstallWebAppFromManifest(
+      browser(),
+      embedded_test_server()->GetURL("/banners/manifest_test_page.html"));
+  mock_app_publisher.Wait();
+  // We may receive more than one delta for the new app.
+  EXPECT_GE(mock_app_publisher.get_deltas().size(), 3U);
+  EXPECT_EQ(mock_app_publisher.get_deltas().back()->app_id, app_id);
+  EXPECT_EQ(mock_app_publisher.get_deltas().back()->readiness,
+            apps::mojom::Readiness::kReady);
+
+  {
+    base::RunLoop run_loop;
+    web_app::UninstallWebAppWithCallback(
+        profile(), app_id,
+        base::BindLambdaForTesting([&run_loop](bool uninstalled) {
+          EXPECT_TRUE(uninstalled);
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    mock_app_publisher.Wait();
+    EXPECT_EQ(mock_app_publisher.get_deltas().back()->app_id, app_id);
+    EXPECT_EQ(mock_app_publisher.get_deltas().back()->readiness,
+              apps::mojom::Readiness::kUninstalledByUser);
+  }
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/web_apps_utils.cc b/chrome/browser/apps/app_service/web_apps_utils.cc
index 267bf35..351eab9 100644
--- a/chrome/browser/apps/app_service/web_apps_utils.cc
+++ b/chrome/browser/apps/app_service/web_apps_utils.cc
@@ -144,6 +144,18 @@
   return app;
 }
 
+apps::mojom::AppPtr ConvertUninstalledWebApp(const web_app::WebApp* web_app,
+                                             apps::mojom::AppType app_type) {
+  apps::mojom::AppPtr app = apps::mojom::App::New();
+  app->app_type = app_type;
+  app->app_id = web_app->app_id();
+  // TODO(loyso): Plumb uninstall source (reason) here.
+  app->readiness = apps::mojom::Readiness::kUninstalledByUser;
+
+  apps_util::SetWebAppShowInFields(app, web_app);
+  return app;
+}
+
 webapps::WebappUninstallSource ConvertUninstallSourceToWebAppUninstallSource(
     apps::mojom::UninstallSource uninstall_source) {
   switch (uninstall_source) {
diff --git a/chrome/browser/apps/app_service/web_apps_utils.h b/chrome/browser/apps/app_service/web_apps_utils.h
index df18567..c04d1db7 100644
--- a/chrome/browser/apps/app_service/web_apps_utils.h
+++ b/chrome/browser/apps/app_service/web_apps_utils.h
@@ -37,6 +37,11 @@
                                   apps::mojom::AppType app_type,
                                   apps::mojom::Readiness readiness);
 
+// Constructs an App with only the information required to identify an
+// uninstallation.
+apps::mojom::AppPtr ConvertUninstalledWebApp(const web_app::WebApp* web_app,
+                                             apps::mojom::AppType app_type);
+
 // Converts |uninstall_source| to a |WebappUninstallSource|.
 webapps::WebappUninstallSource ConvertUninstallSourceToWebAppUninstallSource(
     apps::mojom::UninstallSource uninstall_source);
diff --git a/chrome/browser/ash/accessibility/speech_monitor.cc b/chrome/browser/ash/accessibility/speech_monitor.cc
index d586ecb1..1c35f49b 100644
--- a/chrome/browser/ash/accessibility/speech_monitor.cc
+++ b/chrome/browser/ash/accessibility/speech_monitor.cc
@@ -57,6 +57,7 @@
 }
 
 bool SpeechMonitor::StopSpeaking() {
+  ++stop_count_;
   return true;
 }
 
diff --git a/chrome/browser/ash/accessibility/speech_monitor.h b/chrome/browser/ash/accessibility/speech_monitor.h
index 19a07c2..2dc35a3 100644
--- a/chrome/browser/ash/accessibility/speech_monitor.h
+++ b/chrome/browser/ash/accessibility/speech_monitor.h
@@ -67,6 +67,8 @@
   // Delayed utterances.
   double GetDelayForLastUtteranceMS();
 
+  int stop_count() { return stop_count_; }
+
  private:
   typedef std::pair<std::function<bool()>, std::string> ReplayArgs;
 
@@ -125,6 +127,9 @@
   // Whether |Replay| was called.
   bool replay_called_ = false;
 
+  // The number of times StopSpeaking() has been called.
+  int stop_count_ = 0;
+
   base::WeakPtrFactory<SpeechMonitor> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(SpeechMonitor);
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.cc b/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.cc
index 6b761d8..997a1d4 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.cc
+++ b/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.cc
@@ -45,11 +45,11 @@
 }
 
 void ArcPipWindowThrottleObserver::StopObserving() {
+  ThrottleObserver::StopObserving();
   auto* const container = GetPipContainer();
   if (!container)  // for testing
     return;
   container->RemoveObserver(this);
-  ThrottleObserver::StopObserving();
 }
 
 void ArcPipWindowThrottleObserver::OnWindowAdded(aura::Window* window) {
@@ -61,10 +61,16 @@
   // Check if there are any ARC windows left in the PipContainer. An old PIP
   // window may be removed after a new one is added.
   auto* const container = GetPipContainer();
-  if (std::none_of(container->children().begin(), container->children().end(),
+  if (!container ||
+      std::none_of(container->children().begin(), container->children().end(),
                    &ash::IsArcWindow)) {
     SetActive(false);
   }
 }
 
+void ArcPipWindowThrottleObserver::OnWindowDestroying(aura::Window* window) {
+  SetActive(false);
+  StopObserving();
+}
+
 }  // namespace arc
diff --git a/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.h b/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.h
index fee10f6..b56efe26 100644
--- a/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.h
+++ b/chrome/browser/ash/arc/instance_throttle/arc_pip_window_throttle_observer.h
@@ -33,6 +33,7 @@
   // aura::WindowObserver:
   void OnWindowAdded(aura::Window* window) override;
   void OnWindowRemoved(aura::Window* window) override;
+  void OnWindowDestroying(aura::Window* window) override;
 };
 
 }  // namespace arc
diff --git a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
index e1e9c09..914d07cc 100644
--- a/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/welcome_screen_browsertest.cc
@@ -799,6 +799,25 @@
   ASSERT_TRUE(IdleDetectionCancelledForTesting());
 }
 
+// Verifies that TTS output stops after the user has closed the dialog.
+IN_PROC_BROWSER_TEST_F(WelcomeScreenChromeVoxHintTest, StopSpeechAfterClose) {
+  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
+  TtsExtensionEngine::GetInstance()->DisableBuiltInTTSEngineForTesting();
+  test::ExecuteOobeJS(kSetAvailableVoices);
+  const std::string set_is_speaking = R"(
+    chrome.tts.isSpeaking = function(callback) {
+      callback(true);
+    };)";
+  test::ExecuteOobeJS(set_is_speaking);
+  test::SpeechMonitor monitor;
+  GiveChromeVoxHintForTesting();
+  WaitForChromeVoxHintDialogToOpen();
+  test::OobeJS().ExpectAttributeEQ("open", kChromeVoxHintDialog, true);
+  int expected_stop_count = monitor.stop_count() + 1;
+  test::OobeJS().ClickOnPath(kDismissChromeVoxButton);
+  ASSERT_EQ(expected_stop_count, monitor.stop_count());
+}
+
 class WelcomeScreenInternationalChromeVoxHintTest
     : public WelcomeScreenChromeVoxHintTest {
  public:
diff --git a/chrome/browser/ash/notifications/deprecation_notification_controller.cc b/chrome/browser/ash/notifications/deprecation_notification_controller.cc
index 690efff..d458b4b 100644
--- a/chrome/browser/ash/notifications/deprecation_notification_controller.cc
+++ b/chrome/browser/ash/notifications/deprecation_notification_controller.cc
@@ -65,21 +65,21 @@
   return true;
 }
 
-bool DeprecationNotificationController::NotifyDeprecatedAltBasedKeyRewrite(
+bool DeprecationNotificationController::NotifyDeprecatedSixPackKeyRewrite(
     ui::KeyboardCode key_code) {
-  if (!ShouldShowAltBasedDeprecationNotification(key_code)) {
+  if (!ShouldShowSixPackKeyDeprecationNotification(key_code)) {
     return false;
   }
 
   // The notification id is not user visible.
   const std::string id =
       std::string(kNotificationIdPrefix) + base::NumberToString(key_code);
-  const int message_id = GetAltBasedDeprecationMessageId(key_code);
+  const int message_id = GetSixPackKeyDeprecationMessageId(key_code);
   ShowNotificationFromIdWithLauncherKey(id, message_id);
 
   // Keep track that the notification was shown to decide whether to show it
   // again in future.
-  RecordAltBasedDeprecationNotificationShown(key_code);
+  RecordSixPackKeyDeprecationNotificationShown(key_code);
   return true;
 }
 
@@ -126,21 +126,23 @@
 }
 
 bool DeprecationNotificationController::
-    ShouldShowAltBasedDeprecationNotification(ui::KeyboardCode key_code) {
+    ShouldShowSixPackKeyDeprecationNotification(ui::KeyboardCode key_code) {
   return !shown_key_notifications_.contains(key_code);
 }
 
 void DeprecationNotificationController::
-    RecordAltBasedDeprecationNotificationShown(ui::KeyboardCode key_code) {
+    RecordSixPackKeyDeprecationNotificationShown(ui::KeyboardCode key_code) {
   DCHECK(!shown_key_notifications_.contains(key_code));
   shown_key_notifications_.insert(key_code);
 }
 
-int DeprecationNotificationController::GetAltBasedDeprecationMessageId(
+int DeprecationNotificationController::GetSixPackKeyDeprecationMessageId(
     ui::KeyboardCode key_code) {
   switch (key_code) {
     case ui::VKEY_DELETE:
       return IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_DELETE;
+    case ui::VKEY_INSERT:
+      return IDS_ASH_SHORTCUT_DEPRECATION_SEARCH_PERIOD_INSERT;
     case ui::VKEY_HOME:
       return IDS_ASH_SHORTCUT_DEPRECATION_ALT_BASED_HOME;
     case ui::VKEY_END:
diff --git a/chrome/browser/ash/notifications/deprecation_notification_controller.h b/chrome/browser/ash/notifications/deprecation_notification_controller.h
index a6b1a2d..ad1b95f 100644
--- a/chrome/browser/ash/notifications/deprecation_notification_controller.h
+++ b/chrome/browser/ash/notifications/deprecation_notification_controller.h
@@ -37,11 +37,11 @@
   // notification will only be shown once per user session.
   bool NotifyDeprecatedFKeyRewrite();
 
-  // Call to inform the notification controller that an Alt based
-  // key rewrite (eg. Alt+Up -> PageUp) was deprecated. The key_code
-  // is the key that would have been generated by the rewrite. The notification
-  // will only be shown once per user session.
-  bool NotifyDeprecatedAltBasedKeyRewrite(ui::KeyboardCode key_code);
+  // Call to inform the notification controller that a legacy six-pack (PageUp,
+  // PageDown, Insert, Delete, Home, End) key rewrite (eg. Alt+Up -> PageUp)
+  // was deprecated. The |key_code| is the key that would have been generated
+  // by the rewrite. The notification will only be shown once per user session.
+  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code);
 
   // Reset the state for which notifications have been seen before.
   void ResetStateForTesting();
@@ -58,14 +58,14 @@
                         const std::u16string& message_body);
 
   // Returns whether to show the deprecation notice for |key_code|.
-  bool ShouldShowAltBasedDeprecationNotification(ui::KeyboardCode key_code);
+  bool ShouldShowSixPackKeyDeprecationNotification(ui::KeyboardCode key_code);
 
   // Records that a notification was shown to decide whether to show it again
   // in future.
-  void RecordAltBasedDeprecationNotificationShown(ui::KeyboardCode key_code);
+  void RecordSixPackKeyDeprecationNotificationShown(ui::KeyboardCode key_code);
 
   // Return the localization message id for |key_code| deprecation.
-  int GetAltBasedDeprecationMessageId(ui::KeyboardCode key_code);
+  int GetSixPackKeyDeprecationMessageId(ui::KeyboardCode key_code);
 
   // Used to only show the right click notification once per user session.
   bool right_click_notification_shown_ = false;
diff --git a/chrome/browser/ash/notifications/deprecation_notification_controller_unittest.cc b/chrome/browser/ash/notifications/deprecation_notification_controller_unittest.cc
index c9f212c..f96dc60 100644
--- a/chrome/browser/ash/notifications/deprecation_notification_controller_unittest.cc
+++ b/chrome/browser/ash/notifications/deprecation_notification_controller_unittest.cc
@@ -34,15 +34,17 @@
   controller_.NotifyDeprecatedRightClickRewrite();
   EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
 
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_DELETE);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_DELETE);
   EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_HOME);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_INSERT);
   EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_END);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_HOME);
   EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_PRIOR);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_END);
   EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_NEXT);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_PRIOR);
+  EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_NEXT);
   EXPECT_EQ(message_center_.NotificationCount(), expected_notification_count++);
 
   // Clear the messages from the message center.
@@ -51,11 +53,12 @@
 
   // No additional notifications should be generated.
   controller_.NotifyDeprecatedRightClickRewrite();
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_DELETE);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_HOME);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_END);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_PRIOR);
-  controller_.NotifyDeprecatedAltBasedKeyRewrite(ui::VKEY_NEXT);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_DELETE);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_INSERT);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_HOME);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_END);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_PRIOR);
+  controller_.NotifyDeprecatedSixPackKeyRewrite(ui::VKEY_NEXT);
   EXPECT_EQ(message_center_.NotificationCount(), 0);
 }
 
diff --git a/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc b/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc
index 2b252d5..cb7563b0 100644
--- a/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc
+++ b/chrome/browser/ash/plugin_vm/plugin_vm_diagnostics.cc
@@ -18,8 +18,8 @@
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chromeos/dbus//concierge_client.h"
 #include "chromeos/dbus/concierge/concierge_service.pb.h"
+#include "chromeos/dbus/concierge_client.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/prefs/pref_service.h"
 #include "third_party/protobuf/src/google/protobuf/repeated_field.h"
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc
index 0ca1c60..592ddfe6 100644
--- a/chrome/browser/chrome_browser_main.cc
+++ b/chrome/browser/chrome_browser_main.cc
@@ -124,6 +124,7 @@
 #include "components/language/core/common/language_experiments.h"
 #include "components/metrics/call_stack_profile_metrics_provider.h"
 #include "components/metrics/call_stack_profile_params.h"
+#include "components/metrics/clean_exit_beacon.h"
 #include "components/metrics/expired_histogram_util.h"
 #include "components/metrics/metrics_reporting_default_state.h"
 #include "components/metrics/metrics_service.h"
@@ -1796,6 +1797,7 @@
   // not finish.
   NOTREACHED();
 #else
+
   browser_shutdown::RestartMode restart_mode =
       browser_shutdown::RestartMode::kNoRestart;
 
@@ -1809,8 +1811,21 @@
   }
 
   browser_process_->PostDestroyThreads();
-  // browser_shutdown takes care of deleting browser_process, so we need to
-  // release it.
+
+  // We need to do this check as late as possible, but due to modularity, this
+  // may be the last point in Chrome. This would be more effective if done at a
+  // higher level on the stack, so that it is impossible for an early return to
+  // bypass this code. Perhaps we need a *final* hook that is called on all
+  // paths from content/browser/browser_main.
+  //
+  // Since we use |browser_process_|'s local state for this CHECK, it must be
+  // done before |browser_process_| is released.
+  metrics::CleanExitBeacon::EnsureCleanShutdown(
+      browser_process_->local_state());
+
+  // The below call to browser_shutdown::ShutdownPostThreadsStop() deletes
+  // |browser_process_|. We release it so that we don't keep holding onto an
+  // invalid reference.
   ignore_result(browser_process_.release());
 
 #if BUILDFLAG(ENABLE_DOWNGRADE_PROCESSING)
@@ -1834,13 +1849,6 @@
   process_singleton_.reset();
   device_event_log::Shutdown();
 
-  // We need to do this check as late as possible, but due to modularity, this
-  // may be the last point in Chrome.  This would be more effective if done at
-  // a higher level on the stack, so that it is impossible for an early return
-  // to bypass this code.  Perhaps we need a *final* hook that is called on all
-  // paths from content/browser/browser_main.
-  CHECK(metrics::MetricsService::UmaMetricsProperlyShutdown());
-
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   arc::StabilityMetricsManager::Shutdown();
   ash::StatsReportingController::Shutdown();
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 51855dc..ad6a126 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2099,15 +2099,10 @@
 
 void ChromeContentBrowserClient::PersistIsolatedOrigin(
     content::BrowserContext* context,
-    const url::Origin& origin) {
-  DCHECK(!context->IsOffTheRecord());
-  Profile* profile = Profile::FromBrowserContext(context);
-  ListPrefUpdate update(profile->GetPrefs(),
-                        site_isolation::prefs::kUserTriggeredIsolatedOrigins);
-  base::ListValue* list = update.Get();
-  base::Value value(origin.Serialize());
-  if (!base::Contains(list->GetList(), value))
-    list->Append(std::move(value));
+    const url::Origin& origin,
+    content::ChildProcessSecurityPolicy::IsolatedOriginSource source) {
+  site_isolation::SiteIsolationPolicy::PersistIsolatedOrigin(context, origin,
+                                                             source);
 }
 
 bool ChromeContentBrowserClient::IsFileAccessAllowed(
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index c16fb05d..dbc831e8 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -23,6 +23,7 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/startup_data.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
@@ -217,8 +218,11 @@
   bool ShouldEnableStrictSiteIsolation() override;
   bool ShouldDisableSiteIsolation() override;
   std::vector<std::string> GetAdditionalSiteIsolationModes() override;
-  void PersistIsolatedOrigin(content::BrowserContext* context,
-                             const url::Origin& origin) override;
+  void PersistIsolatedOrigin(
+      content::BrowserContext* context,
+      const url::Origin& origin,
+      content::ChildProcessSecurityPolicy::IsolatedOriginSource source)
+      override;
   bool IsFileAccessAllowed(const base::FilePath& path,
                            const base::FilePath& absolute_path,
                            const base::FilePath& profile_path) override;
diff --git a/chrome/browser/chromeos/events/event_rewriter_delegate_impl.cc b/chrome/browser/chromeos/events/event_rewriter_delegate_impl.cc
index 13cdd60..1e5de99 100644
--- a/chrome/browser/chromeos/events/event_rewriter_delegate_impl.cc
+++ b/chrome/browser/chromeos/events/event_rewriter_delegate_impl.cc
@@ -126,9 +126,9 @@
   return deprecation_controller_->NotifyDeprecatedFKeyRewrite();
 }
 
-bool EventRewriterDelegateImpl::NotifyDeprecatedAltBasedKeyRewrite(
+bool EventRewriterDelegateImpl::NotifyDeprecatedSixPackKeyRewrite(
     ui::KeyboardCode key_code) {
-  return deprecation_controller_->NotifyDeprecatedAltBasedKeyRewrite(key_code);
+  return deprecation_controller_->NotifyDeprecatedSixPackKeyRewrite(key_code);
 }
 
 const PrefService* EventRewriterDelegateImpl::GetPrefService() const {
diff --git a/chrome/browser/chromeos/events/event_rewriter_delegate_impl.h b/chrome/browser/chromeos/events/event_rewriter_delegate_impl.h
index cea9134..cbe7948 100644
--- a/chrome/browser/chromeos/events/event_rewriter_delegate_impl.h
+++ b/chrome/browser/chromeos/events/event_rewriter_delegate_impl.h
@@ -40,7 +40,7 @@
   bool IsSearchKeyAcceleratorReserved() const override;
   bool NotifyDeprecatedRightClickRewrite() override;
   bool NotifyDeprecatedFKeyRewrite() override;
-  bool NotifyDeprecatedAltBasedKeyRewrite(ui::KeyboardCode key_code) override;
+  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override;
 
  private:
   const PrefService* GetPrefService() const;
diff --git a/chrome/browser/chromeos/events/event_rewriter_unittest.cc b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
index 891427f..67c954a 100644
--- a/chrome/browser/chromeos/events/event_rewriter_unittest.cc
+++ b/chrome/browser/chromeos/events/event_rewriter_unittest.cc
@@ -1759,8 +1759,10 @@
   });
 }
 
-TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsert) {
+TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsert_Old) {
   chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
+  scoped_feature_list_.InitAndDisableFeature(
+      ::features::kImprovedKeyboardShortcuts);
   TestNonAppleKeyboardVariants({
       // Period -> Period
       {ui::ET_KEY_PRESSED,
@@ -1783,6 +1785,58 @@
   });
 }
 
+TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsert_DeprecatedNotification) {
+  chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
+  scoped_feature_list_.InitAndEnableFeature(
+      ::features::kImprovedKeyboardShortcuts);
+  TestNonAppleKeyboardVariants({
+      // Period -> Period
+      {ui::ET_KEY_PRESSED,
+       {ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_NONE,
+        ui::DomKey::Constant<'.'>::Character},
+       {ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_NONE,
+        ui::DomKey::Constant<'.'>::Character}},
+      // Search+Period -> No rewrite (and shows notification)
+      {ui::ET_KEY_PRESSED,
+       {ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_COMMAND_DOWN,
+        ui::DomKey::Constant<'.'>::Character},
+       {ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD, ui::EF_COMMAND_DOWN,
+        ui::DomKey::Constant<'.'>::Character},
+       kKeyboardDeviceId,
+       /*triggers_notification=*/true},
+      // Control+Search+Period -> No rewrite (and shows notification)
+      {ui::ET_KEY_PRESSED,
+       {ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD,
+        ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
+        ui::DomKey::Constant<'.'>::Character},
+       {ui::VKEY_OEM_PERIOD, ui::DomCode::PERIOD,
+        ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN,
+        ui::DomKey::Constant<'.'>::Character},
+       kKeyboardDeviceId,
+       /*triggers_notification=*/true},
+  });
+}
+
+TEST_F(EventRewriterTest, TestRewriteExtendedKeyInsert_New) {
+  chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
+  scoped_feature_list_.InitAndEnableFeature(
+      ::features::kImprovedKeyboardShortcuts);
+  TestNonAppleKeyboardVariants({
+      // Search+Shift+Backspace -> Insert
+      {ui::ET_KEY_PRESSED,
+       {ui::VKEY_BACK, ui::DomCode::BACKSPACE,
+        ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN, ui::DomKey::BACKSPACE},
+       {ui::VKEY_INSERT, ui::DomCode::INSERT, ui::EF_NONE, ui::DomKey::INSERT}},
+      // Control+Search+Shift+Backspace -> Control+Insert
+      {ui::ET_KEY_PRESSED,
+       {ui::VKEY_BACK, ui::DomCode::BACKSPACE,
+        ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN,
+        ui::DomKey::BACKSPACE},
+       {ui::VKEY_INSERT, ui::DomCode::INSERT, ui::EF_CONTROL_DOWN,
+        ui::DomKey::INSERT}},
+  });
+}
+
 TEST_F(EventRewriterTest, TestRewriteExtendedKeys_SearchVariants) {
   chromeos::Preferences::RegisterProfilePrefs(prefs()->registry());
   TestNonAppleKeyboardVariants({
@@ -4521,7 +4575,7 @@
   bool IsSearchKeyAcceleratorReserved() const override { return false; }
   bool NotifyDeprecatedRightClickRewrite() override { return false; }
   bool NotifyDeprecatedFKeyRewrite() override { return false; }
-  bool NotifyDeprecatedAltBasedKeyRewrite(ui::KeyboardCode key_code) override {
+  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
     return false;
   }
 
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
index 084976bb..154c4b4c 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller.cc
@@ -8,6 +8,7 @@
 #include "base/notreached.h"
 #include "base/syslog_logging.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_histogram_helper.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_reporting_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
 #include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager_factory.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -34,6 +35,43 @@
   return data_dst && data_dst->type() == ui::EndpointType::kClipboardHistory;
 }
 
+void ReportEvent(const DlpRulesManager& dlp_rules_manager,
+                 const GURL& src_url,
+                 const GURL& dst_url,
+                 DlpRulesManager::Level level) {
+  if (level != DlpRulesManager::Level::kReport &&
+      level != DlpRulesManager::Level::kBlock)
+    return;
+
+  auto* reporting_manager = dlp_rules_manager.GetReportingManager();
+  if (!reporting_manager)
+    return;
+
+  auto src_dst_pair = dlp_rules_manager.GetSrcAndDstUrlPatterns(
+      src_url, dst_url, DlpRulesManager::Restriction::kClipboard, level);
+  reporting_manager->ReportEvent(src_dst_pair.first, src_dst_pair.second,
+                                 DlpRulesManager::Restriction::kClipboard,
+                                 level);
+}
+
+void ReportEvent(const DlpRulesManager& dlp_rules_manager,
+                 const GURL& src_url,
+                 DlpRulesManager::Component component,
+                 DlpRulesManager::Level level) {
+  if (level != DlpRulesManager::Level::kReport &&
+      level != DlpRulesManager::Level::kBlock)
+    return;
+
+  auto* reporting_manager = dlp_rules_manager.GetReportingManager();
+  if (!reporting_manager)
+    return;
+
+  auto src_pattern = dlp_rules_manager.GetSourceUrlPattern(
+      src_url, component, DlpRulesManager::Restriction::kClipboard, level);
+  reporting_manager->ReportEvent(
+      src_pattern, component, DlpRulesManager::Restriction::kClipboard, level);
+}
+
 DlpRulesManager::Level IsDataTransferAllowed(
     const DlpRulesManager& dlp_rules_manager,
     const ui::DataTransferEndpoint* const data_src,
@@ -56,6 +94,7 @@
       // the src against any dst (*), otherwise it will return ALLOW.
       level = dlp_rules_manager.IsRestrictedDestination(
           src_url, GURL(), DlpRulesManager::Restriction::kClipboard);
+      ReportEvent(dlp_rules_manager, src_url, GURL(), level);
       break;
     }
 
@@ -63,6 +102,8 @@
       GURL dst_url = data_dst->origin()->GetURL();
       level = dlp_rules_manager.IsRestrictedDestination(
           src_url, dst_url, DlpRulesManager::Restriction::kClipboard);
+      if (!IsFilesApp(data_dst))
+        ReportEvent(dlp_rules_manager, src_url, GURL(), level);
       break;
     }
 
@@ -70,6 +111,8 @@
       level = dlp_rules_manager.IsRestrictedComponent(
           src_url, DlpRulesManager::Component::kCrostini,
           DlpRulesManager::Restriction::kClipboard);
+      ReportEvent(dlp_rules_manager, src_url,
+                  DlpRulesManager::Component::kCrostini, level);
       break;
     }
 
@@ -77,6 +120,8 @@
       level = dlp_rules_manager.IsRestrictedComponent(
           src_url, DlpRulesManager::Component::kPluginVm,
           DlpRulesManager::Restriction::kClipboard);
+      ReportEvent(dlp_rules_manager, src_url,
+                  DlpRulesManager::Component::kPluginVm, level);
       break;
     }
 
@@ -84,6 +129,8 @@
       level = dlp_rules_manager.IsRestrictedComponent(
           src_url, DlpRulesManager::Component::kArc,
           DlpRulesManager::Restriction::kClipboard);
+      ReportEvent(dlp_rules_manager, src_url, DlpRulesManager::Component::kArc,
+                  level);
       break;
     }
 
diff --git a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
index e98792e..01a3494c 100644
--- a/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
+++ b/chrome/browser/chromeos/policy/dlp/data_transfer_dlp_controller_unittest.cc
@@ -267,6 +267,9 @@
       CreateEndpoint(base::OptionalOrNullptr(endpoint_type), do_notify);
   auto* dst_ptr = base::OptionalOrNullptr(data_dst);
 
+  EXPECT_CALL(rules_manager_, GetReportingManager)
+      .WillRepeatedly(::testing::Return(nullptr));
+
   // IsClipboardReadAllowed
   EXPECT_CALL(rules_manager_, IsRestrictedDestination)
       .WillOnce(testing::Return(DlpRulesManager::Level::kBlock));
@@ -400,6 +403,9 @@
   ASSERT_TRUE(endpoint_type.has_value());
   ui::DataTransferEndpoint data_dst(endpoint_type.value(), do_notify);
 
+  EXPECT_CALL(rules_manager_, GetReportingManager)
+      .WillRepeatedly(::testing::Return(nullptr));
+
   // IsClipboardReadAllowed
   EXPECT_CALL(rules_manager_, IsRestrictedComponent)
       .WillOnce(testing::Return(DlpRulesManager::Level::kBlock));
diff --git a/chrome/browser/chromeos/printing/printer_setup_util.cc b/chrome/browser/chromeos/printing/printer_setup_util.cc
index ea2239a..ae53bbf 100644
--- a/chrome/browser/chromeos/printing/printer_setup_util.cc
+++ b/chrome/browser/chromeos/printing/printer_setup_util.cc
@@ -22,7 +22,7 @@
 #include "chrome/browser/chromeos/printing/cups_printers_manager.h"
 #include "chrome/browser/chromeos/printing/cups_printers_manager_factory.h"
 #include "chrome/browser/chromeos/printing/printer_configurer.h"
-#include "chrome/browser/printing/print_backend_service.h"
+#include "chrome/browser/printing/print_backend_service_manager.h"
 #include "chromeos/printing/printer_configuration.h"
 #include "components/crash/core/common/crash_keys.h"
 #include "content/public/browser/browser_thread.h"
@@ -115,9 +115,9 @@
 
   if (base::FeatureList::IsEnabled(features::kEnableOopPrintDrivers)) {
     VLOG(1) << "Fetching printer capabilities via service";
-    GetPrintBackendService(g_browser_process->GetApplicationLocale(),
-                           printer_id)
-        ->GetPrinterSemanticCapsAndDefaults(printer_id, std::move(cb));
+    auto& service = PrintBackendServiceManager::GetInstance().GetService(
+        g_browser_process->GetApplicationLocale(), printer_id);
+    service->GetPrinterSemanticCapsAndDefaults(printer_id, std::move(cb));
   } else {
     VLOG(1) << "Fetching printer capabilities in-process";
     // USER_VISIBLE because the result is displayed in the print preview dialog.
diff --git a/chrome/browser/download/download_offline_content_provider_unittest.cc b/chrome/browser/download/download_offline_content_provider_unittest.cc
index 17a6d90..a636229 100644
--- a/chrome/browser/download/download_offline_content_provider_unittest.cc
+++ b/chrome/browser/download/download_offline_content_provider_unittest.cc
@@ -10,8 +10,8 @@
 #include "base/guid.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "components/download/public/common//mock_simple_download_manager.h"
 #include "components/download/public/common/mock_download_item.h"
+#include "components/download/public/common/mock_simple_download_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc b/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc
index 80c547dc..7ac9ec0 100644
--- a/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/profile_report_generator_unittest.cc
@@ -35,6 +35,7 @@
 const int kMaxNumberOfExtensionRequest = 1000;
 
 constexpr char kProfile[] = "Profile";
+constexpr char16_t kProfile16[] = u"Profile";
 constexpr char kIdleProfile[] = "IdleProfile";
 constexpr char16_t kIdleProfile16[] = u"IdleProfile";
 constexpr char kExtensionId[] = "abcdefghijklmnopabcdefghijklmnop";
@@ -68,7 +69,7 @@
     InitPolicyMap();
 
     profile_ = profile_manager_.CreateTestingProfile(
-        kProfile, {}, base::UTF8ToUTF16(kProfile), 0, {},
+        kProfile, {}, kProfile16, 0, {},
         IdentityTestEnvironmentProfileAdaptor::
             GetIdentityTestEnvironmentFactories(),
         base::nullopt, std::move(policy_service_));
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index a3d6b262..567f4646 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -803,6 +803,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/resource_coordinator:intervention_policy_database_proto",
     "//chrome/browser/resource_coordinator:mojo_bindings",
+    "//chrome/browser/resources/pdf/ink:buildflags",
     "//chrome/browser/safe_browsing",
     "//chrome/browser/safe_browsing:metrics_collector",
     "//chrome/browser/web_applications",
@@ -1094,7 +1095,6 @@
       "//chrome/browser/chromeos/crostini:crostini_installer_types_mojom",
       "//chrome/browser/devtools",
       "//chrome/browser/nearby_sharing/common",
-      "//chrome/browser/resources/pdf/ink:buildflags",
       "//chrome/browser/ui/webui/settings/chromeos/constants:mojom",
       "//chromeos",
       "//chromeos/attestation",
diff --git a/chrome/browser/extensions/DEPS b/chrome/browser/extensions/DEPS
index 07e3a322..619e643 100644
--- a/chrome/browser/extensions/DEPS
+++ b/chrome/browser/extensions/DEPS
@@ -3,7 +3,6 @@
   "+components/live_caption",
   "+extensions/strings/grit/extensions_strings.h",
   "+services/network/public",
-  "+third_party/ink/grit",
   "+ui/base",
 
   # For access to testing command line switches.
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 8ff4f9a4..a3efafef 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -1118,9 +1118,8 @@
   // Once the download item is deleted, we should return kInvalidId.
   int id = download_item->GetId();
   download_item->Remove();
-  download_item = NULL;
-  EXPECT_EQ(static_cast<DownloadItem*>(NULL),
-            GetCurrentManager()->GetDownload(id));
+  download_item = nullptr;
+  EXPECT_EQ(nullptr, GetCurrentManager()->GetDownload(id));
   error = RunFunctionAndReturnError(new DownloadsGetFileIconFunction(), args32);
   EXPECT_STREQ(errors::kInvalidId,
                error.c_str());
diff --git a/chrome/browser/extensions/blocklist_extension_prefs.cc b/chrome/browser/extensions/blocklist_extension_prefs.cc
index 4704f68..84a34a0 100644
--- a/chrome/browser/extensions/blocklist_extension_prefs.cc
+++ b/chrome/browser/extensions/blocklist_extension_prefs.cc
@@ -21,13 +21,42 @@
 constexpr BitMapBlocklistState kDefaultBitMapBlocklistState =
     BitMapBlocklistState::NOT_BLOCKLISTED;
 
+// Extensions in these states should be put into the extension greylist.
+const BitMapBlocklistState kGreylistStates[] = {
+    BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY,
+    BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION,
+    BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED};
+const int kAllGreylistStates =
+    static_cast<int>(BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY) |
+    static_cast<int>(BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION) |
+    static_cast<int>(BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED);
+
 }  // namespace
 
 namespace blocklist_prefs {
 
+BitMapBlocklistState BlocklistStateToBitMapBlocklistState(
+    BlocklistState blocklist_state) {
+  switch (blocklist_state) {
+    case NOT_BLOCKLISTED:
+      return BitMapBlocklistState::NOT_BLOCKLISTED;
+    case BLOCKLISTED_MALWARE:
+      return BitMapBlocklistState::BLOCKLISTED_MALWARE;
+    case BLOCKLISTED_SECURITY_VULNERABILITY:
+      return BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY;
+    case BLOCKLISTED_CWS_POLICY_VIOLATION:
+      return BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION;
+    case BLOCKLISTED_POTENTIALLY_UNWANTED:
+      return BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED;
+    case BLOCKLISTED_UNKNOWN:
+      NOTREACHED() << "The unknown state should not be added into prefs.";
+      return BitMapBlocklistState::NOT_BLOCKLISTED;
+  }
+}
+
 void AddOmahaBlocklistState(const std::string& extension_id,
                             BitMapBlocklistState state,
-                            extensions::ExtensionPrefs* extension_prefs) {
+                            ExtensionPrefs* extension_prefs) {
   extension_prefs->ModifyBitMapPrefBits(
       extension_id, static_cast<int>(state), ExtensionPrefs::BIT_MAP_PREF_ADD,
       kPrefOmahaBlocklistState, static_cast<int>(kDefaultBitMapBlocklistState));
@@ -35,7 +64,7 @@
 
 void RemoveOmahaBlocklistState(const std::string& extension_id,
                                BitMapBlocklistState state,
-                               extensions::ExtensionPrefs* extension_prefs) {
+                               ExtensionPrefs* extension_prefs) {
   extension_prefs->ModifyBitMapPrefBits(
       extension_id, static_cast<int>(state),
       ExtensionPrefs::BIT_MAP_PREF_REMOVE, kPrefOmahaBlocklistState,
@@ -44,41 +73,74 @@
 
 bool HasOmahaBlocklistState(const std::string& extension_id,
                             BitMapBlocklistState state,
-                            extensions::ExtensionPrefs* extension_prefs) {
+                            ExtensionPrefs* extension_prefs) {
   int current_states = extension_prefs->GetBitMapPrefBits(
       extension_id, kPrefOmahaBlocklistState,
       static_cast<int>(kDefaultBitMapBlocklistState));
   return (current_states & static_cast<int>(state)) != 0;
 }
 
-void AddAcknowledgedBlocklistState(
-    const std::string& extension_id,
-    BitMapBlocklistState state,
-    extensions::ExtensionPrefs* extension_prefs) {
+bool HasAnyOmahaGreylistState(const std::string& extension_id,
+                              ExtensionPrefs* extension_prefs) {
+  int current_states = extension_prefs->GetBitMapPrefBits(
+      extension_id, kPrefOmahaBlocklistState,
+      static_cast<int>(kDefaultBitMapBlocklistState));
+  return (current_states & kAllGreylistStates) != 0;
+}
+
+void AddAcknowledgedBlocklistState(const std::string& extension_id,
+                                   BitMapBlocklistState state,
+                                   ExtensionPrefs* extension_prefs) {
   extension_prefs->ModifyBitMapPrefBits(
       extension_id, static_cast<int>(state), ExtensionPrefs::BIT_MAP_PREF_ADD,
       kPrefAcknowledgedBlocklistState,
       static_cast<int>(kDefaultBitMapBlocklistState));
 }
 
-void ClearAcknowledgedBlocklistState(
+void RemoveAcknowledgedBlocklistState(
     const std::string& extension_id,
+    BitMapBlocklistState state,
     extensions::ExtensionPrefs* extension_prefs) {
   extension_prefs->ModifyBitMapPrefBits(
+      extension_id, static_cast<int>(state),
+      ExtensionPrefs::BIT_MAP_PREF_REMOVE, kPrefAcknowledgedBlocklistState,
+      static_cast<int>(kDefaultBitMapBlocklistState));
+}
+
+void ClearAcknowledgedBlocklistStates(const std::string& extension_id,
+                                      ExtensionPrefs* extension_prefs) {
+  extension_prefs->ModifyBitMapPrefBits(
       extension_id, 0, ExtensionPrefs::BIT_MAP_PREF_CLEAR,
       kPrefAcknowledgedBlocklistState,
       static_cast<int>(kDefaultBitMapBlocklistState));
 }
 
-bool HasAcknowledgedBlocklistState(
-    const std::string& extension_id,
-    BitMapBlocklistState state,
-    extensions::ExtensionPrefs* extension_prefs) {
+bool HasAcknowledgedBlocklistState(const std::string& extension_id,
+                                   BitMapBlocklistState state,
+                                   ExtensionPrefs* extension_prefs) {
   int current_states = extension_prefs->GetBitMapPrefBits(
       extension_id, kPrefAcknowledgedBlocklistState,
       static_cast<int>(kDefaultBitMapBlocklistState));
   return (current_states & static_cast<int>(state)) != 0;
 }
 
+void UpdateCurrentGreylistStatesAsAcknowledged(
+    const std::string& extension_id,
+    ExtensionPrefs* extension_prefs) {
+  for (auto state : kGreylistStates) {
+    bool is_on_sb_list =
+        (BlocklistStateToBitMapBlocklistState(
+             extension_prefs->GetExtensionBlocklistState(extension_id)) ==
+         state);
+    bool is_on_omaha_list =
+        HasOmahaBlocklistState(extension_id, state, extension_prefs);
+    if (is_on_sb_list || is_on_omaha_list) {
+      AddAcknowledgedBlocklistState(extension_id, state, extension_prefs);
+    } else {
+      RemoveAcknowledgedBlocklistState(extension_id, state, extension_prefs);
+    }
+  }
+}
+
 }  // namespace blocklist_prefs
 }  // namespace extensions
diff --git a/chrome/browser/extensions/blocklist_extension_prefs.h b/chrome/browser/extensions/blocklist_extension_prefs.h
index 51eb812..b013b18 100644
--- a/chrome/browser/extensions/blocklist_extension_prefs.h
+++ b/chrome/browser/extensions/blocklist_extension_prefs.h
@@ -16,34 +16,52 @@
 // Helper namespace for adding/removing/querying prefs for the blocklist.
 namespace blocklist_prefs {
 
+// Converts BlocklistState to BitMapBlocklistState.
+BitMapBlocklistState BlocklistStateToBitMapBlocklistState(
+    BlocklistState blocklist_state);
+
 // Adds the `state` to the Omaha blocklist state pref.
 void AddOmahaBlocklistState(const std::string& extension_id,
                             BitMapBlocklistState state,
-                            extensions::ExtensionPrefs* extension_prefs);
+                            ExtensionPrefs* extension_prefs);
 // Removes the `state` from the Omaha blocklist state pref. It doesn't clear
 // the other states in the pref.
 void RemoveOmahaBlocklistState(const std::string& extension_id,
                                BitMapBlocklistState state,
-                               extensions::ExtensionPrefs* extension_prefs);
+                               ExtensionPrefs* extension_prefs);
 // Checks whether the `extension_id` has the `state` in the Omaha blocklist
 // state pref.
 bool HasOmahaBlocklistState(const std::string& extension_id,
                             BitMapBlocklistState state,
-                            extensions::ExtensionPrefs* extension_prefs);
+                            ExtensionPrefs* extension_prefs);
+// Checks whether the `extension_id` is in any Omaha greylist state.
+bool HasAnyOmahaGreylistState(const std::string& extension_id,
+                              ExtensionPrefs* extension_prefs);
 
 // Adds the `state` to the acknowledged blocklist state pref.
 void AddAcknowledgedBlocklistState(const std::string& extension_id,
                                    BitMapBlocklistState state,
-                                   extensions::ExtensionPrefs* extension_prefs);
-// Clears all states in the acknowledged blocklist state pref.
-void ClearAcknowledgedBlocklistState(
+                                   ExtensionPrefs* extension_prefs);
+// Removes the `state` from the acknowledged blocklist state pref. It doesn't
+// clear the other states in the pref.
+void RemoveAcknowledgedBlocklistState(
     const std::string& extension_id,
+    BitMapBlocklistState state,
     extensions::ExtensionPrefs* extension_prefs);
+// Clears all states in the acknowledged blocklist state pref.
+void ClearAcknowledgedBlocklistStates(const std::string& extension_id,
+                                      ExtensionPrefs* extension_prefs);
 // Checks whether the `extension_id` has the `state` in the acknowledged
 // blocklist state pref.
 bool HasAcknowledgedBlocklistState(const std::string& extension_id,
                                    BitMapBlocklistState state,
-                                   extensions::ExtensionPrefs* extension_prefs);
+                                   ExtensionPrefs* extension_prefs);
+// Set all current greylist states for this `extension_id` as acknowledged.
+// It will consider both Safe Browsing greylist state and Omaha attribute
+// greylist state. Previous acknowledged states will be cleared if the
+// `extension_id` is no longer in that state.
+void UpdateCurrentGreylistStatesAsAcknowledged(const std::string& extension_id,
+                                               ExtensionPrefs* extension_prefs);
 
 }  // namespace blocklist_prefs
 }  // namespace extensions
diff --git a/chrome/browser/extensions/blocklist_extension_prefs_unittest.cc b/chrome/browser/extensions/blocklist_extension_prefs_unittest.cc
index 7e1f718..0b440ac 100644
--- a/chrome/browser/extensions/blocklist_extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/blocklist_extension_prefs_unittest.cc
@@ -38,13 +38,19 @@
       BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED;
   BitMapBlocklistState state2 =
       BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY;
+  BitMapBlocklistState state3 =
+      BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION;
   EXPECT_FALSE(blocklist_prefs::HasOmahaBlocklistState(kExtensionId, state1,
                                                        extension_prefs()));
+  EXPECT_FALSE(blocklist_prefs::HasAnyOmahaGreylistState(kExtensionId,
+                                                         extension_prefs()));
 
   blocklist_prefs::AddOmahaBlocklistState(kExtensionId, state1,
                                           extension_prefs());
   EXPECT_TRUE(blocklist_prefs::HasOmahaBlocklistState(kExtensionId, state1,
                                                       extension_prefs()));
+  EXPECT_TRUE(blocklist_prefs::HasAnyOmahaGreylistState(kExtensionId,
+                                                        extension_prefs()));
 
   blocklist_prefs::AddOmahaBlocklistState(kExtensionId, state2,
                                           extension_prefs());
@@ -53,6 +59,8 @@
   // Doesn't clear the other blocklist state.
   EXPECT_TRUE(blocklist_prefs::HasOmahaBlocklistState(kExtensionId, state1,
                                                       extension_prefs()));
+  EXPECT_TRUE(blocklist_prefs::HasAnyOmahaGreylistState(kExtensionId,
+                                                        extension_prefs()));
 
   blocklist_prefs::RemoveOmahaBlocklistState(kExtensionId, state1,
                                              extension_prefs());
@@ -61,6 +69,20 @@
   // Doesn't remove the other blocklist state.
   EXPECT_TRUE(blocklist_prefs::HasOmahaBlocklistState(kExtensionId, state2,
                                                       extension_prefs()));
+  EXPECT_TRUE(blocklist_prefs::HasAnyOmahaGreylistState(kExtensionId,
+                                                        extension_prefs()));
+
+  blocklist_prefs::AddOmahaBlocklistState(kExtensionId, state3,
+                                          extension_prefs());
+  blocklist_prefs::RemoveOmahaBlocklistState(kExtensionId, state2,
+                                             extension_prefs());
+  EXPECT_TRUE(blocklist_prefs::HasAnyOmahaGreylistState(kExtensionId,
+                                                        extension_prefs()));
+
+  blocklist_prefs::RemoveOmahaBlocklistState(kExtensionId, state3,
+                                             extension_prefs());
+  EXPECT_FALSE(blocklist_prefs::HasAnyOmahaGreylistState(kExtensionId,
+                                                         extension_prefs()));
 }
 
 TEST_F(BlocklistExtensionPrefsUnitTest, AcknowledgedBlocklistState) {
@@ -84,12 +106,72 @@
   EXPECT_TRUE(blocklist_prefs::HasAcknowledgedBlocklistState(
       kExtensionId, state1, extension_prefs()));
 
-  blocklist_prefs::ClearAcknowledgedBlocklistState(kExtensionId,
-                                                   extension_prefs());
+  blocklist_prefs::ClearAcknowledgedBlocklistStates(kExtensionId,
+                                                    extension_prefs());
   EXPECT_FALSE(blocklist_prefs::HasAcknowledgedBlocklistState(
       kExtensionId, state1, extension_prefs()));
   EXPECT_FALSE(blocklist_prefs::HasAcknowledgedBlocklistState(
       kExtensionId, state2, extension_prefs()));
 }
 
+TEST_F(BlocklistExtensionPrefsUnitTest,
+       UpdateCurrentGreylistStatesAsAcknowledged) {
+  blocklist_prefs::AddAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_MALWARE,
+      extension_prefs());
+  blocklist_prefs::AddAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY,
+      extension_prefs());
+  extension_prefs()->SetExtensionBlocklistState(
+      kExtensionId, BLOCKLISTED_POTENTIALLY_UNWANTED);
+  blocklist_prefs::AddOmahaBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION,
+      extension_prefs());
+
+  blocklist_prefs::UpdateCurrentGreylistStatesAsAcknowledged(kExtensionId,
+                                                             extension_prefs());
+
+  // The BLOCKLISTED_SECURITY_VULNERABILITY should be cleared because it is not
+  // in any greylist state.
+  EXPECT_FALSE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY,
+      extension_prefs()));
+  // BLOCKLISTED_POTENTIALLY_UNWANTED should be acknowledged because it is in
+  // the Safe Browsing greylist state.
+  EXPECT_TRUE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED,
+      extension_prefs()));
+  // BLOCKLISTED_CWS_POLICY_VIOLATION should be acknowledged because it is in
+  // the Omaha greylist state.
+  EXPECT_TRUE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION,
+      extension_prefs()));
+  // BLOCKLISTED_MALWARE should not be cleared because it is not a greylist
+  // state.
+  EXPECT_TRUE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_MALWARE,
+      extension_prefs()));
+
+  extension_prefs()->SetExtensionBlocklistState(
+      kExtensionId, BLOCKLISTED_SECURITY_VULNERABILITY);
+  blocklist_prefs::UpdateCurrentGreylistStatesAsAcknowledged(kExtensionId,
+                                                             extension_prefs());
+
+  // The BLOCKLISTED_SECURITY_VULNERABILITY should be acknowledged because it is
+  // in the Safe Browsing greylist state.
+  EXPECT_TRUE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY,
+      extension_prefs()));
+  // BLOCKLISTED_POTENTIALLY_UNWANTED should be cleared because it is not in any
+  // greylist state.
+  EXPECT_FALSE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED,
+      extension_prefs()));
+  // BLOCKLISTED_CWS_POLICY_VIOLATION should be acknowledged because it is in
+  // the Omaha greylist state.
+  EXPECT_TRUE(blocklist_prefs::HasAcknowledgedBlocklistState(
+      kExtensionId, BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION,
+      extension_prefs()));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/blocklist_states_interaction_unittest.cc b/chrome/browser/extensions/blocklist_states_interaction_unittest.cc
index 7415ed9..7506c4f 100644
--- a/chrome/browser/extensions/blocklist_states_interaction_unittest.cc
+++ b/chrome/browser/extensions/blocklist_states_interaction_unittest.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 "chrome/browser/extensions/blocklist_extension_prefs.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/test_blocklist.h"
@@ -161,6 +162,50 @@
   EXPECT_FALSE(ExtensionPrefs::Get(profile())->HasDisableReason(
       kTestExtensionId, disable_reason::DISABLE_GREYLIST));
 }
+
+// 1. The extension is added to the Safe Browsing greylist with
+// BLOCKLISTED_CWS_POLICY_VIOLATION state.
+// 2. The extension is added to the Omaha attribute greylist with
+// _policy_violation attribute.
+// 3. The extension is removed from the Safe Browsing greylist.
+// 4. The extension is removed from the Omaha attribute greylist.
+TEST_F(BlocklistStatesInteractionUnitTest,
+       SafeBrowsingPolicyViolationThenOmahaAttributePolicyViolation) {
+  const ExtensionSet& enabled_extensions = registry()->enabled_extensions();
+  EXPECT_TRUE(enabled_extensions.Contains(kTestExtensionId));
+
+  SetSafeBrowsingBlocklistStateForExtension(kTestExtensionId,
+                                            BLOCKLISTED_CWS_POLICY_VIOLATION);
+  EXPECT_FALSE(enabled_extensions.Contains(kTestExtensionId));
+  EXPECT_TRUE(ExtensionPrefs::Get(profile())->HasDisableReason(
+      kTestExtensionId, disable_reason::DISABLE_GREYLIST));
+
+  // TODO(crbug.com/1180996): Call SetOmahaBlocklistStateForExtension directly
+  // once we start to consume the _policy_violation attribute.
+  blocklist_prefs::AddOmahaBlocklistState(
+      kTestExtensionId, BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION,
+      ExtensionPrefs::Get(profile()));
+  EXPECT_FALSE(enabled_extensions.Contains(kTestExtensionId));
+  EXPECT_TRUE(ExtensionPrefs::Get(profile())->HasDisableReason(
+      kTestExtensionId, disable_reason::DISABLE_GREYLIST));
+
+  SetSafeBrowsingBlocklistStateForExtension(kTestExtensionId, NOT_BLOCKLISTED);
+  // The extension should be kept disabled because it's still in the Omaha
+  // attribute greylist.
+  EXPECT_FALSE(enabled_extensions.Contains(kTestExtensionId));
+  EXPECT_TRUE(ExtensionPrefs::Get(profile())->HasDisableReason(
+      kTestExtensionId, disable_reason::DISABLE_GREYLIST));
+
+  // TODO(crbug.com/1180996): Call SetOmahaBlocklistStateForExtension directly
+  // once we start to consume the _policy_violation attribute.
+  blocklist_prefs::RemoveOmahaBlocklistState(
+      kTestExtensionId, BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION,
+      ExtensionPrefs::Get(profile()));
+  service()->ClearGreylistedAcknowledgedStateAndMaybeReenable(kTestExtensionId);
+  EXPECT_TRUE(enabled_extensions.Contains(kTestExtensionId));
+  EXPECT_FALSE(ExtensionPrefs::Get(profile())->HasDisableReason(
+      kTestExtensionId, disable_reason::DISABLE_GREYLIST));
+}
 #endif
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
index 1928aa2..7aa8e3c 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
@@ -37,12 +37,9 @@
 #include "ui/file_manager/grit/file_manager_gen_resources_map.h"
 #include "ui/file_manager/grit/file_manager_resources_map.h"
 
-#if BUILDFLAG(ENABLE_USE_MEDIA_APP_INK)
+#if BUILDFLAG(ENABLE_INK)
 #include "chromeos/grit/chromeos_media_app_bundle_resources.h"
-#else
-// TODO(crbug/1150244): Remove when deprecated.
-#include "third_party/ink/grit/ink_resources.h"
-#endif  // BUILDFLAG(ENABLE_USE_MEDIA_APP_INK)
+#endif  // BUILDFLAG(ENABLE_INK)
 
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -96,20 +93,14 @@
 #if BUILDFLAG(IS_CHROMEOS_ASH)
     {"chrome_app/chrome_app_icon_32.png", IDR_CHROME_APP_ICON_32},
     {"chrome_app/chrome_app_icon_192.png", IDR_CHROME_APP_ICON_192},
-#if BUILDFLAG(ENABLE_USE_MEDIA_APP_INK)
+#if BUILDFLAG(ENABLE_INK)
     // Built in go/bbsrc/lib/BUILD
     {"pdf/ink/ink_engine_ink.worker.js",
      IDR_MEDIA_APP_INK_ENGINE_INK_WORKER_JS},
     {"pdf/ink/ink_engine_ink.wasm", IDR_MEDIA_APP_INK_ENGINE_INK_WASM},
     {"pdf/ink/ink_lib_binary.js", IDR_MEDIA_APP_EXPORT_CANVAS_BIN_JS},
     {"pdf/ink/ink_loader.js", IDR_MEDIA_APP_INK_JS},
-#else
-    // TODO(crbug/1150244): Remove these when deprecated.
-    {"pdf/ink/ink_lib_binary.js", IDR_INK_LIB_BINARY_JS},
-    {"pdf/ink/wasm_ink.worker.js", IDR_INK_WORKER_JS},
-    {"pdf/ink/wasm_ink.wasm", IDR_INK_WASM},
-    {"pdf/ink/ink_loader.js", IDR_INK_LOADER_JS},
-#endif  // BUILDFLAG(ENABLE_USE_MEDIA_APP_INK)
+#endif  // BUILDFLAG(ENABLE_INK)
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   };
 
diff --git a/chrome/browser/extensions/crx_installer_browsertest.cc b/chrome/browser/extensions/crx_installer_browsertest.cc
index 7d05f18..bcaceda 100644
--- a/chrome/browser/extensions/crx_installer_browsertest.cc
+++ b/chrome/browser/extensions/crx_installer_browsertest.cc
@@ -82,9 +82,9 @@
 namespace {
 
 const char kAppUrl[] = "http://www.google.com";
-const char kAppTitle[] = "Test title";
-const char kAppDescription[] = "Test description";
-const char kShortcutItemName[] = "shortcut";
+const char16_t kAppTitle[] = u"Test title";
+const char16_t kAppDescription[] = u"Test description";
+const char16_t kShortcutItemName[] = u"shortcut";
 const char kShortcutUrl[] = "http://www.google.com/shortcut";
 const char kShortcutIconUrl[] = "http://www.google.com/shortcut/icon.png";
 
@@ -140,14 +140,14 @@
   return bitmap;
 }
 
-WebApplicationInfo CreateWebAppInfo(const char* title,
-                                    const char* description,
+WebApplicationInfo CreateWebAppInfo(const char16_t* title,
+                                    const char16_t* description,
                                     const char* start_url,
                                     int size,
                                     bool create_with_shortcuts) {
   WebApplicationInfo web_app_info;
-  web_app_info.title = base::UTF8ToUTF16(title);
-  web_app_info.description = base::UTF8ToUTF16(description);
+  web_app_info.title = title;
+  web_app_info.description = description;
   web_app_info.start_url = GURL(start_url);
   web_app_info.scope = GURL(start_url);
   web_app_info.icon_bitmaps.any[size] = CreateSquareBitmap(size);
@@ -155,7 +155,7 @@
     WebApplicationShortcutsMenuItemInfo shortcut_item;
     WebApplicationShortcutsMenuItemInfo::Icon icon;
     IconBitmaps shortcut_icon_bitmaps;
-    shortcut_item.name = base::UTF8ToUTF16(kShortcutItemName);
+    shortcut_item.name = kShortcutItemName;
     shortcut_item.url = GURL(kShortcutUrl);
     icon.url = GURL(kShortcutIconUrl);
     icon.square_size_px = size;
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index ae6ef370b..d272afc2 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -898,16 +898,17 @@
 
 void ExtensionService::ClearGreylistedAcknowledgedStateAndMaybeReenable(
     const std::string& extension_id) {
-  // TODO(crbug.com/1180996): Check Omaha blocklist state too.
   bool is_on_sb_list = (extension_prefs_->GetExtensionBlocklistState(
                             extension_id) != NOT_BLOCKLISTED);
-  if (is_on_sb_list) {
+  bool is_on_omaha_list =
+      blocklist_prefs::HasAnyOmahaGreylistState(extension_id, extension_prefs_);
+  if (is_on_sb_list || is_on_omaha_list) {
     return;
   }
   // Clear all acknowledged states so the extension will still get disabled if
   // it is added to the greylist again.
-  blocklist_prefs::ClearAcknowledgedBlocklistState(extension_id,
-                                                   extension_prefs_);
+  blocklist_prefs::ClearAcknowledgedBlocklistStates(extension_id,
+                                                    extension_prefs_);
   RemoveDisableReasonAndMaybeEnable(extension_id,
                                     disable_reason::DISABLE_GREYLIST);
 }
@@ -915,9 +916,15 @@
 void ExtensionService::MaybeDisableGreylistedExtension(
     const std::string& extension_id,
     BitMapBlocklistState new_state) {
-  DCHECK_EQ(SafeBrowsingVerdictHandler::BlocklistStateToBitMapBlocklistState(
-                extension_prefs_->GetExtensionBlocklistState(extension_id)),
-            new_state);
+#if DCHECK_IS_ON()
+  bool has_new_state_on_sb_list =
+      (blocklist_prefs::BlocklistStateToBitMapBlocklistState(
+           extension_prefs_->GetExtensionBlocklistState(extension_id)) ==
+       new_state);
+  bool has_new_state_on_omaha_list = blocklist_prefs::HasOmahaBlocklistState(
+      extension_id, new_state, extension_prefs_);
+  DCHECK(has_new_state_on_sb_list || has_new_state_on_omaha_list);
+#endif
   if (blocklist_prefs::HasAcknowledgedBlocklistState(extension_id, new_state,
                                                      extension_prefs_)) {
     // If the extension is already acknowledged, don't disable it again
@@ -928,19 +935,12 @@
     return;
   }
 
-  // Set the new state to acknowledge immediately because the extension is
-  // disabled silently. Clear the other acknowledged state because when the
-  // state changes to another greylist state in the future, we'd like to disable
-  // the extension again.
-  // TODO(crbug.com/1180996): Clearing all acknowledged state is safe for now,
-  // because there can be only one blocklist state for Safe Browsing blocklist.
-  // Once we start to consume Omaha attributes for greylist, we need to check
-  // both Safe Browsing and Omaha blocklist states before we clear the
-  // acknowledged bit.
-  blocklist_prefs::ClearAcknowledgedBlocklistState(extension_id,
-                                                   extension_prefs_);
-  blocklist_prefs::AddAcknowledgedBlocklistState(extension_id, new_state,
-                                                 extension_prefs_);
+  // Set the current greylist states to acknowledge immediately because the
+  // extension is disabled silently. Clear the other acknowledged state because
+  // when the state changes to another greylist state in the future, we'd like
+  // to disable the extension again.
+  blocklist_prefs::UpdateCurrentGreylistStatesAsAcknowledged(extension_id,
+                                                             extension_prefs_);
   DisableExtension(extension_id, disable_reason::DISABLE_GREYLIST);
 }
 
diff --git a/chrome/browser/extensions/safe_browsing_verdict_handler.cc b/chrome/browser/extensions/safe_browsing_verdict_handler.cc
index 66ca4b1..c3802bf 100644
--- a/chrome/browser/extensions/safe_browsing_verdict_handler.cc
+++ b/chrome/browser/extensions/safe_browsing_verdict_handler.cc
@@ -36,7 +36,8 @@
       // possible that the acknowledged state is not set. Backfill the
       // acknowledged state if that's the case.
       blocklist_prefs::AddAcknowledgedBlocklistState(
-          extension->id(), BlocklistStateToBitMapBlocklistState(state),
+          extension->id(),
+          blocklist_prefs::BlocklistStateToBitMapBlocklistState(state),
           extension_prefs_);
       greylist_.Insert(extension);
     }
@@ -68,27 +69,6 @@
 }
 
 // static
-BitMapBlocklistState
-SafeBrowsingVerdictHandler::BlocklistStateToBitMapBlocklistState(
-    BlocklistState blocklist_state) {
-  switch (blocklist_state) {
-    case NOT_BLOCKLISTED:
-      return BitMapBlocklistState::NOT_BLOCKLISTED;
-    case BLOCKLISTED_MALWARE:
-      return BitMapBlocklistState::BLOCKLISTED_MALWARE;
-    case BLOCKLISTED_SECURITY_VULNERABILITY:
-      return BitMapBlocklistState::BLOCKLISTED_SECURITY_VULNERABILITY;
-    case BLOCKLISTED_CWS_POLICY_VIOLATION:
-      return BitMapBlocklistState::BLOCKLISTED_CWS_POLICY_VIOLATION;
-    case BLOCKLISTED_POTENTIALLY_UNWANTED:
-      return BitMapBlocklistState::BLOCKLISTED_POTENTIALLY_UNWANTED;
-    case BLOCKLISTED_UNKNOWN:
-      NOTREACHED() << "The unknown state should not be added into prefs.";
-      return BitMapBlocklistState::NOT_BLOCKLISTED;
-  }
-}
-
-// static
 void SafeBrowsingVerdictHandler::Partition(const ExtensionIdSet& before,
                                            const ExtensionIdSet& after,
                                            const ExtensionIdSet& unchanged,
@@ -140,7 +120,8 @@
     BlocklistState greylist_state = state_map.find(id)->second;
     extension_prefs_->SetExtensionBlocklistState(id, greylist_state);
     extension_service_->MaybeDisableGreylistedExtension(
-        id, BlocklistStateToBitMapBlocklistState(greylist_state));
+        id,
+        blocklist_prefs::BlocklistStateToBitMapBlocklistState(greylist_state));
   }
 }
 
diff --git a/chrome/browser/extensions/safe_browsing_verdict_handler.h b/chrome/browser/extensions/safe_browsing_verdict_handler.h
index ebc31a0..7eaf1f1 100644
--- a/chrome/browser/extensions/safe_browsing_verdict_handler.h
+++ b/chrome/browser/extensions/safe_browsing_verdict_handler.h
@@ -33,10 +33,6 @@
                         ExtensionIdSet* no_longer,
                         ExtensionIdSet* not_yet);
 
-  // Converts BlocklistState to BitMapBlocklistState.
-  static BitMapBlocklistState BlocklistStateToBitMapBlocklistState(
-      BlocklistState blocklist_state);
-
   // Initializes and load greylist from prefs.
   void Init();
 
diff --git a/chrome/browser/extensions/safe_browsing_verdict_handler_unittest.cc b/chrome/browser/extensions/safe_browsing_verdict_handler_unittest.cc
index 288b7240..25d5c5f 100644
--- a/chrome/browser/extensions/safe_browsing_verdict_handler_unittest.cc
+++ b/chrome/browser/extensions/safe_browsing_verdict_handler_unittest.cc
@@ -459,7 +459,7 @@
   EXPECT_FALSE(disabled_extensions.Contains(kGood0));
 
   // To simulate an old Chrome version, the acknowledged state is cleared.
-  blocklist_prefs::ClearAcknowledgedBlocklistState(
+  blocklist_prefs::ClearAcknowledgedBlocklistStates(
       kGood0, ExtensionPrefs::Get(profile()));
   // The browser is restarted.
   service()->safe_browsing_verdict_handler_.Init();
diff --git a/chrome/browser/feed/android/java/res/values/dimens.xml b/chrome/browser/feed/android/java/res/values/dimens.xml
index 3de11764..d8ec3ded 100644
--- a/chrome/browser/feed/android/java/res/values/dimens.xml
+++ b/chrome/browser/feed/android/java/res/values/dimens.xml
@@ -16,4 +16,7 @@
     <dimen name="web_feed_dialog_details_padding_top">16dp</dimen>
     <dimen name="web_feed_dialog_details_padding_bottom">8dp</dimen>
     <dimen name="web_feed_dialog_text_horizontal_padding">16dp</dimen>
-</resources>
\ No newline at end of file
+
+    <!-- Intro accelerator dimens. -->
+    <dimen name="web_feed_intro_y_inset">17dp</dimen>
+</resources>
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedFollowIntroView.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedFollowIntroView.java
index 2d3f91b..9be0b9b 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedFollowIntroView.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/webfeed/WebFeedFollowIntroView.java
@@ -25,6 +25,9 @@
 
 /**
  * Manages the view of the WebFeed follow intro.
+ *
+ * This is the chip that shows up under the 3-dot menu informing users that this is a page
+ * they can follow.
  */
 class WebFeedFollowIntroView {
     private static final int sAcceleratorTimeout = 10 * 1000; // 10 seconds
@@ -100,8 +103,8 @@
 
     private ViewRectProvider createRectProvider() {
         ViewRectProvider rectProvider = new ViewRectProvider(mMenuButtonAnchorView);
-        int yInsetPx = mActivity.getResources().getDimensionPixelOffset(
-                R.dimen.iph_text_bubble_menu_anchor_y_inset);
+        int yInsetPx =
+                mActivity.getResources().getDimensionPixelOffset(R.dimen.web_feed_intro_y_inset);
         Rect insetRect = new Rect(0, 0, 0, yInsetPx);
         rectProvider.setInsetPx(insetRect);
 
diff --git a/chrome/browser/history_clusters/history_clusters_tab_helper.cc b/chrome/browser/history_clusters/history_clusters_tab_helper.cc
index 93f1711..ee955606 100644
--- a/chrome/browser/history_clusters/history_clusters_tab_helper.cc
+++ b/chrome/browser/history_clusters/history_clusters_tab_helper.cc
@@ -39,7 +39,7 @@
     base::OnceCallback<void(history::URLRow, history::VisitVector)>;
 
 // Gets the 2 most recent visits to a URL. Used to associate a memories visit
-// with its history rows and compute the |duration_since_last_visit|
+// with its history rows and compute the `duration_since_last_visit`
 // context annotation.
 class GetMostRecentVisitsToUrl : public history::HistoryDBTask {
  public:
@@ -87,7 +87,7 @@
   return false;
 }
 
-// Pass in a separate |url| parameter to ensure that we check the same URL that
+// Pass in a separate `url` parameter to ensure that we check the same URL that
 // is being logged in History.
 bool IsPageBookmarked(content::WebContents* contents, const GURL& url) {
   DCHECK(contents);
@@ -148,10 +148,10 @@
       .is_existing_bookmark = IsPageBookmarked(web_contents(), url);
 
   if (auto* history_service = GetHistoryService()) {
-    // This |GetMostRecentVisitsToUrl| task will find at least 1 visit since
-    // |HistoryTabHelper::UpdateHistoryForNavigation()|, invoked prior to
-    // |OnUpdatedHistoryForNavigation()|, will have posted a task to add the
-    // visit associated to |incomplete_visit_context_annotations|.
+    // This `GetMostRecentVisitsToUrl` task will find at least 1 visit since
+    // `HistoryTabHelper::UpdateHistoryForNavigation()`, invoked prior to
+    // `OnUpdatedHistoryForNavigation()`, will have posted a task to add the
+    // visit associated to `incomplete_visit_context_annotations`.
     history_service->ScheduleDBTask(
         FROM_HERE,
         std::make_unique<GetMostRecentVisitsToUrl>(
@@ -210,15 +210,15 @@
       memories_service->GetIncompleteVisitContextAnnotations(navigation_id);
   incomplete_visit_context_annotations.context_annotations.page_end_reason =
       page_end_reason;
-  // |RecordPageEndMetricsIfNeeded()| will fail to complete the
-  // `IncompleteVisitContextAnnotations` as |ukm_page_end_signals| hasn't been
+  // `RecordPageEndMetricsIfNeeded()` will fail to complete the
+  // `IncompleteVisitContextAnnotations` as `ukm_page_end_signals` hasn't been
   // set yet, but it will record metrics if needed (i.e. not already recorded)
-  // and possible (i.e. the history request has resolved and |history_rows| have
+  // and possible (i.e. the history request has resolved and `history_rows` have
   // been recorded).
   RecordPageEndMetricsIfNeeded(navigation_id);
   // Make a copy of the context annotations as the referenced
   // incomplete_visit_context_annotations may be destroyed in
-  // |CompleteVisitContextAnnotationsIfReady()|.
+  // `CompleteVisitContextAnnotationsIfReady()`.
   auto context_annotations_copy =
       incomplete_visit_context_annotations.context_annotations;
   DCHECK(
@@ -256,7 +256,7 @@
   incomplete_visit_context_annotations.status.navigation_ended = true;
   // Don't record page end metrics if the history rows request hasn't resolved
   // because some of the metrics rely on |url_row.url()|. Setting
-  // |navigation_ended| above will ensure |RecordPageEndMetricsIfNeeded()| is
+  // `navigation_ended` above will ensure `RecordPageEndMetricsIfNeeded()` is
   // re-invoked once the history request resolves.
   if (!incomplete_visit_context_annotations.status.history_rows)
     return;
diff --git a/chrome/browser/history_clusters/history_clusters_tab_helper.h b/chrome/browser/history_clusters/history_clusters_tab_helper.h
index a1aacc1b..d023a84 100644
--- a/chrome/browser/history_clusters/history_clusters_tab_helper.h
+++ b/chrome/browser/history_clusters/history_clusters_tab_helper.h
@@ -32,22 +32,22 @@
   // Called when the user shares the URL via mobile sharing hub.
   void OnOmniboxUrlShared();
 
-  // Called by |HistoryTabHelper| right after submitting a new navigation for
-  // |web_contents()| to HistoryService. We need close coordination with
+  // Called by `HistoryTabHelper` right after submitting a new navigation for
+  // `web_contents()` to HistoryService. We need close coordination with
   // History's conception of the visit lifetime.
   void OnUpdatedHistoryForNavigation(int64_t navigation_id, const GURL& url);
 
   // Invoked for navigations that are tracked by UKM. Specifically, same-app
   // navigations aren't tracked individually in UKM and therefore won't receive
-  // UKM's |page_end_reason| signal. Visits for such navigations will be
+  // UKM's `page_end_reason` signal. Visits for such navigations will be
   // completed as soon as both the history rows query completes and the history
   // navigation ends. Visits that are tracked by UKM will additionally wait for
-  // a UKM |page_end_reason|.
+  // a UKM `page_end_reason`.
   void TagNavigationAsExpectingUkmNavigationComplete(int64_t navigation_id);
 
-  // Updates the visit with |navigation_id| with |page_end_reason|. This also
+  // Updates the visit with `navigation_id` with `page_end_reason`. This also
   // records the page end metrics, if necessary. It returns a copy of the
-  // completed |AnnotatedVisit|'s |VisitContextAnnotations|, if available.
+  // completed `AnnotatedVisit`'s `VisitContextAnnotations`, if available.
   //
   // This should only be called once per navigation, as this may flush the visit
   // to MemoriesService.
@@ -74,17 +74,17 @@
   void WebContentsDestroyed() override;
 
   // Helper functions to return the memories and history services.
-  // |GetMemoriesService()| will never return nullptr.
+  // `GetMemoriesService()` will never return nullptr.
   history_clusters::MemoriesService* GetMemoriesService();
-  // |GetHistoryService()| may return nullptr.
+  // `GetHistoryService()` may return nullptr.
   history::HistoryService* GetHistoryService();
 
   // The navigations initiated in this tab. Used for:
-  // 1) On |OnUpdatedHistoryForNavigation()|, the last navigation will be
+  // 1) On `OnUpdatedHistoryForNavigation()`, the last navigation will be
   //    assumed ended and its page end metrics will be recorded.
-  // 2) On |OnOmniboxUrlCopied()|, the last navigation will be assumed to be the
+  // 2) On `OnOmniboxUrlCopied()`, the last navigation will be assumed to be the
   //    subject.
-  // 3) On |WebContentsDestroyed()|, the |AnnotatedVisit| corresponding to these
+  // 3) On `WebContentsDestroyed()`, the `AnnotatedVisit` corresponding to these
   //    IDs will be assumed ended and their page end metrics will be recorded if
   //    they haven't already.
   std::vector<int64_t> navigation_ids_;
diff --git a/chrome/browser/history_clusters/history_clusters_tab_helper_unittest.cc b/chrome/browser/history_clusters/history_clusters_tab_helper_unittest.cc
index 2b7ce72..a114491 100644
--- a/chrome/browser/history_clusters/history_clusters_tab_helper_unittest.cc
+++ b/chrome/browser/history_clusters/history_clusters_tab_helper_unittest.cc
@@ -31,8 +31,8 @@
 
 namespace {
 
-// Used to invoke a callback after |WebContentsDestroyed()| is invoked, but
-// before the |WebContents| has been destroyed.
+// Used to invoke a callback after `WebContentsDestroyed()` is invoked, but
+// before the `WebContents` has been destroyed.
 class OnDestroyWebContentsObserver : content::WebContentsObserver {
  public:
   OnDestroyWebContentsObserver(content::WebContents* web_contents,
@@ -47,7 +47,7 @@
   base::OnceCallback<void()> callback_;
 };
 
-// Returns a Time that's |seconds| seconds after Windows epoch.
+// Returns a Time that's `seconds` seconds after Windows epoch.
 base::Time IntToTime(int seconds) {
   return base::Time::FromDeltaSinceWindowsEpoch(
       base::TimeDelta::FromSeconds(seconds));
@@ -127,25 +127,25 @@
 
 // There are multiple events that occur with nondeterministic order:
 // - History (w/ N visits): a history navigation occurs,
-//   |OnUpdatedHistoryForNavigation()| is invoked, and a history query is made
+//   `OnUpdatedHistoryForNavigation()` is invoked, and a history query is made
 //   which will (possibly after other events in the timeline) resolve with N
 //   visits.
 // - History resolve: the history query made above resolves.
-// - Copy: the omnibox URL is copied and |OnOmniboxUrlCopied()| is invoked.
+// - Copy: the omnibox URL is copied and `OnOmniboxUrlCopied()` is invoked.
 // - Expect UKM: UKM begins tracking a navigation and
-//   |TagNavigationAsExpectingUkmNavigationComplete()| is invoked.
-// - UKM: UKM ends tracking a navigation and |OnUkmNavigationComplete()| is
+//   `TagNavigationAsExpectingUkmNavigationComplete()` is invoked.
+// - UKM: UKM ends tracking a navigation and `OnUkmNavigationComplete()` is
 //   invoked.
-// - Destroy: The |WebContents| is destroyed (i.e. the tab is closed) and
-//   |WebContentsDestroyed()| is invoked.
+// - Destroy: The `WebContents` is destroyed (i.e. the tab is closed) and
+//   `WebContentsDestroyed()` is invoked.
 // The below tests test different permutations of these events.
 
 // History (w/ 0 visits) -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked but its history request isn't
+// 1) `OnUpdatedHistoryForNavigation()` is invoked but its history request isn't
 //     resolved (because either the tab is closed too soon or there are no
 //     matching visits).
-// 2) |WebContentsDestroyed()| is invoked.
+// 2) `WebContentsDestroyed()` is invoked.
 // Then: 0 visits should be committed.
 TEST_F(HistoryClustersTabHelperTest, NavigationWith0HistoryVisits) {
   AddToHistory(GURL{"https://google.com"});
@@ -157,10 +157,10 @@
 
 // History (w/ 1 visit) -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked and 1 history visit are
+// 1) `OnUpdatedHistoryForNavigation()` is invoked and 1 history visit are
 //    fetched.
-// 2) |WebContentsDestroyed()| is invoked.
-// Then: 1 visit should be committed w/o |duration_since_last_visit|.
+// 2) `WebContentsDestroyed()` is invoked.
+// Then: 1 visit should be committed w/o `duration_since_last_visit`.
 TEST_F(HistoryClustersTabHelperTest, NavigationWith1HistoryVisits) {
   AddToHistory(GURL{"https://github.com"});
   helper_->OnUpdatedHistoryForNavigation(0, GURL{"https://github.com"});
@@ -179,9 +179,9 @@
 
 // History (w/ 2 visits) -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked and 2 history visits are
+// 1) `OnUpdatedHistoryForNavigation()` is invoked and 2 history visits are
 //    fetched.
-// 2) |WebContentsDestroyed()| is invoked.
+// 2) `WebContentsDestroyed()` is invoked.
 // Then: 1 visit should be committed.
 TEST_F(HistoryClustersTabHelperTest, NavigationWith2HistoryVisits) {
   AddToHistory(GURL{"https://github.com"}, u"Title", IntToTime(19));
@@ -200,13 +200,13 @@
 
 // History (w/ 0 visits) -> history (w/ 0 visits) -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked but its history request isn't
+// 1) `OnUpdatedHistoryForNavigation()` is invoked but its history request isn't
 //     resolved (because either the tab is closed too soon or there are no
 //     matching visits).
-// 2) |OnUpdatedHistoryForNavigation()| is invoked but its history request isn't
+// 2) `OnUpdatedHistoryForNavigation()` is invoked but its history request isn't
 //     resolved (because either the tab is closed too soon or there are no
 //     matching visits).
-// 3) |WebContentsDestroyed()| is invoked.
+// 3) `WebContentsDestroyed()` is invoked.
 // Then: 0 visits should be committed.
 TEST_F(HistoryClustersTabHelperTest, TwoNavigationsWith0HistoryVisits) {
   helper_->OnUpdatedHistoryForNavigation(0, GURL{"https://github.com"});
@@ -217,11 +217,11 @@
 
 // History (w/ 2 visits) -> history (w/ 2 visits) -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked and 2 history visits are
+// 1) `OnUpdatedHistoryForNavigation()` is invoked and 2 history visits are
 //    fetched.
-// 2) |OnUpdatedHistoryForNavigation()| is invoked and 2 history visits are
+// 2) `OnUpdatedHistoryForNavigation()` is invoked and 2 history visits are
 //    fetched.
-// 3) |WebContentsDestroyed()| is invoked.
+// 3) `WebContentsDestroyed()` is invoked.
 // Then: 2 visits should be committed.
 TEST_F(HistoryClustersTabHelperTest, TwoNavigationsWith2HistoryVisits) {
   AddToHistory(GURL{"https://github.com"});
@@ -247,8 +247,8 @@
 
 // History -> destroy -> history resolve
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |WebContentsDestroyed()| is invoked before the previous history request is
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `WebContentsDestroyed()` is invoked before the previous history request is
 //    resolved.
 // Then: 0 visits should be committed.
 TEST_F(HistoryClustersTabHelperTest, HistoryResolvedAfterDestroy) {
@@ -261,10 +261,10 @@
 
 // History -> history -> history resolve -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |OnUpdatedHistoryForNavigation()| is invoked before the previous history
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `OnUpdatedHistoryForNavigation()` is invoked before the previous history
 //    request is resolved.
-// 3) |WebContentsDestroyed()| is invoked.
+// 3) `WebContentsDestroyed()` is invoked.
 // Then: 2 visits should be committed.
 TEST_F(HistoryClustersTabHelperTest, HistoryResolvedAfter2ndNavigation) {
   AddToHistory(GURL{"https://google.com"});
@@ -288,16 +288,16 @@
 
 // History -> copy -> history resolve -> history -> history -> copy -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |OnOmniboxUrlCopied()| is invoked before the previous history request is
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `OnOmniboxUrlCopied()` is invoked before the previous history request is
 //    resolved.
-// 3) |OnUpdatedHistoryForNavigation()| is invoked.
-// 4) |OnUpdatedHistoryForNavigation()| is invoked.
-// 5) |OnOmniboxUrlCopied()| is invoked after the previous history request is
+// 3) `OnUpdatedHistoryForNavigation()` is invoked.
+// 4) `OnUpdatedHistoryForNavigation()` is invoked.
+// 5) `OnOmniboxUrlCopied()` is invoked after the previous history request is
 //    resolved
-// 6) |WebContentsDestroyed()| is invoked.
+// 6) `WebContentsDestroyed()` is invoked.
 // Then: 3 visits should be committed; the 1st and 3rd should have
-//       |omnibox_url_copied| true.
+//       `omnibox_url_copied` true.
 TEST_F(HistoryClustersTabHelperTest, UrlsCopied) {
   AddToHistory(GURL{"https://github.com"});
   AddToHistory(GURL{"https://google.com"});
@@ -330,11 +330,11 @@
 
 // History -> expect UKM -> UKM -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked.
-// 3) |OnUkmNavigationComplete()| is invoked.
-// 4) |WebContentsDestroyed()| is invoked.
-// Then: 1 visit should be committed after step 3 w/ a |page_end_reason|.
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked.
+// 3) `OnUkmNavigationComplete()` is invoked.
+// 4) `WebContentsDestroyed()` is invoked.
+// Then: 1 visit should be committed after step 3 w/ a `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest, NavigationWithUkmBeforeDestroy) {
   AddToHistory(GURL{"https://github.com"});
   helper_->OnUpdatedHistoryForNavigation(0, GURL{"https://github.com"});
@@ -352,19 +352,19 @@
 
 // History -> expect UKM -> destroy -> UKM
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked.
-// 3) |WebContentsDestroyed()| is invoked.
-// 4) |OnUkmNavigationComplete()| is invoked.
-// Then: 1 visit should be committed after step 4 w/ a |page_end_reason|.
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked.
+// 3) `WebContentsDestroyed()` is invoked.
+// 4) `OnUkmNavigationComplete()` is invoked.
+// Then: 1 visit should be committed after step 4 w/ a `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest, NavigationWithUkmAfterDestroy) {
   AddToHistory(GURL{"https://github.com"});
   helper_->OnUpdatedHistoryForNavigation(0, GURL{"https://github.com"});
   history::BlockUntilHistoryProcessesPendingRequests(history_service_);
   helper_->TagNavigationAsExpectingUkmNavigationComplete(0);
 
-  // Invoke |OnUkmNavigationComplete()| after |WebContentsDestroyed()| is
-  // invoked, but before the |WebContents| has been destroyed.
+  // Invoke `OnUkmNavigationComplete()` after `WebContentsDestroyed()` is
+  // invoked, but before the `WebContents` has been destroyed.
   EXPECT_TRUE(GetVisits().empty());
   OnDestroyWebContentsObserver test_web_contents_observer(
       web_contents(), base::BindLambdaForTesting([&]() {
@@ -384,11 +384,11 @@
 
 // Expect UKM -> history -> UKM -> destroy
 // When:
-// 1) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked.
-// 2) |OnUpdatedHistoryForNavigation()| is invoked.
-// 3) |OnUkmNavigationComplete()| is invoked.
-// 4) |WebContentsDestroyed()| is invoked.
-// Then: 1 visit should be committed after step 3 w/ a |page_end_reason|.
+// 1) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked.
+// 2) `OnUpdatedHistoryForNavigation()` is invoked.
+// 3) `OnUkmNavigationComplete()` is invoked.
+// 4) `WebContentsDestroyed()` is invoked.
+// Then: 1 visit should be committed after step 3 w/ a `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest,
        NavigationAfterUkmExpectAndWithUkmBeforeDestroy) {
   AddToHistory(GURL{"https://github.com"});
@@ -407,12 +407,12 @@
 
 // History -> expect UKM -> UKM -> destroy -> history resolve
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked.
-// 3) |OnUkmNavigationComplete()| is invoked.
-// 4) |WebContentsDestroyed()| is invoked before the previous history request is
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked.
+// 3) `OnUkmNavigationComplete()` is invoked.
+// 4) `WebContentsDestroyed()` is invoked before the previous history request is
 //    resolved.
-// Then: 1 visit should be committed after step 4 w/ a |page_end_reason|.
+// Then: 1 visit should be committed after step 4 w/ a `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest,
        NavigationWithUkmBeforeDestroyAndHistoryResolvedAfterDestroy) {
   AddToHistory(GURL{"https://github.com"});
@@ -421,8 +421,8 @@
   helper_->OnUkmNavigationComplete(0,
                                    page_load_metrics::PageEndReason::END_OTHER);
 
-  // Resolve the history request after |WebContentsDestroyed()| is invoked, but
-  // before the |WebContents| has been destroyed.
+  // Resolve the history request after `WebContentsDestroyed()` is invoked, but
+  // before the `WebContents` has been destroyed.
   EXPECT_TRUE(GetVisits().empty());
   OnDestroyWebContentsObserver test_web_contents_observer(
       web_contents(), base::BindLambdaForTesting([&]() {
@@ -443,14 +443,14 @@
 
 // Expect History -> expect UKM 1 -> UKM 1 -> history -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked for the above
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked for the above
 //    navigation.
-// 3) |OnUkmNavigationComplete()| is invoked for the above navigation.
-// 4) |OnUpdatedHistoryForNavigation()| is invoked.
-// 5) |WebContentsDestroyed()| is invoked.
+// 3) `OnUkmNavigationComplete()` is invoked for the above navigation.
+// 4) `OnUpdatedHistoryForNavigation()` is invoked.
+// 5) `WebContentsDestroyed()` is invoked.
 // Then: 2 visits should be committed after steps 3 and 5; the 1st should have a
-//       |page_end_reason|.
+//       `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest,
        TwoNavigationsWith1stUkmBefore2ndNavigation) {
   AddToHistory(GURL{"https://google.com"});
@@ -479,14 +479,14 @@
 
 // Expect History -> Expect UKM 1 -> history -> UKM 1 -> destroy
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked for the above
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked for the above
 //    navigation.
-// 3) |OnUpdatedHistoryForNavigation()| is invoked.
-// 4) |OnUkmNavigationComplete()| is invoked for the 1st navigation.
-// 5) |WebContentsDestroyed()| is invoked.
+// 3) `OnUpdatedHistoryForNavigation()` is invoked.
+// 4) `OnUkmNavigationComplete()` is invoked for the 1st navigation.
+// 5) `WebContentsDestroyed()` is invoked.
 // Then: 2 visits should be committed after steps 4 and 5; the 1st should have a
-//       |page_end_reason|.
+//       `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest,
        TwoNavigationsWith1stUkmAfter2ndNavigation) {
   AddToHistory(GURL{"https://google.com"});
@@ -513,14 +513,14 @@
 
 // Expect History -> Expect UKM 2 -> history -> destroy -> UKM 2
 // When:
-// 1) |OnUpdatedHistoryForNavigation()| is invoked.
-// 2) |TagNavigationAsExpectingUkmNavigationComplete()| is invoked for the below
+// 1) `OnUpdatedHistoryForNavigation()` is invoked.
+// 2) `TagNavigationAsExpectingUkmNavigationComplete()` is invoked for the below
 //    navigation.
-// 3) |OnUpdatedHistoryForNavigation()| is invoked.
-// 4) |WebContentsDestroyed()| is invoked.
-// 5) |OnUkmNavigationComplete()| is invoked for the 2nd navigation.
+// 3) `OnUpdatedHistoryForNavigation()` is invoked.
+// 4) `WebContentsDestroyed()` is invoked.
+// 5) `OnUkmNavigationComplete()` is invoked for the 2nd navigation.
 // Then: 2 visits should be committed after steps 2 and 5; the 2nd should have a
-//       |page_end_reason|.
+//       `page_end_reason`.
 TEST_F(HistoryClustersTabHelperTest, TwoNavigations2ndUkmBefore2ndNavigation) {
   AddToHistory(GURL{"https://google.com"});
   AddToHistory(GURL{"https://github.com"});
@@ -536,8 +536,8 @@
   helper_->OnUpdatedHistoryForNavigation(1, GURL{"https://google.com"});
   history::BlockUntilHistoryProcessesPendingRequests(history_service_);
 
-  // Invoke |OnUkmNavigationComplete()| after |WebContentsDestroyed()| is
-  // invoked, but before the |WebContents| has been destroyed.
+  // Invoke `OnUkmNavigationComplete()` after `WebContentsDestroyed()` is
+  // invoked, but before the `WebContents` has been destroyed.
   ASSERT_EQ(GetVisits().size(), 1u);
   OnDestroyWebContentsObserver test_web_contents_observer(
       web_contents(), base::BindLambdaForTesting([&]() {
diff --git a/chrome/browser/importer/firefox_profile_lock_unittest.cc b/chrome/browser/importer/firefox_profile_lock_unittest.cc
index 972ed1d..52a7a47 100644
--- a/chrome/browser/importer/firefox_profile_lock_unittest.cc
+++ b/chrome/browser/importer/firefox_profile_lock_unittest.cc
@@ -36,10 +36,8 @@
   base::FilePath lock_file_path =
       test_path.Append(FirefoxProfileLock::kLockFileName);
 
-  std::unique_ptr<FirefoxProfileLock> lock;
-  EXPECT_EQ(static_cast<FirefoxProfileLock*>(NULL), lock.get());
   EXPECT_FALSE(base::PathExists(lock_file_path));
-  lock = std::make_unique<FirefoxProfileLock>(test_path);
+  auto lock = std::make_unique<FirefoxProfileLock>(test_path);
   EXPECT_TRUE(lock->HasAcquired());
   EXPECT_TRUE(base::PathExists(lock_file_path));
   lock->Unlock();
@@ -75,9 +73,7 @@
   base::CloseFile(lock_file);
   EXPECT_TRUE(base::PathExists(lock_file_path));
 
-  std::unique_ptr<FirefoxProfileLock> lock;
-  EXPECT_EQ(static_cast<FirefoxProfileLock*>(NULL), lock.get());
-  lock = std::make_unique<FirefoxProfileLock>(test_path);
+  auto lock = std::make_unique<FirefoxProfileLock>(test_path);
   EXPECT_TRUE(lock->HasAcquired());
   lock->Unlock();
   EXPECT_FALSE(lock->HasAcquired());
@@ -90,14 +86,10 @@
 TEST_F(FirefoxProfileLockTest, ProfileLockContention) {
   base::FilePath test_path = temp_dir_.GetPath();
 
-  std::unique_ptr<FirefoxProfileLock> lock1;
-  EXPECT_EQ(static_cast<FirefoxProfileLock*>(NULL), lock1.get());
-  lock1 = std::make_unique<FirefoxProfileLock>(test_path);
+  auto lock1 = std::make_unique<FirefoxProfileLock>(test_path);
   EXPECT_TRUE(lock1->HasAcquired());
 
-  std::unique_ptr<FirefoxProfileLock> lock2;
-  EXPECT_EQ(static_cast<FirefoxProfileLock*>(NULL), lock2.get());
-  lock2 = std::make_unique<FirefoxProfileLock>(test_path);
+  auto lock2 = std::make_unique<FirefoxProfileLock>(test_path);
   EXPECT_FALSE(lock2->HasAcquired());
 
   lock1->Unlock();
diff --git a/chrome/browser/interstitials/security_interstitial_idn_test.cc b/chrome/browser/interstitials/security_interstitial_idn_test.cc
index 37147f69..7009e4c 100644
--- a/chrome/browser/interstitials/security_interstitial_idn_test.cc
+++ b/chrome/browser/interstitials/security_interstitial_idn_test.cc
@@ -20,7 +20,7 @@
 
 testing::AssertionResult SecurityInterstitialIDNTest::VerifyIDNDecoded() const {
   const char kHostname[] = "xn--d1abbgf6aiiy.xn--p1ai";
-  const char kHostnameUnicode[] = "президент.рф";
+  const char16_t kHostnameUnicode[] = u"президент.рф";
   std::string request_url_spec = base::StringPrintf("https://%s/", kHostname);
   GURL request_url(request_url_spec);
 
@@ -35,9 +35,9 @@
       net::ERR_BLOCKED_BY_CLIENT);
   observer.Wait();
   delete blocking_page;
-  if (ui_test_utils::FindInPage(contents, base::UTF8ToUTF16(kHostnameUnicode),
-                                true /*forward*/, true /*case_sensitive*/,
-                                nullptr, nullptr) == 1) {
+  if (ui_test_utils::FindInPage(contents, kHostnameUnicode, true /*forward*/,
+                                true /*case_sensitive*/, nullptr,
+                                nullptr) == 1) {
     return testing::AssertionSuccess();
   }
   return testing::AssertionFailure() << "Interstitial not displaying text";
diff --git a/chrome/browser/net/stub_resolver_config_reader.cc b/chrome/browser/net/stub_resolver_config_reader.cc
index 600fb12..d1cf055a 100644
--- a/chrome/browser/net/stub_resolver_config_reader.cc
+++ b/chrome/browser/net/stub_resolver_config_reader.cc
@@ -169,9 +169,8 @@
   pref_change_registrar_.Add(prefs::kBuiltInDnsClientEnabled, pref_callback);
   pref_change_registrar_.Add(prefs::kDnsOverHttpsMode, pref_callback);
   pref_change_registrar_.Add(prefs::kDnsOverHttpsTemplates, pref_callback);
-
-  // TODO(crbug.com/1203427): Watch for `prefs::kAdditionalDnsQueryTypesEnabled`
-  // changes.
+  pref_change_registrar_.Add(prefs::kAdditionalDnsQueryTypesEnabled,
+                             pref_callback);
 
   parental_controls_delay_timer_.Start(
       FROM_HERE, kParentalControlsCheckDelay,
@@ -386,11 +385,10 @@
   }
 
   if (update_network_service) {
-    // TODO(crbug.com/1203427): Configure for
-    // `prefs::kAdditionalDnsQueryTypesEnabled` value.
     content::GetNetworkService()->ConfigureStubHostResolver(
         GetInsecureStubResolverEnabled(), secure_dns_mode,
-        std::move(servers_mojo));
+        std::move(servers_mojo),
+        local_state_->GetBoolean(prefs::kAdditionalDnsQueryTypesEnabled));
   }
 
   return SecureDnsConfig(secure_dns_mode, std::move(dns_over_https_servers),
diff --git a/chrome/browser/notifications/scheduler/internal/notification_scheduler_unittest.cc b/chrome/browser/notifications/scheduler/internal/notification_scheduler_unittest.cc
index 0e821bb..96fc8c9 100644
--- a/chrome/browser/notifications/scheduler/internal/notification_scheduler_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/notification_scheduler_unittest.cc
@@ -38,7 +38,7 @@
 namespace {
 
 const char kGuid[] = "guid";
-const char kTitle[] = "title";
+const char16_t kTitle[] = u"title";
 
 class NotificationSchedulerTest : public testing::Test {
  public:
@@ -293,8 +293,8 @@
   OnStartTask();
 }
 
-MATCHER_P(NotifcationDataEq, title, "Verify notification data.") {
-  EXPECT_EQ(arg->title, base::UTF8ToUTF16(title));
+MATCHER_P(NotificationDataEq, title, "Verify notification data.") {
+  EXPECT_EQ(arg->title, title);
   return true;
 }
 
@@ -313,7 +313,7 @@
       std::make_unique<NotificationEntry>(SchedulerClientType::kTest1, kGuid);
   EXPECT_CALL(
       *display_agent(),
-      ShowNotification(NotifcationDataEq(kTitle),
+      ShowNotification(NotificationDataEq(kTitle),
                        SystemDataEq(SchedulerClientType::kTest1, kGuid)));
   DisplayDecider::Results result({kGuid});
   EXPECT_CALL(*display_decider(), FindNotificationsToShow(_, _, _))
@@ -333,7 +333,7 @@
           [&](std::unique_ptr<NotificationData> notification_data,
               NotificationSchedulerClient::NotificationDataCallback callback) {
             // The client updates the notification data here.
-            notification_data->title = base::UTF8ToUTF16(kTitle);
+            notification_data->title = kTitle;
             std::move(callback).Run(std::move(notification_data));
           }));
 
diff --git a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
index 921c364..a1de226 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
+++ b/chrome/browser/notifications/scheduler/internal/scheduled_notification_manager_unittest.cc
@@ -30,7 +30,7 @@
 
 const char kGuid[] = "test_guid_1234";
 const char kNonExistentGuid[] = "guid_non_existent";
-const char kTitle[] = "test_title";
+const char16_t kTitle[] = u"test_title";
 const char kSmallIconUuid[] = "test_small_icon_uuid";
 const char kLargeIconUuid[] = "test_large_icon_uuid";
 
@@ -298,7 +298,7 @@
 TEST_F(ScheduledNotificationManagerTest, ScheduleNotification) {
   InitWithData(std::vector<NotificationEntry>());
   NotificationData notification_data;
-  notification_data.title = base::UTF8ToUTF16(kTitle);
+  notification_data.title = kTitle;
   ScheduleParams schedule_params;
   schedule_params.priority = ScheduleParams::Priority::kLow;
   auto params = std::make_unique<NotificationParams>(
@@ -333,7 +333,7 @@
   EXPECT_NE(entry->create_time, base::Time());
 
   // TODO(xingliu): change these to compare with operator==.
-  EXPECT_EQ(base::UTF16ToUTF8(entry->notification_data.title), kTitle);
+  EXPECT_EQ(entry->notification_data.title, kTitle);
   EXPECT_EQ(entry->schedule_params.priority, ScheduleParams::Priority::kLow);
 
   // Verify that |enable_ihnr_buttons| will add the helpful/unhelpful buttons.
@@ -349,7 +349,7 @@
 TEST_F(ScheduledNotificationManagerTest, ScheduleInvalidNotification) {
   InitWithData(std::vector<NotificationEntry>());
   NotificationData notification_data;
-  notification_data.title = base::UTF8ToUTF16(kTitle);
+  notification_data.title = kTitle;
   ScheduleParams schedule_params;
   // Client type kTest3 is not registered.
   auto params = std::make_unique<NotificationParams>(
@@ -367,7 +367,7 @@
   InitWithData(std::vector<NotificationEntry>({entry}));
 
   NotificationData notification_data;
-  notification_data.title = base::UTF8ToUTF16(kTitle);
+  notification_data.title = kTitle;
   ScheduleParams schedule_params;
   auto params = std::make_unique<NotificationParams>(
       SchedulerClientType::kTest1, notification_data, schedule_params);
diff --git a/chrome/browser/notifications/win/notification_template_builder_unittest.cc b/chrome/browser/notifications/win/notification_template_builder_unittest.cc
index 35c129c..991acaa 100644
--- a/chrome/browser/notifications/win/notification_template_builder_unittest.cc
+++ b/chrome/browser/notifications/win/notification_template_builder_unittest.cc
@@ -29,8 +29,8 @@
 const char kContextMenuLabel[] = "settings";
 const char kEncodedId[] = "0|0|Default|0|https://example.com/|notification_id";
 const char kNotificationId[] = "notification_id";
-const char kNotificationTitle[] = "My Title";
-const char kNotificationMessage[] = "My Message";
+const char16_t kNotificationTitle[] = u"My Title";
+const char16_t kNotificationMessage[] = u"My Message";
 const char kNotificationOrigin[] = "https://example.com";
 
 base::Time FixedTime() {
@@ -67,8 +67,7 @@
     GURL origin_url(kNotificationOrigin);
     message_center::Notification notification(
         message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId,
-        base::UTF8ToUTF16(kNotificationTitle),
-        base::UTF8ToUTF16(kNotificationMessage), gfx::Image() /* icon */,
+        kNotificationTitle, kNotificationMessage, gfx::Image() /* icon */,
         std::u16string() /* display_source */, origin_url,
         NotifierId(origin_url), RichNotificationData(), nullptr /* delegate */);
     // Set a fixed timestamp, to avoid having to test against current timestamp.
diff --git a/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogBridge.java b/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogBridge.java
index 16d74eb..baeeab6d 100644
--- a/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogBridge.java
+++ b/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogBridge.java
@@ -48,15 +48,15 @@
     }
 
     @Override
-    public void onDialogDismissed() {
+    public void onDialogDismissed(boolean dialogAccepted) {
         assert mNativeDialog != 0;
-        PasswordEditDialogBridgeJni.get().onDialogDismissed(mNativeDialog);
+        PasswordEditDialogBridgeJni.get().onDialogDismissed(mNativeDialog, dialogAccepted);
         mNativeDialog = 0;
     }
 
     @NativeMethods
     interface Natives {
         void onDialogAccepted(long nativePasswordEditDialogBridge, int selectedUsernameIndex);
-        void onDialogDismissed(long nativePasswordEditDialogBridge);
+        void onDialogDismissed(long nativePasswordEditDialogBridge, boolean dialogAccepted);
     }
 }
\ No newline at end of file
diff --git a/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogCoordinator.java b/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogCoordinator.java
index aeea149c..bc3f977 100644
--- a/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogCoordinator.java
+++ b/chrome/browser/password_edit_dialog/android/java/src/org/chromium/chrome/browser/password_edit_dialog/PasswordEditDialogCoordinator.java
@@ -37,8 +37,12 @@
          */
         void onDialogAccepted(int selectedUsernameIndex);
 
-        /** Called when the dialog is dismissed. */
-        void onDialogDismissed();
+        /**
+         * Called when the dialog is dismissed.
+         *
+         * @param dialogAccepted Indicates whether the dialog was accepted or cancelled by the user.
+         */
+        void onDialogDismissed(boolean dialogAccepted);
     }
 
     private final Context mContext;
@@ -155,7 +159,7 @@
 
     @Override
     public void onDismiss(PropertyModel model, @DialogDismissalCause int dismissalCause) {
-        mDelegate.onDialogDismissed();
+        mDelegate.onDialogDismissed(dismissalCause == DialogDismissalCause.POSITIVE_BUTTON_CLICKED);
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.cc b/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.cc
index e70cd200..0d8a4ea 100644
--- a/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.cc
+++ b/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.cc
@@ -10,11 +10,13 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/android/window_android.h"
 
+PasswordEditDialog::~PasswordEditDialog() = default;
+
 // static
-std::unique_ptr<PasswordEditDialogBridge> PasswordEditDialogBridge::Create(
+std::unique_ptr<PasswordEditDialog> PasswordEditDialogBridge::Create(
     content::WebContents* web_contents,
     DialogAcceptedCallback dialog_accepted_callback,
-    base::OnceClosure dialog_dismissed_callback) {
+    DialogDismissedCallback dialog_dismissed_callback) {
   DCHECK(web_contents);
 
   ui::WindowAndroid* window_android = web_contents->GetTopLevelNativeWindow();
@@ -28,7 +30,7 @@
 PasswordEditDialogBridge::PasswordEditDialogBridge(
     base::android::ScopedJavaLocalRef<jobject> j_window_android,
     DialogAcceptedCallback dialog_accepted_callback,
-    base::OnceClosure dialog_dismissed_callback)
+    DialogDismissedCallback dialog_dismissed_callback)
     : dialog_accepted_callback_(std::move(dialog_accepted_callback)),
       dialog_dismissed_callback_(std::move(dialog_dismissed_callback)) {
   JNIEnv* env = base::android::AttachCurrentThread();
@@ -73,7 +75,8 @@
   std::move(dialog_accepted_callback_).Run(selected_username_index);
 }
 
-void PasswordEditDialogBridge::OnDialogDismissed(JNIEnv* env) {
+void PasswordEditDialogBridge::OnDialogDismissed(JNIEnv* env,
+                                                 jboolean dialogAccepted) {
   java_password_dialog_.Reset();
-  std::move(dialog_dismissed_callback_).Run();
+  std::move(dialog_dismissed_callback_).Run(dialogAccepted);
 }
diff --git a/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.h b/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.h
index ca3ff7545..583f9610 100644
--- a/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.h
+++ b/chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.h
@@ -44,19 +44,40 @@
 //   void OnDialogDismissed() {
 //     m_dialog_bridge.reset();
 //   }
-class PasswordEditDialogBridge {
+//
+// The PasswordEditDialog is the interface created to facilitate mocking in
+// tests. PasswordEditDialogBridge contains the implementation.
+class PasswordEditDialog {
  public:
   using DialogAcceptedCallback = base::OnceCallback<void(int)>;
+  using DialogDismissedCallback = base::OnceCallback<void(bool)>;
 
-  ~PasswordEditDialogBridge();
+  virtual ~PasswordEditDialog();
+
+  // Calls Java side of the bridge to display password edit modal dialog.
+  virtual void Show(const std::vector<std::u16string>& usernames,
+                    int selected_username_index,
+                    const std::u16string& password,
+                    const std::u16string& origin,
+                    const std::string& account_email) = 0;
+
+  // Dismisses displayed dialog. The owner of PassworDeidtDialogBridge should
+  // call this function to correctly dismiss and destroy the dialog. The object
+  // can be safely destroyed after dismiss callback is executed.
+  virtual void Dismiss() = 0;
+};
+
+class PasswordEditDialogBridge : public PasswordEditDialog {
+ public:
+  ~PasswordEditDialogBridge() override;
 
   // Creates and returns an instance of PasswordEditDialogBridge and
   // corresponding Java counterpart.
   // Returns nullptr if |web_contents| is not attached to a window.
-  static std::unique_ptr<PasswordEditDialogBridge> Create(
+  static std::unique_ptr<PasswordEditDialog> Create(
       content::WebContents* web_contents,
       DialogAcceptedCallback dialog_accepted_callback,
-      base::OnceClosure dialog_dismissed_callback);
+      DialogDismissedCallback dialog_dismissed_callback);
 
   // Disallow copy and assign.
   PasswordEditDialogBridge(const PasswordEditDialogBridge&) = delete;
@@ -67,29 +88,29 @@
             int selected_username_index,
             const std::u16string& password,
             const std::u16string& origin,
-            const std::string& account_email);
+            const std::string& account_email) override;
 
   // Dismisses displayed dialog. The owner of PassworDeidtDialogBridge should
   // call this function to correctly dismiss and destroy the dialog. The object
   // can be safely destroyed after dismiss callback is executed.
-  void Dismiss();
+  void Dismiss() override;
 
   // Called from Java to indicate that the user tapped the positive button with
   // |selected_username| being selected from usernames list.
   void OnDialogAccepted(JNIEnv* env, jint selected_username_index);
 
   // Called from Java when the modal dialog is dismissed.
-  void OnDialogDismissed(JNIEnv* env);
+  void OnDialogDismissed(JNIEnv* env, jboolean dialogAccepted);
 
  private:
   PasswordEditDialogBridge(
       base::android::ScopedJavaLocalRef<jobject> jwindow_android,
-      DialogAcceptedCallback save_password_callback,
-      base::OnceClosure dismiss_callback);
+      DialogAcceptedCallback dialog_accepted_callback,
+      DialogDismissedCallback dialog_dismissed_callback);
 
   base::android::ScopedJavaGlobalRef<jobject> java_password_dialog_;
   DialogAcceptedCallback dialog_accepted_callback_;
-  base::OnceClosure dialog_dismissed_callback_;
+  DialogDismissedCallback dialog_dismissed_callback_;
 };
 
 #endif  // CHROME_BROWSER_PASSWORD_EDIT_DIALOG_ANDROID_PASSWORD_EDIT_DIALOG_BRIDGE_H_
diff --git a/chrome/browser/password_manager/android/save_password_message_delegate.cc b/chrome/browser/password_manager/android/save_password_message_delegate.cc
index 6e295fa8..1f6818d 100644
--- a/chrome/browser/password_manager/android/save_password_message_delegate.cc
+++ b/chrome/browser/password_manager/android/save_password_message_delegate.cc
@@ -4,30 +4,42 @@
 
 #include "chrome/browser/password_manager/android/save_password_message_delegate.h"
 
+#include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/android/resource_mapper.h"
 #include "chrome/browser/password_manager/android/password_infobar_utils.h"
+#include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/messages/android/message_dispatcher_bridge.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_metrics_recorder.h"
+#include "components/password_manager/core/browser/password_ui_utils.h"
+#include "components/url_formatter/elide_url.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "url/origin.h"
 
-SavePasswordMessageDelegate::SavePasswordMessageDelegate() = default;
+SavePasswordMessageDelegate::SavePasswordMessageDelegate()
+    : SavePasswordMessageDelegate(
+          base::BindRepeating(PasswordEditDialogBridge::Create)) {}
+
+SavePasswordMessageDelegate::SavePasswordMessageDelegate(
+    PasswordEditDialogFactory password_edit_dialog_factory)
+    : password_edit_dialog_factory_(std::move(password_edit_dialog_factory)) {}
 
 SavePasswordMessageDelegate::~SavePasswordMessageDelegate() {
-  DismissSavePasswordPrompt();
+  DCHECK(web_contents_ == nullptr);
 }
 
 void SavePasswordMessageDelegate::DisplaySavePasswordPrompt(
     content::WebContents* web_contents,
-    std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save) {
+    std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save,
+    bool update_password) {
   DCHECK_NE(nullptr, web_contents);
   DCHECK(form_to_save);
 
@@ -43,14 +55,17 @@
       password_manager::GetAccountInfoForPasswordMessages(
           profile, is_saving_google_account);
   DisplaySavePasswordPromptInternal(web_contents, std::move(form_to_save),
-                                    account_info);
+                                    std::move(account_info), update_password);
 }
 
 void SavePasswordMessageDelegate::DismissSavePasswordPrompt() {
-  DismissSavePasswordPromptInternal(messages::DismissReason::UNKNOWN);
+  if (password_edit_dialog_ != nullptr) {
+    password_edit_dialog_->Dismiss();
+  }
+  DismissSavePasswordMessage(messages::DismissReason::UNKNOWN);
 }
 
-void SavePasswordMessageDelegate::DismissSavePasswordPromptInternal(
+void SavePasswordMessageDelegate::DismissSavePasswordMessage(
     messages::DismissReason dismiss_reason) {
   if (message_ != nullptr) {
     messages::MessageDispatcherBridge::Get()->DismissMessage(
@@ -61,90 +76,215 @@
 void SavePasswordMessageDelegate::DisplaySavePasswordPromptInternal(
     content::WebContents* web_contents,
     std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save,
-    base::Optional<AccountInfo> account_info) {
+    base::Optional<AccountInfo> account_info,
+    bool update_password) {
   // Dismiss previous message if it is displayed.
   DismissSavePasswordPrompt();
   DCHECK(message_ == nullptr);
+  DCHECK(password_edit_dialog_ == nullptr);
 
   web_contents_ = web_contents;
-  form_to_save_ = std::move(form_to_save);
-
-  // Binding with base::Unretained(this) is safe here because
-  // SavePasswordMessageDelegate owns message_. Callbacks won't be called after
-  // the current object is destroyed.
-  message_ = std::make_unique<messages::MessageWrapper>(
-      base::BindOnce(&SavePasswordMessageDelegate::HandleSaveClick,
-                     base::Unretained(this)),
-      base::BindOnce(&SavePasswordMessageDelegate::HandleDismissCallback,
-                     base::Unretained(this)));
-
-  const password_manager::PasswordForm& pending_credentials =
-      form_to_save_->GetPendingCredentials();
-
-  int title_message_id = pending_credentials.federation_origin.opaque()
-                             ? IDS_SAVE_PASSWORD
-                             : IDS_SAVE_ACCOUNT;
-
-  message_->SetTitle(l10n_util::GetStringUTF16(title_message_id));
-
-  const std::u16string masked_password =
-      std::u16string(pending_credentials.password_value.size(), L'•');
-  std::u16string description;
-  if (account_info.has_value()) {
-    description = l10n_util::GetStringFUTF16(
-        IDS_SAVE_PASSWORD_SIGNED_IN_MESSAGE_DESCRIPTION_GOOGLE_ACCOUNT,
-        pending_credentials.username_value, masked_password,
-        base::UTF8ToUTF16(account_info.value().email));
+  passwords_state_.set_client(
+      ChromePasswordManagerClient::FromWebContents(web_contents_));
+  if (update_password) {
+    passwords_state_.OnUpdatePassword(std::move(form_to_save));
   } else {
-    description.append(pending_credentials.username_value)
-        .append(u" ")
-        .append(masked_password);
+    passwords_state_.OnPendingPassword(std::move(form_to_save));
   }
+  account_email_ =
+      account_info.has_value() ? account_info.value().email : std::string();
 
-  message_->SetDescription(description);
-
-  message_->SetPrimaryButtonText(
-      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SAVE_BUTTON));
-  message_->SetIconResourceId(
-      ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_INFOBAR_SAVE_PASSWORD));
-  message_->SetSecondaryIconResourceId(
-      ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_AUTOFILL_SETTINGS));
-  message_->SetSecondaryButtonMenuText(
-      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BLOCKLIST_BUTTON));
-
-  message_->SetSecondaryActionCallback(base::BindOnce(
-      &SavePasswordMessageDelegate::HandleNeverClick, base::Unretained(this)));
-
+  CreateMessage(update_password);
   RecordMessageShownMetrics();
   messages::MessageDispatcherBridge::Get()->EnqueueMessage(
       message_.get(), web_contents_, messages::MessageScopeType::NAVIGATION);
 }
 
-void SavePasswordMessageDelegate::HandleSaveClick() {
-  form_to_save_->Save();
+void SavePasswordMessageDelegate::CreateMessage(bool update_password) {
+  // Binding with base::Unretained(this) is safe here because
+  // SavePasswordMessageDelegate owns message_. Callbacks won't be called after
+  // the current object is destroyed.
+  message_ = std::make_unique<messages::MessageWrapper>(
+      base::BindOnce(&SavePasswordMessageDelegate::HandleSaveButtonClicked,
+                     base::Unretained(this)),
+      base::BindOnce(&SavePasswordMessageDelegate::HandleMessageDismissed,
+                     base::Unretained(this)));
+
+  const password_manager::PasswordForm& pending_credentials =
+      passwords_state_.form_manager()->GetPendingCredentials();
+
+  int title_message_id;
+  if (update_password) {
+    title_message_id = IDS_UPDATE_PASSWORD;
+  } else if (pending_credentials.federation_origin.opaque()) {
+    title_message_id = IDS_SAVE_PASSWORD;
+  } else {
+    title_message_id = IDS_SAVE_ACCOUNT;
+  }
+  message_->SetTitle(l10n_util::GetStringUTF16(title_message_id));
+
+  // TODO(crbug.com/1188971): There is no password when federation_origin is
+  // set. Instead we should display federated provider in the description.
+  // GetDisplayFederation() returns federation origin for a given form.
+  const std::u16string masked_password =
+      std::u16string(pending_credentials.password_value.size(), L'•');
+  std::u16string description;
+  if (!account_email_.empty()) {
+    description = l10n_util::GetStringFUTF16(
+        IDS_SAVE_PASSWORD_SIGNED_IN_MESSAGE_DESCRIPTION_GOOGLE_ACCOUNT,
+        pending_credentials.username_value, masked_password,
+        base::UTF8ToUTF16(account_email_));
+  } else {
+    description.append(pending_credentials.username_value)
+        .append(u" ")
+        .append(masked_password);
+  }
+  message_->SetDescription(description);
+
+  int primary_button_message_id = update_password
+                                      ? IDS_PASSWORD_MANAGER_UPDATE_BUTTON
+                                      : IDS_PASSWORD_MANAGER_SAVE_BUTTON;
+  message_->SetPrimaryButtonText(
+      l10n_util::GetStringUTF16(primary_button_message_id));
+
+  message_->SetIconResourceId(
+      ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_INFOBAR_SAVE_PASSWORD));
+
+  message_->SetSecondaryIconResourceId(
+      ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_AUTOFILL_SETTINGS));
+  if (!update_password) {
+    message_->SetSecondaryButtonMenuText(
+        l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_BLOCKLIST_BUTTON));
+  }
+  // TODO(crbug.com/1188971): Currently only update password message triggers
+  // password edit dialog in response to tap on the gear icon. Update password
+  // edit dialog for save password scenario and switch save password message
+  // behavior to match update password message.
+  base::OnceClosure secondary_action_callback =
+      update_password
+          ? base::BindOnce(
+                &SavePasswordMessageDelegate::HandleDisplayEditDialog,
+                base::Unretained(this))
+          : base::BindOnce(&SavePasswordMessageDelegate::HandleNeverSaveClicked,
+                           base::Unretained(this));
+  message_->SetSecondaryActionCallback(std::move(secondary_action_callback));
 }
 
-void SavePasswordMessageDelegate::HandleNeverClick() {
-  form_to_save_->Blocklist();
-  DismissSavePasswordPromptInternal(messages::DismissReason::SECONDARY_ACTION);
-}
-
-void SavePasswordMessageDelegate::HandleDismissCallback(
+void SavePasswordMessageDelegate::HandleMessageDismissed(
     messages::DismissReason dismiss_reason) {
-  // The message is dismissed. Record metrics and cleanup state.
+  message_.reset();
+  if (password_edit_dialog_) {
+    // The user triggered password edit dialog. Don't cleanup internal
+    // datastructures, dialog dismiss callback will perform cleanup.
+    return;
+  }
+  // Record metrics and cleanup state.
   RecordDismissalReasonMetrics(
       MessageDismissReasonToPasswordManagerUIDismissalReason(dismiss_reason));
-  message_.reset();
-  form_to_save_.reset();
+  ClearState();
+}
+
+void SavePasswordMessageDelegate::HandleSaveButtonClicked() {
+  passwords_state_.form_manager()->Save();
+}
+
+void SavePasswordMessageDelegate::HandleNeverSaveClicked() {
+  passwords_state_.form_manager()->Blocklist();
+  DismissSavePasswordMessage(messages::DismissReason::SECONDARY_ACTION);
+}
+
+void SavePasswordMessageDelegate::HandleDisplayEditDialog() {
+  // Binding with base::Unretained(this) is safe here because
+  // SavePasswordMessageDelegate owns password_edit_dialog_. Callbacks won't be
+  // called after the SavePasswordMessageDelegate object is destroyed.
+  password_edit_dialog_ = password_edit_dialog_factory_.Run(
+      web_contents_,
+      base::BindOnce(&SavePasswordMessageDelegate::HandleSavePasswordFromDialog,
+                     base::Unretained(this)),
+      base::BindOnce(&SavePasswordMessageDelegate::HandleDialogDismissed,
+                     base::Unretained(this)));
+  // It is important to dismiss the message after the dialog is created. The
+  // code in HandleMessageDismissed checks password_edit_dialog_ to decide
+  // whether to clear state.
+  DismissSavePasswordMessage(messages::DismissReason::SECONDARY_ACTION);
+
+  // Password edit dialog factory method can return nullptr when web_contents
+  // is not attached to a window. See crbug.com/1049090 for details.
+  if (!password_edit_dialog_)
+    return;
+
+  std::vector<std::u16string> usernames;
+  int selected_username_index = GetDisplayUsernames(&usernames);
+
+  std::u16string origin = url_formatter::FormatOriginForSecurityDisplay(
+      url::Origin::Create(passwords_state_.form_manager()->GetURL()),
+      url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS);
+  password_edit_dialog_->Show(
+      usernames, selected_username_index,
+      passwords_state_.form_manager()->GetPendingCredentials().password_value,
+      origin, account_email_);
+}
+
+unsigned int SavePasswordMessageDelegate::GetDisplayUsernames(
+    std::vector<std::u16string>* usernames) {
+  unsigned int selected_username_index = 0;
+  // TODO(crbug.com/1054410): Fix the update logic to use all best matches,
+  // rather than current_forms which is best_matches without PSL-matched
+  // credentials.
+  const std::vector<std::unique_ptr<password_manager::PasswordForm>>&
+      password_forms = passwords_state_.GetCurrentForms();
+  const std::u16string& default_username =
+      passwords_state_.form_manager()->GetPendingCredentials().username_value;
+  if (password_forms.size() > 1) {
+    // If multiple credentials can be updated, we display a dropdown with all
+    // the corresponding usernames.
+    for (const auto& form : password_forms) {
+      usernames->push_back(GetDisplayUsername(*form));
+      if (form->username_value == default_username) {
+        selected_username_index = usernames->size() - 1;
+      }
+    }
+  } else {
+    usernames->push_back(GetDisplayUsername(
+        passwords_state_.form_manager()->GetPendingCredentials()));
+  }
+  return selected_username_index;
+}
+
+void SavePasswordMessageDelegate::HandleDialogDismissed(bool dialogAccepted) {
+  password_edit_dialog_.reset();
+  RecordDismissalReasonMetrics(
+      dialogAccepted ? password_manager::metrics_util::CLICKED_ACCEPT
+                     : password_manager::metrics_util::CLICKED_CANCEL);
+  ClearState();
+}
+
+void SavePasswordMessageDelegate::HandleSavePasswordFromDialog(
+    int selected_username_index) {
+  if (passwords_state_.GetCurrentForms().size() > 1) {
+    UpdatePasswordFormUsernameAndPassword(
+        passwords_state_.GetCurrentForms()[selected_username_index]
+            ->username_value,
+        passwords_state_.form_manager()->GetPendingCredentials().password_value,
+        passwords_state_.form_manager());
+  }
+  passwords_state_.form_manager()->Save();
+}
+
+void SavePasswordMessageDelegate::ClearState() {
+  DCHECK(message_ == nullptr);
+  DCHECK(password_edit_dialog_ == nullptr);
+
+  passwords_state_.OnInactive();
   // web_contents_ is set in DisplaySavePasswordPromptInternal(). Resetting it
   // here to keep the state clean when no message is enqueued.
   web_contents_ = nullptr;
 }
 
 void SavePasswordMessageDelegate::RecordMessageShownMetrics() {
-  if (auto* recorder = form_to_save_->GetMetricsRecorder()) {
+  if (auto* recorder = passwords_state_.form_manager()->GetMetricsRecorder()) {
     recorder->RecordPasswordBubbleShown(
-        form_to_save_->GetCredentialSource(),
+        passwords_state_.form_manager()->GetCredentialSource(),
         password_manager::metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING);
   }
 }
@@ -153,11 +293,11 @@
     password_manager::metrics_util::UIDismissalReason ui_dismissal_reason) {
   password_manager::metrics_util::LogSaveUIDismissalReason(
       ui_dismissal_reason, /*user_state=*/base::nullopt);
-  if (form_to_save_->WasUnblocklisted()) {
+  if (passwords_state_.form_manager()->WasUnblocklisted()) {
     password_manager::metrics_util::LogSaveUIDismissalReasonAfterUnblocklisting(
         ui_dismissal_reason);
   }
-  if (auto* recorder = form_to_save_->GetMetricsRecorder()) {
+  if (auto* recorder = passwords_state_.form_manager()->GetMetricsRecorder()) {
     recorder->RecordUIDismissalReason(ui_dismissal_reason);
   }
 }
diff --git a/chrome/browser/password_manager/android/save_password_message_delegate.h b/chrome/browser/password_manager/android/save_password_message_delegate.h
index ad35f0f2..aa44e8f 100644
--- a/chrome/browser/password_manager/android/save_password_message_delegate.h
+++ b/chrome/browser/password_manager/android/save_password_message_delegate.h
@@ -7,7 +7,10 @@
 
 #include <memory>
 
+#include "base/callback.h"
 #include "base/optional.h"
+#include "chrome/browser/password_edit_dialog/android/password_edit_dialog_bridge.h"
+#include "chrome/browser/ui/passwords/manage_passwords_state.h"
 #include "components/messages/android/message_enums.h"
 #include "components/messages/android/message_wrapper.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
@@ -24,6 +27,12 @@
 // saving password form in response to user interactions and recording metrics.
 class SavePasswordMessageDelegate {
  public:
+  using PasswordEditDialogFactory =
+      base::RepeatingCallback<std::unique_ptr<PasswordEditDialog>(
+          content::WebContents*,
+          PasswordEditDialog::DialogAcceptedCallback,
+          PasswordEditDialog::DialogDismissedCallback)>;
+
   SavePasswordMessageDelegate();
   ~SavePasswordMessageDelegate();
 
@@ -31,26 +40,43 @@
   // |form_to_save|.
   void DisplaySavePasswordPrompt(
       content::WebContents* web_contents,
-      std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save);
-  // Dismisses currently displayed message.
+      std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save,
+      bool update_password);
+
+  // Dismisses currently displayed message or dialog. Because the implementation
+  // uses some of the dependencies (e.g. log manager) this method needs to be
+  // called before the object is destroyed.
   void DismissSavePasswordPrompt();
 
  private:
   friend class SavePasswordMessageDelegateTest;
 
-  void DismissSavePasswordPromptInternal(
-      messages::DismissReason dismiss_reason);
+  SavePasswordMessageDelegate(
+      PasswordEditDialogFactory password_edit_dialog_factory);
+
+  void DismissSavePasswordMessage(messages::DismissReason dismiss_reason);
 
   void DisplaySavePasswordPromptInternal(
       content::WebContents* web_contents,
       std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save,
-      base::Optional<AccountInfo> account_info);
+      base::Optional<AccountInfo> account_info,
+      bool update_password);
+  void CreateMessage(bool update_password);
 
-  // Called in response to user clicking "Save" and "Never" buttons.
-  void HandleSaveClick();
-  void HandleNeverClick();
-  // Called when the message is dismissed.
-  void HandleDismissCallback(messages::DismissReason dismiss_reason);
+  // Populates |usernames| with the list of usernames from best saved matches to
+  // be presented to the user in a dropdown. Returns the index of the username
+  // that matches the one from pending credentials.
+  unsigned int GetDisplayUsernames(std::vector<std::u16string>* usernames);
+
+  // Following methods handle events associated with user interaction with UI.
+  void HandleSaveButtonClicked();
+  void HandleNeverSaveClicked();
+  void HandleDisplayEditDialog();
+  void HandleMessageDismissed(messages::DismissReason dismiss_reason);
+  void HandleSavePasswordFromDialog(int selected_username);
+  void HandleDialogDismissed(bool dialogAccepted);
+
+  void ClearState();
 
   void RecordMessageShownMetrics();
   void RecordDismissalReasonMetrics(
@@ -60,11 +86,17 @@
   MessageDismissReasonToPasswordManagerUIDismissalReason(
       messages::DismissReason dismiss_reason);
 
+  PasswordEditDialogFactory password_edit_dialog_factory_;
+
   content::WebContents* web_contents_ = nullptr;
+  std::string account_email_;
+
+  // ManagePasswordsState maintains the password form that is being
+  // saved/updated. It provides helper functions for populating username list.
+  ManagePasswordsState passwords_state_;
+
   std::unique_ptr<messages::MessageWrapper> message_;
-  // The PasswordFormManager managing the form we're asking the user about,
-  // and should update as per their decision.
-  std::unique_ptr<password_manager::PasswordFormManagerForUI> form_to_save_;
+  std::unique_ptr<PasswordEditDialog> password_edit_dialog_;
 };
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_SAVE_PASSWORD_MESSAGE_DELEGATE_H_
diff --git a/chrome/browser/password_manager/android/save_password_message_delegate_unittest.cc b/chrome/browser/password_manager/android/save_password_message_delegate_unittest.cc
index 77debd8..b0d915e 100644
--- a/chrome/browser/password_manager/android/save_password_message_delegate_unittest.cc
+++ b/chrome/browser/password_manager/android/save_password_message_delegate_unittest.cc
@@ -6,11 +6,13 @@
 
 #include "base/android/jni_android.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/android/android_theme_resources.h"
 #include "chrome/browser/android/resource_mapper.h"
+#include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/messages/android/mock_message_dispatcher_bridge.h"
@@ -25,11 +27,17 @@
 #include "url/gurl.h"
 
 using password_manager::MockPasswordFormManagerForUI;
+using password_manager::PasswordForm;
 using password_manager::PasswordFormManagerForUI;
 using password_manager::PasswordFormMetricsRecorder;
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Return;
+using ::testing::ReturnRef;
 
 namespace {
 constexpr char kDefaultUrl[] = "http://example.com";
+constexpr char16_t kOrigin[] = u"example.com";
 constexpr char16_t kUsername[] = u"username";
 constexpr char16_t kPassword[] = u"password";
 constexpr char kAccountEmail[] = "account@example.com";
@@ -38,28 +46,61 @@
     "PasswordManager.SaveUIDismissalReason";
 }  // namespace
 
+class MockPasswordEditDialog : public PasswordEditDialog {
+ public:
+  MOCK_METHOD(void,
+              Show,
+              (const std::vector<std::u16string>& usernames,
+               int selected_username_index,
+               const std::u16string& password,
+               const std::u16string& origin,
+               const std::string& account_email),
+              (override));
+  MOCK_METHOD(void, Dismiss, (), (override));
+};
+
 class SavePasswordMessageDelegateTest : public ChromeRenderViewHostTestHarness {
  public:
-  SavePasswordMessageDelegateTest() = default;
+  SavePasswordMessageDelegateTest();
 
  protected:
   void SetUp() override;
   void TearDown() override;
 
   std::unique_ptr<MockPasswordFormManagerForUI> CreateFormManager(
-      const GURL& url);
+      const GURL& password_form_url,
+      const std::vector<const PasswordForm*>* best_matches);
   void SetUsernameAndPassword(std::u16string username, std::u16string password);
 
   void EnqueueMessage(std::unique_ptr<PasswordFormManagerForUI> form_to_save,
-                      bool user_signed_in);
+                      bool user_signed_in,
+                      bool update_password);
   void TriggerActionClick();
-  void TriggerBlocklistClick();
+  void TriggerPasswordEditDialog();
 
   void ExpectDismissMessageCall();
   void DismissMessage(messages::DismissReason dismiss_reason);
+  void DestroyDelegate();
 
   messages::MessageWrapper* GetMessageWrapper();
 
+  // Password edit dialog factory function that is passed to
+  // SavePasswordMessageDelegate. Passes the dialog prepared by
+  // PreparePasswordEditDialog. Captures accept and dismiss callbacks.
+  std::unique_ptr<PasswordEditDialog> CreatePasswordEditDialog(
+      content::WebContents* web_contents,
+      PasswordEditDialog::DialogAcceptedCallback dialog_accepted_callback,
+      PasswordEditDialog::DialogDismissedCallback dialog_dismissed_callback);
+
+  // Creates a mock of PasswordEditDialog that will be passed to
+  // SavePasswordMessageDelegate through CreatePasswordEditDialog factory.
+  // Returns non-owning pointer to the mock for test to configure mock
+  // expectations.
+  MockPasswordEditDialog* PreparePasswordEditDialog();
+
+  void TriggerDialogAcceptedCallback(int selected_user_index);
+  void TriggerDialogDismissedCallback(bool dialog_accepted);
+
   void CommitPasswordFormMetrics();
   void VerifyUkmMetrics(const ukm::TestUkmRecorder& ukm_recorder,
                         PasswordFormMetricsRecorder::BubbleDismissalReason
@@ -69,17 +110,34 @@
     return &message_dispatcher_bridge_;
   }
 
+  const std::vector<const PasswordForm*>* empty_best_matches() {
+    return &kEmptyBestMatches;
+  }
+
  private:
-  SavePasswordMessageDelegate delegate_;
-  password_manager::PasswordForm form_;
+  const std::vector<const PasswordForm*> kEmptyBestMatches = {};
+
+  PasswordForm password_form_;
+  std::unique_ptr<SavePasswordMessageDelegate> delegate_;
   GURL password_form_url_;
   scoped_refptr<PasswordFormMetricsRecorder> metrics_recorder_;
   ukm::SourceId ukm_source_id_;
   messages::MockMessageDispatcherBridge message_dispatcher_bridge_;
+  std::unique_ptr<MockPasswordEditDialog> mock_password_edit_dialog_;
+  PasswordEditDialog::DialogAcceptedCallback dialog_accepted_callback_;
+  PasswordEditDialog::DialogDismissedCallback dialog_dismissed_callback_;
 };
 
+SavePasswordMessageDelegateTest::SavePasswordMessageDelegateTest()
+    : delegate_(
+          base::WrapUnique(new SavePasswordMessageDelegate(base::BindRepeating(
+              &SavePasswordMessageDelegateTest::CreatePasswordEditDialog,
+              base::Unretained(this))))) {}
+
 void SavePasswordMessageDelegateTest::SetUp() {
   ChromeRenderViewHostTestHarness::SetUp();
+  ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
+      web_contents(), nullptr);
   ukm_source_id_ = ukm::UkmRecorder::GetNewSourceID();
   metrics_recorder_ = base::MakeRefCounted<PasswordFormMetricsRecorder>(
       true /*is_main_frame_secure*/, ukm_source_id_, nullptr /*pref_service*/);
@@ -97,48 +155,53 @@
 
 std::unique_ptr<MockPasswordFormManagerForUI>
 SavePasswordMessageDelegateTest::CreateFormManager(
-    const GURL& password_form_url) {
+    const GURL& password_form_url,
+    const std::vector<const PasswordForm*>* best_matches) {
   password_form_url_ = password_form_url;
   auto form_manager =
       std::make_unique<testing::NiceMock<MockPasswordFormManagerForUI>>();
   ON_CALL(*form_manager, GetPendingCredentials())
-      .WillByDefault(testing::ReturnRef(form_));
+      .WillByDefault(ReturnRef(password_form_));
   ON_CALL(*form_manager, GetCredentialSource())
-      .WillByDefault(
-          testing::Return(password_manager::metrics_util::CredentialSourceType::
-                              kPasswordManager));
-  ON_CALL(*form_manager, GetURL())
-      .WillByDefault(testing::ReturnRef(password_form_url_));
+      .WillByDefault(Return(password_manager::metrics_util::
+                                CredentialSourceType::kPasswordManager));
+  ON_CALL(*form_manager, GetURL()).WillByDefault(ReturnRef(password_form_url_));
+  ON_CALL(*form_manager, GetBestMatches())
+      .WillByDefault(ReturnRef(*best_matches));
+  ON_CALL(*form_manager, GetFederatedMatches())
+      .WillByDefault(Return(std::vector<const PasswordForm*>{}));
+
   ON_CALL(*form_manager, GetMetricsRecorder())
-      .WillByDefault(testing::Return(metrics_recorder_.get()));
+      .WillByDefault(Return(metrics_recorder_.get()));
   return form_manager;
 }
 
 void SavePasswordMessageDelegateTest::SetUsernameAndPassword(
     std::u16string username,
     std::u16string password) {
-  form_.username_value = std::move(username);
-  form_.password_value = std::move(password);
+  password_form_.username_value = std::move(username);
+  password_form_.password_value = std::move(password);
 }
 
 void SavePasswordMessageDelegateTest::EnqueueMessage(
     std::unique_ptr<PasswordFormManagerForUI> form_to_save,
-    bool user_signed_in) {
+    bool user_signed_in,
+    bool update_password) {
   base::Optional<AccountInfo> account_info;
   if (user_signed_in) {
     account_info = AccountInfo();
     account_info.value().email = kAccountEmail;
   }
   EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
-  delegate_.DisplaySavePasswordPromptInternal(
-      web_contents(), std::move(form_to_save), account_info);
+  delegate_->DisplaySavePasswordPromptInternal(
+      web_contents(), std::move(form_to_save), account_info, update_password);
 }
 
 void SavePasswordMessageDelegateTest::TriggerActionClick() {
   GetMessageWrapper()->HandleActionClick(base::android::AttachCurrentThread());
 }
 
-void SavePasswordMessageDelegateTest::TriggerBlocklistClick() {
+void SavePasswordMessageDelegateTest::TriggerPasswordEditDialog() {
   GetMessageWrapper()->HandleSecondaryActionClick(
       base::android::AttachCurrentThread());
 }
@@ -156,14 +219,43 @@
 void SavePasswordMessageDelegateTest::DismissMessage(
     messages::DismissReason dismiss_reason) {
   ExpectDismissMessageCall();
-  delegate_.DismissSavePasswordPromptInternal(dismiss_reason);
+  delegate_->DismissSavePasswordMessage(dismiss_reason);
   EXPECT_EQ(nullptr, GetMessageWrapper());
 }
 
-messages::MessageWrapper* SavePasswordMessageDelegateTest::GetMessageWrapper() {
-  return delegate_.message_.get();
+void SavePasswordMessageDelegateTest::DestroyDelegate() {
+  delegate_.reset();
 }
 
+messages::MessageWrapper* SavePasswordMessageDelegateTest::GetMessageWrapper() {
+  return delegate_->message_.get();
+}
+
+std::unique_ptr<PasswordEditDialog>
+SavePasswordMessageDelegateTest::CreatePasswordEditDialog(
+    content::WebContents* web_contents,
+    PasswordEditDialog::DialogAcceptedCallback dialog_accepted_callback,
+    PasswordEditDialog::DialogDismissedCallback dialog_dismissed_callback) {
+  dialog_accepted_callback_ = std::move(dialog_accepted_callback);
+  dialog_dismissed_callback_ = std::move(dialog_dismissed_callback);
+  return std::move(mock_password_edit_dialog_);
+}
+
+MockPasswordEditDialog*
+SavePasswordMessageDelegateTest::PreparePasswordEditDialog() {
+  mock_password_edit_dialog_ = std::make_unique<MockPasswordEditDialog>();
+  return mock_password_edit_dialog_.get();
+}
+
+void SavePasswordMessageDelegateTest::TriggerDialogAcceptedCallback(
+    int selected_username_index) {
+  std::move(dialog_accepted_callback_).Run(selected_username_index);
+}
+
+void SavePasswordMessageDelegateTest::TriggerDialogDismissedCallback(
+    bool dialog_accepted) {
+  std::move(dialog_dismissed_callback_).Run(dialog_accepted);
+}
 void SavePasswordMessageDelegateTest::CommitPasswordFormMetrics() {
   // PasswordFormMetricsRecorder::dtor commits accumulated metrics.
   metrics_recorder_.reset();
@@ -191,11 +283,13 @@
 }
 
 // Tests that message properties (title, description, icon, button text) are
-// set correctly.
-TEST_F(SavePasswordMessageDelegateTest, MessagePropertyValues) {
+// set correctly for save password message.
+TEST_F(SavePasswordMessageDelegateTest, MessagePropertyValues_SavePassword) {
   SetUsernameAndPassword(kUsername, kPassword);
-  auto form_manager = CreateFormManager(GURL(kDefaultUrl));
-  EnqueueMessage(std::move(form_manager), false /*user_signed_in*/);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/false);
 
   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_SAVE_PASSWORD),
             GetMessageWrapper()->GetTitle());
@@ -219,11 +313,33 @@
   DismissMessage(messages::DismissReason::UNKNOWN);
 }
 
+// Tests that message properties (title, description, icon, button text) are
+// set correctly for update password message.
+TEST_F(SavePasswordMessageDelegateTest, MessagePropertyValues_UpdatePassword) {
+  SetUsernameAndPassword(kUsername, kPassword);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/true);
+
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_UPDATE_PASSWORD),
+            GetMessageWrapper()->GetTitle());
+
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UPDATE_BUTTON),
+            GetMessageWrapper()->GetPrimaryButtonText());
+  EXPECT_EQ(std::u16string(),
+            GetMessageWrapper()->GetSecondaryButtonMenuText());
+
+  DismissMessage(messages::DismissReason::UNKNOWN);
+}
+
 // Tests that the description is set correctly when the user is signed.
 TEST_F(SavePasswordMessageDelegateTest, SignedInDescription) {
   SetUsernameAndPassword(kUsername, kPassword);
-  auto form_manager = CreateFormManager(GURL(kDefaultUrl));
-  EnqueueMessage(std::move(form_manager), true /*user_signed_in*/);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/false);
 
   EXPECT_NE(std::u16string::npos,
             GetMessageWrapper()->GetDescription().find(kUsername));
@@ -238,12 +354,16 @@
 // Tests that the previous prompt gets dismissed when the new one is enqueued.
 TEST_F(SavePasswordMessageDelegateTest, OnlyOnePromptAtATime) {
   SetUsernameAndPassword(kUsername, kPassword);
-  auto form_manager = CreateFormManager(GURL(kDefaultUrl));
-  EnqueueMessage(std::move(form_manager), true /*user_signed_in*/);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/false);
 
   ExpectDismissMessageCall();
-  auto form_manager2 = CreateFormManager(GURL(kDefaultUrl));
-  EnqueueMessage(std::move(form_manager2), true /*user_signed_in*/);
+  auto form_manager2 =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EnqueueMessage(std::move(form_manager2), /*user_signed_in=*/true,
+                 /*update_password=*/false);
   DismissMessage(messages::DismissReason::UNKNOWN);
 }
 
@@ -253,9 +373,11 @@
   base::HistogramTester histogram_tester;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
 
-  auto form_manager = CreateFormManager(GURL(kDefaultUrl));
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
   EXPECT_CALL(*form_manager, Save());
-  EnqueueMessage(std::move(form_manager), false /*user_signed_in*/);
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/false);
   EXPECT_NE(nullptr, GetMessageWrapper());
   TriggerActionClick();
   EXPECT_NE(nullptr, GetMessageWrapper());
@@ -277,9 +399,11 @@
   base::HistogramTester histogram_tester;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
 
-  auto form_manager = CreateFormManager(GURL(kDefaultUrl));
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
   EXPECT_CALL(*form_manager, Save()).Times(0);
-  EnqueueMessage(std::move(form_manager), false /*user_signed_in*/);
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/false);
   EXPECT_NE(nullptr, GetMessageWrapper());
   DismissMessage(messages::DismissReason::GESTURE);
   EXPECT_EQ(nullptr, GetMessageWrapper());
@@ -299,9 +423,11 @@
   base::HistogramTester histogram_tester;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
 
-  auto form_manager = CreateFormManager(GURL(kDefaultUrl));
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
   EXPECT_CALL(*form_manager, Save()).Times(0);
-  EnqueueMessage(std::move(form_manager), false /*user_signed_in*/);
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/false);
   EXPECT_NE(nullptr, GetMessageWrapper());
   DismissMessage(messages::DismissReason::TIMER);
   EXPECT_EQ(nullptr, GetMessageWrapper());
@@ -314,3 +440,79 @@
       kDismissalReasonHistogramName,
       password_manager::metrics_util::NO_DIRECT_INTERACTION, 1);
 }
+
+TEST_F(SavePasswordMessageDelegateTest, DialogProperties) {
+  SetUsernameAndPassword(kUsername, kPassword);
+  PasswordForm password_form;
+  password_form.username_value = kUsername;
+  password_form.password_value = kPassword;
+  const std::vector<const PasswordForm*> best_matches = {&password_form};
+  auto form_manager = CreateFormManager(GURL(kDefaultUrl), &best_matches);
+  MockPasswordEditDialog* mock_dialog = PreparePasswordEditDialog();
+  // Verify parameters to Show() call.
+  EXPECT_CALL(*mock_dialog, Show(ElementsAre(std::u16string(kUsername)), 0,
+                                 std::u16string(kPassword),
+                                 std::u16string(kOrigin), kAccountEmail));
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/true);
+  ExpectDismissMessageCall();
+  TriggerPasswordEditDialog();
+  TriggerDialogDismissedCallback(/*dialog_accepted=*/false);
+}
+
+// Tests triggering password edit dialog and saving credentials after the
+// user accepts the dialog.
+TEST_F(SavePasswordMessageDelegateTest, TriggerEditDialog_Accept) {
+  base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EXPECT_CALL(*form_manager, Save());
+  MockPasswordEditDialog* mock_dialog = PreparePasswordEditDialog();
+  EXPECT_CALL(*mock_dialog, Show);
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/true);
+  EXPECT_NE(nullptr, GetMessageWrapper());
+  ExpectDismissMessageCall();
+  TriggerPasswordEditDialog();
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+  TriggerDialogAcceptedCallback(/*selected_username_index=*/0);
+  TriggerDialogDismissedCallback(/*dialog_accepted=*/true);
+
+  CommitPasswordFormMetrics();
+  VerifyUkmMetrics(
+      test_ukm_recorder,
+      PasswordFormMetricsRecorder::BubbleDismissalReason::kAccepted);
+  histogram_tester.ExpectUniqueSample(
+      kDismissalReasonHistogramName,
+      password_manager::metrics_util::CLICKED_ACCEPT, 1);
+}
+
+// Tests that credentials are not saved if the user cancels password edit
+// dialog.
+TEST_F(SavePasswordMessageDelegateTest, TriggerEditDialog_Cancel) {
+  base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder test_ukm_recorder;
+
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EXPECT_CALL(*form_manager, Save).Times(0);
+  MockPasswordEditDialog* mock_dialog = PreparePasswordEditDialog();
+  EXPECT_CALL(*mock_dialog, Show);
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/true);
+  EXPECT_NE(nullptr, GetMessageWrapper());
+  ExpectDismissMessageCall();
+  TriggerPasswordEditDialog();
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+  TriggerDialogDismissedCallback(/*dialog_accepted=*/false);
+
+  CommitPasswordFormMetrics();
+  VerifyUkmMetrics(
+      test_ukm_recorder,
+      PasswordFormMetricsRecorder::BubbleDismissalReason::kDeclined);
+  histogram_tester.ExpectUniqueSample(
+      kDismissalReasonHistogramName,
+      password_manager::metrics_util::CLICKED_CANCEL, 1);
+}
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 2ef3ebc..65c9171 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -307,7 +307,7 @@
   } else {
     if (messages::IsPasswordMessagesUiEnabled()) {
       save_password_message_delegate_.DisplaySavePasswordPrompt(
-          web_contents(), std::move(form_to_save));
+          web_contents(), std::move(form_to_save), /*update_password=*/false);
     } else {
       SavePasswordInfoBarDelegate::Create(web_contents(),
                                           std::move(form_to_save));
@@ -1255,6 +1255,10 @@
   if (autofill_assistant_manager) {
     autofill_assistant_manager->RemoveObserver(this);
   }
+
+#if defined(OS_ANDROID)
+  save_password_message_delegate_.DismissSavePasswordPrompt();
+#endif
 }
 
 #if !defined(OS_ANDROID)
diff --git a/chrome/browser/password_manager/password_manager_signin_intercept_test_helper.cc b/chrome/browser/password_manager/password_manager_signin_intercept_test_helper.cc
index 53fa1b63..27186a3 100644
--- a/chrome/browser/password_manager/password_manager_signin_intercept_test_helper.cc
+++ b/chrome/browser/password_manager/password_manager_signin_intercept_test_helper.cc
@@ -37,6 +37,7 @@
 constexpr char kGaiaUsername[] = "username";
 constexpr char16_t kGaiaUsername16[] = u"username";
 constexpr char kGaiaEmail[] = "username@gmail.com";
+constexpr char16_t kGaiaEmail16[] = u"username@gmail.com";
 constexpr char kGaiaId[] = "test_gaia_id";
 
 }  // namespace
@@ -102,7 +103,7 @@
   params.profile_path = profile_path;
   params.profile_name = u"TestProfileName";
   params.gaia_id = kGaiaId;
-  params.user_name = base::UTF8ToUTF16(kGaiaEmail);
+  params.user_name = kGaiaEmail16;
   profile_storage->AddProfile(std::move(params));
 
   // Check that the signin qualifies for interception.
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 43341c3..6932fe4 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -46,6 +46,7 @@
 #include "chrome/browser/plugins/plugin_test_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h"
+#include "chrome/browser/resources/pdf/ink/buildflags.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -986,14 +987,6 @@
   RunTestsInJsModule("material_elements_test.js", "test.pdf");
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, ElementsCros) {
-  // Although this test file does not require a PDF to be loaded, loading the
-  // elements without loading a PDF is difficult.
-  RunTestsInJsModule("material_elements_cros_test.js", "test.pdf");
-}
-#endif
-
 IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, DownloadControls) {
   // Although this test file does not require a PDF to be loaded, loading the
   // elements without loading a PDF is difficult.
@@ -1062,10 +1055,17 @@
 IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, Printing) {
   RunTestsInJsModule("printing_icon_test.js", "test.pdf");
 }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-// TODO(https://crbug.com/920684): Test times out under sanatizers
-// TODO(crbug.com/1177131) Re-enable test
-IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, DISABLED_AnnotationsFeatureEnabled) {
+#if BUILDFLAG(ENABLE_INK)
+// TODO(https://crbug.com/920684): Test times out under sanitizers.
+#if defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER) || \
+    defined(ADDRESS_SANITIZER) || defined(_DEBUG)
+#define MAYBE_AnnotationsFeatureEnabled DISABLED_AnnotationsFeatureEnabled
+#else
+#define MAYBE_AnnotationsFeatureEnabled AnnotationsFeatureEnabled
+#endif
+IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, MAYBE_AnnotationsFeatureEnabled) {
   RunTestsInJsModule("annotations_feature_enabled_test.js", "test.pdf");
 }
 
@@ -1074,7 +1074,13 @@
   // elements without loading a PDF is difficult.
   RunTestsInJsModule("annotations_toolbar_test.js", "test.pdf");
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, ViewerToolbarDropdown) {
+  // Although this test file does not require a PDF to be loaded, loading the
+  // elements without loading a PDF is difficult.
+  RunTestsInJsModule("viewer_toolbar_dropdown_test.js", "test.pdf");
+}
+#endif  // BUILDFLAG(ENABLE_INK)
 
 class PDFExtensionContentSettingJSTest : public PDFExtensionJSTestBase {
  public:
diff --git a/chrome/browser/policy/messaging_layer/public/report_client.h b/chrome/browser/policy/messaging_layer/public/report_client.h
index c6bbe7a..754add56 100644
--- a/chrome/browser/policy/messaging_layer/public/report_client.h
+++ b/chrome/browser/policy/messaging_layer/public/report_client.h
@@ -11,9 +11,9 @@
 
 #include "base/memory/singleton.h"
 #include "chrome/browser/policy/messaging_layer/upload/upload_client.h"
-#include "components/reporting//proto/record.pb.h"
 #include "components/reporting/client/report_queue_configuration.h"
 #include "components/reporting/client/report_queue_provider.h"
+#include "components/reporting/proto/record.pb.h"
 #include "components/reporting/storage/storage_module_interface.h"
 #include "components/reporting/storage/storage_uploader_interface.h"
 #include "components/reporting/storage_selector/storage_selector.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 568392c0..0f58727d 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -407,7 +407,7 @@
 #endif
 
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
-#include "chrome/browser/device_identity//device_oauth2_token_store_desktop.h"
+#include "chrome/browser/device_identity/device_oauth2_token_store_desktop.h"
 #include "chrome/browser/downgrade/downgrade_prefs.h"
 #include "chrome/browser/ui/startup/default_browser_prompt.h"
 #endif
diff --git a/chrome/browser/printing/print_backend_service.h b/chrome/browser/printing/print_backend_service.h
deleted file mode 100644
index b19653a..0000000
--- a/chrome/browser/printing/print_backend_service.h
+++ /dev/null
@@ -1,24 +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 CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_H_
-#define CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_H_
-
-#include <string>
-
-#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-// Acquires a remote handle to the Print Backend Service instance, launching a
-// process to host the service if necessary.
-const mojo::Remote<printing::mojom::PrintBackendService>&
-GetPrintBackendService(const std::string& locale,
-                       const std::string& printer_name);
-
-// Test support to override the print backend service for testing.  Caller
-// retains ownership of `remote`.
-void SetPrintBackendServiceForTesting(
-    mojo::Remote<printing::mojom::PrintBackendService>* remote);
-
-#endif  // CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_H_
diff --git a/chrome/browser/printing/print_backend_service.cc b/chrome/browser/printing/print_backend_service_manager.cc
similarity index 76%
rename from chrome/browser/printing/print_backend_service.cc
rename to chrome/browser/printing/print_backend_service_manager.cc
index 1ecaa0631..daa52f3 100644
--- a/chrome/browser/printing/print_backend_service.cc
+++ b/chrome/browser/printing/print_backend_service_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/printing/print_backend_service.h"
+#include "chrome/browser/printing/print_backend_service_manager.h"
 
 #include <string>
 
@@ -16,27 +16,25 @@
 #include "content/public/browser/service_process_host.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
+namespace printing {
+
 namespace {
 
 // Amount of idle time to wait before resetting the connection to the service.
 constexpr base::TimeDelta kResetOnIdleTimeout =
     base::TimeDelta::FromSeconds(20);
 
-// `PrintBackendService` override for testing.
-mojo::Remote<printing::mojom::PrintBackendService>*
-    g_print_backend_service_for_test = nullptr;
-
 }  // namespace
 
-const mojo::Remote<printing::mojom::PrintBackendService>&
-GetPrintBackendService(const std::string& locale,
-                       const std::string& printer_name) {
-  static base::NoDestructor<base::flat_map<
-      std::string, mojo::Remote<printing::mojom::PrintBackendService>>>
-      remotes;
+PrintBackendServiceManager::PrintBackendServiceManager() = default;
 
-  if (g_print_backend_service_for_test)
-    return *g_print_backend_service_for_test;
+PrintBackendServiceManager::~PrintBackendServiceManager() = default;
+
+const mojo::Remote<printing::mojom::PrintBackendService>&
+PrintBackendServiceManager::GetService(const std::string& locale,
+                                       const std::string& printer_name) {
+  if (service_remote_for_test_)
+    return *service_remote_for_test_;
 
   std::string remote_id;
 #if defined(OS_WIN)
@@ -45,10 +43,10 @@
   // https://crbug.com/957242
   remote_id = printer_name;
 #endif
-  auto iter = remotes->find(remote_id);
-  if (iter == remotes->end()) {
+  auto iter = remotes_.find(remote_id);
+  if (iter == remotes_.end()) {
     // First time for this `remote_id`.
-    auto result = remotes->emplace(
+    auto result = remotes_.emplace(
         printer_name, mojo::Remote<printing::mojom::PrintBackendService>());
     iter = result.first;
   }
@@ -84,7 +82,15 @@
   return service;
 }
 
-void SetPrintBackendServiceForTesting(
+void PrintBackendServiceManager::SetServiceForTesting(
     mojo::Remote<printing::mojom::PrintBackendService>* remote) {
-  g_print_backend_service_for_test = remote;
+  service_remote_for_test_ = remote;
 }
+
+// static
+PrintBackendServiceManager& PrintBackendServiceManager::GetInstance() {
+  static base::NoDestructor<PrintBackendServiceManager> singleton;
+  return *singleton;
+}
+
+}  // namespace printing
diff --git a/chrome/browser/printing/print_backend_service_manager.h b/chrome/browser/printing/print_backend_service_manager.h
new file mode 100644
index 0000000..7e3c80f
--- /dev/null
+++ b/chrome/browser/printing/print_backend_service_manager.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_MANAGER_H_
+#define CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_MANAGER_H_
+
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/no_destructor.h"
+#include "chrome/services/printing/public/mojom/print_backend_service.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace printing {
+
+class PrintBackendServiceManager {
+ public:
+  PrintBackendServiceManager(const PrintBackendServiceManager&) = delete;
+  PrintBackendServiceManager& operator=(const PrintBackendServiceManager&) =
+      delete;
+
+  // Acquires a remote handle to the Print Backend Service instance, launching a
+  // process to host the service if necessary.
+  const mojo::Remote<printing::mojom::PrintBackendService>& GetService(
+      const std::string& locale,
+      const std::string& printer_name);
+
+  // Overrides the print backend service for testing.  Caller retains ownership
+  // of `remote`.
+  void SetServiceForTesting(
+      mojo::Remote<printing::mojom::PrintBackendService>* remote);
+
+  // There is to be at most one instance of this at a time.
+  static PrintBackendServiceManager& GetInstance();
+
+ private:
+  friend base::NoDestructor<PrintBackendServiceManager>;
+
+  PrintBackendServiceManager();
+  ~PrintBackendServiceManager();
+
+  using RemotesMap =
+      base::flat_map<std::string,
+                     mojo::Remote<printing::mojom::PrintBackendService>>;
+
+  RemotesMap remotes_;
+
+  // Override of service to use for testing.
+  mojo::Remote<printing::mojom::PrintBackendService>* service_remote_for_test_ =
+      nullptr;
+};
+
+}  // namespace printing
+
+#endif  // CHROME_BROWSER_PRINTING_PRINT_BACKEND_SERVICE_MANAGER_H_
diff --git a/chrome/browser/printing/print_backend_service_test_impl.cc b/chrome/browser/printing/print_backend_service_test_impl.cc
index 94703df4..cd8f4ea6 100644
--- a/chrome/browser/printing/print_backend_service_test_impl.cc
+++ b/chrome/browser/printing/print_backend_service_test_impl.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/memory/scoped_refptr.h"
-#include "chrome/browser/printing/print_backend_service.h"
+#include "chrome/browser/printing/print_backend_service_manager.h"
 #include "printing/backend/test_print_backend.h"
 
 namespace printing {
@@ -47,7 +47,7 @@
 
   // Register this test version of print backend service to be used instead of
   // launching instances out-of-process on-demand.
-  SetPrintBackendServiceForTesting(&remote);
+  PrintBackendServiceManager::GetInstance().SetServiceForTesting(&remote);
 
   return service;
 }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 76379733..d3ddfb74 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -215,9 +215,8 @@
     auto web_app_info = std::make_unique<WebApplicationInfo>();
     web_app_info->start_url = start_url;
     web_app_info->scope = start_url;
-    web_app_info->title = base::UTF8ToUTF16("Test app \xF0\x9F\x90\x90");
-    web_app_info->description =
-        base::UTF8ToUTF16("Test description \xF0\x9F\x90\x90");
+    web_app_info->title = u"Test app 🐐";
+    web_app_info->description = u"Test description 🐐";
     web_app_info->open_as_window = open_as_window;
 
     return web_app::test::InstallWebApp(browser()->profile(),
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index 92d38264d..74e21b8 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -93,16 +93,30 @@
    */
   static performAction(action) {
     switch (action) {
+      // Global actions:
+      case SwitchAccessMenuAction.SETTINGS:
+        chrome.accessibilityPrivate.openSettingsSubpage(
+            'manageAccessibility/switchAccess');
+        ActionManager.exitCurrentMenu();
+        break;
+      case SwitchAccessMenuAction.POINT_SCAN:
+        ActionManager.exitCurrentMenu();
+        Navigator.byPoint.start();
+        break;
+      case SwitchAccessMenuAction.ITEM_SCAN:
+        Navigator.byItem.restart();
+        break;
+      // Point scan actions:
       case SwitchAccessMenuAction.LEFT_CLICK:
       case SwitchAccessMenuAction.RIGHT_CLICK:
         // Exit menu, then click (so the action will hit the desired target,
         // instead of the menu).
         ActionManager.exitCurrentMenu();
         Navigator.byPoint.performMouseAction(action);
-        return;
+        break;
+      // Item scan actions:
       default:
-        ActionManager.instance.handleGlobalActions_(action) ||
-            ActionManager.instance.performActionOnCurrentNode_(action);
+        ActionManager.instance.performActionOnCurrentNode_(action);
         ActionManager.exitCurrentMenu();
     }
   }
@@ -244,31 +258,6 @@
     return undefined;
   }
 
-  /**
-   * If the action is a global action, perform the action and return true.
-   * Otherwise return false.
-   * @param {!SwitchAccessMenuAction} action
-   * @return {boolean}
-   * @private
-   */
-  handleGlobalActions_(action) {
-    switch (action) {
-      case SwitchAccessMenuAction.SETTINGS:
-        chrome.accessibilityPrivate.openSettingsSubpage(
-            'manageAccessibility/switchAccess');
-        return true;
-      case SwitchAccessMenuAction.POINT_SCAN:
-        ActionManager.exitCurrentMenu();
-        Navigator.byPoint.start();
-        return true;
-      case SwitchAccessMenuAction.ITEM_SCAN:
-        Navigator.byItem.restart();
-        return true;
-      default:
-        return false;
-    }
-  }
-
   /** @private */
   openCurrentMenu_() {
     const actions = this.getActionsForCurrentMenuAndNode_();
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.js b/chrome/browser/resources/chromeos/login/oobe_welcome.js
index a91a0f8b..9f132c37 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.js
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.js
@@ -594,6 +594,11 @@
   /** @private */
   onChromeVoxHintDismissed_() {
     this.userActed('dismissChromeVoxHint');
+    chrome.tts.isSpeaking((speaking) => {
+      if (speaking) {
+        chrome.tts.stop();
+      }
+    });
   },
 
   /**
diff --git a/chrome/browser/resources/internals/user_education/user_education_internals.js b/chrome/browser/resources/internals/user_education/user_education_internals.js
index a2ffaed..0ba33a5 100644
--- a/chrome/browser/resources/internals/user_education/user_education_internals.js
+++ b/chrome/browser/resources/internals/user_education/user_education_internals.js
@@ -35,9 +35,9 @@
   /** @override */
   ready() {
     super.ready();
-    // TODO(crbug.com/1194751): fetch tutorial IDs from handler and display
-    // them.
-    this.tutorials_ = ['test1', 'test2'];
+    this.handler_.getTutorials().then(({tutorialIds}) => {
+      this.tutorials_ = tutorialIds;
+    });
   }
 
   /**
diff --git a/chrome/browser/resources/memories/app.js b/chrome/browser/resources/memories/app.js
index 6c82768..598ab47 100644
--- a/chrome/browser/resources/memories/app.js
+++ b/chrome/browser/resources/memories/app.js
@@ -240,7 +240,7 @@
    */
   onMemoriesQueryResult_(result) {
     if (result.isContinuation) {
-      // Do not replace the existing result. |result| contains a partial set of
+      // Do not replace the existing result. `result` contains a partial set of
       // memories that should be appended to the existing ones.
       this.push('result_.memories', ...result.memories);
       this.result_.continuationQueryParams = result.continuationQueryParams;
@@ -259,7 +259,7 @@
     }
 
     this.onBrowserIdle_().then(() => {
-      // Request up to |RESULTS_PER_PAGE| of the freshest Memories until now.
+      // Request up to `RESULTS_PER_PAGE` of the freshest Memories until now.
       const queryParams = {
         query: this.query_.trim(),
         maxCount: RESULTS_PER_PAGE,
diff --git a/chrome/browser/resources/memories/memory_card.js b/chrome/browser/resources/memories/memory_card.js
index 451e0199..b5e616c 100644
--- a/chrome/browser/resources/memories/memory_card.js
+++ b/chrome/browser/resources/memories/memory_card.js
@@ -90,7 +90,7 @@
   /**
    * @param {!Array} array
    * @param {number} num
-   * @return {!Array} Shallow copy of the first |num| items of the input array.
+   * @return {!Array} Shallow copy of the first `num` items of the input array.
    * @private
    */
   arrayItems_(array, num) {
diff --git a/chrome/browser/resources/memories/router.js b/chrome/browser/resources/memories/router.js
index f8fd007..25d4750 100644
--- a/chrome/browser/resources/memories/router.js
+++ b/chrome/browser/resources/memories/router.js
@@ -53,7 +53,7 @@
       /**
        * The string query parameters of the page URL ('?foo=bar'), provided by
        * <iron-location> and parsed by <iron-query-params> into
-       * |queryParamsObject_| and vice versa. Not to be modified directly.
+       * `queryParamsObject_` and vice versa. Not to be modified directly.
        * @private {string}
        */
       queryParamsString_: {
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index 930e15d9..5320273d 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/chromeos/ui_mode.gni")
+import("//chrome/browser/resources/pdf/ink/ink.gni")
 import("//chrome/common/features.gni")
 import("//pdf/features.gni")
 import("//third_party/closure_compiler/compile_js.gni")
@@ -22,6 +22,7 @@
   in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_manifest"
+  defines = [ "enable_ink=$enable_ink" ]
   in_files = [
     "bookmark_type.js",
     "constants.js",
@@ -40,7 +41,7 @@
     "zoom_manager.js",
   ]
 
-  if (is_chromeos_ash) {
+  if (enable_ink) {
     in_files += [ "ink_controller.js" ]
   }
 }
@@ -50,6 +51,7 @@
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
+  defines = [ "enable_ink=$enable_ink" ]
   in_files = [
     "elements/icons.js",
     "elements/shared-css.js",
@@ -68,7 +70,7 @@
     "pdf_viewer.js",
     "pdf_viewer_shared_style.js",
   ]
-  if (is_chromeos_ash) {
+  if (enable_ink) {
     in_files += [
       "elements/viewer-annotations-bar.js",
       "elements/viewer-annotations-mode-dialog.js",
@@ -108,7 +110,7 @@
     "index.html",
     "main.js",
   ]
-  if (is_chromeos_ash) {
+  if (enable_ink) {
     input_files += [
       "ink/index.html",
       "ink/ink_api.js",
@@ -137,7 +139,7 @@
 }
 
 grit("resources") {
-  defines = chrome_grit_defines
+  defines = chrome_grit_defines + [ "enable_ink=$enable_ink" ]
 
   enable_input_discovery_for_gn_analyze = false
   source = "$target_gen_dir/resources.grd"
@@ -174,7 +176,7 @@
     ":closure_compile_local",
     "elements:closure_compile",
   ]
-  if (is_chromeos_ash) {
+  if (enable_ink) {
     deps += [ "ink:closure_compile" ]
   }
 }
diff --git a/chrome/browser/resources/pdf/elements/BUILD.gn b/chrome/browser/resources/pdf/elements/BUILD.gn
index 60d88fd..6743367 100644
--- a/chrome/browser/resources/pdf/elements/BUILD.gn
+++ b/chrome/browser/resources/pdf/elements/BUILD.gn
@@ -2,7 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/chromeos/ui_mode.gni")
+import("//chrome/browser/resources/pdf/ink/ink.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/polymer/html_to_js.gni")
 
@@ -24,7 +24,7 @@
     ":viewer-zoom-button",
     ":viewer-zoom-toolbar",
   ]
-  if (is_chromeos_ash) {
+  if (enable_ink) {
     deps += [
       ":viewer-annotations-bar",
       ":viewer-annotations-mode-dialog",
@@ -81,7 +81,7 @@
   ]
 }
 
-if (is_chromeos_ash) {
+if (enable_ink) {
   js_library("viewer-ink-host") {
     deps = [
       "..:metrics",
@@ -199,7 +199,7 @@
     "viewer-zoom-button.js",
     "viewer-zoom-toolbar.js",
   ]
-  if (is_chromeos_ash) {
+  if (enable_ink) {
     js_files += [
       "viewer-annotations-bar.js",
       "viewer-annotations-mode-dialog.js",
diff --git a/chrome/browser/resources/pdf/elements/viewer-toolbar.html b/chrome/browser/resources/pdf/elements/viewer-toolbar.html
index 3c5c0e8b..325cad1 100644
--- a/chrome/browser/resources/pdf/elements/viewer-toolbar.html
+++ b/chrome/browser/resources/pdf/elements/viewer-toolbar.html
@@ -163,7 +163,7 @@
     visibility: visible;
   }
 
-<if expr="chromeos">
+<if expr="enable_ink">
   #annotate {
     margin-inline-end: 4px;
   }
@@ -199,7 +199,7 @@
     <cr-icon-button id="sidenavToggle" iron-icon="cr20:menu"
         title="$i18n{menu}" aria-label="$i18n{menu}"
         aria-expanded$="[[getAriaExpanded_(sidenavCollapsed)]]"
-<if expr="chromeos">
+<if expr="enable_ink">
         disabled="[[annotationMode]]"
 </if>
         on-click="onSidenavToggleClick_">
@@ -236,7 +236,7 @@
         on-click="onFitToButtonClick_">
     </cr-icon-button>
     <cr-icon-button iron-icon="pdf:rotate-left" dir="ltr"
-<if expr="chromeos">
+<if expr="enable_ink">
         disabled="[[annotationMode]]"
 </if>
         aria-label="$i18n{tooltipRotateCCW}" title="$i18n{tooltipRotateCCW}"
@@ -244,7 +244,7 @@
     </cr-icon-button>
   </div>
   <div id="end">
-  <if expr="chromeos">
+  <if expr="enable_ink">
     <template is="dom-if" if="[[pdfAnnotationsEnabled]]">
       <cr-icon-button id="annotate" iron-icon="pdf:create"
           on-click="onAnnotationClick_"
@@ -273,7 +273,7 @@
 <cr-action-menu on-open-changed="onMoreOpenChanged_">
   <button id="two-page-view-button"
       class="dropdown-item" on-click="toggleTwoPageViewClick_" role="checkbox"
-<if expr="chromeos">
+<if expr="enable_ink">
       disabled="[[annotationMode]]"
 </if>
       aria-checked="[[getTwoPageViewAriaChecked_(twoUpViewEnabled)]]">
@@ -312,14 +312,14 @@
   </template>
 </cr-action-menu>
 
-<if expr="chromeos">
+<if expr="enable_ink">
   <template is="dom-if" if="[[showAnnotationsModeDialog_]]" restamp>
     <viewer-annotations-mode-dialog two-up-view-enabled="[[twoUpViewEnabled]]"
         rotated="[[rotated]]" on-close="onDialogClose_">
     </viewer-annotations-mode-dialog>
   </template>
 </if>
-<if expr="chromeos">
+<if expr="enable_ink">
   <template is="dom-if" if="[[showAnnotationsBar_]]">
     <viewer-annotations-bar annotation-mode="[[annotationMode]]">
     </viewer-annotations-bar>
diff --git a/chrome/browser/resources/pdf/elements/viewer-toolbar.js b/chrome/browser/resources/pdf/elements/viewer-toolbar.js
index 8ce3556..a8451469 100644
--- a/chrome/browser/resources/pdf/elements/viewer-toolbar.js
+++ b/chrome/browser/resources/pdf/elements/viewer-toolbar.js
@@ -8,7 +8,7 @@
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
 import './icons.js';
-// <if expr="chromeos">
+// <if expr="enable_ink">
 import './viewer-annotations-bar.js';
 // </if>
 import './viewer-download-controls.js';
@@ -22,7 +22,7 @@
 
 import {FittingType} from '../constants.js';
 import {record, UserAction} from '../metrics.js';
-// <if expr="chromeos">
+// <if expr="enable_ink">
 import {ViewerAnnotationsModeDialogElement} from './viewer-annotations-mode-dialog.js';
 // </if>
 
@@ -37,7 +37,7 @@
 
   static get properties() {
     return {
-      // <if expr="chromeos">
+      // <if expr="enable_ink">
       annotationAvailable: Boolean,
       annotationMode: {
         type: Boolean,
@@ -94,7 +94,7 @@
         observer: 'viewportZoomPercentChanged_',
       },
 
-      // <if expr="chromeos">
+      // <if expr="enable_ink">
       /** @private */
       showAnnotationsModeDialog_: {
         type: Boolean,
@@ -183,7 +183,7 @@
     this.getZoomInput_().value = `${this.viewportZoomPercent_}%`;
   }
 
-  // <if expr="chromeos">
+  // <if expr="enable_ink">
   /**
    * @return {boolean}
    * @private
@@ -211,7 +211,7 @@
         'display-annotations-changed', {detail: this.displayAnnotations_}));
     this.getMenu_().close();
 
-    // <if expr="chromeos">
+    // <if expr="enable_ink">
     if (!this.displayAnnotations_ && this.annotationMode) {
       this.toggleAnnotation();
     }
@@ -390,7 +390,7 @@
         this.viewportZoomPercent_ === this.zoomBounds.max;
   }
 
-  // <if expr="chromeos">
+  // <if expr="enable_ink">
   /** @private */
   onDialogClose_() {
     const confirmed =
diff --git a/chrome/browser/resources/pdf/ink/BUILD.gn b/chrome/browser/resources/pdf/ink/BUILD.gn
index fff2530..2b4e78b 100644
--- a/chrome/browser/resources/pdf/ink/BUILD.gn
+++ b/chrome/browser/resources/pdf/ink/BUILD.gn
@@ -6,23 +6,25 @@
 import("//chrome/browser/resources/pdf/ink/ink.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 
-js_type_check("closure_compile") {
-  deps = [ ":ink_api" ]
-}
+# Guard the typepchecking targets so the buildflag header can be provided
+# without coupling to enable_pdf.
 
-js_library("ink_api") {
-  deps = [ "..:annotation_tool" ]
-  externs_list = [ "$externs_path/pending.js" ]
+if (enable_ink) {
+  js_type_check("closure_compile") {
+    deps = [ ":ink_api" ]
+  }
 
-  if (enable_use_media_app_ink) {
-    externs_list += [ "drawing_canvas_externs.js" ]
-  } else {
-    externs_list += [ "//third_party/ink/build/ink_lib_externs.js" ]
+  js_library("ink_api") {
+    deps = [ "..:annotation_tool" ]
+    externs_list = [
+      "$externs_path/pending.js",
+      "drawing_canvas_externs.js",
+    ]
   }
 }
 
 buildflag_header("buildflags") {
   header = "buildflags.h"
 
-  flags = [ "ENABLE_USE_MEDIA_APP_INK=$enable_use_media_app_ink" ]
+  flags = [ "ENABLE_INK=$enable_ink" ]
 }
diff --git a/chrome/browser/resources/pdf/ink/ink.gni b/chrome/browser/resources/pdf/ink/ink.gni
index 27bdb7a..8dcc6014 100644
--- a/chrome/browser/resources/pdf/ink/ink.gni
+++ b/chrome/browser/resources/pdf/ink/ink.gni
@@ -2,5 +2,6 @@
 import("//chromeos/components/media_app_ui/media_app_ui.gni")
 
 declare_args() {
-  enable_use_media_app_ink = enable_cros_media_app
+  # Ink libraries are provided by the ChromeOS media app dependency.
+  enable_ink = enable_cros_media_app
 }
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index efe40e13..2b4e275 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -140,7 +140,7 @@
     sidenav-collapsed="[[sidenavCollapsed_]]"
     two-up-view-enabled="[[twoUpViewEnabled_]]"
     viewport-zoom="[[viewportZoom_]]" zoom-bounds="[[zoomBounds_]]"
-<if expr="chromeos">
+<if expr="enable_ink">
     annotation-available="[[annotationAvailable_]]"
     pdf-annotations-enabled="[[pdfAnnotationsEnabled_]]"
 </if>
@@ -154,7 +154,7 @@
     on-two-up-view-changed="onTwoUpViewChanged_"
     on-zoom-changed="onZoomChanged" on-zoom-in="onZoomIn"
     on-zoom-out="onZoomOut" on-rotate-left="rotateCounterclockwise"
-<if expr="chromeos">
+<if expr="enable_ink">
     on-annotation-mode-toggled="onAnnotationModeToggled_"
 </if>
     on-print="onPrint_" on-save="onToolbarSave_" hidden>
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index 8e94766..872f12f 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// <if expr="chromeos">
+// <if expr="enable_ink">
 import './elements/viewer-ink-host.js';
 // </if>
 import './elements/viewer-password-dialog.js';
@@ -25,7 +25,7 @@
 import {ViewerErrorDialogElement} from './elements/viewer-error-dialog.js';
 import {ViewerPdfSidenavElement} from './elements/viewer-pdf-sidenav.js';
 import {ViewerToolbarElement} from './elements/viewer-toolbar.js';
-// <if expr="chromeos">
+// <if expr="enable_ink">
 import {InkController, InkControllerEventType} from './ink_controller.js';
 //</if>
 import {LocalStorageProxyImpl} from './local_storage_proxy.js';
@@ -309,7 +309,7 @@
     /** @private {?PluginController} */
     this.pluginController_ = null;
 
-    // <if expr="chromeos">
+    // <if expr="enable_ink">
     /** @private {?InkController} */
     this.inkController_ = null;
     // </if>
@@ -346,7 +346,7 @@
 
     this.pluginController_ = PluginController.getInstance();
 
-    // <if expr="chromeos">
+    // <if expr="enable_ink">
     this.inkController_ = InkController.getInstance();
     this.inkController_.init(
         this.viewport, /** @type {!HTMLDivElement} */ (this.getContent()));
@@ -452,7 +452,7 @@
     this.handleToolbarKeyEvent_(e);
   }
 
-  // <if expr="chromeos">
+  // <if expr="enable_ink">
   /** @private */
   onResetView_() {
     if (this.twoUpViewEnabled_) {
@@ -1122,7 +1122,7 @@
     if (requestType !== SaveRequestType.ORIGINAL || !this.annotationMode_) {
       result = await this.currentController.save(requestType);
     } else {
-      // <if expr="chromeos">
+      // <if expr="enable_ink">
       // Request type original in annotation mode --> need to exit annotation
       // mode before saving. See https://crbug.com/919364.
       await this.exitAnnotationMode_();
@@ -1165,7 +1165,7 @@
           });
         });
 
-    // <if expr="chromeos">
+    // <if expr="enable_ink">
     // Saving in Annotation mode is destructive: crbug.com/919364
     this.exitAnnotationMode_();
     // </if>
@@ -1196,7 +1196,7 @@
   /** @private */
   async onPrint_() {
     record(UserAction.PRINT);
-    // <if expr="chromeos">
+    // <if expr="enable_ink">
     await this.exitAnnotationMode_();
     // </if>
     this.currentController.print();
diff --git a/chrome/browser/resources/pdf/pdf_viewer_wrapper.js b/chrome/browser/resources/pdf/pdf_viewer_wrapper.js
index 95066484..e6d5ec4 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_wrapper.js
+++ b/chrome/browser/resources/pdf/pdf_viewer_wrapper.js
@@ -24,6 +24,6 @@
 export {LayoutOptions, PAGE_SHADOW, Viewport} from './viewport.js';
 export {ZoomManager} from './zoom_manager.js';
 
-// <if expr="chromeos">
+// <if expr="enable_ink">
 export {ViewerToolbarDropdownElement} from './elements/viewer-toolbar-dropdown.js';
 // </if>
diff --git a/chrome/browser/resources/read_later/side_panel/BUILD.gn b/chrome/browser/resources/read_later/side_panel/BUILD.gn
index 70159e0..8e00ee1 100644
--- a/chrome/browser/resources/read_later/side_panel/BUILD.gn
+++ b/chrome/browser/resources/read_later/side_panel/BUILD.gn
@@ -33,6 +33,8 @@
 js_library("bookmark_folder") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
+    "//ui/webui/resources/js:icon.m",
   ]
 }
 
diff --git a/chrome/browser/resources/read_later/side_panel/app.html b/chrome/browser/resources/read_later/side_panel/app.html
index 85a78eb..2373950 100644
--- a/chrome/browser/resources/read_later/side_panel/app.html
+++ b/chrome/browser/resources/read_later/side_panel/app.html
@@ -18,6 +18,11 @@
     --cr-tabs-height: 40px;
     --cr-tabs-font-size: 13px;
   }
+
+  #content {
+    flex: 1;
+    overflow: auto;
+  }
 </style>
 
 <header>
@@ -25,7 +30,9 @@
   <cr-tabs tab-names="[[tabs_]]" selected="{{selectedTab_}}"></cr-tabs>
 </header>
 
-<iron-pages selected="[[selectedTab_]]">
-  <read-later-app side-panel></read-later-app>
-  <bookmarks-list></bookmarks-list>
-</iron-pages>
\ No newline at end of file
+<div id="content">
+  <iron-pages selected="[[selectedTab_]]">
+    <read-later-app side-panel></read-later-app>
+    <bookmarks-list></bookmarks-list>
+  </iron-pages>
+</div>
\ No newline at end of file
diff --git a/chrome/browser/resources/read_later/side_panel/bookmark_folder.html b/chrome/browser/resources/read_later/side_panel/bookmark_folder.html
index 496fddf..e5c739b 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmark_folder.html
+++ b/chrome/browser/resources/read_later/side_panel/bookmark_folder.html
@@ -1,7 +1,95 @@
 <style>
   :host {
     display: block;
+    user-select: none;
+    white-space: nowrap;
+  }
+
+  .row {
+    align-items: center;
+    cursor: pointer;
+    display: grid;
+    grid-template-areas: 'arrow icon title';
+    grid-template-columns: 20px 20px auto;
+    height: 40px;
+    line-height: 40px;
+    padding-inline-start: calc(var(--node-depth) * 17px);
+  }
+
+  .row:hover {
+    background-color: var(--cr-hover-background-color);
+  }
+
+  .row:active {
+    background-color: var(--cr-active-background-color);
+  }
+
+  #arrow {
+    grid-area: arrow;
+    justify-self: center;
+  }
+
+  #arrowIcon {
+    --cr-icon-button-hover-background-color: transparent;
+    --cr-icon-button-active-background-color: transparent;
+    margin: 0;
+    transform: rotate(-90deg);
+  }
+
+  #arrowIcon[open] {
+    transform: rotate(0);
+  }
+
+  .icon {
+    background-position: left center;
+    background-repeat: no-repeat;
+    grid-area: icon;
+    height: 16px;
+    justify-self: center;
+    width: 16px;
+  }
+
+  #folderIcon {
+    background-image: url(chrome://theme/IDR_FOLDER_CLOSED);
+  }
+
+  .title {
+    grid-area: title;
+    padding: 0 10px;
+  }
+
+  .bookmark {
+    color: currentColor;
+    text-decoration: none;
   }
 </style>
+<div class="row" on-click="onFolderClick_">
+  <div id="arrow" hidden$="[[!folder.children.length]]">
+    <cr-icon-button
+        id="arrowIcon"
+        iron-icon="cr:arrow-drop-down"
+        open$="[[open_]]">
+    </cr-icon-button>
+  </div>
+  <div id="folderIcon" class="icon"></div>
+  <div class="title">[[folder.title]]</div>
+</div>
 
-[[folder.title]]
\ No newline at end of file
+<div id="children" hidden$="[[!open_]]">
+  <template is="dom-repeat" items="[[folder.children]]">
+    <template is="dom-if" if="[[item.children]]">
+      <bookmark-folder folder="[[item]]" depth="[[childDepth_]]">
+      </bookmark-folder>
+    </template>
+
+    <template is="dom-if" if="[[!item.children]]">
+      <a href="[[item.url]]" class="bookmark row" on-click="onBookmarkClick_">
+        <div
+            class="icon"
+            style="background-image: [[getBookmarkIcon_(item.url)]]">
+        </div>
+        <div class="title">[[item.title]]</div>
+      </a>
+    </template>
+  </template>
+</div>
\ No newline at end of file
diff --git a/chrome/browser/resources/read_later/side_panel/bookmark_folder.js b/chrome/browser/resources/read_later/side_panel/bookmark_folder.js
index 5cc3164..da9f5cc 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmark_folder.js
+++ b/chrome/browser/resources/read_later/side_panel/bookmark_folder.js
@@ -2,9 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import 'chrome://resources/cr_elements/shared_vars_css.m.js';
+
+import {getFaviconForPageURL} from 'chrome://resources/js/icon.m.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-class BookmarkFolder extends PolymerElement {
+export class BookmarkFolderElement extends PolymerElement {
   static get is() {
     return 'bookmark-folder';
   }
@@ -15,10 +19,70 @@
 
   static get properties() {
     return {
-      /** @private {!chrome.bookmarks.BookmarkTreeNode} */
+      /** @private */
+      childDepth_: {
+        type: Number,
+        value: 1,
+      },
+
+      depth: {
+        type: Number,
+        observer: 'onDepthChanged_',
+        value: 0,
+      },
+
+      /** @type {!chrome.bookmarks.BookmarkTreeNode} */
       folder: Object,
+
+      /** @private */
+      open_: Boolean,
+
+      openByDefault: {
+        type: Boolean,
+        value: false,
+      },
     };
   }
+
+  ready() {
+    super.ready();
+    this.open_ = this.openByDefault;
+  }
+
+  /**
+   * @param {!Event} event
+   * @private
+   */
+  onBookmarkClick_(event) {
+    event.preventDefault();
+    // TODO(johntlee): Navigate to bookmark URL in current tab.
+  }
+
+  /**
+   * @param {string} url
+   * @return {string}
+   * @private
+   */
+  getBookmarkIcon_(url) {
+    return getFaviconForPageURL(url, false);
+  }
+
+  /** @private */
+  onDepthChanged_() {
+    this.childDepth_ = this.depth + 1;
+    this.style.setProperty('--node-depth', `${this.depth}`);
+    this.$.children.style.setProperty('--node-depth', `${this.childDepth_}`);
+  }
+
+  /** @private */
+  onFolderClick_() {
+    if (!this.folder.children.length) {
+      // No reason to open if there are no children to show.
+      return;
+    }
+
+    this.open_ = !this.open_;
+  }
 }
 
-customElements.define(BookmarkFolder.is, BookmarkFolder);
\ No newline at end of file
+customElements.define(BookmarkFolderElement.is, BookmarkFolderElement);
\ No newline at end of file
diff --git a/chrome/browser/resources/read_later/side_panel/bookmarks_list.html b/chrome/browser/resources/read_later/side_panel/bookmarks_list.html
index 1052101..e7d21b99 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmarks_list.html
+++ b/chrome/browser/resources/read_later/side_panel/bookmarks_list.html
@@ -1,3 +1,5 @@
 <template is="dom-repeat" items="[[folders_]]">
-  <bookmark-folder folder="[[item]]"></bookmark-folder>
+  <bookmark-folder
+      folder="[[item]]" open-by-default="[[!index]]">
+  </bookmark-folder>
 </template>
\ No newline at end of file
diff --git a/chrome/browser/resources/read_later/side_panel/bookmarks_list.js b/chrome/browser/resources/read_later/side_panel/bookmarks_list.js
index 50d60477..1932ea5c 100644
--- a/chrome/browser/resources/read_later/side_panel/bookmarks_list.js
+++ b/chrome/browser/resources/read_later/side_panel/bookmarks_list.js
@@ -7,7 +7,7 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {BookmarksApiProxy, BookmarksApiProxyImpl} from './bookmarks_api_proxy.js';
 
-export class BookmarksList extends PolymerElement {
+export class BookmarksListElement extends PolymerElement {
   static get is() {
     return 'bookmarks-list';
   }
@@ -41,4 +41,4 @@
   }
 }
 
-customElements.define(BookmarksList.is, BookmarksList);
\ No newline at end of file
+customElements.define(BookmarksListElement.is, BookmarksListElement);
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index e58a3c7..e6ce7f7 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -446,6 +446,7 @@
     "chromeos/os_privacy_page/peripheral_data_access_browser_proxy.m.js",
     "chromeos/os_privacy_page/peripheral_data_access_protection_dialog.m.js",
     "chromeos/os_reset_page/os_powerwash_dialog.js",
+    "chromeos/os_reset_page/os_powerwash_dialog_esim_item.js",
     "chromeos/os_reset_page/os_reset_page.js",
     "chromeos/os_route.m.js",
     "chromeos/os_search_page/os_search_page.m.js",
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.js b/chrome/browser/resources/settings/chromeos/lazy_load.js
index 94e68b2..aeaa5d1 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.js
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.js
@@ -48,6 +48,7 @@
 import './os_privacy_page/os_privacy_page.m.js';
 import './os_privacy_page/peripheral_data_access_protection_dialog.m.js';
 import './os_reset_page/os_powerwash_dialog.js';
+import './os_reset_page/os_powerwash_dialog_esim_item.js';
 import './os_reset_page/os_reset_page.js';
 import './os_files_page/smb_shares_page.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
index 493a58f..5d06ff8 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/BUILD.gn
@@ -11,6 +11,7 @@
   is_polymer3 = true
   deps = [
     ":os_powerwash_dialog",
+    ":os_powerwash_dialog_esim_item",
     ":os_reset_browser_proxy",
     ":os_reset_page",
   ]
@@ -18,14 +19,23 @@
 
 js_library("os_powerwash_dialog") {
   deps = [
+    ":os_powerwash_dialog_esim_item",
     ":os_reset_browser_proxy",
     "..:metrics_recorder.m",
     "../..:lifetime_browser_proxy",
     "../localized_link:localized_link.m",
+    "//third_party/polymer/v3_0/components-chromium/iron-list:iron-list",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
 
+js_library("os_powerwash_dialog_esim_item") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:i18n_behavior.m",
+  ]
+}
+
 js_library("os_reset_browser_proxy") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
@@ -48,6 +58,7 @@
 html_to_js("web_components") {
   js_files = [
     "os_powerwash_dialog.js",
+    "os_powerwash_dialog_esim_item.js",
     "os_reset_page.js",
   ]
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
index 9448202..63a59270 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html
@@ -1,27 +1,99 @@
-<style include="settings-shared"></style>
+<style include="settings-shared">
+  :host {
+    --cr-dialog-width: 400px;
+  }
+
+  :host cr-checkbox {
+    --cr-checkbox-label-color: var(--cr-secondary-text-color);
+    --cr-checkbox-label-padding-start: 12px;
+    --cr-checkbox-unchecked-box-color: #888888;
+  }
+
+  iron-list {
+    background-color: #E8EAED61;
+    border-radius: 4px;
+    margin-top: 8px;
+    padding-bottom: 8px;
+  }
+
+  os-settings-powerwash-dialog-esim-item {
+    height: 32px;
+    padding-inline-end: 16px;
+    padding-inline-start: 16px;
+  }
+
+  #profilesListContainer {
+    margin-bottom: 8px;
+    margin-top: 20px;
+  }
+
+  #profilesListTitle {
+    font-size: calc(14 / 13 * 100%);
+  }
+
+  #checkboxLabel {
+    margin-top: 16px;
+  }
+</style>
 <cr-dialog id="dialog" close-text="$i18n{close}"
     ignore-enter-key>
-  <div slot="title">$i18n{powerwashDialogTitle}</div>
-  <div slot="body">
-    <!-- TODO(crbug.com/1196511): UI still in progress. -->
-    <template is="dom-if"
-        if="[[shouldShowESimWarning_(installedESimProfiles)]]">
-
-    </template>
-    <template is="dom-if"
-        if="[[!shouldShowESimWarning_(installedESimProfiles)]]">
+  <template is="dom-if" if="[[shouldShowESimWarning_]]">
+    <div slot="title">$i18n{powerwashDialogESimWarningTitle}</div>
+    <div slot="body">
+      <!-- TODO(crbug.com/1196511): Add link to mobile settings. -->
+      <settings-localized-link
+          localized-string="$i18n{powerwashDialogESimWarning}"
+          link-url="#">
+      </settings-localized-link>
+      <div id="profilesListContainer">
+        <div id="profilesListTitle">
+          $i18n{powerwashDialogESimListTitle}
+        </div>
+        <iron-list items="[[installedESimProfiles]]"
+            scroll-target="profilesListContainer"
+            role="listbox">
+          <template>
+            <os-settings-powerwash-dialog-esim-item
+                profile="[[item]]"
+                tabindex="0"
+                role="option">
+            </os-powerwash-dialog-esim-item>
+          </template>
+        </iron-list>
+      </div>
+      <cr-checkbox>
+        <div id="checkboxLabel">
+          $i18n{powerwashDialogESimWarningCheckbox}
+        </div>
+      </cr-checkbox>
+    </div>
+  </template>
+  <template is="dom-if" if="[[!shouldShowESimWarning_]]">
+    <div slot="title">$i18n{powerwashDialogTitle}</div>
+    <div slot="body">
       <div id="powerwashContainer">
         <settings-localized-link
           localized-string="$i18n{powerwashDialogExplanation}"
           link-url="$i18nRaw{powerwashLearnMoreUrl}">
         </settings-localized-link>
       </div>
-    </template>
-  </div>
+    </div>
+  </template>
   <div slot="button-container">
     <cr-button class="cancel-button" on-click="onCancelTap_"
         id="cancel">$i18n{cancel}</cr-button>
-    <cr-button class="action-button" id="powerwash"
-        on-click="onRestartTap_">$i18n{powerwashDialogButton}</cr-button>
+    <template is="dom-if" if="[[!shouldShowESimWarning_]]">
+      <cr-button class="action-button" id="powerwash"
+          on-click="onRestartTap_">
+        $i18n{powerwashDialogButton}
+      </cr-button>
+    </template>
+    <template is="dom-if" if="[[shouldShowESimWarning_]]">
+      <!-- TODO(crbug.com/1196511): Add continue button logic. -->
+      <cr-button class="action-button" id="continue"
+          hidden="[[!shouldShowESimWarning_]]">
+        $i18n{powerwashContinue}
+      </cr-button>
+    </template>
   </div>
 </cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
index 46ca582..1624dd1 100644
--- a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.js
@@ -8,9 +8,12 @@
  * from the user for a device reset (aka powerwash).
  */
 import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
 import '//resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import '//resources/polymer/v3_0/iron-list/iron-list.js';
 import '../localized_link/localized_link.m.js';
 import '../../settings_shared_css.js';
+import './os_powerwash_dialog_esim_item.js';
 
 import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
@@ -39,6 +42,13 @@
         return [];
       },
     },
+
+    /** @private */
+    shouldShowESimWarning_: {
+      type: Boolean,
+      value: false,
+      computed: 'computeShouldShowESimWarning_(installedESimProfiles)',
+    },
   },
 
   /** @override */
@@ -63,7 +73,7 @@
    * @return {boolean}
    * @private
    */
-  shouldShowESimWarning_() {
+  computeShouldShowESimWarning_() {
     return !!this.installedESimProfiles.length;
   },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.html b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.html
new file mode 100644
index 0000000..c6ec242
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.html
@@ -0,0 +1,13 @@
+<style include="settings-shared">
+  div {
+    color: var(--cr-primary-text-color);
+    margin-top: 10px;
+  }
+
+  /* This id is in the string returned from getItemInnerHtml_() */
+  #providerName {
+    color: var(--google-grey-600);
+  }
+</style>
+<div inner-h-t-m-l="[[getItemInnerHtml_(profileProperties_)]]">
+</div>
diff --git a/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.js b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.js
new file mode 100644
index 0000000..d852715
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.js
@@ -0,0 +1,99 @@
+// 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.
+
+/**
+ * @fileoverview
+ * 'os-settings-powerwash-dialog-esim-item' is an item showing details of an
+ * installed eSIM profile shown in a list in the device reset dialog.
+ */
+import '../../settings_shared_css.js';
+
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+Polymer({
+  _template: html`{__html_template__}`,
+  is: 'os-settings-powerwash-dialog-esim-item',
+
+  behaviors: [
+    I18nBehavior,
+  ],
+
+  properties: {
+    /** @type {?chromeos.cellularSetup.mojom.ESimProfileRemote} */
+    profile: {
+      type: Object,
+      value: null,
+      observer: 'onProfileChanged_',
+    },
+
+    /**
+     * @type {?chromeos.cellularSetup.mojom.ESimProfileProperties}
+     * @private
+     */
+    profileProperties_: {
+      type: Object,
+      value: null,
+    },
+  },
+
+  /** @private */
+  onProfileChanged_() {
+    if (!this.profile) {
+      this.profileProperties_ = null;
+      return;
+    }
+    this.profile.getProperties().then(response => {
+      this.profileProperties_ = response.properties;
+    });
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getItemInnerHtml_() {
+    if (!this.profileProperties_) {
+      return '';
+    }
+    const profileName = this.getProfileName_(this.profileProperties_);
+    const providerName = this.escapeHtml_(
+        String.fromCharCode(...this.profileProperties_.serviceProvider.data));
+    if (!providerName) {
+      return profileName;
+    }
+    return this.i18nAdvanced(
+        'powerwashDialogESimListItemTitle',
+        {attrs: ['id'], substitutions: [profileName, providerName]});
+  },
+
+  /**
+   * @param {chromeos.cellularSetup.mojom.ESimProfileProperties}
+   *     profileProperties
+   * @return {string}
+   * @private
+   */
+  getProfileName_(profileProperties) {
+    if (!profileProperties.nickname.data ||
+        !profileProperties.nickname.data.length) {
+      return this.escapeHtml_(
+          String.fromCharCode(...profileProperties.name.data));
+    }
+    return this.escapeHtml_(
+        String.fromCharCode(...profileProperties.nickname.data));
+  },
+
+  /**
+   * @param {string} string
+   * @return {string}
+   * @private
+   */
+  escapeHtml_(string) {
+    return string.replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/"/g, '&quot;')
+        .replace(/'/g, '&#039;');
+  },
+});
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index 67f9c13d..5827c5df 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -308,6 +308,7 @@
   "chrome/browser/resources/settings/chromeos/os_printing_page/cups_settings_add_printer_dialog.html",
   "chrome/browser/resources/settings/chromeos/os_printing_page/os_printing_page.html",
   "chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog.html",
+  "chrome/browser/resources/settings/chromeos/os_reset_page/os_powerwash_dialog_esim_item.html",
   "chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_browser_proxy.html",
   "chrome/browser/resources/settings/chromeos/os_reset_page/os_reset_page.html",
   "chrome/browser/resources/settings/chromeos/personalization_page/change_picture_browser_proxy.html",
diff --git a/chrome/browser/resources/welcome/google_apps/nux_google_apps.html b/chrome/browser/resources/welcome/google_apps/nux_google_apps.html
index f64c3145..d0e7736d 100644
--- a/chrome/browser/resources/welcome/google_apps/nux_google_apps.html
+++ b/chrome/browser/resources/welcome/google_apps/nux_google_apps.html
@@ -177,7 +177,7 @@
       </cr-button>
       <step-indicator model="[[indicatorModel]]"></step-indicator>
       <cr-button class="action-button" disabled$="[[!hasAppsSelected_]]"
-          on-click="onGetStartedClicked_">
+          on-click="onNextClicked_">
         $i18n{next}
         <iron-icon icon="cr:chevron-right"></iron-icon>
       </cr-button>
diff --git a/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts b/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
index ad8c179..dc21423 100644
--- a/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
+++ b/chrome/browser/resources/welcome/google_apps/nux_google_apps.ts
@@ -73,18 +73,18 @@
     };
   }
 
-  private appProxy_: GoogleAppProxy|null = null;
-  private metricsManager_: ModuleMetricsManager|null = null;
+  private appProxy_: GoogleAppProxy;
+  private metricsManager_: ModuleMetricsManager;
   private finalized_: boolean = false;
-  private bookmarkProxy_: BookmarkProxy|null = null;
-  private bookmarkBarManager_: BookmarkBarManager|null = null;
+  private bookmarkProxy_: BookmarkProxy;
+  private bookmarkBarManager_: BookmarkBarManager;
   private wasBookmarkBarShownOnInit_: boolean = false;
   private appList_: AppItem[]|null = null;
   private hasAppsSelected_: boolean = true;
-  indicatorModel: stepIndicatorModel;
+  indicatorModel?: stepIndicatorModel;
 
-  ready() {
-    super.ready();
+  constructor() {
+    super();
 
     this.appProxy_ = GoogleAppProxyImpl.getInstance();
     this.metricsManager_ =
@@ -125,7 +125,7 @@
       direction *= -1;  // Reverse direction if RTL.
     }
 
-    const buttons = this.root.querySelectorAll('button');
+    const buttons = this.shadowRoot!.querySelectorAll('button');
     const targetIndex = Array.prototype.indexOf.call(buttons, element);
 
     const oldFocus = buttons[targetIndex];
@@ -198,9 +198,9 @@
 
   private onAppKeyUp_(e: KeyboardEvent) {
     if (e.key === 'ArrowRight') {
-      this.changeFocus_(e.currentTarget, 1);
+      this.changeFocus_(e.currentTarget!, 1);
     } else if (e.key === 'ArrowLeft') {
-      this.changeFocus_(e.currentTarget, -1);
+      this.changeFocus_(e.currentTarget!, -1);
     } else {
       (e.currentTarget as HTMLElement).classList.add(KEYBOARD_FOCUSED);
     }
@@ -210,9 +210,9 @@
     (e.currentTarget as HTMLElement).classList.remove(KEYBOARD_FOCUSED);
   }
 
-  private onGetStartedClicked_() {
+  private onNextClicked_() {
     this.finalized_ = true;
-    this.appList_.forEach(app => {
+    this.appList_!.forEach(app => {
       if (app.selected) {
         this.appProxy_.recordProviderSelected(app.id);
       }
@@ -274,7 +274,7 @@
    */
   private updateHasAppsSelected_() {
     this.hasAppsSelected_ =
-        this.appList_ && this.appList_.some(a => a.selected);
+        !!this.appList_ && this.appList_.some(a => a.selected);
     if (!this.hasAppsSelected_) {
       this.bookmarkBarManager_.setShown(this.wasBookmarkBarShownOnInit_);
     }
diff --git a/chrome/browser/resources/welcome/landing_view.ts b/chrome/browser/resources/welcome/landing_view.ts
index bfa29f7..a6fcb47 100644
--- a/chrome/browser/resources/welcome/landing_view.ts
+++ b/chrome/browser/resources/welcome/landing_view.ts
@@ -35,20 +35,18 @@
 
   static get properties() {
     return {
-      signinAllowed_: {
-        type: Boolean,
-        value: () => loadTimeData.getBoolean('signinAllowed'),
-      }
+      signinAllowed_: Boolean,
     };
   }
 
-  private landingViewProxy_: LandingViewProxy|null = null;
+  private landingViewProxy_: LandingViewProxy;
   private finalized_: boolean = false;
   private signinAllowed_: boolean;
 
-  ready() {
-    super.ready();
+  constructor() {
+    super();
     this.landingViewProxy_ = LandingViewProxyImpl.getInstance();
+    this.signinAllowed_ = loadTimeData.getBoolean('signinAllowed');
   }
 
   onRouteEnter() {
diff --git a/chrome/browser/resources/welcome/navigation_behavior.ts b/chrome/browser/resources/welcome/navigation_behavior.ts
index 82a3292..02d6272a 100644
--- a/chrome/browser/resources/welcome/navigation_behavior.ts
+++ b/chrome/browser/resources/welcome/navigation_behavior.ts
@@ -74,7 +74,7 @@
 
   // If currentRouteElement is not null, it means there was a new route.
   if (currentRouteElement) {
-    currentRouteElement.notifyRouteEnter();
+    (currentRouteElement as NavigationBehaviorInterface).notifyRouteEnter();
   }
 }
 
@@ -136,7 +136,9 @@
     // Modules are only attached to DOM if they're for the current route, so
     // as long as the id of an element matches up to the current step, it
     // means that element is for the current route.
-    if (this.id === `step-${step}`) {
+    // TODO(crbug.com/1189595): Figure out the proper way of making TS
+    // understand that this class is an HTMLElement.
+    if ((this as any).id === `step-${step}`) {
       currentRouteElement = this;
       this.notifyRouteEnter();
     }
@@ -154,7 +156,9 @@
 
   /** Called to update focus when progressing through the modules. */
   updateFocusForA11y(): void {
-    const header = this.$$('h1');
+    // TODO(crbug.com/1189595): Figure out the proper way of making TS
+    // understand that this class is also a LegacyElementMixin
+    const header = (this as any).$$('h1');
     if (header) {
       afterNextRender(this, () => header.focus());
     }
diff --git a/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts b/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts
index 6e562ab..0390338 100644
--- a/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts
+++ b/chrome/browser/resources/welcome/ntp_background/nux_ntp_background.ts
@@ -63,13 +63,14 @@
   private backgrounds_: NtpBackgroundData[]|null = null;
   private finalized_: boolean = false;
   private imageIsLoading_: boolean = false;
-  private metricsManager_: ModuleMetricsManager|null = null;
-  private ntpBackgroundProxy_: NtpBackgroundProxy|null = null;
+  private metricsManager_: ModuleMetricsManager;
+  private ntpBackgroundProxy_: NtpBackgroundProxy;
   private selectedBackground_: NtpBackgroundData|undefined;
-  indicatorModel: stepIndicatorModel;
+  indicatorModel?: stepIndicatorModel;
 
-  ready() {
-    super.ready();
+  constructor() {
+    super();
+
     this.ntpBackgroundProxy_ = NtpBackgroundProxyImpl.getInstance();
     this.metricsManager_ =
         new ModuleMetricsManager(NtpBackgroundMetricsProxyImpl.getInstance());
@@ -120,7 +121,7 @@
   }
 
   private hasValidSelectedBackground_(): boolean {
-    return this.selectedBackground_.id > -1;
+    return this.selectedBackground_!.id > -1;
   }
 
   private isSelectedBackground_(background: NtpBackgroundData) {
@@ -128,14 +129,14 @@
   }
 
   private onSelectedBackgroundChange_() {
-    const id = this.selectedBackground_.id;
+    const id = this.selectedBackground_!.id;
     if (id > -1) {
       this.imageIsLoading_ = true;
-      const imageUrl = this.selectedBackground_.imageUrl;
+      const imageUrl = this.selectedBackground_!.imageUrl;
       const beforeLoadTime = window.performance.now();
       this.ntpBackgroundProxy_.preloadImage(imageUrl).then(
           () => {
-            if (this.selectedBackground_.id === id) {
+            if (this.selectedBackground_!.id === id) {
               this.imageIsLoading_ = false;
               this.$.backgroundPreview.classList.add('active');
               this.$.backgroundPreview.style.backgroundImage =
@@ -176,11 +177,11 @@
 
   private onBackgroundKeyUp_(e: KeyboardEvent) {
     if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
-      this.changeFocus_(e.currentTarget, 1);
+      this.changeFocus_(e.currentTarget!, 1);
     } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
-      this.changeFocus_(e.currentTarget, -1);
+      this.changeFocus_(e.currentTarget!, -1);
     } else {
-      this.changeFocus_(e.currentTarget, 0);
+      this.changeFocus_(e.currentTarget!, 0);
     }
   }
 
@@ -191,7 +192,7 @@
 
     // Reverse direction if RTL.
     const buttons =
-        this.shadowRoot.querySelectorAll<HTMLButtonElement>('.option');
+        this.shadowRoot!.querySelectorAll<HTMLButtonElement>('.option');
     const targetIndex = Array.prototype.indexOf.call(buttons, element);
 
     const oldFocus = buttons[targetIndex];
diff --git a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts
index 8edd5054..40de1f3b 100644
--- a/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts
+++ b/chrome/browser/resources/welcome/set_as_default/nux_set_as_default.ts
@@ -52,21 +52,20 @@
     };
   }
 
-  private browserProxy_: NuxSetAsDefaultProxy|null = null;
+  private browserProxy_: NuxSetAsDefaultProxy;
   private finalized_: boolean = false;
   private navigateToNextStep_: Function;
-  indicatorModel: stepIndicatorModel;
+  indicatorModel?: stepIndicatorModel;
 
   constructor() {
     super();
     this.navigateToNextStep_ = navigateToNextStep;
+    this.browserProxy_ = NuxSetAsDefaultProxyImpl.getInstance();
   }
 
   ready() {
     super.ready();
 
-    this.browserProxy_ = NuxSetAsDefaultProxyImpl.getInstance();
-
     this.addWebUIListener(
         'browser-default-state-changed',
         this.onDefaultBrowserChange_.bind(this));
diff --git a/chrome/browser/resources/welcome/shared/bookmark_proxy.ts b/chrome/browser/resources/welcome/shared/bookmark_proxy.ts
index 8f9eed35..bd2ac0f 100644
--- a/chrome/browser/resources/welcome/shared/bookmark_proxy.ts
+++ b/chrome/browser/resources/welcome/shared/bookmark_proxy.ts
@@ -10,8 +10,10 @@
   url: string,
 };
 
+type AddBookmarkCallback = (node: chrome.bookmarks.BookmarkTreeNode) => void;
+
 export interface BookmarkProxy {
-  addBookmark(data: bookmarkData, callback: Function): void;
+  addBookmark(data: bookmarkData, callback: AddBookmarkCallback): void;
 
   /** @param id ID provided by callback when bookmark was added. */
   removeBookmark(id: string): void;
@@ -21,7 +23,7 @@
 }
 
 export class BookmarkProxyImpl implements BookmarkProxy {
-  addBookmark(data: bookmarkData, callback) {
+  addBookmark(data: bookmarkData, callback: AddBookmarkCallback) {
     chrome.bookmarks.create(data, callback);
   }
 
diff --git a/chrome/browser/resources/welcome/shared/onboarding_background.ts b/chrome/browser/resources/welcome/shared/onboarding_background.ts
index 6e0ec66..52dce5e 100644
--- a/chrome/browser/resources/welcome/shared/onboarding_background.ts
+++ b/chrome/browser/resources/welcome/shared/onboarding_background.ts
@@ -28,13 +28,13 @@
     ];
     details.forEach(([id, width]) => {
       this.createLineAnimation_(
-          (this.shadowRoot.querySelector(`#${id}`) as HTMLElement), width);
+          (this.shadowRoot!.querySelector(`#${id}`) as HTMLElement), width);
     });
   }
 
   private createLineAnimation_(lineContainer: HTMLElement, width: number) {
-    const line = lineContainer.firstElementChild;
-    const lineFill = line.firstElementChild;
+    const line = lineContainer.firstElementChild as HTMLElement;
+    const lineFill = line.firstElementChild as HTMLElement;
     const pointOptions = {
       endDelay: 3250,
       fill: 'forwards' as FillMode,
diff --git a/chrome/browser/resources/welcome/shared/step_indicator.ts b/chrome/browser/resources/welcome/shared/step_indicator.ts
index 7524c1c..cc418e2 100644
--- a/chrome/browser/resources/welcome/shared/step_indicator.ts
+++ b/chrome/browser/resources/welcome/shared/step_indicator.ts
@@ -35,8 +35,8 @@
     };
   }
 
-  model: stepIndicatorModel;
-  private dots_: undefined[];
+  model?: stepIndicatorModel;
+  private dots_?: undefined[];
 
   private computeLabel_(active: number, total: number): string {
     return this.i18n('stepsLabel', active + 1, total);
@@ -44,11 +44,11 @@
 
   private computeDots_(): undefined[] {
     // If total is 1, show nothing.
-    return new Array(this.model.total > 1 ? this.model.total : 0);
+    return new Array(this.model!.total > 1 ? this.model!.total : 0);
   }
 
   private getActiveClass_(index: number): string {
-    return index === this.model.active ? 'active' : '';
+    return index === this.model!.active ? 'active' : '';
   }
 
   static get template() {
diff --git a/chrome/browser/resources/welcome/signin_view.ts b/chrome/browser/resources/welcome/signin_view.ts
index 2054335..ef6729a 100644
--- a/chrome/browser/resources/welcome/signin_view.ts
+++ b/chrome/browser/resources/welcome/signin_view.ts
@@ -34,17 +34,14 @@
   }
 
   private finalized_: boolean = false;
-  private welcomeBrowserProxy_: WelcomeBrowserProxy|null = null;
-  private signinViewProxy_: SigninViewProxy|null = null;
+  private welcomeBrowserProxy_: WelcomeBrowserProxy;
+  private signinViewProxy_: SigninViewProxy;
 
   constructor() {
     super();
-  }
 
-  ready() {
-    super.ready();
-    this.welcomeBrowserProxy_ = WelcomeBrowserProxyImpl.getInstance();
     this.signinViewProxy_ = SigninViewProxyImpl.getInstance();
+    this.welcomeBrowserProxy_ = WelcomeBrowserProxyImpl.getInstance();
   }
 
   onRouteEnter() {
diff --git a/chrome/browser/resources/welcome/tsconfig_base.json b/chrome/browser/resources/welcome/tsconfig_base.json
index bc09bd3..cc484e3 100644
--- a/chrome/browser/resources/welcome/tsconfig_base.json
+++ b/chrome/browser/resources/welcome/tsconfig_base.json
@@ -1,7 +1,6 @@
 {
   "extends": "../../../../tools/typescript/tsconfig_base.json",
   "compilerOptions": {
-    "noUnusedLocals": false,
-    "strict": false
+    "noUnusedLocals": false
   }
 }
diff --git a/chrome/browser/resources/welcome/welcome_app.ts b/chrome/browser/resources/welcome/welcome_app.ts
index db0c9a4..88696c8 100644
--- a/chrome/browser/resources/welcome/welcome_app.ts
+++ b/chrome/browser/resources/welcome/welcome_app.ts
@@ -90,14 +90,14 @@
   }
 
   private onDefaultBrowserChange_() {
-    this.shadowRoot.querySelector('cr-toast').show();
+    this.shadowRoot!.querySelector('cr-toast')!.show();
   }
 
   onRouteChange(route: Routes, step: number) {
     const setStep = () => {
       // If the specified step doesn't exist, that means there are no more
       // steps. In that case, replace this page with NTP.
-      if (!this.shadowRoot.querySelector(`#step-${step}`)) {
+      if (!this.shadowRoot!.querySelector(`#step-${step}`)) {
         WelcomeBrowserProxyImpl.getInstance().goToNewTabPage(
             /* replace */ true);
       } else {  // Otherwise, go to the chosen step of that route.
diff --git a/chrome/browser/search_engines/template_url_fetcher_unittest.cc b/chrome/browser/search_engines/template_url_fetcher_unittest.cc
index 134c466..f48bc5e 100644
--- a/chrome/browser/search_engines/template_url_fetcher_unittest.cc
+++ b/chrome/browser/search_engines/template_url_fetcher_unittest.cc
@@ -31,8 +31,6 @@
 
 namespace {
 
-using base::ASCIIToUTF16;
-
 constexpr int32_t kRequestID = 10;
 
 bool GetTestFilePath(const std::string& file_name, base::FilePath* path) {
@@ -299,8 +297,7 @@
   WaitForDownloadToFinish();
   const TemplateURL* t_url =
       test_util()->model()->GetTemplateURLForKeyword(keyword);
-  EXPECT_EQ(base::UTF8ToUTF16("\xd1\x82\xd0\xb5\xd1\x81\xd1\x82"),
-            t_url->short_name());
+  EXPECT_EQ(u"тест", t_url->short_name());
 }
 
 }  // namespace
diff --git a/chrome/browser/search_engines/template_url_service_sync_unittest.cc b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
index fff3861..9442e9a0 100644
--- a/chrome/browser/search_engines/template_url_service_sync_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
@@ -34,10 +34,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::ASCIIToUTF16;
 using base::Time;
 using base::TimeDelta;
-using base::UTF8ToUTF16;
 using testing::NotNull;
 
 namespace {
@@ -971,9 +969,8 @@
   GURL google_url(model()->search_terms_data().GoogleBaseURLValue());
   TemplateURL* guid2 = model()->GetTemplateURLForHost(google_url.host());
   ASSERT_THAT(guid2, NotNull());
-  std::u16string google_keyword(
-      base::ASCIIToUTF16(url_formatter::StripWWW(google_url.host())));
-  EXPECT_EQ(google_keyword, guid2->keyword());
+  std::string google_keyword(url_formatter::StripWWW(google_url.host()));
+  EXPECT_EQ(base::ASCIIToUTF16(google_keyword), guid2->keyword());
 
   // We should also have gotten some corresponding UPDATEs pushed upstream.
   EXPECT_GE(processor()->change_list_size(), 2U);
@@ -984,18 +981,19 @@
   ASSERT_TRUE(processor()->contains_guid("guid2"));
   syncer::SyncChange guid2_change = processor()->change_for_guid("guid2");
   EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, guid2_change.change_type());
-  EXPECT_EQ(google_keyword, UTF8ToUTF16(GetKeyword(guid2_change.sync_data())));
+  EXPECT_EQ(google_keyword, GetKeyword(guid2_change.sync_data()));
 }
 
 TEST_F(TemplateURLServiceSyncTest, AutogeneratedKeywordConflicts) {
   // Sync brings in some autogenerated keywords, but the generated keywords we
   // try to create conflict with ones in the model.
-  std::u16string google_keyword(base::ASCIIToUTF16(url_formatter::StripWWW(
-      GURL(model()->search_terms_data().GoogleBaseURLValue()).host())));
+  std::string google_keyword(url_formatter::StripWWW(
+      GURL(model()->search_terms_data().GoogleBaseURLValue()).host()));
+  std::u16string google_keyword16(base::ASCIIToUTF16(google_keyword));
   const std::string local_google_url =
       "{google:baseURL}1/search?q={searchTerms}";
   TemplateURL* google =
-      model()->Add(CreateTestTemplateURL(google_keyword, local_google_url));
+      model()->Add(CreateTestTemplateURL(google_keyword16, local_google_url));
   TemplateURL* other =
       model()->Add(CreateTestTemplateURL(u"other.com", "http://other.com/foo"));
   syncer::SyncDataList initial_data;
@@ -1024,7 +1022,8 @@
   // the sync TemplateURLs (GUIDs transferred over).
   EXPECT_FALSE(model()->GetTemplateURLForGUID(local_google_guid));
   ASSERT_TRUE(model()->GetTemplateURLForGUID("sync1"));
-  EXPECT_EQ(google_keyword, model()->GetTemplateURLForGUID("sync1")->keyword());
+  EXPECT_EQ(google_keyword16,
+            model()->GetTemplateURLForGUID("sync1")->keyword());
   EXPECT_FALSE(model()->GetTemplateURLForGUID(local_other_guid));
   ASSERT_TRUE(model()->GetTemplateURLForGUID("sync2"));
   EXPECT_EQ(u"other.com", model()->GetTemplateURLForGUID("sync2")->keyword());
@@ -1035,7 +1034,7 @@
   ASSERT_TRUE(processor()->contains_guid("sync1"));
   syncer::SyncChange sync1_change = processor()->change_for_guid("sync1");
   EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, sync1_change.change_type());
-  EXPECT_EQ(google_keyword, UTF8ToUTF16(GetKeyword(sync1_change.sync_data())));
+  EXPECT_EQ(google_keyword, GetKeyword(sync1_change.sync_data()));
   EXPECT_EQ(local_google_url, GetURL(sync1_change.sync_data()));
   ASSERT_TRUE(processor()->contains_guid("sync2"));
   syncer::SyncChange sync2_change = processor()->change_for_guid("sync2");
@@ -1765,7 +1764,8 @@
 }
 
 TEST_F(TemplateURLServiceSyncTest, PreSyncUpdates) {
-  const char* kNewKeyword = "somethingnew";
+  const char kNewKeyword[] = "somethingnew";
+  const char16_t kNewKeyword16[] = u"somethingnew";
   // Fetch the prepopulate search engines so we know what they are.
   std::vector<std::unique_ptr<TemplateURLData>> prepop_turls =
       TemplateURLPrepopulateData::GetPrepopulatedEngines(
@@ -1780,7 +1780,7 @@
   TemplateURLData data_copy(*prepop_turls[0]);
   data_copy.last_modified = Time::FromTimeT(10);
   std::u16string original_keyword = data_copy.keyword();
-  data_copy.SetKeyword(ASCIIToUTF16(kNewKeyword));
+  data_copy.SetKeyword(kNewKeyword16);
   // Set safe_for_autoreplace to false so our keyword survives.
   data_copy.safe_for_autoreplace = false;
   model()->Add(std::make_unique<TemplateURL>(data_copy));
@@ -1792,8 +1792,7 @@
 
   // The newly added search engine should have been safely merged, with an
   // updated time.
-  TemplateURL* added_turl = model()->GetTemplateURLForKeyword(
-      ASCIIToUTF16(kNewKeyword));
+  TemplateURL* added_turl = model()->GetTemplateURLForKeyword(kNewKeyword16);
   ASSERT_TRUE(added_turl);
   base::Time new_timestamp = added_turl->last_modified();
   EXPECT_GE(new_timestamp, pre_merge_time);
@@ -1818,8 +1817,7 @@
   EXPECT_EQ(prepop_turls.size(),
             model()->GetAllSyncData(syncer::SEARCH_ENGINES).size());
 
-  ASSERT_EQ(added_turl, model()->GetTemplateURLForKeyword(
-      ASCIIToUTF16(kNewKeyword)));
+  ASSERT_EQ(added_turl, model()->GetTemplateURLForKeyword(kNewKeyword16));
   EXPECT_EQ(new_timestamp, added_turl->last_modified());
   syncer::SyncChange change = processor()->change_for_guid(sync_guid);
   EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE, change.change_type());
@@ -2548,7 +2546,6 @@
 }
 
 TEST_F(TemplateURLServiceSyncTest, NonAsciiKeywordDoesNotCrash) {
-  model()->Add(CreateTestTemplateURL(UTF8ToUTF16("\xf0\xaf\xa6\x8d"),
-                                     "http://key1.com"));
+  model()->Add(CreateTestTemplateURL(u"\U0002f98d", "http://key1.com"));
   MergeAndExpectNotify(CreateInitialSyncData(), 1);
 }
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
index 4cea4de..ecc05b5 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
@@ -33,7 +33,7 @@
 
 namespace {
 
-const char kText[] = "Text to be copied";
+const char16_t kText[] = u"Text to be copied";
 const char kExpectedText[] = "Text to be copied";
 const char kReceiverGuid[] = "test_receiver_guid";
 const char kReceiverName[] = "test_receiver_name";
@@ -54,7 +54,7 @@
         CreateFakeDeviceInfo(kReceiverGuid, kReceiverName);
     controller_ = SharedClipboardUiController::GetOrCreateFromWebContents(
         web_contents_.get());
-    controller_->OnDeviceSelected(base::UTF8ToUTF16(kText), *device_info.get());
+    controller_->OnDeviceSelected(kText, *device_info.get());
   }
 
  protected:
diff --git a/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc b/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
index af43e3c..35974468 100644
--- a/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
+++ b/chrome/browser/signin/dice_signed_in_profile_creator_unittest.cc
@@ -29,7 +29,7 @@
 
 namespace {
 
-const char kProfileTestName[] = "profile_test_name";
+const char16_t kProfileTestName[] = u"profile_test_name";
 
 std::unique_ptr<TestingProfile> BuildTestingProfile(const base::FilePath& path,
                                                     Profile::Delegate* delegate,
@@ -165,12 +165,11 @@
   AccountInfo account_info =
       identity_test_env()->MakeAccountAvailable("bob@example.com");
   size_t kTestIcon = profiles::GetModernAvatarIconStartIndex();
-  std::u16string kProfileTestName16 = base::UTF8ToUTF16(kProfileTestName);
 
   base::RunLoop loop;
   std::unique_ptr<DiceSignedInProfileCreator> creator =
       std::make_unique<DiceSignedInProfileCreator>(
-          profile(), account_info.account_id, kProfileTestName16, kTestIcon,
+          profile(), account_info.account_id, kProfileTestName, kTestIcon,
           use_guest_profile(),
           base::BindOnce(&DiceSignedInProfileCreatorTest::OnProfileCreated,
                          base::Unretained(this), loop.QuitClosure()));
@@ -201,7 +200,7 @@
   ASSERT_TRUE(entry);
   ASSERT_EQ(entry->IsGuest(), use_guest_profile());
   if (!use_guest_profile()) {
-    EXPECT_EQ(kProfileTestName16, entry->GetLocalProfileName());
+    EXPECT_EQ(kProfileTestName, entry->GetLocalProfileName());
     EXPECT_EQ(kTestIcon, entry->GetAvatarIconIndex());
   }
 }
diff --git a/chrome/browser/signin/signin_global_error_unittest.cc b/chrome/browser/signin/signin_global_error_unittest.cc
index 36746f5..ff40075 100644
--- a/chrome/browser/signin/signin_global_error_unittest.cc
+++ b/chrome/browser/signin/signin_global_error_unittest.cc
@@ -35,6 +35,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 static const char kTestEmail[] = "testuser@test.com";
+static const char16_t kTestEmail16[] = u"testuser@test.com";
 
 class SigninGlobalErrorTest : public testing::Test {
  public:
@@ -64,7 +65,7 @@
             ->GetProfileAttributesWithPath(profile()->GetPath());
     ASSERT_NE(entry, nullptr);
 
-    entry->SetAuthInfo(account_info.gaia, base::UTF8ToUTF16(kTestEmail),
+    entry->SetAuthInfo(account_info.gaia, kTestEmail16,
                        /*is_consented_primary_account=*/true);
 
     global_error_ = SigninGlobalErrorFactory::GetForProfile(profile());
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 7f3c78a1..2867ba5c 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -139,6 +139,7 @@
 enum CertificateStatus { VALID_CERTIFICATE, INVALID_CERTIFICATE };
 
 const char kTestCertificateIssuerName[] = "Test Root CA";
+const char16_t kTestCertificateIssuerName16[] = u"Test Root CA";
 
 bool IsShowingInterstitial(content::WebContents* tab) {
   security_interstitials::SecurityInterstitialTabHelper* helper =
@@ -294,10 +295,9 @@
             expected_cert->issuer().GetDisplayName());
   EXPECT_EQ(l10n_util::GetStringUTF8(IDS_VALID_SERVER_CERTIFICATE),
             explanation.summary);
-  EXPECT_EQ(
-      l10n_util::GetStringFUTF8(IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION,
-                                base::UTF8ToUTF16(kTestCertificateIssuerName)),
-      explanation.description);
+  EXPECT_EQ(l10n_util::GetStringFUTF8(IDS_VALID_SERVER_CERTIFICATE_DESCRIPTION,
+                                      kTestCertificateIssuerName16),
+            explanation.description);
   net::X509Certificate* cert = browser->tab_strip_model()
                                    ->GetActiveWebContents()
                                    ->GetController()
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 7a0c71b..847bc32 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -7278,6 +7278,7 @@
 namespace {
 
 char kTestMITMSoftwareName[] = "Misconfigured Firewall";
+char16_t kTestMITMSoftwareName16[] = u"Misconfigured Firewall";
 
 class SSLUIMITMSoftwareTest : public CertVerifierBrowserTest {
  public:
@@ -7671,12 +7672,12 @@
   SetUpMITMSoftwareCertList(kLargeVersionId);
   TestMITMSoftwareInterstitial();
 
-  const std::string expected_primary_paragraph = l10n_util::GetStringFUTF8(
-      IDS_MITM_SOFTWARE_PRIMARY_PARAGRAPH_ENTERPRISE,
-      net::EscapeForHTML(base::UTF8ToUTF16(kTestMITMSoftwareName)));
+  const std::string expected_primary_paragraph =
+      l10n_util::GetStringFUTF8(IDS_MITM_SOFTWARE_PRIMARY_PARAGRAPH_ENTERPRISE,
+                                net::EscapeForHTML(kTestMITMSoftwareName16));
   const std::string expected_explanation = l10n_util::GetStringFUTF8(
       IDS_MITM_SOFTWARE_EXPLANATION_ENTERPRISE,
-      net::EscapeForHTML(base::UTF8ToUTF16(kTestMITMSoftwareName)),
+      net::EscapeForHTML(kTestMITMSoftwareName16),
       l10n_util::GetStringUTF16(IDS_MITM_SOFTWARE_EXPLANATION));
 
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
@@ -7699,7 +7700,7 @@
   // has escaped HTML characters which throw an error.
   const std::string expected_explanation = l10n_util::GetStringFUTF8(
       IDS_MITM_SOFTWARE_EXPLANATION_NONENTERPRISE,
-      net::EscapeForHTML(base::UTF8ToUTF16(kTestMITMSoftwareName)),
+      net::EscapeForHTML(kTestMITMSoftwareName16),
       l10n_util::GetStringUTF16(IDS_MITM_SOFTWARE_EXPLANATION));
 
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc
index a22613a..a95b7602 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.cc
+++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -40,7 +40,9 @@
 const int kDefaultCardExpMonth = 8;
 const int kDefaultCardExpYear = 2087;
 const char kDefaultCardLastFour[] = "1234";
+const char16_t kDefaultCardLastFour16[] = u"1234";
 const char kDefaultCardName[] = "Patrick Valenzuela";
+const char16_t kDefaultCardName16[] = u"Patrick Valenzuela";
 const sync_pb::WalletMaskedCreditCard_WalletCardType kDefaultCardType =
     sync_pb::WalletMaskedCreditCard::AMEX;
 
@@ -405,17 +407,11 @@
 }
 
 CreditCard GetDefaultCreditCard() {
-  return GetCreditCard(kDefaultCardID, kDefaultCardLastFour);
-}
-
-autofill::CreditCard GetCreditCard(const std::string& name,
-                                   const std::string& last_four) {
-  CreditCard card(CreditCard::MASKED_SERVER_CARD, name);
+  CreditCard card(CreditCard::MASKED_SERVER_CARD, kDefaultCardID);
   card.SetExpirationMonth(kDefaultCardExpMonth);
   card.SetExpirationYear(kDefaultCardExpYear);
-  card.SetNumber(base::UTF8ToUTF16(last_four));
-  card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL,
-                  base::UTF8ToUTF16(kDefaultCardName));
+  card.SetNumber(kDefaultCardLastFour16);
+  card.SetRawInfo(autofill::CREDIT_CARD_NAME_FULL, kDefaultCardName16);
   card.SetServerStatus(CreditCard::OK);
   card.SetNetworkForMaskedCard(autofill::kAmericanExpressCard);
   card.set_billing_address_id(kDefaultBillingAddressID);
@@ -489,11 +485,11 @@
 void ExpectDefaultCreditCardValues(const CreditCard& card) {
   EXPECT_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
   EXPECT_EQ(kDefaultCardID, card.server_id());
-  EXPECT_EQ(base::UTF8ToUTF16(kDefaultCardLastFour), card.LastFourDigits());
+  EXPECT_EQ(kDefaultCardLastFour16, card.LastFourDigits());
   EXPECT_EQ(autofill::kAmericanExpressCard, card.network());
   EXPECT_EQ(kDefaultCardExpMonth, card.expiration_month());
   EXPECT_EQ(kDefaultCardExpYear, card.expiration_year());
-  EXPECT_EQ(base::UTF8ToUTF16(kDefaultCardName),
+  EXPECT_EQ(kDefaultCardName16,
             card.GetRawInfo(autofill::ServerFieldType::CREDIT_CARD_NAME_FULL));
   EXPECT_EQ(kDefaultBillingAddressID, card.billing_address_id());
 }
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
index d6b58a6..8dda202 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabData.java
@@ -126,8 +126,16 @@
     private static class OptimizationGuideBridgeFactoryHolder {
         private static final OptimizationGuideBridgeFactory sOptimizationGuideBridgeFactory;
         static {
-            sOptimizationGuideBridgeFactory = new OptimizationGuideBridgeFactory(
-                    Arrays.asList(HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR));
+            List<HintsProto.OptimizationType> optimizationTypes;
+            if (PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE.getValue()) {
+                optimizationTypes =
+                        Arrays.asList(HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR,
+                                HintsProto.OptimizationType.PRICE_TRACKING);
+            } else {
+                optimizationTypes =
+                        Arrays.asList(HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR);
+            }
+            sOptimizationGuideBridgeFactory = new OptimizationGuideBridgeFactory(optimizationTypes);
         }
     }
 
diff --git a/chrome/browser/themes/theme_helper.cc b/chrome/browser/themes/theme_helper.cc
index 4f48c44..4c7f794 100644
--- a/chrome/browser/themes/theme_helper.cc
+++ b/chrome/browser/themes/theme_helper.cc
@@ -628,9 +628,7 @@
     return blend_toward_max_contrast(bg, 0x0A);
   };
   const auto results_bg_hovered_color = [&]() {
-    return blend_toward_max_contrast(
-        results_bg_color(),
-        OmniboxFieldTrial::IsRefinedFocusStateEnabled() ? 0x0A : 0x1A);
+    return blend_toward_max_contrast(results_bg_color(), 0x1A);
   };
   const auto url_color = [&](OmniboxColor bg) {
     return blend_for_min_contrast(
@@ -640,7 +638,7 @@
   const auto results_bg_selected_color = [&]() {
     return blend_toward_max_contrast(
         results_bg_color(),
-        OmniboxFieldTrial::IsRefinedFocusStateEnabled() ? 0x0A : 0x29);
+        OmniboxFieldTrial::IsRefinedFocusStateEnabled() ? 0x1A : 0x29);
   };
   const auto blend_with_clamped_contrast = [&](OmniboxColor bg) {
     return blend_for_min_contrast(fg, fg, blend_for_min_contrast(bg, bg));
diff --git a/chrome/browser/translate/language_detection_service_browsertest.cc b/chrome/browser/translate/language_detection_service_browsertest.cc
index a85aac7..83204345 100644
--- a/chrome/browser/translate/language_detection_service_browsertest.cc
+++ b/chrome/browser/translate/language_detection_service_browsertest.cc
@@ -18,10 +18,10 @@
                        DetermineLanguageReliable) {
   mojo::Remote<language_detection::mojom::LanguageDetectionService> service =
       language_detection::LaunchLanguageDetectionService();
-  std::u16string text = base::UTF8ToUTF16(
-      "El niño atrapó un dorado muy grande con cebo vivo. Fileteó el "
-      "pescado y lo asó a la parrilla. Sabía excelente. Espera pescar otro "
-      "buen pescado mañana.");
+  std::u16string text =
+      u"El niño atrapó un dorado muy grande con cebo vivo. Fileteó el "
+      u"pescado y lo asó a la parrilla. Sabía excelente. Espera pescar otro "
+      u"buen pescado mañana.";
 
   base::RunLoop run_loop;
   service->DetermineLanguage(
diff --git a/chrome/browser/translate/translate_manager_browsertest.cc b/chrome/browser/translate/translate_manager_browsertest.cc
index cd192b42..b0ff63b 100644
--- a/chrome/browser/translate/translate_manager_browsertest.cc
+++ b/chrome/browser/translate/translate_manager_browsertest.cc
@@ -284,14 +284,6 @@
 
     ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-    // There is a possible race condition, when the language is not yet
-    // detected, so we check for that and wait if necessary.
-    if (chrome_translate_client->GetLanguageState().source_language().empty())
-      WaitUntilLanguageDetermined();
-
-    EXPECT_EQ("und",
-              chrome_translate_client->GetLanguageState().source_language());
-
     // Load a German page and detect it's language
     AddTabAtIndex(0,
                   GURL(embedded_test_server()->GetURL(
@@ -372,15 +364,6 @@
 IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest, PageLanguageDetection) {
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // The InProcessBrowserTest opens a new tab, let's wait for that first.
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in English.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/english_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
@@ -409,15 +392,6 @@
                        DISABLED_PageLanguageDetectionConflict) {
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // The InProcessBrowserTest opens a new tab, let's wait for that first.
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in French with incorrect HTML language
   // attribute specified. The language attribute should be overridden by the
   // language detection.
@@ -452,15 +426,6 @@
   SetTranslateScript(kTestValidScript);
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
-
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   base::HistogramTester histograms;
 
   // Open a new tab with a page in French.
@@ -568,14 +533,6 @@
       true);
   SetTranslateScript(kTestValidScript);
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Load a German page and detect it's language.
   AddTabAtIndex(
       0, GURL(embedded_test_server()->GetURL("/href_translate_test.html")),
@@ -614,14 +571,6 @@
       true);
   SetTranslateScript(kTestValidScript);
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Load a German page and detect it's language
   AddTabAtIndex(0,
                 GURL(embedded_test_server()->GetURL(
@@ -672,14 +621,6 @@
       true);
   SetTranslateScript(kTestValidScript);
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Load a German page and detect it's language
   AddTabAtIndex(0,
                 GURL(embedded_test_server()->GetURL(
@@ -731,14 +672,6 @@
       true);
   SetTranslateScript(kTestValidScript);
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Load a German page and detect it's language.
   AddTabAtIndex(0,
                 GURL(embedded_test_server()->GetURL(
@@ -1094,28 +1027,19 @@
 #define MAYBE_PageTranslationError PageTranslationError
 #endif
 IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest,
-                       MAYBE_PageTranslationError) {
+                       DISABLED_PageTranslationError) {
   SetTranslateScript(kTestValidScript);
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
-  // Open a new tab with about:blank page.
-  AddTabAtIndex(0, GURL("about:blank"), ui::PAGE_TRANSITION_TYPED);
+  // Open a new tab with a page in English and translate to English to force an
+  // error.
+  AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/english_page.html")),
+                ui::PAGE_TRANSITION_TYPED);
   ResetObserver();
   chrome_translate_client = GetChromeTranslateClient();
   WaitUntilLanguageDetermined();
 
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Translate the page through TranslateManager.
   TranslateManager* manager = chrome_translate_client->GetTranslateManager();
   manager->TranslatePage(
@@ -1135,14 +1059,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in French.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
@@ -1172,14 +1088,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in French.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
@@ -1209,14 +1117,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in French.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
@@ -1244,14 +1144,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in French.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
@@ -1282,15 +1174,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
-  // Open a new tab with a page in French.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
   ResetObserver();
@@ -1319,14 +1202,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Open a new tab with a page in French.
   AddTabAtIndex(0, GURL(embedded_test_server()->GetURL("/french_page.html")),
                 ui::PAGE_TRANSITION_TYPED);
@@ -1358,14 +1233,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   ResetObserver();
 
   GURL french_url = ui_test_utils::GetTestUrl(
@@ -1378,11 +1245,6 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TranslateManagerBrowserTest, TranslateSessionRestore) {
-  ChromeTranslateClient* active_translate_client = GetChromeTranslateClient();
-  if (active_translate_client->GetLanguageState().current_language().empty())
-    WaitUntilLanguageDetermined();
-  EXPECT_EQ("und",
-            active_translate_client->GetLanguageState().current_language());
 
   // Make restored tab active to (on some platforms) initiate language
   // detection.
@@ -1417,14 +1279,6 @@
 
   SetTranslateScript(kTestValidScript);
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   // Load a German page and detect it's language
   AddTabAtIndex(0,
                 GURL(embedded_test_server()->GetURL(
@@ -1466,14 +1320,6 @@
 
   ChromeTranslateClient* chrome_translate_client = GetChromeTranslateClient();
 
-  // There is a possible race condition, when the language is not yet detected,
-  // so we check for that and wait if necessary.
-  if (chrome_translate_client->GetLanguageState().source_language().empty())
-    WaitUntilLanguageDetermined();
-
-  EXPECT_EQ("und",
-            chrome_translate_client->GetLanguageState().source_language());
-
   base::HistogramTester histograms;
 
   // Open a new tab with a page in French.
diff --git a/chrome/browser/translate/translate_model_service_browsertest.cc b/chrome/browser/translate/translate_model_service_browsertest.cc
index f6e8f0c..69e7f22e 100644
--- a/chrome/browser/translate/translate_model_service_browsertest.cc
+++ b/chrome/browser/translate/translate_model_service_browsertest.cc
@@ -74,7 +74,29 @@
 
 }  // namespace
 
-using TranslateModelServiceDisabledBrowserTest = InProcessBrowserTest;
+class TranslateModelServiceDisabledBrowserTest : public InProcessBrowserTest {
+ public:
+  TranslateModelServiceDisabledBrowserTest() = default;
+
+  void SetUp() override {
+    origin_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    origin_server_->ServeFilesFromSourceDirectory(
+        "chrome/test/data/optimization_guide");
+
+    ASSERT_TRUE(origin_server_->Start());
+    english_url_ = origin_server_->GetURL("/hello_world.html");
+    InProcessBrowserTest::SetUp();
+  }
+
+  ~TranslateModelServiceDisabledBrowserTest() override = default;
+
+  const GURL& english_url() const { return english_url_; }
+
+ private:
+  GURL english_url_;
+  std::unique_ptr<net::EmbeddedTestServer> origin_server_;
+};
 
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceDisabledBrowserTest,
                        TranslateModelServiceDisabled) {
@@ -109,7 +131,8 @@
 IN_PROC_BROWSER_TEST_F(TranslateModelServiceDisabledBrowserTest,
                        LanguageDetectionModelNotCreated) {
   base::HistogramTester histogram_tester;
-  ui_test_utils::NavigateToURL(browser(), GURL("https://test.com"));
+
+  ui_test_utils::NavigateToURL(browser(), english_url());
   RetryForHistogramUntilCountReached(
       &histogram_tester, "Translate.CLD3.TopLanguageEvaluationDuration", 1);
   histogram_tester.ExpectTotalCount(
diff --git a/chrome/browser/translate/translate_service.cc b/chrome/browser/translate/translate_service.cc
index 276ebfc..9cc74b7 100644
--- a/chrome/browser/translate/translate_service.cc
+++ b/chrome/browser/translate/translate_service.cc
@@ -141,6 +141,7 @@
   // - Chrome OS file manager extension
   // - an FTP page (as FTP pages tend to have long lists of filenames that may
   //   confuse the CLD)
+  // Note: Keep in sync with condition in TranslateAgent::PageCaptured.
   return !url.is_empty() && !url.SchemeIs(content::kChromeUIScheme) &&
          !url.SchemeIs(chrome::kChromeNativeScheme) &&
          !url.SchemeIs(content::kChromeDevToolsScheme) && !url.IsAboutBlank() &&
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 909f023..1de5d68 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3523,6 +3523,9 @@
       "autofill/payments/save_upi_bubble_controller.h",
       "autofill/payments/save_upi_bubble_controller_impl.cc",
       "autofill/payments/save_upi_bubble_controller_impl.h",
+      "autofill/payments/virtual_card_manual_fallback_bubble_controller.h",
+      "autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.cc",
+      "autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h",
       "autofill/save_address_profile_icon_controller.cc",
       "autofill/save_address_profile_icon_controller.h",
       "autofill/save_update_address_profile_bubble_controller.h",
@@ -3617,6 +3620,10 @@
       "views/autofill/payments/save_payment_icon_view.h",
       "views/autofill/payments/save_upi_offer_bubble_views.cc",
       "views/autofill/payments/save_upi_offer_bubble_views.h",
+      "views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc",
+      "views/autofill/payments/virtual_card_manual_fallback_bubble_views.h",
+      "views/autofill/payments/virtual_card_manual_fallback_icon_view.cc",
+      "views/autofill/payments/virtual_card_manual_fallback_icon_view.h",
       "views/autofill/save_address_profile_icon_view.cc",
       "views/autofill/save_address_profile_icon_view.h",
       "views/autofill/save_address_profile_view.cc",
diff --git a/chrome/browser/ui/ash/clipboard_history_browsertest.cc b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
index 010afdbe..6856214 100644
--- a/chrome/browser/ui/ash/clipboard_history_browsertest.cc
+++ b/chrome/browser/ui/ash/clipboard_history_browsertest.cc
@@ -14,6 +14,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/clipboard_image_model_factory.h"
 #include "ash/shell.h"
+#include "base/bind.h"
 #include "base/path_service.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
@@ -510,6 +511,7 @@
               ash::ClipboardHistoryUtil::kDeleteButtonViewID));
   ASSERT_TRUE(delete_button->GetVisible());
   EXPECT_TRUE(const_cast<ash::ClipboardHistoryDeleteButton*>(delete_button)
+                  ->ink_drop()
                   ->GetInkDrop()
                   ->IsHighlightFadingInOrVisible());
 
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_client_impl.cc b/chrome/browser/ui/ash/holding_space/holding_space_client_impl.cc
index 3de2f45..b71baae 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_client_impl.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_client_impl.cc
@@ -156,7 +156,8 @@
 
   for (const HoldingSpaceItem* item : items) {
     if (item->file_path().empty()) {
-      holding_space_metrics::RecordItemFailureToLaunch(item->type());
+      holding_space_metrics::RecordItemFailureToLaunch(item->type(),
+                                                       item->file_path());
       *complete_success_ptr = false;
       barrier_closure.Run();
       return;
@@ -169,7 +170,8 @@
                const base::FilePath& file_path, HoldingSpaceItem::Type type,
                const base::Optional<base::File::Info>& info) {
               if (!weak_ptr || !info.has_value()) {
-                holding_space_metrics::RecordItemFailureToLaunch(type);
+                holding_space_metrics::RecordItemFailureToLaunch(type,
+                                                                 file_path);
                 *complete_success = false;
                 barrier_closure.Run();
                 return;
@@ -181,17 +183,18 @@
                   base::BindOnce(
                       [](base::RepeatingClosure barrier_closure,
                          bool* complete_success, HoldingSpaceItem::Type type,
+                         const base::FilePath& file_path,
                          platform_util::OpenOperationResult result) {
                         const bool success =
                             result == platform_util::OPEN_SUCCEEDED;
                         if (!success) {
                           holding_space_metrics::RecordItemFailureToLaunch(
-                              type);
+                              type, file_path);
                           *complete_success = false;
                         }
                         barrier_closure.Run();
                       },
-                      barrier_closure, complete_success, type));
+                      barrier_closure, complete_success, type, file_path));
             },
             weak_factory_.GetWeakPtr(), barrier_closure, complete_success_ptr,
             item->file_path(), item->type()));
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc b/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
index e71c36c..b0ae2743 100644
--- a/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/holding_space/holding_space_client_impl_browsertest.cc
@@ -10,6 +10,7 @@
 #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"
+#include "ash/public/cpp/holding_space/holding_space_metrics.h"
 #include "ash/public/cpp/holding_space/holding_space_model.h"
 #include "base/callback_helpers.h"
 #include "base/files/file_path.h"
@@ -173,11 +174,13 @@
   // Verify no failures have yet been recorded.
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount("HoldingSpace.Item.FailureToLaunch", 0);
+  histogram_tester.ExpectTotalCount(
+      "HoldingSpace.Item.FailureToLaunch.Extension", 0);
 
   {
     // Create a holding space item backed by a non-existing file.
     auto holding_space_item = HoldingSpaceItem::CreateFileBackedItem(
-        HoldingSpaceItem::Type::kDownload, base::FilePath("foo"),
+        HoldingSpaceItem::Type::kDownload, base::FilePath("foo.pdf"),
         GURL("filesystem:fake"), base::BindOnce(&CreateTestHoldingSpaceImage));
 
     // We expect `HoldingSpaceClient::OpenItems()` to fail when the backing file
@@ -192,6 +195,11 @@
           histogram_tester.ExpectBucketCount(
               "HoldingSpace.Item.FailureToLaunch", holding_space_item->type(),
               1);
+          histogram_tester.ExpectBucketCount(
+              "HoldingSpace.Item.FailureToLaunch.Extension",
+              holding_space_metrics::FilePathToExtension(
+                  holding_space_item->file_path()),
+              1);
 
           run_loop.Quit();
         }));
@@ -216,6 +224,8 @@
 
   // Verify that only the expected failure was recorded.
   histogram_tester.ExpectTotalCount("HoldingSpace.Item.FailureToLaunch", 1);
+  histogram_tester.ExpectTotalCount(
+      "HoldingSpace.Item.FailureToLaunch.Extension", 1);
 }
 
 // Verifies that `HoldingSpaceClient::ShowItemInFolder()` works as intended when
diff --git a/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc b/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
index 4ac5a6f..c15080e 100644
--- a/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
+++ b/chrome/browser/ui/ash/network/network_state_notifier_unittest.cc
@@ -19,12 +19,16 @@
 #include "chromeos/dbus/hermes/hermes_clients.h"
 #include "chromeos/dbus/shill/shill_device_client.h"
 #include "chromeos/dbus/shill/shill_service_client.h"
+#include "chromeos/network/cellular_esim_profile_handler_impl.h"
+#include "chromeos/network/cellular_metrics_logger.h"
 #include "chromeos/network/network_connect.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_handler_test_helper.h"
+#include "chromeos/network/network_metadata_store.h"
 #include "chromeos/network/network_state_handler.h"
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/network/test_cellular_esim_profile_handler.h"
+#include "components/prefs/testing_pref_service.h"
 #include "testing/platform_test.h"
 #include "third_party/cros_system_api/dbus/service_constants.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -76,6 +80,12 @@
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
     network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
+    CellularMetricsLogger::RegisterLocalStatePrefs(local_state_.registry());
+    CellularESimProfileHandlerImpl::RegisterLocalStatePrefs(
+        local_state_.registry());
+    NetworkMetadataStore::RegisterPrefs(user_prefs_.registry());
+    NetworkMetadataStore::RegisterPrefs(local_state_.registry());
+    NetworkHandler::Get()->InitializePrefServices(&user_prefs_, &local_state_);
 
     SetupDefaultShillState();
     base::RunLoop().RunUntilIdle();
@@ -103,6 +113,11 @@
     const char kTestEidName[] = "eid";
     const char kTestIccid[] = "iccid";
 
+    // Disable stub cellular networks so that eSIM profile and service can both
+    // be added at the same time without stub network interfering.
+    NetworkHandler::Get()
+        ->network_state_handler()
+        ->set_stub_cellular_networks_provider(nullptr);
     ShillServiceClient::TestInterface* service_test =
         ShillServiceClient::Get()->GetTestInterface();
     service_test->ClearServices();
@@ -181,12 +196,13 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  std::unique_ptr<NetworkStateHandler> network_state_handler_;
   HermesManagerClient::TestInterface* hermes_manager_test_;
   HermesEuiccClient::TestInterface* hermes_euicc_test_;
   ash::TestSystemTrayClient test_system_tray_client_;
   std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
   std::unique_ptr<NetworkConnectTestDelegate> network_connect_delegate_;
+  TestingPrefServiceSimple user_prefs_;
+  TestingPrefServiceSimple local_state_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(NetworkStateNotifierTest);
diff --git a/chrome/browser/ui/autofill/autofill_bubble_handler.h b/chrome/browser/ui/autofill/autofill_bubble_handler.h
index 52ab722..47780f5 100644
--- a/chrome/browser/ui/autofill/autofill_bubble_handler.h
+++ b/chrome/browser/ui/autofill/autofill_bubble_handler.h
@@ -20,6 +20,7 @@
 class SaveCardBubbleController;
 class SaveUPIBubble;
 class SaveUPIBubbleController;
+class VirtualCardManualFallbackBubbleController;
 
 // Responsible for receiving calls from controllers and showing autofill
 // bubbles.
@@ -61,6 +62,11 @@
       content::WebContents* web_contents,
       EditAddressProfileDialogController* controller) = 0;
 
+  virtual AutofillBubbleBase* ShowVirtualCardManualFallbackBubble(
+      content::WebContents* web_contents,
+      VirtualCardManualFallbackBubbleController* controller,
+      bool is_user_gesture) = 0;
+
   // TODO(crbug.com/964127): Wait for the integration with sign in after local
   // save to be landed to see if we need to merge password saved and credit card
   // saved functions.
diff --git a/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller.h b/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller.h
new file mode 100644
index 0000000..6f201b2
--- /dev/null
+++ b/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller.h
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CONTROLLER_H_
+#define CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CONTROLLER_H_
+
+#include <string>
+
+#include "components/autofill/core/browser/ui/payments/payments_bubble_closed_reasons.h"
+#include "content/public/browser/web_contents.h"
+
+namespace autofill {
+
+class AutofillBubbleBase;
+class CreditCard;
+
+// Interface that exposes controller functionality to
+// VirtualCardManualFallbackBubbleViews. The bubble is shown when the virtual
+// card option in the Autofill credit card suggestion list is clicked. It
+// contains the card number, expiry and CVC information of the virtual card that
+// users select to use, and serves as a fallback if not all the information is
+// filled in the form by Autofill correctly.
+class VirtualCardManualFallbackBubbleController {
+ public:
+  VirtualCardManualFallbackBubbleController() = default;
+  virtual ~VirtualCardManualFallbackBubbleController() = default;
+  VirtualCardManualFallbackBubbleController(
+      const VirtualCardManualFallbackBubbleController&) = delete;
+  VirtualCardManualFallbackBubbleController& operator=(
+      const VirtualCardManualFallbackBubbleController&) = delete;
+
+  // Returns a reference to VirtualCardManualFallbackBubbleController given the
+  // |web_contents|. If the controller does not exist, creates one and returns
+  // it.
+  static VirtualCardManualFallbackBubbleController* GetOrCreate(
+      content::WebContents* web_contents);
+
+  // Returns a reference to VirtualCardManualFallbackBubbleController given the
+  // |web_contents|. If the controller does not exist, returns nullptr.
+  static VirtualCardManualFallbackBubbleController* Get(
+      content::WebContents* web_contents);
+
+  // Returns a reference to the bubble view.
+  virtual AutofillBubbleBase* GetBubble() const = 0;
+
+  // Returns the title text of the bubble.
+  virtual std::u16string GetBubbleTitle() const = 0;
+
+  // Returns the descriptive label of the virtual card number field.
+  virtual std::u16string GetVirtualCardNumberFieldLabel() const = 0;
+
+  // Returns the descriptive label of the expiration date field.
+  virtual std::u16string GetExpirationDateFieldLabel() const = 0;
+
+  // Returns the descriptive label of the CVC field.
+  virtual std::u16string GetCvcFieldLabel() const = 0;
+
+  // Returns the CVC value of the virtual card.
+  virtual std::u16string GetCvc() const = 0;
+
+  // Returns the related virtual card.
+  virtual const CreditCard* GetVirtualCard() const = 0;
+
+  // Returns whether the omnibox icon for the bubble should be visible.
+  virtual bool ShouldIconBeVisible() const = 0;
+
+  // Handles the event of bubble closure. |closed_reason| is the detailed reason
+  // why the bubble was closed.
+  virtual void OnBubbleClosed(PaymentsBubbleClosedReason closed_reason) = 0;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.cc
new file mode 100644
index 0000000..6ed07a9f
--- /dev/null
+++ b/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.cc
@@ -0,0 +1,163 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h"
+
+#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
+#include "chrome/browser/ui/autofill/autofill_bubble_handler.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace autofill {
+
+// static
+VirtualCardManualFallbackBubbleController*
+VirtualCardManualFallbackBubbleController::GetOrCreate(
+    content::WebContents* web_contents) {
+  if (!web_contents)
+    return nullptr;
+
+  VirtualCardManualFallbackBubbleControllerImpl::CreateForWebContents(
+      web_contents);
+  return VirtualCardManualFallbackBubbleControllerImpl::FromWebContents(
+      web_contents);
+}
+
+// static
+VirtualCardManualFallbackBubbleController*
+VirtualCardManualFallbackBubbleController::Get(
+    content::WebContents* web_contents) {
+  if (!web_contents)
+    return nullptr;
+
+  return VirtualCardManualFallbackBubbleControllerImpl::FromWebContents(
+      web_contents);
+}
+
+VirtualCardManualFallbackBubbleControllerImpl::
+    ~VirtualCardManualFallbackBubbleControllerImpl() = default;
+
+void VirtualCardManualFallbackBubbleControllerImpl::ShowBubble(
+    const CreditCard* virtual_card,
+    const std::u16string& virtual_card_cvc) {
+  // If another bubble is visible, dismiss it and show a new one since the card
+  // information can be different.
+  if (bubble_view())
+    HideBubble();
+
+  virtual_card_ = *virtual_card;
+  virtual_card_cvc_ = virtual_card_cvc;
+  is_user_gesture_ = false;
+  should_icon_be_visible_ = true;
+  Show();
+}
+
+void VirtualCardManualFallbackBubbleControllerImpl::ReshowBubble() {
+  // If bubble is already visible, return early.
+  if (bubble_view())
+    return;
+
+  is_user_gesture_ = true;
+  should_icon_be_visible_ = true;
+  Show();
+}
+
+AutofillBubbleBase* VirtualCardManualFallbackBubbleControllerImpl::GetBubble()
+    const {
+  return bubble_view();
+}
+
+std::u16string VirtualCardManualFallbackBubbleControllerImpl::GetBubbleTitle()
+    const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_TITLE);
+}
+
+std::u16string
+VirtualCardManualFallbackBubbleControllerImpl::GetVirtualCardNumberFieldLabel()
+    const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CARD_NUMBER_LABEL);
+}
+
+std::u16string
+VirtualCardManualFallbackBubbleControllerImpl::GetExpirationDateFieldLabel()
+    const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_EXP_DATE_LABEL);
+}
+
+std::u16string VirtualCardManualFallbackBubbleControllerImpl::GetCvcFieldLabel()
+    const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CVC_LABEL);
+}
+
+std::u16string VirtualCardManualFallbackBubbleControllerImpl::GetCvc() const {
+  return virtual_card_cvc_;
+}
+
+const CreditCard*
+VirtualCardManualFallbackBubbleControllerImpl::GetVirtualCard() const {
+  return &virtual_card_;
+}
+
+bool VirtualCardManualFallbackBubbleControllerImpl::ShouldIconBeVisible()
+    const {
+  return should_icon_be_visible_;
+}
+
+void VirtualCardManualFallbackBubbleControllerImpl::OnBubbleClosed(
+    PaymentsBubbleClosedReason closed_reason) {
+  set_bubble_view(nullptr);
+  UpdatePageActionIcon();
+}
+
+VirtualCardManualFallbackBubbleControllerImpl::
+    VirtualCardManualFallbackBubbleControllerImpl(
+        content::WebContents* web_contents)
+    : AutofillBubbleControllerBase(web_contents) {}
+
+void VirtualCardManualFallbackBubbleControllerImpl::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->IsInMainFrame() || !navigation_handle->HasCommitted())
+    return;
+
+  // Don't react to same-document (fragment) navigations.
+  if (navigation_handle->IsSameDocument())
+    return;
+
+  should_icon_be_visible_ = false;
+  UpdatePageActionIcon();
+  HideBubble();
+}
+
+PageActionIconType
+VirtualCardManualFallbackBubbleControllerImpl::GetPageActionIconType() {
+  return PageActionIconType::kVirtualCardManualFallback;
+}
+
+void VirtualCardManualFallbackBubbleControllerImpl::DoShowBubble() {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
+  set_bubble_view(browser->window()
+                      ->GetAutofillBubbleHandler()
+                      ->ShowVirtualCardManualFallbackBubble(
+                          web_contents(), this, is_user_gesture_));
+  DCHECK(bubble_view());
+
+  if (observer_for_test_)
+    observer_for_test_->OnBubbleShown();
+}
+
+void VirtualCardManualFallbackBubbleControllerImpl::SetEventObserverForTesting(
+    ObserverForTest* observer_for_test) {
+  observer_for_test_ = observer_for_test;
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(VirtualCardManualFallbackBubbleControllerImpl)
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h
new file mode 100644
index 0000000..b7a99eb
--- /dev/null
+++ b/chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h
@@ -0,0 +1,91 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CONTROLLER_IMPL_H_
+#define CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CONTROLLER_IMPL_H_
+
+#include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller.h"
+
+#include "base/macros.h"
+#include "chrome/browser/ui/autofill/autofill_bubble_controller_base.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace autofill {
+
+// Implementation of per-tab class to control the virtual card manual fallback
+// bubble and the omnibox icon.
+class VirtualCardManualFallbackBubbleControllerImpl
+    : public AutofillBubbleControllerBase,
+      public VirtualCardManualFallbackBubbleController,
+      public content::WebContentsUserData<
+          VirtualCardManualFallbackBubbleControllerImpl> {
+ public:
+  class ObserverForTest {
+   public:
+    virtual void OnBubbleShown() = 0;
+  };
+
+  ~VirtualCardManualFallbackBubbleControllerImpl() override;
+  VirtualCardManualFallbackBubbleControllerImpl(
+      const VirtualCardManualFallbackBubbleControllerImpl&) = delete;
+  VirtualCardManualFallbackBubbleControllerImpl& operator=(
+      const VirtualCardManualFallbackBubbleControllerImpl&) = delete;
+
+  // Show the bubble view.
+  void ShowBubble(const CreditCard* virtual_card,
+                  const std::u16string& virtual_card_cvc);
+
+  // Invoked when the omnibox icon is clicked.
+  void ReshowBubble();
+
+  // VirtualCardManualFallbackBubbleController:
+  AutofillBubbleBase* GetBubble() const override;
+  std::u16string GetBubbleTitle() const override;
+  std::u16string GetVirtualCardNumberFieldLabel() const override;
+  std::u16string GetExpirationDateFieldLabel() const override;
+  std::u16string GetCvcFieldLabel() const override;
+  std::u16string GetCvc() const override;
+  const CreditCard* GetVirtualCard() const override;
+  bool ShouldIconBeVisible() const override;
+  void OnBubbleClosed(PaymentsBubbleClosedReason closed_reason) override;
+
+ protected:
+  explicit VirtualCardManualFallbackBubbleControllerImpl(
+      content::WebContents* web_contents);
+
+  // AutofillBubbleControllerBase:
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  PageActionIconType GetPageActionIconType() override;
+  void DoShowBubble() override;
+
+ private:
+  friend class content::WebContentsUserData<
+      VirtualCardManualFallbackBubbleControllerImpl>;
+  friend class VirtualCardManualFallbackBubbleViewsInteractiveUiTest;
+
+  void SetEventObserverForTesting(ObserverForTest* observer_for_test);
+
+  // The cvc of the virtual card.
+  std::u16string virtual_card_cvc_;
+
+  // The virtual card to be displayed to the user in the bubble.
+  CreditCard virtual_card_;
+
+  // Denotes whether the bubble is shown due to user gesture. If this is true,
+  // it means the bubble is a reshown bubble.
+  bool is_user_gesture_ = false;
+
+  // Whether the omnibox icon for the bubble should be visible.
+  bool should_icon_be_visible_ = false;
+
+  ObserverForTest* observer_for_test_ = nullptr;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CONTROLLER_IMPL_H_
diff --git a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
index d85ca40..98d6870 100644
--- a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
+++ b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.cc
@@ -73,6 +73,18 @@
   return edit_address_profile_bubble_view_.get();
 }
 
+AutofillBubbleBase*
+TestAutofillBubbleHandler::ShowVirtualCardManualFallbackBubble(
+    content::WebContents* web_contents,
+    VirtualCardManualFallbackBubbleController* controller,
+    bool is_user_gesture) {
+  if (!virtual_card_manual_fallback_bubble_view_) {
+    virtual_card_manual_fallback_bubble_view_ =
+        std::make_unique<TestAutofillBubble>();
+  }
+  return virtual_card_manual_fallback_bubble_view_.get();
+}
+
 void TestAutofillBubbleHandler::OnPasswordSaved() {}
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
index 040bd0db..16b8d66 100644
--- a/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
+++ b/chrome/browser/ui/autofill/test/test_autofill_bubble_handler.h
@@ -54,6 +54,10 @@
   AutofillBubbleBase* ShowEditAddressProfileDialog(
       content::WebContents* contents,
       EditAddressProfileDialogController* controller) override;
+  AutofillBubbleBase* ShowVirtualCardManualFallbackBubble(
+      content::WebContents* web_contents,
+      VirtualCardManualFallbackBubbleController* controller,
+      bool is_user_gesture) override;
   void OnPasswordSaved() override;
 
  private:
@@ -64,6 +68,7 @@
   std::unique_ptr<TestAutofillBubble> save_address_profile_bubble_view_;
   std::unique_ptr<TestAutofillBubble> update_address_profile_bubble_view_;
   std::unique_ptr<TestAutofillBubble> edit_address_profile_bubble_view_;
+  std::unique_ptr<TestAutofillBubble> virtual_card_manual_fallback_bubble_view_;
 
   DISALLOW_COPY_AND_ASSIGN(TestAutofillBubbleHandler);
 };
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index ddbbb962..82f6a0f 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -562,6 +562,9 @@
     case IDC_SAVE_AUTOFILL_ADDRESS:
       SaveAutofillAddress(browser_);
       break;
+    case IDC_VIRTUAL_CARD_MANUAL_FALLBACK:
+      ShowVirtualCardManualFallbackBubble(browser_);
+      break;
     case IDC_TRANSLATE_PAGE:
       Translate(browser_);
       break;
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 64736cb..e31310b 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -40,6 +40,7 @@
 #include "chrome/browser/ui/autofill/payments/manage_migration_ui_controller.h"
 #include "chrome/browser/ui/autofill/payments/offer_notification_bubble_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h"
+#include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h"
 #include "chrome/browser/ui/autofill/save_update_address_profile_bubble_controller_impl.h"
 #include "chrome/browser/ui/bookmarks/bookmark_stats.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
@@ -1209,6 +1210,16 @@
   controller->OnPageActionIconClicked();
 }
 
+void ShowVirtualCardManualFallbackBubble(Browser* browser) {
+  WebContents* web_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+  auto* controller =
+      autofill::VirtualCardManualFallbackBubbleControllerImpl::FromWebContents(
+          web_contents);
+  if (controller)
+    controller->ReshowBubble();
+}
+
 void Translate(Browser* browser) {
   if (!browser->window()->IsActive())
     return;
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index ef3e4f2..06f281c 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -153,6 +153,7 @@
 void SaveCreditCard(Browser* browser);
 void MigrateLocalCards(Browser* browser);
 void SaveAutofillAddress(Browser* browser);
+void ShowVirtualCardManualFallbackBubble(Browser* browser);
 void Translate(Browser* browser);
 void ManagePasswordsForPage(Browser* browser);
 void SendTabToSelfFromPageAction(Browser* browser);
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index b14570f..9cc964d 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -1220,7 +1220,7 @@
   // Insert text: ダ. This is two, 3-byte UTF-8 characters:
   // U+FF80 "HALFWIDTH KATAKANA LETTER TA" and
   // U+FF9E "HALFWIDTH KATAKANA VOICED SOUND MARK".
-  omnibox_view->SetUserText(base::UTF8ToUTF16("\357\276\200\357\276\236"));
+  omnibox_view->SetUserText(u"\uFF80\uFF9E");
   EXPECT_FALSE(omnibox_view->GetText().empty());
 
   // Move the cursor to the end.
@@ -1236,7 +1236,7 @@
   EXPECT_TRUE(omnibox_view->GetText().empty());
 #else
   // Toolkit-views text fields delete just the sound mark.
-  EXPECT_EQ(base::UTF8ToUTF16("\357\276\200"), omnibox_view->GetText());
+  EXPECT_EQ(u"\uFF80", omnibox_view->GetText());
 #endif
 }
 
diff --git a/chrome/browser/ui/page_action/page_action_icon_type.h b/chrome/browser/ui/page_action/page_action_icon_type.h
index bbe2731..2ca72f9 100644
--- a/chrome/browser/ui/page_action/page_action_icon_type.h
+++ b/chrome/browser/ui/page_action/page_action_icon_type.h
@@ -25,6 +25,7 @@
   kSharingHub,
   kSmsRemoteFetcher,
   kTranslate,
+  kVirtualCardManualFallback,
   kWebAuthn,
   kZoom,
 };
diff --git a/chrome/browser/ui/search/search_ipc_router_unittest.cc b/chrome/browser/ui/search/search_ipc_router_unittest.cc
index d8805fbd..f27094c 100644
--- a/chrome/browser/ui/search/search_ipc_router_unittest.cc
+++ b/chrome/browser/ui/search/search_ipc_router_unittest.cc
@@ -215,15 +215,15 @@
 
   SearchTabHelper* GetSearchTabHelper(
       content::WebContents* web_contents) {
-    EXPECT_NE(static_cast<content::WebContents*>(NULL), web_contents);
+    EXPECT_NE(nullptr, web_contents);
     return SearchTabHelper::FromWebContents(web_contents);
   }
 
   void SetupMockDelegateAndPolicy() {
     content::WebContents* contents = web_contents();
-    ASSERT_NE(static_cast<content::WebContents*>(NULL), contents);
+    ASSERT_NE(nullptr, contents);
     SearchTabHelper* search_tab_helper = GetSearchTabHelper(contents);
-    ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
+    ASSERT_NE(nullptr, search_tab_helper);
     search_tab_helper->ipc_router_for_testing().set_delegate_for_testing(
         mock_delegate());
     search_tab_helper->ipc_router_for_testing().set_policy_for_testing(
@@ -239,9 +239,9 @@
 
   MockSearchIPCRouterPolicy* GetSearchIPCRouterPolicy() {
     content::WebContents* contents = web_contents();
-    EXPECT_NE(static_cast<content::WebContents*>(NULL), contents);
+    EXPECT_NE(nullptr, contents);
     SearchTabHelper* search_tab_helper = GetSearchTabHelper(contents);
-    EXPECT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper);
+    EXPECT_NE(nullptr, search_tab_helper);
     return static_cast<MockSearchIPCRouterPolicy*>(
         search_tab_helper->ipc_router_for_testing().policy_for_testing());
   }
diff --git a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
index c2bdaf1..c29d1a2 100644
--- a/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator_browsertest.cc
@@ -149,7 +149,7 @@
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
 
 const char kAppId[] = "dofnemchnjfeendjmdhaldenaiabpiad";
-const char kAppName[] = "Test App";
+const char16_t kAppName[] = u"Test App";
 const char kStartUrl[] = "https://test.com";
 
 // Check that there are two browsers. Find the one that is not |browser|.
@@ -1341,7 +1341,7 @@
     base::RunLoop run_loop;
     WebApplicationInfo info;
     info.start_url = GURL(kStartUrl);
-    info.title = base::UTF8ToUTF16(kAppName);
+    info.title = kAppName;
     info.open_as_window = true;
     web_app_finalizer.FinalizeInstall(
         info, options,
@@ -1445,7 +1445,7 @@
     std::unique_ptr<WebApplicationInfo> info =
         std::make_unique<WebApplicationInfo>();
     info->start_url = GURL(kStartUrl);
-    info->title = base::UTF8ToUTF16(kAppName);
+    info->title = kAppName;
     info->open_as_window = true;
     info->url_handlers = url_handlers;
     web_app::AppId app_id =
@@ -1586,7 +1586,7 @@
     std::unique_ptr<WebApplicationInfo> info =
         std::make_unique<WebApplicationInfo>();
     info->start_url = GURL(kStartUrl);
-    info->title = base::UTF8ToUTF16(kAppName);
+    info->title = kAppName;
     info->open_as_window = true;
     info->protocol_handlers = protocol_handlers;
     web_app::AppId app_id =
diff --git a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
index a66bf708..1c7c3a5 100644
--- a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
+++ b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.cc
@@ -21,6 +21,8 @@
 #include "chrome/browser/ui/views/autofill/payments/save_card_manage_cards_bubble_views.h"
 #include "chrome/browser/ui/views/autofill/payments/save_card_offer_bubble_views.h"
 #include "chrome/browser/ui/views/autofill/payments/save_upi_offer_bubble_views.h"
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h"
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h"
 #include "chrome/browser/ui/views/autofill/save_address_profile_view.h"
 #include "chrome/browser/ui/views/autofill/update_address_profile_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -206,6 +208,30 @@
   return dialog;
 }
 
+AutofillBubbleBase*
+AutofillBubbleHandlerImpl::ShowVirtualCardManualFallbackBubble(
+    content::WebContents* web_contents,
+    VirtualCardManualFallbackBubbleController* controller,
+    bool is_user_gesture) {
+  VirtualCardManualFallbackBubbleViews* bubble =
+      new VirtualCardManualFallbackBubbleViews(
+          toolbar_button_provider_->GetAnchorView(
+              PageActionIconType::kVirtualCardManualFallback),
+          web_contents, controller);
+
+  views::BubbleDialogDelegateView::CreateBubble(bubble);
+  bubble->ShowForReason(is_user_gesture
+                            ? VirtualCardManualFallbackBubbleViews::USER_GESTURE
+                            : VirtualCardManualFallbackBubbleViews::AUTOMATIC);
+  PageActionIconView* icon_view =
+      toolbar_button_provider_->GetPageActionIconView(
+          PageActionIconType::kVirtualCardManualFallback);
+  if (icon_view)
+    bubble->SetHighlightedButton(icon_view);
+
+  return bubble;
+}
+
 void AutofillBubbleHandlerImpl::OnPasswordSaved() {
   if (base::FeatureList::IsEnabled(
           features::kAutofillCreditCardUploadFeedback)) {
diff --git a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h
index aaca784..bfc9f19c 100644
--- a/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h
+++ b/chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h
@@ -60,6 +60,10 @@
   AutofillBubbleBase* ShowEditAddressProfileDialog(
       content::WebContents* web_contents,
       EditAddressProfileDialogController* controller) override;
+  AutofillBubbleBase* ShowVirtualCardManualFallbackBubble(
+      content::WebContents* web_contents,
+      VirtualCardManualFallbackBubbleController* controller,
+      bool is_user_gesture) override;
 
   void OnPasswordSaved() override;
 
diff --git a/chrome/browser/ui/views/autofill/payments/dialog_view_ids.h b/chrome/browser/ui/views/autofill/payments/dialog_view_ids.h
index 1e1cc06f..5ef1472 100644
--- a/chrome/browser/ui/views/autofill/payments/dialog_view_ids.h
+++ b/chrome/browser/ui/views/autofill/payments/dialog_view_ids.h
@@ -8,8 +8,7 @@
 #include "components/autofill/core/browser/field_types.h"
 
 // This defines an enumeration of IDs that can uniquely identify a view within
-// the scope of the local and upload credit card save bubbles as well as the
-// local card migration bubble and dialogs.
+// the scope of various credit-card-related Autofill bubbles and dialogs.
 
 namespace autofill {
 
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.cc
index 839f833..ca57053 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.cc
@@ -122,7 +122,7 @@
     // Fade out inkdrop but only if icon was actually highlighted. Calling
     // SetHighlighted() can result in a spurious fade-out animation and visual
     // glitches.
-    if (this->GetHighlighted())
+    if (this->ink_drop()->GetHighlighted())
       SetHighlighted(false);
     // Handle corner cases where users navigate away or close the tab.
     UnpauseAnimation();
diff --git a/chrome/browser/ui/views/autofill/payments/migratable_card_view.cc b/chrome/browser/ui/views/autofill/payments/migratable_card_view.cc
index 6df10c6f..0f5b8f70 100644
--- a/chrome/browser/ui/views/autofill/payments/migratable_card_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/migratable_card_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/autofill/payments/migratable_card_view.h"
 
+#include "base/bind.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/ui/autofill/payments/local_card_migration_dialog_state.h"
 #include "chrome/browser/ui/views/autofill/payments/local_card_migration_dialog_view.h"
@@ -119,7 +120,7 @@
         // TODO(crbug/867194): Currently the ink drop animation circle is
         // cropped by the border of scroll bar view. Find a way to adjust the
         // format.
-        checkbox_->SetInkDropMode(views::InkDropHostView::InkDropMode::OFF);
+        checkbox_->ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
         checkbox_->SetAssociatedLabel(card_description.get());
       }
       break;
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc
new file mode 100644
index 0000000..2d211bf
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.cc
@@ -0,0 +1,154 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h"
+
+#include "chrome/browser/ui/views/autofill/payments/payments_view_util.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/controls/button/md_text_button.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/flex_layout_types.h"
+#include "ui/views/layout/grid_layout.h"
+#include "ui/views/style/typography.h"
+
+namespace autofill {
+
+namespace {
+
+std::unique_ptr<views::Label> CreateRowItemLabel(std::u16string text) {
+  auto label = std::make_unique<views::Label>(
+      text, views::style::CONTEXT_DIALOG_BODY_TEXT,
+      views::style::STYLE_SECONDARY);
+  label->SetMultiLine(false);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  return label;
+}
+
+std::unique_ptr<views::MdTextButton> CreateRowItemButton(std::u16string text) {
+  auto button = std::make_unique<views::MdTextButton>();
+  button->SetText(text);
+  button->SetCornerRadius(ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
+      views::Emphasis::kMaximum, button->GetPreferredSize()));
+  button->SetEnabledTextColors(
+      views::style::GetColor(*button.get(), views::style::CONTEXT_BUTTON_MD,
+                             views::style::STYLE_SECONDARY));
+  return button;
+}
+
+}  // namespace
+
+VirtualCardManualFallbackBubbleViews::VirtualCardManualFallbackBubbleViews(
+    views::View* anchor_view,
+    content::WebContents* web_contents,
+    VirtualCardManualFallbackBubbleController* controller)
+    : LocationBarBubbleDelegateView(anchor_view, web_contents),
+      controller_(controller) {
+  DCHECK(controller_);
+  SetShowCloseButton(true);
+  SetButtons(ui::DIALOG_BUTTON_NONE);
+}
+
+VirtualCardManualFallbackBubbleViews::~VirtualCardManualFallbackBubbleViews() {
+  Hide();
+}
+
+void VirtualCardManualFallbackBubbleViews::Hide() {
+  CloseBubble();
+  if (controller_)
+    controller_->OnBubbleClosed(closed_reason_);
+  controller_ = nullptr;
+}
+
+void VirtualCardManualFallbackBubbleViews::Init() {
+  views::GridLayout* layout =
+      SetLayoutManager(std::make_unique<views::GridLayout>());
+  views::ColumnSet* column_set = layout->AddColumnSet(0);
+
+  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
+                        views::GridLayout::kFixedSize,
+                        views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+  column_set->AddPaddingColumn(views::GridLayout::kFixedSize,
+                               ChromeLayoutProvider::Get()->GetDistanceMetric(
+                                   views::DISTANCE_RELATED_CONTROL_HORIZONTAL));
+  column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
+                        views::GridLayout::kFixedSize,
+                        views::GridLayout::ColumnSize::kUsePreferred, 0, 0);
+
+  // Adds a row for virtual card number.
+  layout->StartRow(views::GridLayout::kFixedSize, 0);
+  layout->AddView(
+      CreateRowItemLabel(controller_->GetVirtualCardNumberFieldLabel()));
+  layout->AddView(CreateRowItemButton(controller_->GetVirtualCard()->number()));
+
+  // Adds a row for expiration date.
+  layout->StartRowWithPadding(views::GridLayout::kFixedSize, 0,
+                              views::GridLayout::kFixedSize,
+                              ChromeLayoutProvider::Get()->GetDistanceMetric(
+                                  views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
+  layout->AddView(
+      CreateRowItemLabel(controller_->GetExpirationDateFieldLabel()));
+  auto expiry_row = std::make_unique<views::View>();
+  expiry_row->SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kHorizontal)
+      .SetMainAxisAlignment(views::LayoutAlignment::kStart)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
+      .SetIgnoreDefaultMainAxisMargins(true)
+      .SetCollapseMargins(true)
+      .SetDefault(views::kMarginsKey,
+                  gfx::Insets(
+                      /*vertical=*/0,
+                      /*horizontal=*/
+                      ChromeLayoutProvider::Get()->GetDistanceMetric(
+                          views::DISTANCE_RELATED_BUTTON_HORIZONTAL)));
+  expiry_row->AddChildView(CreateRowItemButton(
+      controller_->GetVirtualCard()->Expiration2DigitMonthAsString()));
+  expiry_row->AddChildView(std::make_unique<views::Label>(u"/"));
+  // TODO(crbug.com/1196021): Validate this works when the expiration year field
+  // is for two-digit numbers
+  expiry_row->AddChildView(CreateRowItemButton(
+      controller_->GetVirtualCard()->Expiration4DigitYearAsString()));
+  layout->AddView(std::move(expiry_row));
+
+  // Adds a row for CVC.
+  layout->StartRowWithPadding(views::GridLayout::kFixedSize, 0,
+                              views::GridLayout::kFixedSize,
+                              ChromeLayoutProvider::Get()->GetDistanceMetric(
+                                  views::DISTANCE_UNRELATED_CONTROL_VERTICAL));
+  layout->AddView(CreateRowItemLabel(controller_->GetCvcFieldLabel()));
+  layout->AddView(CreateRowItemButton(controller_->GetCvc()));
+}
+
+void VirtualCardManualFallbackBubbleViews::AddedToWidget() {
+  GetBubbleFrameView()->SetTitleView(
+      std::make_unique<TitleWithIconAndSeparatorView>(GetWindowTitle()));
+}
+
+std::u16string VirtualCardManualFallbackBubbleViews::GetWindowTitle() const {
+  return controller_ ? controller_->GetBubbleTitle() : std::u16string();
+}
+
+void VirtualCardManualFallbackBubbleViews::WindowClosing() {
+  if (controller_) {
+    controller_->OnBubbleClosed(closed_reason_);
+    controller_ = nullptr;
+  }
+}
+
+void VirtualCardManualFallbackBubbleViews::OnWidgetClosing(
+    views::Widget* widget) {
+  LocationBarBubbleDelegateView::OnWidgetClosing(widget);
+  DCHECK_NE(widget->closed_reason(),
+            views::Widget::ClosedReason::kAcceptButtonClicked);
+  DCHECK_NE(widget->closed_reason(),
+            views::Widget::ClosedReason::kCancelButtonClicked);
+  closed_reason_ = GetPaymentsBubbleClosedReasonFromWidgetClosedReason(
+      widget->closed_reason());
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h
new file mode 100644
index 0000000..f567642
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h
@@ -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.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_VIEWS_H_
+#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_VIEWS_H_
+
+#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
+#include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace autofill {
+
+// This class implements the desktop bubble that displays the information of the
+// virtual card that was sent to Chrome from Payments.
+class VirtualCardManualFallbackBubbleViews
+    : public AutofillBubbleBase,
+      public LocationBarBubbleDelegateView {
+ public:
+  // The bubble will be anchored to the |anchor_view|.
+  VirtualCardManualFallbackBubbleViews(
+      views::View* anchor_view,
+      content::WebContents* web_contents,
+      VirtualCardManualFallbackBubbleController* controller);
+  ~VirtualCardManualFallbackBubbleViews() override;
+  VirtualCardManualFallbackBubbleViews(
+      const VirtualCardManualFallbackBubbleViews&) = delete;
+  VirtualCardManualFallbackBubbleViews& operator=(
+      const VirtualCardManualFallbackBubbleViews&) = delete;
+
+ private:
+  // AutofillBubbleBase:
+  void Hide() override;
+
+  // LocationBarBubbleDelegateView:
+  void Init() override;
+  void AddedToWidget() override;
+  std::u16string GetWindowTitle() const override;
+  void WindowClosing() override;
+  void OnWidgetClosing(views::Widget* widget) override;
+
+  VirtualCardManualFallbackBubbleController* controller_;
+
+  PaymentsBubbleClosedReason closed_reason_ =
+      PaymentsBubbleClosedReason::kUnknown;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_VIEWS_H_
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views_interactive_uitest.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views_interactive_uitest.cc
new file mode 100644
index 0000000..ab8dd77
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views_interactive_uitest.cc
@@ -0,0 +1,127 @@
+// 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 <memory>
+#include <string>
+
+#include "base/strings/string_number_conversions.h"
+#include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller_impl.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h"
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_event_waiter.h"
+#include "content/public/test/browser_test.h"
+#include "ui/views/test/widget_test.h"
+
+namespace autofill {
+
+class VirtualCardManualFallbackBubbleViewsInteractiveUiTest
+    : public InProcessBrowserTest,
+      public VirtualCardManualFallbackBubbleControllerImpl::ObserverForTest {
+ public:
+  // Various events that can be waited on by the DialogEventWaiter.
+  enum class BubbleEvent : int {
+    BUBBLE_SHOWN,
+  };
+
+  VirtualCardManualFallbackBubbleViewsInteractiveUiTest() = default;
+  ~VirtualCardManualFallbackBubbleViewsInteractiveUiTest() override = default;
+  VirtualCardManualFallbackBubbleViewsInteractiveUiTest(
+      const VirtualCardManualFallbackBubbleViewsInteractiveUiTest&) = delete;
+  VirtualCardManualFallbackBubbleViewsInteractiveUiTest& operator=(
+      const VirtualCardManualFallbackBubbleViewsInteractiveUiTest&) = delete;
+
+  // VirtualCardManualFallbackBubbleControllerImpl::ObserverForTest:
+  void OnBubbleShown() override {
+    if (event_waiter_)
+      event_waiter_->OnEvent(BubbleEvent::BUBBLE_SHOWN);
+  }
+
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    VirtualCardManualFallbackBubbleControllerImpl* controller =
+        static_cast<VirtualCardManualFallbackBubbleControllerImpl*>(
+            VirtualCardManualFallbackBubbleControllerImpl::GetOrCreate(
+                browser()->tab_strip_model()->GetActiveWebContents()));
+    DCHECK(controller);
+    controller->SetEventObserverForTesting(this);
+  }
+
+  void ShowBubble() {
+    CreditCard card = test::GetFullServerCard();
+    ResetEventWaiterForSequence({BubbleEvent::BUBBLE_SHOWN});
+    GetController()->ShowBubble(&card,
+                                /*virtual_card_cvc=*/u"123");
+    event_waiter_->Wait();
+  }
+
+  VirtualCardManualFallbackBubbleControllerImpl* GetController() {
+    if (!browser() || !browser()->tab_strip_model() ||
+        !browser()->tab_strip_model()->GetActiveWebContents()) {
+      return nullptr;
+    }
+
+    return VirtualCardManualFallbackBubbleControllerImpl::FromWebContents(
+        browser()->tab_strip_model()->GetActiveWebContents());
+  }
+
+  VirtualCardManualFallbackBubbleViews* GetBubbleViews() {
+    VirtualCardManualFallbackBubbleControllerImpl* controller = GetController();
+    if (!controller)
+      return nullptr;
+
+    return static_cast<VirtualCardManualFallbackBubbleViews*>(
+        controller->GetBubble());
+  }
+
+  VirtualCardManualFallbackIconView* GetIconView() {
+    BrowserView* browser_view =
+        BrowserView::GetBrowserViewForBrowser(browser());
+    PageActionIconView* icon =
+        browser_view->toolbar_button_provider()->GetPageActionIconView(
+            PageActionIconType::kVirtualCardManualFallback);
+    DCHECK(icon);
+    return static_cast<VirtualCardManualFallbackIconView*>(icon);
+  }
+
+  void ResetEventWaiterForSequence(std::list<BubbleEvent> event_sequence) {
+    event_waiter_ =
+        std::make_unique<EventWaiter<BubbleEvent>>(std::move(event_sequence));
+  }
+
+ private:
+  std::unique_ptr<EventWaiter<BubbleEvent>> event_waiter_;
+};
+
+// Invokes a bubble showing the complete information for the virtual card
+// selected to fill the form.
+IN_PROC_BROWSER_TEST_F(VirtualCardManualFallbackBubbleViewsInteractiveUiTest,
+                       ShowBubble) {
+  ShowBubble();
+  EXPECT_TRUE(GetBubbleViews());
+  EXPECT_TRUE(GetIconView() && GetIconView()->GetVisible());
+}
+
+// Invokes the bubble and verifies the bubble is dismissed upon page navigation.
+IN_PROC_BROWSER_TEST_F(VirtualCardManualFallbackBubbleViewsInteractiveUiTest,
+                       DismissBubbleUponNavigation) {
+  ShowBubble();
+  ASSERT_TRUE(GetBubbleViews());
+  ASSERT_TRUE(GetIconView() && GetIconView()->GetVisible());
+
+  views::test::WidgetDestroyedWaiter destroyed_waiter(
+      GetBubbleViews()->GetWidget());
+  ui_test_utils::NavigateToURL(browser(), GURL("https://www.google.com"));
+  destroyed_waiter.Wait();
+  EXPECT_FALSE(GetBubbleViews());
+  EXPECT_FALSE(GetIconView()->GetVisible());
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.cc
new file mode 100644
index 0000000..ba7fee6
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.cc
@@ -0,0 +1,79 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h"
+
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/ui/autofill/payments/virtual_card_manual_fallback_bubble_controller.h"
+#include "chrome/browser/ui/browser_command_controller.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/paint_vector_icon.h"
+
+namespace autofill {
+
+VirtualCardManualFallbackIconView::VirtualCardManualFallbackIconView(
+    CommandUpdater* command_updater,
+    IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
+    PageActionIconView::Delegate* delegate)
+    : PageActionIconView(command_updater,
+                         IDC_VIRTUAL_CARD_MANUAL_FALLBACK,
+                         icon_label_bubble_delegate,
+                         delegate) {}
+
+VirtualCardManualFallbackIconView::~VirtualCardManualFallbackIconView() =
+    default;
+
+views::BubbleDialogDelegate* VirtualCardManualFallbackIconView::GetBubble()
+    const {
+  VirtualCardManualFallbackBubbleController* controller = GetController();
+  if (!controller)
+    return nullptr;
+
+  return static_cast<VirtualCardManualFallbackBubbleViews*>(
+      controller->GetBubble());
+}
+
+void VirtualCardManualFallbackIconView::UpdateImpl() {
+  if (!GetWebContents())
+    return;
+
+  // |controller| may be nullptr due to lazy initialization.
+  VirtualCardManualFallbackBubbleController* controller = GetController();
+  bool command_enabled = controller && controller->ShouldIconBeVisible();
+  SetVisible(SetCommandEnabled(command_enabled));
+}
+
+std::u16string
+VirtualCardManualFallbackIconView::GetTextForTooltipAndAccessibleName() const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_TOOLTIP);
+}
+
+void VirtualCardManualFallbackIconView::OnExecuting(
+    PageActionIconView::ExecuteSource execute_source) {}
+
+const gfx::VectorIcon& VirtualCardManualFallbackIconView::GetVectorIcon()
+    const {
+  return kCreditCardIcon;
+}
+
+const char* VirtualCardManualFallbackIconView::GetClassName() const {
+  return "VirtualCardManualFallbackIconView";
+}
+
+VirtualCardManualFallbackBubbleController*
+VirtualCardManualFallbackIconView::GetController() const {
+  content::WebContents* web_contents = GetWebContents();
+  if (!web_contents)
+    return nullptr;
+
+  return VirtualCardManualFallbackBubbleController::Get(web_contents);
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h
new file mode 100644
index 0000000..d6520082
--- /dev/null
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h
@@ -0,0 +1,47 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_VIEW_H_
+
+#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
+
+class CommandUpdater;
+
+namespace autofill {
+
+class VirtualCardManualFallbackBubbleController;
+
+// The icon to show the virtual card manual fallback bubble after the user has
+// selected the virtual card to use and the information has been sent to Chrome.
+class VirtualCardManualFallbackIconView : public PageActionIconView {
+ public:
+  VirtualCardManualFallbackIconView(
+      CommandUpdater* command_updater,
+      IconLabelBubbleView::Delegate* icon_label_bubble_delegate,
+      PageActionIconView::Delegate* delegate);
+  ~VirtualCardManualFallbackIconView() override;
+  VirtualCardManualFallbackIconView(const VirtualCardManualFallbackIconView&) =
+      delete;
+  VirtualCardManualFallbackIconView& operator=(
+      const VirtualCardManualFallbackIconView&) = delete;
+
+  // PageActionIconView:
+  views::BubbleDialogDelegate* GetBubble() const override;
+  void UpdateImpl() override;
+  std::u16string GetTextForTooltipAndAccessibleName() const override;
+
+ protected:
+  // PageActionIconView:
+  void OnExecuting(PageActionIconView::ExecuteSource execute_source) override;
+  const gfx::VectorIcon& GetVectorIcon() const override;
+  const char* GetClassName() const override;
+
+ private:
+  VirtualCardManualFallbackBubbleController* GetController() const;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_VIEW_H_
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
index ef558ab..d5dc119 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view.cc
@@ -342,7 +342,7 @@
       // TODO(bruthig): The ACTION_PENDING triggering logic should be in
       // MenuButton::OnPressed() however there is a bug with the pressed state
       // logic in MenuButton. See http://crbug.com/567252.
-      AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event);
+      ink_drop()->AnimateToState(views::InkDropState::ACTION_PENDING, &event);
     }
     return BookmarkMenuButtonBase::OnMousePressed(event);
   }
diff --git a/chrome/browser/ui/views/bubble_menu_item_factory.cc b/chrome/browser/ui/views/bubble_menu_item_factory.cc
index 559a2b7..6352886 100644
--- a/chrome/browser/ui/views/bubble_menu_item_factory.cc
+++ b/chrome/browser/ui/views/bubble_menu_item_factory.cc
@@ -32,7 +32,7 @@
 
   void OnThemeChanged() override {
     LabelButton::OnThemeChanged();
-    SetInkDropBaseColor(HoverButton::GetInkDropColor(this));
+    ink_drop()->SetBaseColor(HoverButton::GetInkDropColor(this));
   }
 };
 
@@ -44,9 +44,10 @@
 void ConfigureBubbleMenuItem(views::Button* button, int button_id) {
   // Items within a menu should not show focus rings.
   button->SetInstallFocusRingOnFocus(false);
-  button->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
-  button->GetInkDrop()->SetShowHighlightOnFocus(true);
-  button->GetInkDrop()->SetHoverHighlightFadeDuration(base::TimeDelta());
+  button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  button->ink_drop()->GetInkDrop()->SetShowHighlightOnFocus(true);
+  button->ink_drop()->GetInkDrop()->SetHoverHighlightFadeDuration(
+      base::TimeDelta());
   views::InstallRectHighlightPathGenerator(button);
   button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
   button->SetID(button_id);
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 465db44..c296477 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -149,13 +149,13 @@
   explicit TransparentButton(DownloadItemView* parent)
       : Button(Button::PressedCallback()) {
     views::InstallRectHighlightPathGenerator(this);
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     set_context_menu_controller(parent);
     // Button subclasses need to provide this because the default color is
     // kPlaceholderColor. In theory we could statically compute it in the
     // constructor but then it won't be correct after dark mode changes, and to
     // deal with that this class would have to observe NativeTheme and so on.
-    SetInkDropBaseColorCallback(base::BindRepeating(
+    ink_drop()->SetBaseColorCallback(base::BindRepeating(
         [](views::View* host) {
           // This button will be used like a LabelButton, so use the same
           // foreground base color as a label button.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_button.cc b/chrome/browser/ui/views/extensions/extensions_menu_button.cc
index 589a5854..955c484 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_button.cc
@@ -32,9 +32,9 @@
       allow_pinning_(allow_pinning) {
   controller_->SetDelegate(this);
   // TODO(pbos): This currently inherits HoverButton, is this not a no-op?
-  // Also see call in OnThemeChanged() to SetInkDropBaseColor which tries to do
-  // the same thing.
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  // Also see call in OnThemeChanged() to ink_drop()->SetBaseColor which
+  // tries to do the same thing.
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](views::View* host) { return HoverButton::GetInkDropColor(host); },
       this));
 }
@@ -52,7 +52,7 @@
 
 void ExtensionsMenuButton::OnThemeChanged() {
   HoverButton::OnThemeChanged();
-  SetInkDropBaseColor(HoverButton::GetInkDropColor(this));
+  ink_drop()->SetBaseColor(HoverButton::GetInkDropColor(this));
 }
 
 // ToolbarActionViewDelegateViews:
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
index 573bd99..c999696 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_item_view.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
 #include "chrome/app/vector_icons/vector_icons.h"
@@ -123,7 +124,7 @@
           ui::NativeTheme::kColorId_MenuIconColor));
 
   if (pin_button_)
-    pin_button_->SetInkDropBaseColor(icon_color);
+    pin_button_->ink_drop()->SetBaseColor(icon_color);
 
   SetButtonIconWithColor(context_menu_button_, kBrowserToolsIcon, icon_color);
 
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
index 17b6a9b..ebb495d 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
@@ -396,6 +396,7 @@
   EXPECT_EQ(BrowserView::GetBrowserViewForBrowser(browser())
                 ->toolbar()
                 ->GetExtensionsButton()
+                ->ink_drop()
                 ->GetInkDrop()
                 ->GetTargetInkDropState(),
             views::InkDropState::ACTIVATED);
diff --git a/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc b/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc
index b4999d7..ba40ce4 100644
--- a/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc
+++ b/chrome/browser/ui/views/frame/immersive_mode_controller_chromeos_browsertest.cc
@@ -124,10 +124,10 @@
       BrowserNonClientFrameViewChromeOS* frame_view) {
     WebAppFrameToolbarView* container =
         frame_view->web_app_frame_toolbar_for_testing();
-    views::test::InkDropHostViewTestApi ink_drop_api(
-        container->GetAppMenuButton());
+    views::test::InkDropHostTestApi ink_drop_api(
+        container->GetAppMenuButton()->ink_drop());
     EXPECT_TRUE(container->GetContentSettingContainerForTesting()->layer());
-    EXPECT_EQ(views::InkDropHostView::InkDropMode::ON,
+    EXPECT_EQ(views::InkDropHost::InkDropMode::ON,
               ink_drop_api.ink_drop_mode());
   }
 
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
index 8fa4ff9..6dd1bc05 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -55,7 +55,7 @@
   scroll_button->SetImageHorizontalAlignment(
       views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
   scroll_button->SetHasInkDropActionOnClick(true);
-  scroll_button->SetInkDropMode(views::Button::InkDropMode::ON);
+  scroll_button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   scroll_button->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
   scroll_button->SetPreferredSize(gfx::Size(28, 28));
   views::HighlightPathGenerator::Install(
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view_unittest.cc b/chrome/browser/ui/views/frame/tab_strip_region_view_unittest.cc
index 10468a2..03affbd 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view_unittest.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view_unittest.cc
@@ -141,11 +141,11 @@
   // ink drop container size should remain equal to the new tab button visible
   // bounds size. https://crbug.com/814105.
   for (int i = 0; i < 10; ++i) {
-    tab_strip_region_view_->new_tab_button()->AnimateInkDropToStateForTesting(
+    tab_strip_region_view_->new_tab_button()->AnimateToStateForTesting(
         views::InkDropState::ACTION_TRIGGERED);
     controller_->AddTab(i, true /* is_active */);
     CompleteAnimationAndLayout();
-    tab_strip_region_view_->new_tab_button()->AnimateInkDropToStateForTesting(
+    tab_strip_region_view_->new_tab_button()->AnimateToStateForTesting(
         views::InkDropState::HIDDEN);
   }
 }
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
index b49dc00..1c5ca24 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
 
+#include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_functions.h"
@@ -395,7 +396,7 @@
     UpdateDismissButtonIcon();
     if (stop_cast_button_) {
       stop_cast_button_->SetEnabledTextColors(foreground_color_);
-      stop_cast_button_->SetInkDropBaseColor(foreground_color_);
+      stop_cast_button_->ink_drop()->SetBaseColor(foreground_color_);
     }
   }
 
@@ -514,10 +515,10 @@
                               base::Unretained(cast_item)),
           l10n_util::GetStringUTF16(
               IDS_GLOBAL_MEDIA_CONTROLS_STOP_CASTING_BUTTON_LABEL)));
-  stop_cast_button_->SetInkDropMode(InkDropMode::ON);
+  stop_cast_button_->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   stop_cast_button_->SetHasInkDropActionOnClick(true);
-  stop_cast_button_->SetInkDropBaseColor(foreground_color_);
-  stop_cast_button_->SetInkDropLargeCornerRadius(
+  stop_cast_button_->ink_drop()->SetBaseColor(foreground_color_);
+  stop_cast_button_->ink_drop()->SetLargeCornerRadius(
       kStopCastButtonStripSize.height());
   stop_cast_button_->SetEnabledTextColors(foreground_color_);
   stop_cast_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc
index b1299e3..3d0587bf 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.cc
@@ -77,10 +77,10 @@
                    subtitle(), icon_, foreground_color, background_color);
 
   SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
-  SetInkDropMode(Button::InkDropMode::ON);
-  SetInkDropBaseColor(foreground_color);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetBaseColor(foreground_color);
   // Bypass color-callback setup in HoverButton.
-  SetInkDropBaseColorCallback({});
+  ink_drop()->SetBaseColorCallback({});
   SetHasInkDropActionOnClick(true);
   SetPreferredSize(kDeviceEntryViewSize);
 }
@@ -91,12 +91,12 @@
   }
   is_highlighted_ = highlighted;
   if (highlighted) {
-    SetInkDropMode(Button::InkDropMode::OFF);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
     SetHasInkDropActionOnClick(false);
     SetBackground(views::CreateSolidBackground(
-        SkColorSetA(GetInkDropBaseColor(), kEntryHighlightOpacity)));
+        SkColorSetA(ink_drop()->GetBaseColor(), kEntryHighlightOpacity)));
   } else {
-    SetInkDropMode(Button::InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
     SetBackground(nullptr);
   }
@@ -109,7 +109,7 @@
 
 void AudioDeviceEntryView::OnColorsChanged(SkColor foreground_color,
                                            SkColor background_color) {
-  SetInkDropBaseColor(foreground_color);
+  ink_drop()->SetBaseColor(foreground_color);
 
   ChangeEntryColor(static_cast<views::ImageView*>(icon_view()), title(),
                    subtitle(), icon_, foreground_color, background_color);
@@ -136,17 +136,17 @@
   ChangeCastEntryColor(sink, foreground_color, background_color);
 
   SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
-  SetInkDropMode(Button::InkDropMode::ON);
-  SetInkDropBaseColor(foreground_color);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetBaseColor(foreground_color);
   SetHasInkDropActionOnClick(true);
   // Bypass color-callback setup in HoverButton.
-  SetInkDropBaseColorCallback({});
+  ink_drop()->SetBaseColorCallback({});
   SetPreferredSize(kDeviceEntryViewSize);
 }
 
 void CastDeviceEntryView::OnColorsChanged(SkColor foreground_color,
                                           SkColor background_color) {
-  SetInkDropBaseColor(foreground_color);
+  ink_drop()->SetBaseColor(foreground_color);
   ChangeCastEntryColor(sink(), foreground_color, background_color);
 }
 
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
index 777d01f6..79bfc2e 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h"
 
+#include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
@@ -56,7 +57,7 @@
       delegate_(delegate) {
   SetLabel(l10n_util::GetStringUTF16(
       IDS_GLOBAL_MEDIA_CONTROLS_DEVICES_BUTTON_LABEL));
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
   SetFocusBehavior(FocusBehavior::ALWAYS);
 
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc
index e4934a8..c1691fb 100644
--- a/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc
+++ b/chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_unittest.cc
@@ -34,11 +34,12 @@
 
 constexpr char kSinkId[] = "sink_id";
 constexpr char kSinkFriendlyName[] = "Nest Hub";
+constexpr char16_t kSinkFriendlyName16[] = u"Nest Hub";
 
 UIMediaSink CreateMediaSink(
     UIMediaSinkState state = UIMediaSinkState::AVAILABLE) {
   UIMediaSink sink;
-  sink.friendly_name = base::UTF8ToUTF16(kSinkFriendlyName);
+  sink.friendly_name = kSinkFriendlyName16;
   sink.id = kSinkId;
   sink.state = state;
   sink.cast_modes = {media_router::MediaCastMode::PRESENTATION};
diff --git a/chrome/browser/ui/views/hover_button.cc b/chrome/browser/ui/views/hover_button.cc
index 1dedade5..aa57cc1 100644
--- a/chrome/browser/ui/views/hover_button.cc
+++ b/chrome/browser/ui/views/hover_button.cc
@@ -96,9 +96,9 @@
                            2;
   SetBorder(CreateBorderWithVerticalSpacing(vert_spacing));
 
-  SetInkDropMode(InkDropMode::ON);
-  views::InkDrop::UseInkDropForFloodFillRipple(this);
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  views::InkDrop::UseInkDropForFloodFillRipple(ink_drop());
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](views::View* host) { return GetInkDropColor(host); }, this));
 
   SetTriggerableEventFlags(ui::EF_LEFT_MOUSE_BUTTON |
diff --git a/chrome/browser/ui/views/hover_button_controller.cc b/chrome/browser/ui/views/hover_button_controller.cc
index 5d6cfa1..3c3a1730 100644
--- a/chrome/browser/ui/views/hover_button_controller.cc
+++ b/chrome/browser/ui/views/hover_button_controller.cc
@@ -34,11 +34,11 @@
   if (button()->GetRequestFocusOnPress())
     button()->RequestFocus();
   if (callback_) {
-    button()->AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED,
-                             ui::LocatedEvent::FromIfValid(&event));
+    button()->ink_drop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED,
+                                         ui::LocatedEvent::FromIfValid(&event));
   } else {
-    button()->AnimateInkDrop(views::InkDropState::HIDDEN,
-                             ui::LocatedEvent::FromIfValid(&event));
+    button()->ink_drop()->AnimateToState(views::InkDropState::HIDDEN,
+                                         ui::LocatedEvent::FromIfValid(&event));
   }
   return true;
 }
@@ -51,7 +51,7 @@
     if (callback_)
       callback_.Run(event);
   } else {
-    button()->AnimateInkDrop(views::InkDropState::HIDDEN, &event);
+    button()->ink_drop()->AnimateToState(views::InkDropState::HIDDEN, &event);
     ButtonController::OnMouseReleased(event);
   }
 }
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc
index 37f6f74..1ae444a 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/feature_list.h"
 #include "base/i18n/rtl.h"
@@ -104,30 +105,30 @@
                     base::UTF8ToUTF16(base::StringPiece(display_name))) {
     SetHorizontalAlignment(gfx::ALIGN_LEFT);
     SetMinSize(gfx::Size(kMaxIntentPickerLabelButtonWidth, kRowHeight));
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     if (!icon_model.IsEmpty()) {
       SetImageModel(views::ImageButton::STATE_NORMAL, icon_model);
     }
     SetBorder(views::CreateEmptyBorder(8, 16, 8, 0));
-    SetInkDropBaseColor(SK_ColorGRAY);
-    SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
+    ink_drop()->SetBaseColor(SK_ColorGRAY);
+    ink_drop()->SetVisibleOpacity(kToolbarInkDropVisibleOpacity);
   }
   IntentPickerLabelButton(const IntentPickerLabelButton&) = delete;
   IntentPickerLabelButton& operator=(const IntentPickerLabelButton&) = delete;
   ~IntentPickerLabelButton() override = default;
 
   void MarkAsUnselected(const ui::Event* event) {
-    AnimateInkDrop(views::InkDropState::HIDDEN,
-                   ui::LocatedEvent::FromIfValid(event));
+    ink_drop()->AnimateToState(views::InkDropState::HIDDEN,
+                               ui::LocatedEvent::FromIfValid(event));
   }
 
   void MarkAsSelected(const ui::Event* event) {
-    AnimateInkDrop(views::InkDropState::ACTIVATED,
-                   ui::LocatedEvent::FromIfValid(event));
+    ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                               ui::LocatedEvent::FromIfValid(event));
   }
 
   views::InkDropState GetTargetInkDropState() {
-    return GetInkDrop()->GetTargetInkDropState();
+    return ink_drop()->GetInkDrop()->GetTargetInkDropState();
   }
 };
 
diff --git a/chrome/browser/ui/views/location_bar/find_bar_icon.cc b/chrome/browser/ui/views/location_bar/find_bar_icon.cc
index 72f2f70..4598649 100644
--- a/chrome/browser/ui/views/location_bar/find_bar_icon.cc
+++ b/chrome/browser/ui/views/location_bar/find_bar_icon.cc
@@ -28,17 +28,17 @@
 FindBarIcon::~FindBarIcon() {}
 
 void FindBarIcon::SetActive(bool activate, bool should_animate) {
-  if (activate ==
-      (GetInkDrop()->GetTargetInkDropState() == views::InkDropState::ACTIVATED))
+  if (activate == (ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
+                   views::InkDropState::ACTIVATED))
     return;
   if (activate) {
     if (should_animate) {
-      AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
+      ink_drop()->AnimateToState(views::InkDropState::ACTIVATED, nullptr);
     } else {
-      GetInkDrop()->SnapToActivated();
+      ink_drop()->GetInkDrop()->SnapToActivated();
     }
   } else {
-    AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
+    ink_drop()->AnimateToState(views::InkDropState::HIDDEN, nullptr);
   }
 }
 
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index 6f4ecf4..da310c0 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -93,7 +93,7 @@
     return;
   }
 
-  views::InkDrop* ink_drop = owner_->GetInkDrop();
+  views::InkDrop* ink_drop = owner_->ink_drop()->GetInkDrop();
   DCHECK(ink_drop);
 
   // If an inkdrop highlight or ripple is animating in or visible, the
@@ -145,20 +145,22 @@
 
   separator_view_->SetVisible(ShouldShowSeparator());
 
-  SetInkDropVisibleOpacity(GetOmniboxStateOpacity(OmniboxPartState::SELECTED));
-  SetInkDropHighlightOpacity(GetOmniboxStateOpacity(OmniboxPartState::HOVERED));
+  ink_drop()->SetVisibleOpacity(
+      GetOmniboxStateOpacity(OmniboxPartState::SELECTED));
+  ink_drop()->SetHighlightOpacity(
+      GetOmniboxStateOpacity(OmniboxPartState::HOVERED));
 
-  SetCreateInkDropCallback(base::BindRepeating(
+  ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
       [](IconLabelBubbleView* host) {
         std::unique_ptr<views::InkDrop> ink_drop =
             views::InkDrop::CreateInkDropForFloodFillRipple(
-                host, /*highlight_on_hover=*/false,
+                host->ink_drop(), /*highlight_on_hover=*/false,
                 /*highlight_on_focus=*/!host->focus_ring());
         ink_drop->AddObserver(host);
         return ink_drop;
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](IconLabelBubbleView* host) {
         return host->delegate_->GetIconLabelBubbleInkDropColor();
       },
@@ -377,8 +379,8 @@
     PreferredSizeChanged();
   }
 
-  GetInkDrop()->SetShowHighlightOnHover(true);
-  GetInkDrop()->SetShowHighlightOnFocus(!focus_ring());
+  ink_drop()->GetInkDrop()->SetShowHighlightOnHover(true);
+  ink_drop()->GetInkDrop()->SetShowHighlightOnFocus(!focus_ring());
 }
 
 void IconLabelBubbleView::AnimationProgressed(const gfx::Animation* animation) {
@@ -452,7 +454,7 @@
 }
 
 void IconLabelBubbleView::SetUpForAnimation() {
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
   label()->SetElideBehavior(gfx::NO_ELIDE);
   label()->SetVisible(false);
@@ -547,14 +549,14 @@
 
 void IconLabelBubbleView::ShowAnimation() {
   slide_animation_.Show();
-  GetInkDrop()->SetShowHighlightOnHover(false);
-  GetInkDrop()->SetShowHighlightOnFocus(false);
+  ink_drop()->GetInkDrop()->SetShowHighlightOnHover(false);
+  ink_drop()->GetInkDrop()->SetShowHighlightOnFocus(false);
 }
 
 void IconLabelBubbleView::HideAnimation() {
   slide_animation_.Hide();
-  GetInkDrop()->SetShowHighlightOnHover(false);
-  GetInkDrop()->SetShowHighlightOnFocus(false);
+  ink_drop()->GetInkDrop()->SetShowHighlightOnHover(false);
+  ink_drop()->GetInkDrop()->SetShowHighlightOnFocus(false);
 }
 
 SkPath IconLabelBubbleView::GetHighlightPath() const {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
index 126acc7..de2c04c 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
@@ -27,7 +27,7 @@
 #include "ui/aura/window.h"
 #endif
 
-using views::test::InkDropHostViewTestApi;
+using views::test::InkDropHostTestApi;
 using views::test::TestInkDrop;
 
 namespace {
@@ -79,7 +79,8 @@
   }
 
   void HideBubble() {
-    AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
+    ink_drop()->AnimateToState(views::InkDropState::HIDDEN,
+                               nullptr /* event */);
     is_bubble_showing_ = false;
   }
 
@@ -114,7 +115,8 @@
   bool IsShrinking() const override { return state() == SHRINKING; }
 
   bool ShowBubble(const ui::Event& event) override {
-    AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr /* event */);
+    ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                               nullptr /* event */);
     is_bubble_showing_ = true;
     return true;
   }
@@ -183,7 +185,8 @@
 
   void AttachInkDrop() {
     ink_drop_ = new TestInkDrop();
-    InkDropHostViewTestApi(view_).SetInkDrop(base::WrapUnique(ink_drop_));
+    InkDropHostTestApi(view_->ink_drop())
+        .SetInkDrop(base::WrapUnique(ink_drop_));
   }
 
  private:
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 4edca684..7b05cfb1 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -300,6 +300,8 @@
           autofill::features::kAutofillEnableToolbarStatusChip)) {
     params.types_enabled.push_back(PageActionIconType::kSaveCard);
     params.types_enabled.push_back(PageActionIconType::kLocalCardMigration);
+    params.types_enabled.push_back(
+        PageActionIconType::kVirtualCardManualFallback);
     if (base::FeatureList::IsEnabled(
             autofill::features::kAutofillAddressProfileSavePrompt)) {
       // TODO(crbug.com/1167060): Place this in the proper order upon having
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.cc b/chrome/browser/ui/views/location_bar/location_icon_view.cc
index 464ac353..24f2235 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.cc
@@ -144,7 +144,7 @@
 }
 
 const views::InkDrop* LocationIconView::get_ink_drop_for_testing() {
-  return GetInkDrop();
+  return ink_drop()->GetInkDrop();
 }
 
 std::u16string LocationIconView::GetText() const {
@@ -236,18 +236,19 @@
                      : l10n_util::GetStringUTF16(IDS_TOOLTIP_LOCATION_ICON));
 
   // We should only enable/disable the InkDrop if the editing state has changed,
-  // as the drop gets recreated when SetInkDropMode is called. This can result
-  // in strange behaviour, like the the InkDrop disappearing mid animation.
+  // as the drop gets recreated when ink_drop()->SetMode() is called.
+  // This can result in strange behaviour, like the the InkDrop disappearing mid
+  // animation.
   if (is_editing_or_empty != was_editing_or_empty_) {
     // If the omnibox is empty or editing, the user should not be able to left
     // click on the icon. As such, the icon should not show a highlight or be
     // focusable. Note: using the middle mouse to copy-and-paste should still
     // work on the icon.
     if (is_editing_or_empty) {
-      SetInkDropMode(InkDropMode::OFF);
+      ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
       SetFocusBehavior(FocusBehavior::NEVER);
     } else {
-      SetInkDropMode(InkDropMode::ON);
+      ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
 
 #if defined(OS_MAC)
       SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc b/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc
index 60bea0c8..eabbc4c 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view_browsertest.cc
@@ -46,12 +46,14 @@
   model->SetInputInProgress(true);
   icon_view()->Update(/*suppress_animations=*/true);
 
-  EXPECT_EQ(IconLabelBubbleView::InkDropMode::OFF,
-            views::test::InkDropHostViewTestApi(icon_view()).ink_drop_mode());
+  EXPECT_EQ(
+      views::InkDropHost::InkDropMode::OFF,
+      views::test::InkDropHostTestApi(icon_view()->ink_drop()).ink_drop_mode());
 
   model->SetInputInProgress(false);
   icon_view()->Update(/*suppress_animations=*/true);
 
-  EXPECT_EQ(IconLabelBubbleView::InkDropMode::ON,
-            views::test::InkDropHostViewTestApi(icon_view()).ink_drop_mode());
+  EXPECT_EQ(
+      views::InkDropHost::InkDropMode::ON,
+      views::test::InkDropHostTestApi(icon_view()->ink_drop()).ink_drop_mode());
 }
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index 719fc00..4085c3a 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -6,6 +6,7 @@
 
 #include <string>
 
+#include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -90,7 +91,7 @@
     } else {
       next_state = views::InkDropState::DEACTIVATED;
     }
-    GetInkDrop()->AnimateToState(next_state);
+    ink_drop()->GetInkDrop()->AnimateToState(next_state);
   }
 }
 
diff --git a/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
index abf75d7..55b5fe29 100644
--- a/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
@@ -125,7 +125,7 @@
 
 IN_PROC_BROWSER_TEST_F(StarViewTest, InkDropHighlighted) {
   PageActionIconView* star_icon = GetStarIcon();
-  views::test::InkDropHostViewTestApi ink_drop_test_api(star_icon);
+  views::test::InkDropHostTestApi ink_drop_test_api(star_icon->ink_drop());
 
   if (ink_drop_test_api.HasInkDrop()) {
     GURL url("http://test.com");
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc b/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc
index 91c93e1..cd6b92b 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view.cc
@@ -85,7 +85,7 @@
   icon->SetBorder(views::CreateEmptyBorder(media_router::kPrimaryIconBorder));
   icon->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_MEDIA_ROUTER_NO_DEVICES_FOUND_BUTTON));
-  icon->SetInkDropMode(views::InkDropHostView::InkDropMode::OFF);
+  icon->ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
   icon_ = icon;
 
   label_->SetText(
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc
index 20bb25f..4f929c3 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_sink_button.cc
@@ -155,7 +155,8 @@
 void CastDialogSinkButton::OnEnabledChanged() {
   // Prevent a DCHECK failure seen at https://crbug.com/912687 by not having an
   // InkDrop if the button is disabled.
-  SetInkDropMode(GetEnabled() ? InkDropMode::ON : InkDropMode::OFF);
+  ink_drop()->SetMode(GetEnabled() ? views::InkDropHost::InkDropMode::ON
+                                   : views::InkDropHost::InkDropMode::OFF);
   // If the button has a state other than AVAILABLE (e.g. CONNECTED), there is
   // no need to change the status or the icon.
   if (sink_.state != UIMediaSinkState::AVAILABLE)
diff --git a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
index 3fb2fd5..a11f63a 100644
--- a/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
+++ b/chrome/browser/ui/views/media_router/cast_toolbar_button.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/media_router/cast_toolbar_button.h"
 
+#include "base/bind.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -92,11 +93,11 @@
 }
 
 void CastToolbarButton::ActivateIcon() {
-  AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
+  ink_drop()->AnimateToState(views::InkDropState::ACTIVATED, nullptr);
 }
 
 void CastToolbarButton::DeactivateIcon() {
-  AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+  ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED, nullptr);
 }
 
 void CastToolbarButton::OnIssue(const media_router::Issue& issue) {
diff --git a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
index e5ff3e2..1b93872f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_row_view.cc
@@ -158,7 +158,7 @@
 
     SkColor icon_color = GetOmniboxColor(GetThemeProvider(),
                                          OmniboxPart::RESULTS_ICON, part_state);
-    header_toggle_button_->SetInkDropBaseColor(icon_color);
+    header_toggle_button_->ink_drop()->SetBaseColor(icon_color);
 
     int dip_size = GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
     const gfx::ImageSkia arrow_down =
diff --git a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
index d55cbb3..c351a69 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_suggestion_button_row_view.cc
@@ -55,9 +55,9 @@
     SetCornerRadius(GetInsets().height() +
                     GetLayoutConstant(LOCATION_BAR_ICON_SIZE));
 
-    SetInkDropHighlightOpacity(
+    ink_drop()->SetHighlightOpacity(
         GetOmniboxStateOpacity(OmniboxPartState::HOVERED));
-    SetInkDropBaseColorCallback(base::BindRepeating(
+    ink_drop()->SetBaseColorCallback(base::BindRepeating(
         [](OmniboxSuggestionRowButton* host) {
           return color_utils::GetColorWithMaxContrast(
               host->omnibox_bg_color_.value());
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
index ae9cba0..59936ed 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_controller.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/bind.h"
 #include "base/feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/ui/views/autofill/payments/local_card_migration_icon_view.h"
 #include "chrome/browser/ui/views/autofill/payments/offer_notification_icon_view.h"
 #include "chrome/browser/ui/views/autofill/payments/save_payment_icon_view.h"
+#include "chrome/browser/ui/views/autofill/payments/virtual_card_manual_fallback_icon_view.h"
 #include "chrome/browser/ui/views/autofill/save_address_profile_icon_view.h"
 #include "chrome/browser/ui/views/file_system_access/file_system_access_icon_view.h"
 #include "chrome/browser/ui/views/location_bar/cookie_controls_icon_view.h"
@@ -54,7 +56,7 @@
   auto add_page_action_icon = [&params, this](PageActionIconType type,
                                               auto icon) {
     icon->SetVisible(false);
-    icon->SetInkDropVisibleOpacity(
+    icon->ink_drop()->SetVisibleOpacity(
         params.page_action_icon_delegate->GetPageActionInkDropVisibleOpacity());
     if (params.icon_color)
       icon->SetIconColor(*params.icon_color);
@@ -215,6 +217,12 @@
                       params.command_updater, params.icon_label_bubble_delegate,
                       params.page_action_icon_delegate));
         break;
+      case PageActionIconType::kVirtualCardManualFallback:
+        add_page_action_icon(
+            type, std::make_unique<autofill::VirtualCardManualFallbackIconView>(
+                      params.command_updater, params.icon_label_bubble_delegate,
+                      params.page_action_icon_delegate));
+        break;
       case PageActionIconType::kZoom:
         zoom_icon_ = add_page_action_icon(
             type, std::make_unique<ZoomView>(params.icon_label_bubble_delegate,
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index 1234ebfa1..14542b1 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -63,7 +63,7 @@
   DCHECK(delegate_);
 
   image()->SetFlipCanvasOnPaintForRTLUI(true);
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
   // Only shows bubble after mouse is released.
   button_controller()->set_notify_action(
diff --git a/chrome/browser/ui/views/payments/payment_request_item_list.cc b/chrome/browser/ui/views/payments/payment_request_item_list.cc
index ffac3cc8..3d60af3 100644
--- a/chrome/browser/ui/views/payments/payment_request_item_list.cc
+++ b/chrome/browser/ui/views/payments/payment_request_item_list.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/bind.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
 #include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
 #include "chrome/browser/ui/views/payments/payment_request_views_util.h"
@@ -115,7 +116,7 @@
     edit_button->SetImage(views::Button::STATE_NORMAL,
                           gfx::CreateVectorIcon(vector_icons::kEditIcon,
                                                 kEditIconSize, icon_color));
-    edit_button->SetInkDropBaseColor(icon_color);
+    edit_button->ink_drop()->SetBaseColor(icon_color);
     edit_button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
     edit_button->SetID(static_cast<int>(DialogViewID::EDIT_ITEM_BUTTON));
     edit_button->SetAccessibleName(
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 30b9ebd7..01a82c29 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -8,6 +8,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_functions.h"
@@ -186,7 +187,7 @@
         background_profile_color_(background_profile_color),
         show_border_(show_border) {
     SetTooltipText(text);
-    SetInkDropMode(views::Button::InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
 
     InstallCircleHighlightPathGenerator(this);
   }
@@ -207,7 +208,7 @@
         ImageForMenu(icon_, kShortcutIconToImageRatio, icon_color);
     SetImage(views::Button::STATE_NORMAL,
              SizeImage(image, kCircularImageButtonSize));
-    SetInkDropBaseColor(icon_color);
+    ink_drop()->SetBaseColor(icon_color);
 
     if (show_border_) {
       const SkColor separator_color = GetNativeTheme()->GetSystemColor(
@@ -550,7 +551,8 @@
   SetPaintClientToLayer(true);
   set_margins(gfx::Insets(0));
   DCHECK(anchor_button);
-  anchor_button->AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
+  anchor_button->ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                                            nullptr);
 
   SetEnableArrowKeyTraversal(true);
   GetViewAccessibility().OverrideRole(ax::mojom::Role::kMenu);
@@ -687,9 +689,17 @@
   sync_background_state_ = sync_info.background_state;
 
   sync_info_container_->RemoveAllChildViews(/*delete_children=*/true);
-  sync_info_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
-      kSyncInfoInsidePadding));
+  sync_info_container_->SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical)
+      .SetIgnoreDefaultMainAxisMargins(true)
+      .SetCollapseMargins(true)
+      .SetDefault(views::kMarginsKey, gfx::Insets(kSyncInfoInsidePadding, 0));
+  sync_info_container_->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::LayoutOrientation::kVertical,
+                               views::MinimumFlexSizeRule::kPreferred,
+                               views::MaximumFlexSizeRule::kUnbounded, true,
+                               views::MinimumFlexSizeRule::kScaleToZero));
 
   if (description.empty()) {
     sync_info_container_->AddChildView(std::make_unique<SyncButton>(
@@ -705,24 +715,36 @@
   // Add icon + description at the top.
   views::View* description_container =
       sync_info_container_->AddChildView(std::make_unique<views::View>());
-  views::BoxLayout* description_layout =
-      description_container->SetLayoutManager(
-          std::make_unique<views::BoxLayout>(
-              views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
-              kDescriptionIconSpacing));
+  description_container->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::LayoutOrientation::kVertical,
+                               views::MinimumFlexSizeRule::kPreferred,
+                               views::MaximumFlexSizeRule::kUnbounded, true,
+                               views::MinimumFlexSizeRule::kScaleToZero));
+  views::FlexLayout* description_layout =
+      &description_container
+           ->SetLayoutManager(std::make_unique<views::FlexLayout>())
+           ->SetOrientation(views::LayoutOrientation::kHorizontal)
+           .SetIgnoreDefaultMainAxisMargins(true)
+           .SetCollapseMargins(true)
+           .SetDefault(views::kMarginsKey,
+                       gfx::Insets(0, kDescriptionIconSpacing));
 
   if (show_badge) {
     description_container->AddChildView(std::make_unique<SyncImageView>(this));
   } else {
     // If there is no image, the description is centered.
-    description_layout->set_main_axis_alignment(
-        views::BoxLayout::MainAxisAlignment::kCenter);
+    description_layout->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
   }
 
   views::Label* label = description_container->AddChildView(
       std::make_unique<views::Label>(description));
   label->SetMultiLine(true);
   label->SetHandlesTooltips(false);
+  label->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+                               views::MaximumFlexSizeRule::kPreferred, true));
 
   // Set sync info description as the name of the parent container, so
   // accessibility tools can read it together with the button text. The role
@@ -923,8 +945,8 @@
   RemoveAllChildViews(/*delete_childen=*/true);
 
   auto components = std::make_unique<views::View>();
-  components->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical));
+  components->SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical);
 
   // Create and add new component containers in the correct order.
   // First, add the parts of the current profile.
@@ -997,7 +1019,8 @@
 void ProfileMenuViewBase::OnWindowClosing() {
   DCHECK_EQ(g_profile_bubble_, this);
   if (anchor_button())
-    anchor_button()->AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
+    anchor_button()->ink_drop()->AnimateToState(
+        views::InkDropState::DEACTIVATED, nullptr);
   g_profile_bubble_ = nullptr;
 }
 
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 55d4261b..8ae7a001 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -75,8 +75,8 @@
 enum class ForceEphemeralProfilesPolicy { kUnset, kEnabled, kDisabled };
 
 const SkColor kProfileColor = SK_ColorRED;
-const char kWork[] = "Work";
-const char kOriginalProfileName[] = "OriginalProfile";
+const char16_t kWork[] = u"Work";
+const char16_t kOriginalProfileName[] = u"OriginalProfile";
 
 AccountInfo FillAccountInfo(
     const CoreAccountInfo& core_info,
@@ -654,7 +654,7 @@
   ASSERT_NE(entry, nullptr);
   EXPECT_FALSE(entry->IsEphemeral());
   EXPECT_FALSE(entry->IsAuthenticated());
-  EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16(kWork));
+  EXPECT_EQ(entry->GetLocalProfileName(), kWork);
   // The color is not applied if the user enters the SAML flow.
   EXPECT_FALSE(ThemeServiceFactory::GetForProfile(profile_being_created)
                    ->UsingAutogeneratedTheme());
@@ -1444,11 +1444,11 @@
   }
 
   // Checks if a profile matching `name` exists in the profile manager.
-  bool ProfileWithNameExists(const std::string& name) {
+  bool ProfileWithNameExists(const std::u16string& name) {
     for (const auto* entry : profile_manager()
                                  ->GetProfileAttributesStorage()
                                  .GetAllProfilesAttributes()) {
-      if (entry->GetLocalProfileName() == base::UTF8ToUTF16(name))
+      if (entry->GetLocalProfileName() == name)
         return true;
     }
     return false;
@@ -1491,7 +1491,7 @@
               ->GetProfileAttributesStorage()
               .GetProfileAttributesWithPath(browser()->profile()->GetPath());
       ASSERT_NE(entry, nullptr);
-      entry->SetLocalProfileName(base::UTF8ToUTF16(kOriginalProfileName),
+      entry->SetLocalProfileName(kOriginalProfileName,
                                  entry->IsUsingDefaultName());
     }
     CheckPolicyApplied(browser()->profile());
@@ -1549,14 +1549,14 @@
     // If the policy is set, all profiles should have been deleted.
     EXPECT_EQ(1u, profile_manager()->GetNumberOfProfiles());
     // The current profile is not the one that was created in the previous run.
-    EXPECT_FALSE(ProfileWithNameExists("Joe"));
+    EXPECT_FALSE(ProfileWithNameExists(u"Joe"));
     EXPECT_FALSE(OriginalProfileExists());
     return;
   }
 
   // If the policy is disabled or unset, the two profiles are still here.
   EXPECT_EQ(2u, profile_manager()->GetNumberOfProfiles());
-  EXPECT_TRUE(ProfileWithNameExists("Joe"));
+  EXPECT_TRUE(ProfileWithNameExists(u"Joe"));
   EXPECT_TRUE(OriginalProfileExists());
 }
 
diff --git a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
index 288ae456..a6c2dc0 100644
--- a/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
+++ b/chrome/browser/ui/views/reader_mode/reader_mode_icon_view.cc
@@ -62,7 +62,7 @@
 void ReaderModeIconView::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
   if (GetVisible())
-    AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
+    ink_drop()->AnimateToState(views::InkDropState::HIDDEN, nullptr);
 }
 
 void ReaderModeIconView::ReadyToCommitNavigation(
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
index ef4e1b6e..ce380c65 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
@@ -121,7 +121,8 @@
     views::View* starting_from,
     bool is_visible) {
   views::Button::AsButton(GetAnchorView())
-      ->AnimateInkDrop(is_visible ? views::InkDropState::ACTIVATED
+      ->ink_drop()
+      ->AnimateToState(is_visible ? views::InkDropState::ACTIVATED
                                   : views::InkDropState::DEACTIVATED,
                        nullptr);
 }
diff --git a/chrome/browser/ui/views/sharing/sharing_icon_view.cc b/chrome/browser/ui/views/sharing/sharing_icon_view.cc
index 1954c8cd..5336660 100644
--- a/chrome/browser/ui/views/sharing/sharing_icon_view.cc
+++ b/chrome/browser/ui/views/sharing/sharing_icon_view.cc
@@ -137,8 +137,8 @@
 void SharingIconView::UpdateInkDrop(bool activate) {
   auto target_state =
       activate ? views::InkDropState::ACTIVATED : views::InkDropState::HIDDEN;
-  if (GetInkDrop()->GetTargetInkDropState() != target_state)
-    AnimateInkDrop(target_state, /*event=*/nullptr);
+  if (ink_drop()->GetInkDrop()->GetTargetInkDropState() != target_state)
+    ink_drop()->AnimateToState(target_state, /*event=*/nullptr);
 }
 
 bool SharingIconView::IsTriggerableEvent(const ui::Event& event) {
diff --git a/chrome/browser/ui/views/tabs/color_picker_view.cc b/chrome/browser/ui/views/tabs/color_picker_view.cc
index 2e92d795..ccac3f2 100644
--- a/chrome/browser/ui/views/tabs/color_picker_view.cc
+++ b/chrome/browser/ui/views/tabs/color_picker_view.cc
@@ -91,7 +91,7 @@
                              : gfx::Insets(padding);
     SetBorder(views::CreateEmptyBorder(insets));
 
-    SetInkDropMode(InkDropMode::OFF);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
     SetAnimateOnStateChange(true);
   }
 
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index 0c6ca12..5bc9e0b 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -66,9 +66,9 @@
   ink_drop_container_ =
       AddChildView(std::make_unique<views::InkDropContainerView>());
 
-  SetInkDropMode(InkDropMode::ON);
-  SetInkDropHighlightOpacity(0.16f);
-  SetInkDropVisibleOpacity(0.14f);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetHighlightOpacity(0.16f);
+  ink_drop()->SetVisibleOpacity(0.14f);
 
   SetInstallFocusRingOnFocus(true);
   views::HighlightPathGenerator::Install(
@@ -85,8 +85,8 @@
   SchedulePaint();
 }
 
-void NewTabButton::AnimateInkDropToStateForTesting(views::InkDropState state) {
-  GetInkDrop()->AnimateToState(state);
+void NewTabButton::AnimateToStateForTesting(views::InkDropState state) {
+  ink_drop()->GetInkDrop()->AnimateToState(state);
 }
 
 void NewTabButton::AddLayerBeneathView(ui::Layer* new_layer) {
@@ -138,7 +138,8 @@
 
 void NewTabButton::NotifyClick(const ui::Event& event) {
   ImageButton::NotifyClick(event);
-  GetInkDrop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
+  ink_drop()->GetInkDrop()->AnimateToState(
+      views::InkDropState::ACTION_TRIGGERED);
 }
 
 void NewTabButton::PaintButtonContents(gfx::Canvas* canvas) {
@@ -264,7 +265,7 @@
 }
 
 void NewTabButton::UpdateInkDropBaseColor() {
-  SetInkDropBaseColor(
+  ink_drop()->SetBaseColor(
       color_utils::GetColorWithMaxContrast(GetButtonFillColor()));
 }
 
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h
index 1572541..0996862 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.h
+++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -41,7 +41,7 @@
   // the best contrast on the background.
   virtual void FrameColorsChanged();
 
-  void AnimateInkDropToStateForTesting(views::InkDropState state);
+  void AnimateToStateForTesting(views::InkDropState state);
 
   // views::ImageButton:
   void AddLayerBeneathView(ui::Layer* new_layer) override;
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.cc b/chrome/browser/ui/views/tabs/tab_close_button.cc
index 8f64b0c..1cf86d1 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_close_button.cc
@@ -49,14 +49,14 @@
   SetAccessibleName(l10n_util::GetStringUTF16(IDS_ACCNAME_CLOSE));
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
 
-  SetInkDropMode(InkDropMode::ON);
-  SetInkDropHighlightOpacity(0.16f);
-  SetInkDropVisibleOpacity(0.14f);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetHighlightOpacity(0.16f);
+  ink_drop()->SetVisibleOpacity(0.14f);
 
   // Disable animation so that the hover indicator shows up immediately to help
   // avoid mis-clicks.
   SetAnimationDuration(base::TimeDelta());
-  GetInkDrop()->SetHoverHighlightFadeDuration(base::TimeDelta());
+  ink_drop()->GetInkDrop()->SetHoverHighlightFadeDuration(base::TimeDelta());
 
   // The ink drop highlight path is the same as the focus ring highlight path,
   // but needs to be explicitly mirrored for RTL.
@@ -98,7 +98,7 @@
   if (colors == colors_)
     return;
   colors_ = std::move(colors);
-  SetInkDropBaseColor(
+  ink_drop()->SetBaseColor(
       color_utils::GetColorWithMaxContrast(colors_.background_color));
   OnPropertyChanged(&colors_, views::kPropertyEffectsPaint);
 }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
index 096ad87..f7121159 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_account_icon_container_view.cc
@@ -40,6 +40,7 @@
       PageActionIconType::kManagePasswords,
       PageActionIconType::kLocalCardMigration,
       PageActionIconType::kSaveCard,
+      PageActionIconType::kVirtualCardManualFallback,
   };
   if (base::FeatureList::IsEnabled(
           autofill::features::kAutofillAddressProfileSavePrompt)) {
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
index 2d2bc57..c0f00a3 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view.cc
@@ -182,7 +182,7 @@
       // TODO(bruthig): The ACTION_PENDING triggering logic should be in
       // MenuButton::OnPressed() however there is a bug with the pressed state
       // logic in MenuButton. See http://crbug.com/567252.
-      AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event);
+      ink_drop()->AnimateToState(views::InkDropState::ACTION_PENDING, &event);
     }
   }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index 60046a5..20a620db 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -165,18 +165,19 @@
   if (base::FeatureList::IsEnabled(views::kInstallableInkDropFeature)) {
     installable_ink_drop_ = std::make_unique<views::InstallableInkDrop>(this);
     installable_ink_drop_->SetConfig(GetToolbarInstallableInkDropConfig(this));
-    SetCreateInkDropCallback(base::BindRepeating(
+    ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
         [](InkDropHostView* host) -> std::unique_ptr<views::InkDrop> {
           // Ensure this doesn't get called when InstallableInkDrops are
           // enabled.
           DCHECK(
               !base::FeatureList::IsEnabled(views::kInstallableInkDropFeature));
-          return views::InkDrop::CreateInkDropForFloodFillRipple(host);
+          return views::InkDrop::CreateInkDropForFloodFillRipple(
+              host->ink_drop());
         },
         this));
   }
 
-  SetCreateInkDropMaskCallback(base::BindRepeating(
+  ink_drop()->SetCreateMaskCallback(base::BindRepeating(
       [](ToolbarButton* host) -> std::unique_ptr<views::InkDropMask> {
         if (host->has_in_product_help_promo_) {
           // This gets the latest ink drop insets. |SetTrailingMargin()| is
@@ -195,7 +196,7 @@
                                                         GetHighlightPath(host));
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](ToolbarButton* host) {
         // Ensure this doesn't get called when InstallableInkDrops are enabled.
         DCHECK(
@@ -620,13 +621,13 @@
 
   has_in_product_help_promo_ = has_in_product_help_promo;
 
-  // We call SetInkDropBaseColorCallback() and SetCreateInkDropMaskCallback(),
+  // We call SetBaseColorCallback() and SetCreateMaskCallback(),
   // returning the promo values if we are showing an in-product help promo.
   // Calling HostSizeChanged() will force the new mask and color to be fetched.
   //
   // TODO(collinbaker): Consider adding explicit way to recreate mask instead
   // of relying on HostSizeChanged() to do so.
-  GetInkDrop()->HostSizeChanged(size());
+  ink_drop()->GetInkDrop()->HostSizeChanged(size());
 
   views::InkDropState next_state;
   if (has_in_product_help_promo_ || GetVisible()) {
@@ -642,7 +643,7 @@
     // else should keep this ACTIVATED or in some other state. Consider adding
     // code to track the correct state and restore to that.
   }
-  GetInkDrop()->AnimateToState(next_state);
+  ink_drop()->GetInkDrop()->AnimateToState(next_state);
 
   UpdateIcon();
   SchedulePaint();
@@ -722,7 +723,8 @@
 
   menu_showing_ = true;
 
-  AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr /* event */);
+  ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                             nullptr /* event */);
 
   // Exit if the model is null. Although ToolbarButton::ShouldShowMenu()
   // performs the same check, its overrides may not.
@@ -744,13 +746,14 @@
 }
 
 void ToolbarButton::OnMenuClosed() {
-  AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr /* event */);
+  ink_drop()->AnimateToState(views::InkDropState::DEACTIVATED,
+                             nullptr /* event */);
 
   menu_showing_ = false;
 
   // Set the state back to normal after the drop down menu is closed.
   if (GetState() != STATE_DISABLED) {
-    GetInkDrop()->SetHovered(IsMouseHovered());
+    ink_drop()->GetInkDrop()->SetHovered(IsMouseHovered());
     SetState(STATE_NORMAL);
   }
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.h b/chrome/browser/ui/views/toolbar/toolbar_button.h
index 6900af0b..55846dc 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.h
@@ -303,8 +303,8 @@
   // |views::kInstallableInkDropFeature| is enabled.
   // TODO(crbug.com/931964): When InkDrops can be externally installed, connect
   // this InkDrop when the experiment is enabled. This is currently not working
-  // as a virtual GetInkDrop() override was removed to finish InkDropHostView
-  // migration from the View hierarchy.
+  // as a virtual GetInkDrop() override was removed to finish
+  // InkDropHostView migration from the View hierarchy.
   std::unique_ptr<views::InstallableInkDrop> installable_ink_drop_;
 
   // Class responsible for animating highlight color (calling a callback on
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
index ef44b4c..338267b 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 
+#include "base/bind.h"
 #include "base/containers/contains.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/themes/theme_properties.h"
@@ -141,7 +142,7 @@
   // We don't care about the main button being highlighted.
   if (button != main_button_) {
     subscriptions_.push_back(
-        button->AddHighlightedChangedCallback(base::BindRepeating(
+        button->ink_drop()->AddHighlightedChangedCallback(base::BindRepeating(
             &ToolbarIconContainerView::OnButtonHighlightedChanged,
             base::Unretained(this), base::Unretained(button))));
   }
@@ -274,7 +275,7 @@
 
 void ToolbarIconContainerView::OnButtonHighlightedChanged(
     views::Button* button) {
-  if (button->GetHighlighted())
+  if (button->ink_drop()->GetHighlighted())
     highlighted_buttons_.insert(button);
   else
     highlighted_buttons_.erase(button);
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
index d7077c6..376448e 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
 
+#include "base/bind.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/layout_constants.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -82,9 +83,9 @@
   host->SetHasInkDropActionOnClick(true);
   views::HighlightPathGenerator::Install(
       host, std::make_unique<ToolbarButtonHighlightPathGenerator>());
-  host->SetInkDropMode(views::InkDropHostView::InkDropMode::ON);
-  host->SetInkDropVisibleOpacity(kToolbarInkDropVisibleOpacity);
-  host->SetInkDropHighlightOpacity(kToolbarInkDropHighlightVisibleOpacity);
-  host->SetInkDropBaseColorCallback(
+  host->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  host->ink_drop()->SetVisibleOpacity(kToolbarInkDropVisibleOpacity);
+  host->ink_drop()->SetHighlightOpacity(kToolbarInkDropHighlightVisibleOpacity);
+  host->ink_drop()->SetBaseColorCallback(
       base::BindRepeating(&GetToolbarInkDropBaseColor, host));
 }
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
index 4a80b44..074ccaa 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -238,7 +238,7 @@
       toolbar_button_provider->GetAppMenuButton();
 
   const SkColor original_ink_drop_color =
-      app_menu_button->GetInkDropBaseColor();
+      app_menu_button->ink_drop()->GetBaseColor();
 
   {
     content::ThemeChangeWaiter theme_change_waiter(web_contents);
@@ -247,7 +247,8 @@
                                 "setAttribute('content', '#246')"));
     theme_change_waiter.Wait();
 
-    EXPECT_NE(app_menu_button->GetInkDropBaseColor(), original_ink_drop_color);
+    EXPECT_NE(app_menu_button->ink_drop()->GetBaseColor(),
+              original_ink_drop_color);
   }
 
   {
@@ -256,7 +257,8 @@
         web_contents, "document.getElementById('theme-color').remove()"));
     theme_change_waiter.Wait();
 
-    EXPECT_EQ(app_menu_button->GetInkDropBaseColor(), original_ink_drop_color);
+    EXPECT_EQ(app_menu_button->ink_drop()->GetBaseColor(),
+              original_ink_drop_color);
   }
 #endif
 }
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc
index b11fa18..86f2d48 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.cc
@@ -35,8 +35,8 @@
       browser_view_(browser_view) {
   views::SetHitTestComponent(this, static_cast<int>(HTMENU));
 
-  SetInkDropMode(InkDropMode::ON);
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](WebAppMenuButton* host) { return host->GetColor(); }, this));
 
   SetFocusBehavior(FocusBehavior::ALWAYS);
@@ -75,10 +75,10 @@
 }
 
 void WebAppMenuButton::StartHighlightAnimation() {
-  GetInkDrop()->SetHoverHighlightFadeDuration(
+  ink_drop()->GetInkDrop()->SetHoverHighlightFadeDuration(
       WebAppToolbarButtonContainer::kOriginFadeInDuration);
-  GetInkDrop()->SetHovered(true);
-  GetInkDrop()->UseDefaultHoverHighlightFadeDuration();
+  ink_drop()->GetInkDrop()->SetHovered(true);
+  ink_drop()->GetInkDrop()->UseDefaultHoverHighlightFadeDuration();
 
   highlight_off_timer_.Start(
       FROM_HERE,
@@ -101,10 +101,10 @@
 
 void WebAppMenuButton::FadeHighlightOff() {
   if (!ShouldEnterHoveredState()) {
-    GetInkDrop()->SetHoverHighlightFadeDuration(
+    ink_drop()->GetInkDrop()->SetHoverHighlightFadeDuration(
         WebAppToolbarButtonContainer::kOriginFadeOutDuration);
-    GetInkDrop()->SetHovered(false);
-    GetInkDrop()->UseDefaultHoverHighlightFadeDuration();
+    ink_drop()->GetInkDrop()->SetHovered(false);
+    ink_drop()->GetInkDrop()->UseDefaultHoverHighlightFadeDuration();
   }
 }
 
diff --git a/chrome/browser/ui/views/web_apps/web_app_hover_button.cc b/chrome/browser/ui/views/web_apps/web_app_hover_button.cc
index c6c5b5e..c0024baf 100644
--- a/chrome/browser/ui/views/web_apps/web_app_hover_button.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_hover_button.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/memory/weak_ptr.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
@@ -65,13 +66,13 @@
 inline WebAppHoverButton::~WebAppHoverButton() = default;
 
 void WebAppHoverButton::MarkAsUnselected(const ui::Event* event) {
-  AnimateInkDrop(views::InkDropState::HIDDEN,
-                 ui::LocatedEvent::FromIfValid(event));
+  ink_drop()->AnimateToState(views::InkDropState::HIDDEN,
+                             ui::LocatedEvent::FromIfValid(event));
 }
 
 void WebAppHoverButton::MarkAsSelected(const ui::Event* event) {
-  AnimateInkDrop(views::InkDropState::ACTIVATED,
-                 ui::LocatedEvent::FromIfValid(event));
+  ink_drop()->AnimateToState(views::InkDropState::ACTIVATED,
+                             ui::LocatedEvent::FromIfValid(event));
 }
 
 void WebAppHoverButton::OnIconsRead(
diff --git a/chrome/browser/ui/views/webauthn/webauthn_bubble_view.cc b/chrome/browser/ui/views/webauthn/webauthn_bubble_view.cc
index b0c00de..0af0304 100644
--- a/chrome/browser/ui/views/webauthn/webauthn_bubble_view.cc
+++ b/chrome/browser/ui/views/webauthn/webauthn_bubble_view.cc
@@ -65,8 +65,8 @@
 
 std::u16string WebAuthnBubbleView::GetWindowTitle() const {
   // TODO(crbug.com/1179014): go through ux review and i18n this string.
-  return base::UTF8ToUTF16(users_.empty() ? "Sign in with your security key"
-                                          : "Choose an account to sign in");
+  return users_.empty() ? u"Sign in with your security key"
+                        : u"Choose an account to sign in";
 }
 
 void WebAuthnBubbleView::Init() {
@@ -75,8 +75,7 @@
   if (users_.empty()) {
     // TODO(crbug.com/1179014): go through ux review and i18n this string.
     std::u16string label_text = base::ReplaceStringPlaceholders(
-        base::UTF8ToUTF16(
-            "To sign in to $1 with your security key, insert it and tap it"),
+        u"To sign in to $1 with your security key, insert it and tap it",
         webauthn_ui_helpers::RpIdToElidedHost(relying_party_id_, fixed_width()),
         /*offset=*/nullptr);
     auto label = std::make_unique<views::Label>(
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 278a116..4bd0581 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -85,6 +85,7 @@
 namespace {
 
 constexpr const char kExampleURL[] = "http://example.org/";
+constexpr const char16_t kExampleURL16[] = u"http://example.org/";
 constexpr const char kExampleManifestURL[] = "http://example.org/manifest";
 
 constexpr char kLaunchWebAppDisplayModeHistogram[] = "Launch.WebAppDisplayMode";
@@ -672,7 +673,7 @@
   std::u16string result;
   clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr,
                       &result);
-  EXPECT_EQ(result, base::UTF8ToUTF16(kExampleURL));
+  EXPECT_EQ(result, kExampleURL16);
 }
 
 // Tests that the command for popping a tab out to a PWA window is disabled in
@@ -985,8 +986,7 @@
   EXPECT_TRUE(app_menu_model->GetModelAndIndexForCommandId(IDC_INSTALL_PWA,
                                                            &model, &index));
   EXPECT_EQ(app_menu_model.get(), model);
-  EXPECT_EQ(model->GetLabelAt(index),
-            base::UTF8ToUTF16("Install Manifest test app\xE2\x80\xA6"));
+  EXPECT_EQ(model->GetLabelAt(index), u"Install Manifest test app…");
 }
 
 // Check that no assertions are hit when showing a permission request bubble.
diff --git a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
index 9f0fe13..73faa01 100644
--- a/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_file_handling_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/values.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_proxy.h"
@@ -25,10 +26,16 @@
 #include "chrome/browser/web_applications/components/web_app_prefs_utils.h"
 #include "chrome/browser/web_applications/components/web_app_provider_base.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
+#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app.h"
+#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/embedder_support/switches.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
@@ -663,6 +670,65 @@
             content::EvalJs(web_content, "window.launchParams.files[0].name"));
 }
 
+class WebAppFileHandlingPolicyBrowserTest
+    : public WebAppFileHandlingBrowserTest {
+ public:
+  // Set the file handling policy to BLOCK the app between the PRE test and the
+  // actual test.
+  void SetUpInProcessBrowserTestFixture() override {
+    if (GetTestPreCount() == 0) {
+      SetFileHandlingBlockPolicy();
+    }
+  }
+
+ private:
+  void SetFileHandlingBlockPolicy() {
+    ON_CALL(provider_, IsInitializationComplete(testing::_))
+        .WillByDefault(testing::Return(true));
+    ON_CALL(provider_, IsFirstPolicyLoadComplete(testing::_))
+        .WillByDefault(testing::Return(true));
+
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_);
+
+    policy::PolicyMap values;
+    base::Value list(base::Value::Type::LIST);
+    list.Append(base::Value("https://app.com"));
+    policy::PolicyMap::Entry entry_list(
+        policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_MACHINE,
+        policy::POLICY_SOURCE_CLOUD, std::move(list), nullptr);
+
+    values.Set(policy::key::kFileHandlingBlockedForUrls, std::move(entry_list));
+    provider_.UpdateChromePolicy(values);
+  }
+  testing::NiceMock<policy::MockConfigurationPolicyProvider> provider_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppFileHandlingPolicyBrowserTest,
+                       PRE_PolicySettingsBlockedUrl) {
+  InstallFileHandlingPWA();
+  EXPECT_EQ(registrar().GetAppIds().size(), 1u);
+  EXPECT_FALSE(registrar()
+                   .AsWebAppRegistrar()
+                   ->GetAppById(app_id())
+                   ->file_handler_permission_blocked());
+}
+
+// Test that the app's `file_handler_permission_blocked` state should be updated
+// on WebAppProvider system setup based on current permission settings.
+IN_PROC_BROWSER_TEST_F(WebAppFileHandlingPolicyBrowserTest,
+                       PolicySettingsBlockedUrl) {
+  auto* provider = web_app::WebAppProvider::Get(profile());
+  DCHECK(provider);
+  web_app::test::WaitUntilReady(provider);
+
+  std::vector<web_app::AppId> app_ids = registrar().GetAppIds();
+  EXPECT_EQ(app_ids.size(), 1u);
+  EXPECT_TRUE(registrar()
+                  .AsWebAppRegistrar()
+                  ->GetAppById(app_ids[0])
+                  ->file_handler_permission_blocked());
+}
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 
 // End-to-end test to ensure the file handler is registered on ChromeOS when the
diff --git a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
index 9cfbc6d7..0cb8a562f 100644
--- a/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/emulator/device_emulator_message_handler.cc
@@ -407,7 +407,7 @@
   // Try to find the previously selected source in the list.
   const power_manager::PowerSupplyProperties_PowerSource* selected_source =
       nullptr;
-  for (const auto& val : *sources) {
+  for (const auto& val : sources->GetList()) {
     const base::DictionaryValue* dict;
     CHECK(val.GetAsDictionary(&dict));
     power_manager::PowerSupplyProperties_PowerSource* source =
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom b/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom
index bbca5549d..2861192 100644
--- a/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom
+++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom
@@ -7,6 +7,10 @@
 // Provides access to browser-side user education data (including IPH) for
 // chrome://internals/user-education
 interface UserEducationInternalsPageHandler {
-  // TODO(crbug.com/1194751): add methods for getting list of tutorials
-  // and starting a tutorial.
+  // Get the list of all available tutorials. Only needs to be called once
+  // since the browser-side list is static and does not change.
+  GetTutorials() => (array<string> tutorial_ids);
+
+  // Start a tutorial listed in the `GetTutorials` result.
+  StartTutorial(string tutorial_id);
 };
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
index aaf5a2a..2e6fdf7 100644
--- a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
+++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.cc
@@ -4,15 +4,36 @@
 
 #include "chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h"
 
+#include "chrome/browser/ui/user_education/feature_tutorial_service.h"
+#include "chrome/browser/ui/user_education/feature_tutorial_service_factory.h"
+#include "chrome/browser/ui/user_education/feature_tutorials.h"
 #include "chrome/grit/dev_ui_browser_resources.h"
 #include "ui/base/webui/resource_path.h"
 
 UserEducationInternalsPageHandlerImpl::UserEducationInternalsPageHandlerImpl(
     Profile* profile)
-/* : profile_(profile) */ {
-  // TODO(crbug.com/1194751): get reference to tutorial registry (once
-  // implemented).
+    : tutorial_service_(FeatureTutorialServiceFactory::GetForProfile(profile)) {
 }
 
 UserEducationInternalsPageHandlerImpl::
     ~UserEducationInternalsPageHandlerImpl() = default;
+
+void UserEducationInternalsPageHandlerImpl::GetTutorials(
+    GetTutorialsCallback callback) {
+  std::vector<base::StringPiece> id_pieces = GetAllFeatureTutorialStringIds();
+
+  std::vector<std::string> ids;
+  for (base::StringPiece piece : id_pieces)
+    ids.emplace_back(piece.data(), piece.size());
+
+  std::move(callback).Run(std::move(ids));
+}
+
+void UserEducationInternalsPageHandlerImpl::StartTutorial(
+    const std::string& tutorial_id) {
+  base::Optional<FeatureTutorial> tutorial =
+      GetFeatureTutorialFromStringId(tutorial_id);
+  if (!tutorial)
+    return;
+  tutorial_service_->StartTutorial(*tutorial);
+}
diff --git a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h
index 25740ec..dae079d9 100644
--- a/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h
+++ b/chrome/browser/ui/webui/internals/user_education/user_education_internals_page_handler_impl.h
@@ -9,6 +9,8 @@
 #include "chrome/browser/ui/webui/internals/user_education/user_education_internals.mojom.h"
 #include "content/public/browser/web_ui_data_source.h"
 
+class FeatureTutorialService;
+
 class UserEducationInternalsPageHandlerImpl
     : public mojom::user_education_internals::
           UserEducationInternalsPageHandler {
@@ -21,8 +23,12 @@
   UserEducationInternalsPageHandlerImpl& operator=(
       const UserEducationInternalsPageHandlerImpl&) = delete;
 
+  // mojom::user_education_internals::UserEducationInternalsPageHandler:
+  void GetTutorials(GetTutorialsCallback callback) override;
+  void StartTutorial(const std::string& tutorial_id) override;
+
  private:
-  // Profile* profile_ = nullptr;
+  FeatureTutorialService* const tutorial_service_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_INTERNALS_USER_EDUCATION_USER_EDUCATION_INTERNALS_PAGE_HANDLER_IMPL_H_
diff --git a/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc b/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc
index 20bb8cd9..f5d47e1 100644
--- a/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc
+++ b/chrome/browser/ui/webui/media_router/cast_feedback_ui.cc
@@ -92,14 +92,12 @@
       "formDescription",
       l10n_util::GetStringFUTF8(
           IDS_MEDIA_ROUTER_FEEDBACK_FORM_DESCRIPTION,
-          base::UTF8ToUTF16("https://support.google.com/"
-                            "chromecast?p=troubleshoot_chromecast")));
+          u"https://support.google.com/chromecast?p=troubleshoot_chromecast"));
   source->AddString(
       "setupVisibilityQuestion",
       l10n_util::GetStringFUTF8(
           IDS_MEDIA_ROUTER_FEEDBACK_SETUP_VISIBILITY_QUESTION,
-          base::UTF8ToUTF16(
-              "https://support.google.com/chromecast?p=set_up_chromecast")));
+          u"https://support.google.com/chromecast?p=set_up_chromecast"));
 
   // TODO(jrw): Attach real log data.
   source->AddString("logData", "dummy log data");
diff --git a/chrome/browser/ui/webui/memories/memories.mojom b/chrome/browser/ui/webui/memories/memories.mojom
index 8c53b922..c0b7fb3 100644
--- a/chrome/browser/ui/webui/memories/memories.mojom
+++ b/chrome/browser/ui/webui/memories/memories.mojom
@@ -19,7 +19,7 @@
   url.mojom.Url? thumbnail_url;
   // The Memories in the result set in reverse chronological order.
   array<history_clusters.mojom.Memory> memories;
-  // True if this is in response to a continuation query. If so, |memories| is a
+  // True if this is in response to a continuation query. If so, `memories` is a
   // partial set which must be appended to the existing Memories on the page and
   // not replace them.
   bool is_continuation;
@@ -34,27 +34,27 @@
   SetPage(pending_remote<Page> page);
 
   // Requests the freshest Memories created from the user visit history, in
-  // reverse chronological order, based on the parameters in |query_params|.
+  // reverse chronological order, based on the parameters in `query_params`.
   // Note: Returns mock Memories in non-chrome-branded builds if no remote
   // endpoint is specified.
   QueryMemories(QueryParams query_params);
 
   // Requests to remove all visits to the specified URLs in the specified
-  // timespan in |visits|. This includes the less recent visits to the same set
-  // of URLs whose information is preserved in |visits|. The returned Promise
+  // timespan in `visits`. This includes the less recent visits to the same set
+  // of URLs whose information is preserved in `visits`. The returned Promise
   // indicates if the request was accepted by the browser.
   RemoveVisits(array<Visit> visits) => (bool accepted);
 };
 
 // WebUI-side handler for requests from the browser.
 interface Page {
-  // Called with the results of the last call to |QueryMemories()|. |result|
+  // Called with the results of the last call to `QueryMemories()`. `result`
   // contains the freshest Memories in reverse chronological order, along with
   // continuation query params meant to be used in the follow-up request to load
   // older Memories.
   OnMemoriesQueryResult(MemoriesResult result);
 
   // Called with the set of removed visits when the last accepted call to
-  // |RemoveVisits()| succeeds. |removed_visits| will be used to update the UI.
+  // `RemoveVisits()` succeeds. `removed_visits` will be used to update the UI.
   OnVisitsRemoved(array<Visit> removed_visits);
 };
diff --git a/chrome/browser/ui/webui/memories/memories_handler.cc b/chrome/browser/ui/webui/memories/memories_handler.cc
index 4fc0f66..af203d8b 100644
--- a/chrome/browser/ui/webui/memories/memories_handler.cc
+++ b/chrome/browser/ui/webui/memories/memories_handler.cc
@@ -91,7 +91,7 @@
   auto result_callback =
       base::BindOnce(&MemoriesHandler::OnMemoriesQueryResult,
                      weak_ptr_factory_.GetWeakPtr(), std::move(result_mojom));
-  if (history_clusters::RemoteModelEndpointForDebugging().is_valid()) {
+  if (history_clusters::RemoteModelEndpoint().is_valid()) {
     // Cancel pending queries, if any.
     query_task_tracker_.TryCancelAll();
     auto* memory_service =
@@ -288,7 +288,7 @@
       std::function<void(std::vector<history_clusters::mojom::VisitPtr>&, bool)>
           add_visit;
       add_visit = [&visit, &add_visit](auto& visits, bool are_top_visits) {
-        // Count |visit| toward duplicate visits if the same URL is seen before.
+        // Count `visit` toward duplicate visits if the same URL is seen before.
         auto duplicate_visit_it = std::find_if(
             visits.begin(), visits.end(), [&visit](const auto& visit_ptr) {
               return visit_ptr->url == visit->url;
@@ -298,7 +298,7 @@
           (*duplicate_visit_it)->first_visit_time = visit->time;
           return;
         }
-        // For the top visits, if the domain name is seen before, add |visit| to
+        // For the top visits, if the domain name is seen before, add `visit` to
         // the related visits of the respective top visit recursively.
         if (are_top_visits) {
           auto related_visit_it = std::find_if(
@@ -310,7 +310,7 @@
             return;
           }
         }
-        // Otherwise, simply add |visit| to the list of visits.
+        // Otherwise, simply add `visit` to the list of visits.
         visits.push_back(std::move(visit));
       };
       add_visit(memory_mojom->top_visits, true);
@@ -376,7 +376,7 @@
   }
 
   // Continue to extract Memories. Set the recency threshold to 11:59:59pm of
-  // the day before the Memory's |last_visit_time|.
+  // the day before the Memory's `last_visit_time`.
   query_params->recency_threshold =
       memory_mojom->last_visit_time.LocalMidnight() -
       base::TimeDelta::FromSeconds(1);
diff --git a/chrome/browser/ui/webui/memories/memories_handler.h b/chrome/browser/ui/webui/memories/memories_handler.h
index 240b92aa..cfd1501e 100644
--- a/chrome/browser/ui/webui/memories/memories_handler.h
+++ b/chrome/browser/ui/webui/memories/memories_handler.h
@@ -55,16 +55,16 @@
   void OnMemoriesDebugMessage(const std::string& message) override;
 
  private:
-  // Called with |memory_mojoms| and |continuation_query_params| when the
+  // Called with `memory_mojoms` and `continuation_query_params` when the
   // results of querying the MemoriesService are available. The latter is
   // created in anticipation of a continuation query. Subsequently, the bound
-  // partially constructed |result_mojom| parameter is supplied with
-  // |memory_mojoms| and |continuation_query_params| and sent to the JS.
+  // partially constructed `result_mojom` parameter is supplied with
+  // `memory_mojoms` and `continuation_query_params` and sent to the JS.
   void OnMemoriesQueryResult(
       history_clusters::mojom::MemoriesResultPtr result_mojom,
       history_clusters::mojom::QueryParamsPtr continuation_query_params,
       std::vector<history_clusters::mojom::MemoryPtr> memory_mojoms);
-  // Called with the set of removed visits. Subsequently, |visits| is sent to
+  // Called with the set of removed visits. Subsequently, `visits` is sent to
   // the JS to update the UI.
   void OnVisitsRemoved(std::vector<history_clusters::mojom::VisitPtr> visits);
 
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc
index 665bde8..d5a7a3b 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos_unittest.cc
@@ -19,7 +19,6 @@
 #include "base/values.h"
 #include "chrome/browser/chromeos/printing/test_cups_printers_manager.h"
 #include "chrome/browser/chromeos/printing/test_printer_configurer.h"
-#include "chrome/browser/printing/print_backend_service.h"
 #include "chrome/browser/printing/print_backend_service_test_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
index 254a93a..4503dd3 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
@@ -16,7 +16,7 @@
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/printing/print_backend_service.h"
+#include "chrome/browser/printing/print_backend_service_manager.h"
 #include "chrome/browser/ui/webui/print_preview/print_preview_utils.h"
 #include "chrome/common/printing/printer_capabilities.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -191,10 +191,11 @@
 
   if (base::FeatureList::IsEnabled(features::kEnableOopPrintDrivers)) {
     VLOG(1) << "Getting default printer via service";
-    GetPrintBackendService(g_browser_process->GetApplicationLocale(),
-                           /*printer_name=*/std::string())
-        ->GetDefaultPrinterName(
-            base::BindOnce(&OnDidGetDefaultPrinterName, std::move(cb)));
+    auto& service = PrintBackendServiceManager::GetInstance().GetService(
+        g_browser_process->GetApplicationLocale(),
+        /*printer_name=*/std::string());
+    service->GetDefaultPrinterName(
+        base::BindOnce(&OnDidGetDefaultPrinterName, std::move(cb)));
   } else {
     VLOG(1) << "Getting default printer in-process";
     base::PostTaskAndReplyWithResult(
@@ -212,11 +213,12 @@
 
   if (base::FeatureList::IsEnabled(features::kEnableOopPrintDrivers)) {
     VLOG(1) << "Enumerate printers start via service";
-    GetPrintBackendService(g_browser_process->GetApplicationLocale(),
-                           /*printer_name=*/std::string())
-        ->EnumeratePrinters(base::BindOnce(&OnDidEnumeratePrinters,
-                                           std::move(callback),
-                                           std::move(done_callback)));
+    auto& service = PrintBackendServiceManager::GetInstance().GetService(
+        g_browser_process->GetApplicationLocale(),
+        /*printer_name=*/std::string());
+    service->EnumeratePrinters(base::BindOnce(&OnDidEnumeratePrinters,
+                                              std::move(callback),
+                                              std::move(done_callback)));
   } else {
     VLOG(1) << "Enumerate printers start in-process";
     base::PostTaskAndReplyWithResult(
@@ -235,12 +237,12 @@
 
   if (base::FeatureList::IsEnabled(features::kEnableOopPrintDrivers)) {
     VLOG(1) << "Getting printer capabilities via service for " << device_name;
-    GetPrintBackendService(g_browser_process->GetApplicationLocale(),
-                           device_name)
-        ->FetchCapabilities(
-            device_name,
-            base::BindOnce(&OnDidFetchCapabilities, device_name,
-                           /*has_secure_protocol=*/false, std::move(cb)));
+    auto& service = PrintBackendServiceManager::GetInstance().GetService(
+        g_browser_process->GetApplicationLocale(), device_name);
+    service->FetchCapabilities(
+        device_name,
+        base::BindOnce(&OnDidFetchCapabilities, device_name,
+                       /*has_secure_protocol=*/false, std::move(cb)));
   } else {
     VLOG(1) << "Getting printer capabilities in-process for " << device_name;
     base::PostTaskAndReplyWithResult(
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
index 3f457064..e6afbe5 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default_unittest.cc
@@ -13,7 +13,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_piece.h"
 #include "base/values.h"
-#include "chrome/browser/printing/print_backend_service.h"
 #include "chrome/browser/printing/print_backend_service_test_impl.h"
 #include "chrome/common/printing/printer_capabilities.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index c8307a0e5..c7981a5 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -91,10 +91,9 @@
 namespace {
 
 #if defined(OS_MAC)
-// U+0028 U+21E7 U+2318 U+0050 U+0029 in UTF8
-const char kBasicPrintShortcut[] = "\x28\xE2\x8c\xA5\xE2\x8C\x98\x50\x29";
+const char16_t kBasicPrintShortcut[] = u"\u0028\u21e7\u2318\u0050\u0029";
 #elif !BUILDFLAG(IS_CHROMEOS_ASH)
-const char kBasicPrintShortcut[] = "(Ctrl+Shift+P)";
+const char16_t kBasicPrintShortcut[] = u"(Ctrl+Shift+P)";
 #endif
 
 constexpr char kInvalidArgsForDidStartPreview[] =
@@ -380,7 +379,7 @@
                     chrome::kCloudPrintCertificateErrorLearnMoreURL);
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  const std::u16string shortcut_text(base::UTF8ToUTF16(kBasicPrintShortcut));
+  const std::u16string shortcut_text(kBasicPrintShortcut);
   source->AddString("systemDialogOption",
                     l10n_util::GetStringFUTF16(
                         IDS_PRINT_PREVIEW_SYSTEM_DIALOG_OPTION, shortcut_text));
diff --git a/chrome/browser/ui/webui/settings/chromeos/reset_section.cc b/chrome/browser/ui/webui/settings/chromeos/reset_section.cc
index 6800b720..3f14606 100644
--- a/chrome/browser/ui/webui/settings/chromeos/reset_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/reset_section.cc
@@ -69,6 +69,16 @@
       {"powerwashLearnMoreUrl", IDS_FACTORY_RESET_HELP_URL},
       {"powerwashButtonRoleDescription",
        IDS_SETTINGS_FACTORY_RESET_BUTTON_ROLE},
+      {"powerwashDialogESimWarningTitle",
+       IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_TITLE},
+      {"powerwashDialogESimWarning", IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING},
+      {"powerwashDialogESimListTitle",
+       IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_TITLE},
+      {"powerwashDialogESimListItemTitle",
+       IDS_SETTINGS_FACTORY_RESET_ESIM_LIST_ITEM_TITLE},
+      {"powerwashDialogESimWarningCheckbox",
+       IDS_SETTINGS_FACTORY_RESET_ESIM_WARNING_CHECKBOX_LABEL},
+      {"powerwashContinue", IDS_SETTINGS_FACTORY_CONTINUE_BUTTON_LABEL},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h
index de60fb2..6f9d5ff 100644
--- a/chrome/browser/web_applications/components/app_registrar.h
+++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -108,6 +108,7 @@
       const AppId& app_id) const = 0;
   virtual const apps::FileHandlers* GetAppFileHandlers(
       const AppId& app_id) const = 0;
+  virtual bool IsAppFileHandlerPermissionBlocked(const AppId& app_id) const = 0;
 
   // Returns the start_url with launch_query_params appended to the end if any.
   GURL GetAppLaunchUrl(const AppId& app_id) const;
diff --git a/chrome/browser/web_applications/components/file_handler_manager.cc b/chrome/browser/web_applications/components/file_handler_manager.cc
index e331d3f..dce5809 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.cc
+++ b/chrome/browser/web_applications/components/file_handler_manager.cc
@@ -103,7 +103,7 @@
 void FileHandlerManager::DisableAndUnregisterOsFileHandlers(
     const AppId& app_id,
     std::unique_ptr<ShortcutInfo> info,
-    base::OnceCallback<void()> callback) {
+    base::OnceCallback<void(bool)> callback) {
   // Updating prefs must be done on the UI Thread.
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   UpdateBoolWebAppPref(profile()->GetPrefs(), app_id, kFileHandlersEnabled,
@@ -116,6 +116,9 @@
 
   if (!ShouldRegisterFileHandlersWithOs() || !file_handlers ||
       file_handlers->empty() || disable_os_integration_for_testing_) {
+    // This bool signals if there was not an error. Exiting early here is WAI,
+    // so this is a success.
+    std::move(callback).Run(true);
     return;
   }
 
@@ -173,7 +176,8 @@
 
 const apps::FileHandlers* FileHandlerManager::GetEnabledFileHandlers(
     const AppId& app_id) {
-  if (AreFileHandlersEnabled(app_id) && IsFileHandlingAPIAvailable(app_id))
+  if (AreFileHandlersEnabled(app_id) && IsFileHandlingAPIAvailable(app_id) &&
+      !registrar_->IsAppFileHandlerPermissionBlocked(app_id))
     return GetAllFileHandlers(app_id);
 
   return nullptr;
diff --git a/chrome/browser/web_applications/components/file_handler_manager.h b/chrome/browser/web_applications/components/file_handler_manager.h
index d92125b..745a165f 100644
--- a/chrome/browser/web_applications/components/file_handler_manager.h
+++ b/chrome/browser/web_applications/components/file_handler_manager.h
@@ -69,9 +69,10 @@
   // Disables file handlers for all OSs and unregisters OS specific file
   // handlers for OSs that need them. On Chrome OS file handlers are registered
   // separately but they are still enabled and disabled here.
-  void DisableAndUnregisterOsFileHandlers(const AppId& app_id,
-                                          std::unique_ptr<ShortcutInfo> info,
-                                          base::OnceCallback<void()> callback);
+  void DisableAndUnregisterOsFileHandlers(
+      const AppId& app_id,
+      std::unique_ptr<ShortcutInfo> info,
+      base::OnceCallback<void(bool)> callback);
 
   // Updates the file handling origin trial expiry timer based on a currently
   // open instance of the site. This will not update the expiry timer if
diff --git a/chrome/browser/web_applications/components/file_handler_manager_unittest.cc b/chrome/browser/web_applications/components/file_handler_manager_unittest.cc
index e47ec63..618697b0 100644
--- a/chrome/browser/web_applications/components/file_handler_manager_unittest.cc
+++ b/chrome/browser/web_applications/components/file_handler_manager_unittest.cc
@@ -164,8 +164,8 @@
   }
 
   // Ensure they can be disabled.
-  file_handler_manager().DisableAndUnregisterOsFileHandlers(app_id, nullptr,
-                                                            base::DoNothing());
+  file_handler_manager().DisableAndUnregisterOsFileHandlers(
+      app_id, nullptr, base::DoNothing::Once<bool>());
 
   {
     const auto* handlers =
diff --git a/chrome/browser/web_applications/components/os_integration_manager.cc b/chrome/browser/web_applications/components/os_integration_manager.cc
index 8079334..cda0d82 100644
--- a/chrome/browser/web_applications/components/os_integration_manager.cc
+++ b/chrome/browser/web_applications/components/os_integration_manager.cc
@@ -10,9 +10,11 @@
 #include "base/auto_reset.h"
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/callback_forward.h"
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
@@ -210,11 +212,13 @@
           barrier->CreateBarrierCallbackForType(OsHookType::kShortcuts));
     }
   }
-  // TODO(https://crbug.com/1108109) we should return the result of file handler
   // unregistration and record errors during unregistration.
   // TODO(crbug.com/1076688): Retrieve shortcuts before they're unregistered.
-  if (os_hooks[OsHookType::kFileHandlers])
-    UnregisterFileHandlers(app_id, nullptr, base::DoNothing());
+  if (os_hooks[OsHookType::kFileHandlers]) {
+    UnregisterFileHandlers(
+        app_id, nullptr,
+        barrier->CreateBarrierCallbackForType(OsHookType::kFileHandlers));
+  }
 
   // TODO(https://crbug.com/1108109) we should return the result of protocol
   // handler unregistration and record errors during unregistration.
@@ -234,13 +238,13 @@
     const AppId& app_id,
     base::StringPiece old_name,
     std::unique_ptr<ShortcutInfo> old_shortcut,
-    bool file_handlers_need_os_update,
+    FileHandlerUpdateAction file_handlers_need_os_update,
     const WebApplicationInfo& web_app_info) {
   if (g_suppress_os_hooks_for_testing_)
     return;
 
-  if (file_handlers_need_os_update)
-    UpdateFileHandlers(app_id, std::move(old_shortcut));
+  UpdateFileHandlersWithShortcutInfo(app_id, file_handlers_need_os_update,
+                                     std::move(old_shortcut));
 
   UpdateShortcuts(app_id, old_name);
   UpdateShortcutsMenu(app_id, web_app_info);
@@ -489,7 +493,7 @@
 void OsIntegrationManager::UnregisterFileHandlers(
     const AppId& app_id,
     std::unique_ptr<ShortcutInfo> info,
-    base::OnceCallback<void()> callback) {
+    base::OnceCallback<void(bool)> callback) {
   DCHECK(file_handler_manager_);
 
   file_handler_manager_->DisableAndUnregisterOsFileHandlers(
@@ -552,18 +556,51 @@
 
 void OsIntegrationManager::UpdateFileHandlers(
     const AppId& app_id,
+    FileHandlerUpdateAction file_handlers_need_os_update) {
+  GetShortcutInfoForApp(
+      app_id,
+      base::BindOnce(&OsIntegrationManager::UpdateFileHandlersWithShortcutInfo,
+                     weak_ptr_factory_.GetWeakPtr(), app_id,
+                     file_handlers_need_os_update));
+}
+
+void OsIntegrationManager::UpdateFileHandlersWithShortcutInfo(
+    const AppId& app_id,
+    FileHandlerUpdateAction file_handlers_need_os_update,
     std::unique_ptr<ShortcutInfo> info) {
   if (!IsFileHandlingAPIAvailable(app_id))
     return;
 
-  // Update file handlers via complete uninstallation, then reinstallation.
-  auto callback = base::BindOnce(&OsIntegrationManager::RegisterFileHandlers,
-                                 weak_ptr_factory_.GetWeakPtr(), app_id,
-                                 base::DoNothing::Once<bool>());
+  base::OnceCallback<void(bool)> callback_after_removal;
+  switch (file_handlers_need_os_update) {
+    case FileHandlerUpdateAction::kNoUpdate:
+      return;
+    case FileHandlerUpdateAction::kUpdate:
+      callback_after_removal = base::BindOnce(
+          [](base::WeakPtr<OsIntegrationManager> os_integration_manager,
+             const AppId& app_id, bool unregister_success) {
+            // Re-register file handlers regardless of `unregister_success`.
+            // TODO(https://crbug.com/1124047): Report `unregister_success` in
+            // an UMA metric.
+            if (!os_integration_manager)
+              return;
+            os_integration_manager->RegisterFileHandlers(
+                app_id, base::DoNothing::Once<bool>());
+          },
+          weak_ptr_factory_.GetWeakPtr(), app_id);
+      break;
+    case FileHandlerUpdateAction::kRemove:
+      callback_after_removal = base::DoNothing::Once<bool>();
+      break;
+  }
+
+  // Update file handlers via complete uninstallation, then potential
+  // reinstallation.
   content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&OsIntegrationManager::UnregisterFileHandlers,
-                                weak_ptr_factory_.GetWeakPtr(), app_id,
-                                std::move(info), std::move(callback)));
+      FROM_HERE,
+      base::BindOnce(&OsIntegrationManager::UnregisterFileHandlers,
+                     weak_ptr_factory_.GetWeakPtr(), app_id, std::move(info),
+                     std::move(callback_after_removal)));
 }
 
 std::unique_ptr<ShortcutInfo> OsIntegrationManager::BuildShortcutInfo(
diff --git a/chrome/browser/web_applications/components/os_integration_manager.h b/chrome/browser/web_applications/components/os_integration_manager.h
index 75799c1..9683f5dd 100644
--- a/chrome/browser/web_applications/components/os_integration_manager.h
+++ b/chrome/browser/web_applications/components/os_integration_manager.h
@@ -110,11 +110,12 @@
 
   // Update all needed OS hooks for the web app.
   // virtual for testing
-  virtual void UpdateOsHooks(const AppId& app_id,
-                             base::StringPiece old_name,
-                             std::unique_ptr<ShortcutInfo> old_shortcut,
-                             bool file_handlers_need_os_update,
-                             const WebApplicationInfo& web_app_info);
+  virtual void UpdateOsHooks(
+      const AppId& app_id,
+      base::StringPiece old_name,
+      std::unique_ptr<ShortcutInfo> old_shortcut,
+      FileHandlerUpdateAction file_handlers_need_os_update,
+      const WebApplicationInfo& web_app_info);
 
   // Proxy calls for AppShortcutManager.
   // virtual for testing
@@ -163,6 +164,10 @@
       const AppId& app_id,
       base::OnceCallback<void(bool success)> callback);
 
+  virtual void UpdateFileHandlers(
+      const AppId& app_id,
+      FileHandlerUpdateAction file_handlers_need_os_update);
+
  protected:
   AppShortcutManager* shortcut_manager() { return shortcut_manager_.get(); }
   FileHandlerManager* file_handler_manager() {
@@ -229,7 +234,7 @@
                                DeleteShortcutsCallback callback);
   virtual void UnregisterFileHandlers(const AppId& app_id,
                                       std::unique_ptr<ShortcutInfo> info,
-                                      base::OnceCallback<void()> callback);
+                                      base::OnceCallback<void(bool)> callback);
   virtual void UnregisterProtocolHandlers(const AppId& app_id);
   virtual void UnregisterUrlHandlers(const AppId& app_id);
   virtual void UnregisterWebAppOsUninstallation(const AppId& app_id);
@@ -238,8 +243,10 @@
   virtual void UpdateShortcuts(const AppId& app_id, base::StringPiece old_name);
   virtual void UpdateShortcutsMenu(const AppId& app_id,
                                    const WebApplicationInfo& web_app_info);
-  virtual void UpdateFileHandlers(const AppId& app_id,
-                                  std::unique_ptr<ShortcutInfo> info);
+  virtual void UpdateFileHandlersWithShortcutInfo(
+      const AppId& app_id,
+      FileHandlerUpdateAction file_handlers_need_os_update,
+      std::unique_ptr<ShortcutInfo> info);
 
   // Utility methods:
   virtual std::unique_ptr<ShortcutInfo> BuildShortcutInfo(const AppId& app_id);
diff --git a/chrome/browser/web_applications/components/os_integration_manager_unittest.cc b/chrome/browser/web_applications/components/os_integration_manager_unittest.cc
index 47b995cc..b466097 100644
--- a/chrome/browser/web_applications/components/os_integration_manager_unittest.cc
+++ b/chrome/browser/web_applications/components/os_integration_manager_unittest.cc
@@ -108,7 +108,7 @@
               UnregisterFileHandlers,
               (const AppId& app_id,
                std::unique_ptr<ShortcutInfo> info,
-               base::OnceCallback<void()> callback),
+               base::OnceCallback<void(bool)> callback),
               (override));
   MOCK_METHOD(void,
               UnregisterProtocolHandlers,
@@ -122,8 +122,10 @@
 
   // Update:
   MOCK_METHOD(void,
-              UpdateFileHandlers,
-              (const AppId& app_id, std::unique_ptr<ShortcutInfo> info),
+              UpdateFileHandlersWithShortcutInfo,
+              (const AppId& app_id,
+               FileHandlerUpdateAction file_handlers_need_os_update,
+               std::unique_ptr<ShortcutInfo> info),
               (override));
   MOCK_METHOD(void,
               UpdateShortcuts,
@@ -306,12 +308,16 @@
   WebApplicationInfo web_app_info;
   base::StringPiece old_name = "test-name";
 
-  EXPECT_CALL(manager, UpdateFileHandlers(app_id, testing::_)).Times(1);
+  EXPECT_CALL(manager,
+              UpdateFileHandlersWithShortcutInfo(
+                  app_id, FileHandlerUpdateAction::kUpdate, testing::_))
+      .Times(1);
   EXPECT_CALL(manager, UpdateShortcuts(app_id, old_name)).Times(1);
   EXPECT_CALL(manager, UpdateShortcutsMenu(app_id, testing::_)).Times(1);
   EXPECT_CALL(manager, UpdateUrlHandlers(app_id, testing::_)).Times(1);
 
-  manager.UpdateOsHooks(app_id, old_name, nullptr, true, web_app_info);
+  manager.UpdateOsHooks(app_id, old_name, nullptr,
+                        FileHandlerUpdateAction::kUpdate, web_app_info);
 }
 
 }  // namespace
diff --git a/chrome/browser/web_applications/components/web_app_constants.h b/chrome/browser/web_applications/components/web_app_constants.h
index c6eb157..0cb59da 100644
--- a/chrome/browser/web_applications/components/web_app_constants.h
+++ b/chrome/browser/web_applications/components/web_app_constants.h
@@ -265,6 +265,17 @@
 // chrome:// web applications are exempt from this limit.
 constexpr size_t kMaxFileHandlers = 10;
 
+// Expected file handler update actions to be taken by OsIntegrationManager
+// during UpdateOsHooks.
+enum class FileHandlerUpdateAction {
+  // Perform update, removing and re-adding all file handlers.
+  kUpdate = 0,
+  // Remove all file handlers.
+  kRemove = 1,
+  // Do not perform update.
+  kNoUpdate = 2,
+};
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_CONSTANTS_H_
diff --git a/chrome/browser/web_applications/components/web_app_data_retriever_unittest.cc b/chrome/browser/web_applications/components/web_app_data_retriever_unittest.cc
index 5fe8399..8235153 100644
--- a/chrome/browser/web_applications/components/web_app_data_retriever_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_data_retriever_unittest.cc
@@ -37,7 +37,7 @@
 
 namespace {
 
-const char kFooTitle[] = "Foo Title";
+const char16_t kFooTitle[] = u"Foo Title";
 
 }  // namespace
 
@@ -130,23 +130,6 @@
     std::move(quit_closure).Run();
   }
 
-  std::unique_ptr<WebApplicationInfo> CreateWebApplicationInfo(
-      const GURL& url,
-      const std::string name,
-      const std::string description,
-      const GURL& scope,
-      base::Optional<SkColor> theme_color) {
-    auto web_app_info = std::make_unique<WebApplicationInfo>();
-
-    web_app_info->start_url = url;
-    web_app_info->title = base::UTF8ToUTF16(name);
-    web_app_info->description = base::UTF8ToUTF16(description);
-    web_app_info->scope = scope;
-    web_app_info->theme_color = theme_color;
-
-    return web_app_info;
-  }
-
  protected:
   content::WebContentsTester* web_contents_tester() {
     return content::WebContentsTester::For(web_contents());
@@ -228,8 +211,7 @@
 
   web_contents_tester()->NavigateAndCommit(GURL("https://foo.example"));
 
-  const auto web_contents_title = base::UTF8ToUTF16(kFooTitle);
-  web_contents_tester()->SetTitle(web_contents_title);
+  web_contents_tester()->SetTitle(kFooTitle);
 
   WebApplicationInfo original_web_app_info;
   original_web_app_info.title = u"";
@@ -246,7 +228,7 @@
 
   // If the WebApplicationInfo has no title, we fallback to the WebContents
   // title.
-  EXPECT_EQ(web_contents_title, web_app_info()->title);
+  EXPECT_EQ(kFooTitle, web_app_info()->title);
 }
 
 TEST_F(WebAppDataRetrieverTest,
@@ -362,8 +344,7 @@
 TEST_F(WebAppDataRetrieverTest, GetWebApplicationInfo_FrameNavigated) {
   SetFakeWebPageMetadataAgent();
 
-  const auto web_contents_title = base::UTF8ToUTF16(kFooTitle);
-  web_contents_tester()->SetTitle(web_contents_title);
+  web_contents_tester()->SetTitle(kFooTitle);
 
   const GURL kFooUrl("https://foo.example/bar");
   web_contents_tester()->NavigateAndCommit(kFooUrl.GetOrigin());
@@ -378,7 +359,7 @@
   run_loop.Run();
 
   EXPECT_EQ(kFooUrl.GetOrigin(), web_app_info()->start_url);
-  EXPECT_EQ(web_contents_title, web_app_info()->title);
+  EXPECT_EQ(kFooTitle, web_app_info()->title);
 }
 
 TEST_F(WebAppDataRetrieverTest, CheckInstallabilityAndRetrieveManifest) {
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration.cc
index 1eb15af..89e37a4 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration.cc
@@ -29,10 +29,10 @@
 void UnregisterFileHandlersWithOs(const AppId& app_id,
                                   Profile* profile,
                                   std::unique_ptr<ShortcutInfo> info,
-                                  base::OnceCallback<void()> callback) {
+                                  base::OnceCallback<void(bool)> callback) {
   DCHECK(ShouldRegisterFileHandlersWithOs());
   // Stub function for OS's which don't register file handlers with the OS.
-  std::move(callback).Run();
+  std::move(callback).Run(true);
 }
 #endif
 
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration.h b/chrome/browser/web_applications/components/web_app_file_handler_registration.h
index 788a767..83b63ac 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration.h
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration.h
@@ -40,7 +40,7 @@
 void UnregisterFileHandlersWithOs(const AppId& app_id,
                                   Profile* profile,
                                   std::unique_ptr<ShortcutInfo> info,
-                                  base::OnceCallback<void()> callback);
+                                  base::OnceCallback<void(bool)> callback);
 
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
 using RegisterMimeTypesOnLinuxCallback =
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_linux.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_linux.cc
index 4cfc5cb..44b44b3 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_linux.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_linux.cc
@@ -55,20 +55,21 @@
   UMA_HISTOGRAM_ENUMERATION(kRegistrationResultMetric, result);
 }
 
-void OnCreateShortcut(base::OnceCallback<void()> callback,
+void OnCreateShortcut(base::OnceCallback<void(bool)> callback,
                       bool shortcut_created) {
   UMA_HISTOGRAM_ENUMERATION(
       kRecreateShortcutResultMetric,
       shortcut_created ? RecreateShortcutResult::kSuccess
                        : RecreateShortcutResult::kFailToCreateShortcut);
-  std::move(callback).Run();
+  std::move(callback).Run(shortcut_created);
 }
 
-void OnShortcutInfoReceived(base::OnceCallback<void()> callback,
+void OnShortcutInfoReceived(base::OnceCallback<void(bool)> callback,
                             std::unique_ptr<ShortcutInfo> info) {
   if (!info) {
     UMA_HISTOGRAM_ENUMERATION(kRecreateShortcutResultMetric,
                               RecreateShortcutResult::kFailToCreateShortcut);
+    std::move(callback).Run(false);
     return;
   }
 
@@ -83,10 +84,11 @@
       base::BindOnce(OnCreateShortcut, std::move(callback)));
 }
 
-void UpdateFileHandlerRegistrationInOs(const AppId& app_id,
-                                       Profile* profile,
-                                       std::unique_ptr<ShortcutInfo> info,
-                                       base::OnceCallback<void()> callback) {
+void UpdateFileHandlerRegistrationInOs(
+    const AppId& app_id,
+    Profile* profile,
+    std::unique_ptr<ShortcutInfo> info,
+    base::OnceCallback<void(bool)> callback) {
   if (info) {
     // `info` may be prepopulated for unregistration, to avoid updating file
     // handler registrations based on deleted shortcuts.
@@ -166,13 +168,15 @@
 void UnregisterFileHandlersWithOs(const AppId& app_id,
                                   Profile* profile,
                                   std::unique_ptr<ShortcutInfo> info,
-                                  base::OnceCallback<void()> callback) {
+                                  base::OnceCallback<void(bool)> callback) {
   // If this was triggered as part of the uninstallation process, nothing more
   // is needed. Uninstalling already cleans up shortcuts (and thus, file
   // handlers).
   auto* provider = WebAppProviderBase::GetProviderBase(profile);
-  if (!provider->registrar().IsInstalled(app_id))
+  if (!provider->registrar().IsInstalled(app_id)) {
+    std::move(callback).Run(false);
     return;
+  }
 
   // TODO(crbug.com/1076688): Fix file handlers unregistration. We can't update
   // registration here asynchronously because app_id is being uninstalled.
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc
index eb171ae..a12db077 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_mac.cc
@@ -22,12 +22,12 @@
 void UnregisterFileHandlersWithOs(const AppId& app_id,
                                   Profile* profile,
                                   std::unique_ptr<ShortcutInfo> info,
-                                  base::OnceCallback<void()> callback) {
+                                  base::OnceCallback<void(bool)> callback) {
   // On MacOS, file associations are managed through app shims in the
   // Applications directory. File handler unregistration is handled via
   // shortcuts deletion on MacOS.
   NOTREACHED();
-  std::move(callback).Run();
+  std::move(callback).Run(true);
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
index 96c3efc..790a39c8a 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration_win.cc
@@ -83,7 +83,7 @@
                      base::UTF8ToWide(app_name), profile->GetPath(),
                      file_extensions_wide, app_name_extension),
       base::BindOnce(&CheckAndUpdateExternalInstallations, profile->GetPath(),
-                     app_id, base::DoNothing::Once()));
+                     app_id, base::DoNothing::Once<bool>()));
 }
 
 void UnregisterFileHandlersWithOsTask(const AppId& app_id,
@@ -105,7 +105,7 @@
 void UnregisterFileHandlersWithOs(const AppId& app_id,
                                   Profile* profile,
                                   std::unique_ptr<ShortcutInfo> info,
-                                  base::OnceCallback<void()> callback) {
+                                  base::OnceCallback<void(bool)> callback) {
   base::ThreadPool::PostTaskAndReply(
       FROM_HERE,
       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
diff --git a/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.cc b/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.cc
index ce71fb6e..3a97f8d 100644
--- a/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.cc
+++ b/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.cc
@@ -91,10 +91,11 @@
                            const base::FilePath& profile_path,
                            const std::wstring& prog_id,
                            const std::wstring& app_name_extension,
-                           base::OnceCallback<void()> callback) {
+                           base::OnceCallback<void(bool)> callback) {
   if (!base::DeleteFile(ShellUtil::GetApplicationPathForProgId(prog_id))) {
     web_app::RecordRegistration(
         web_app::RegistrationResult::kFailToDeleteExistingRegistration);
+    std::move(callback).Run(false);
     return;
   }
 
@@ -106,8 +107,10 @@
           app_name, app_name_extension,
           web_app::GetOsIntegrationResourcesDirectoryForApp(profile_path,
                                                             app_id, GURL()));
-  if (!app_launcher_path)
+  if (!app_launcher_path) {
+    std::move(callback).Run(false);
     return;
+  }
 
   base::CommandLine app_launch_cmd = web_app::GetAppLauncherCommand(
       app_id, app_launcher_path.value(), profile_path);
@@ -116,7 +119,7 @@
 
   ShellUtil::AddApplicationClass(prog_id, app_launch_cmd, user_visible_app_name,
                                  app_name, icon_path);
-  std::move(callback).Run();
+  std::move(callback).Run(true);
 }
 
 bool AppNameHasProfileExtension(const std::wstring& app_name,
@@ -241,9 +244,10 @@
   return app_specific_launcher_path;
 }
 
-void CheckAndUpdateExternalInstallations(const base::FilePath& cur_profile_path,
-                                         const AppId& app_id,
-                                         base::OnceCallback<void()> callback) {
+void CheckAndUpdateExternalInstallations(
+    const base::FilePath& cur_profile_path,
+    const AppId& app_id,
+    base::OnceCallback<void(bool)> callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   std::wstring prog_id = GetProgIdForApp(cur_profile_path, app_id);
@@ -255,8 +259,12 @@
                                         &external_installation_profile_path);
 
   // Naming updates are only required if a single external installation exists.
-  if (external_installation_profile_path.empty())
+  if (external_installation_profile_path.empty()) {
+    // This bool signals if there was not an error. Exiting early here is WAI,
+    // so this is a success.
+    std::move(callback).Run(true);
     return;
+  }
 
   std::wstring external_installation_prog_id =
       GetProgIdForApp(external_installation_profile_path, app_id);
@@ -273,6 +281,9 @@
     // profile-specific name.
     if (AppNameHasProfileExtension(external_installation_name,
                                    external_installation_profile_path)) {
+      // This bool signals if there was not an error. Exiting early here is WAI,
+      // so this is a success.
+      std::move(callback).Run(true);
       return;
     }
 
@@ -284,6 +295,9 @@
     // profile-specific name.
     if (!AppNameHasProfileExtension(external_installation_name,
                                     external_installation_profile_path)) {
+      // This bool signals if there was not an error. Exiting early here is WAI,
+      // so this is a success.
+      std::move(callback).Run(true);
       return;
     }
 
diff --git a/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.h b/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.h
index 6671abb..0d49209d 100644
--- a/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.h
+++ b/chrome/browser/web_applications/components/web_app_handler_registration_utils_win.h
@@ -42,9 +42,10 @@
 
 // Checks if there is an installation of this app in another profile that needs
 // to be updated with a profile specific name and executes required update.
-void CheckAndUpdateExternalInstallations(const base::FilePath& cur_profile_path,
-                                         const AppId& app_id,
-                                         base::OnceCallback<void()> callback);
+void CheckAndUpdateExternalInstallations(
+    const base::FilePath& cur_profile_path,
+    const AppId& app_id,
+    base::OnceCallback<void(bool)> callback);
 
 // Result of file handler registration process.
 // These values are persisted to logs. Entries should not be renumbered and
diff --git a/chrome/browser/web_applications/components/web_app_handler_registration_utils_win_unittest.cc b/chrome/browser/web_applications/components/web_app_handler_registration_utils_win_unittest.cc
index a67b865..5d21ef3a 100644
--- a/chrome/browser/web_applications/components/web_app_handler_registration_utils_win_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_handler_registration_utils_win_unittest.cc
@@ -172,7 +172,7 @@
 
   // Update installations external to profile 2 (i.e. profile1).
   CheckAndUpdateExternalInstallations(profile2->GetPath(), app_id(),
-                                      base::DoNothing());
+                                      base::DoNothing::Once<bool>());
   base::ThreadPoolInstance::Get()->FlushForTesting();
 
   // Test that the profile1 installation is updated with a profile-specific
@@ -193,7 +193,7 @@
   Profile* profile2 =
       testing_profile_manager()->CreateTestingProfile("Profile 2");
   CheckAndUpdateExternalInstallations(profile2->GetPath(), app_id(),
-                                      base::DoNothing());
+                                      base::DoNothing::Once<bool>());
   base::ThreadPoolInstance::Get()->FlushForTesting();
 
   // Ensure that after updating from profile2 (which has no installation),
@@ -220,7 +220,7 @@
   // in other profiles shouldn't change the original 2 installations since they
   // already have app-specific names.
   CheckAndUpdateExternalInstallations(profile3->GetPath(), app_id(),
-                                      base::DoNothing());
+                                      base::DoNothing::Once<bool>());
   base::ThreadPoolInstance::Get()->FlushForTesting();
 
   TestRegisteredApp(app_id(), app_name(), L" (Default)", profile1->GetPath());
diff --git a/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc b/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
index 06cee0c..9f76acb 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
@@ -28,8 +28,8 @@
 
 const char16_t kAppShortName[] = u"Test short name";
 const char16_t kAppTitle[] = u"Test title";
-const char kAlternativeAppTitle[] = "Different test title";
-const char kShortcutItemName[] = "shortcut item ";
+const char16_t kAlternativeAppTitle[] = u"Different test title";
+const char16_t kShortcutItemName[] = u"shortcut item ";
 
 constexpr SquareSizePx kIconSize = 64;
 
@@ -51,7 +51,7 @@
 
 TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifest) {
   WebApplicationInfo web_app_info;
-  web_app_info.title = base::UTF8ToUTF16(kAlternativeAppTitle);
+  web_app_info.title = kAlternativeAppTitle;
   web_app_info.start_url = GURL("http://www.notchromium.org");
   WebApplicationIconInfo info;
   const GURL kAppIcon1("fav1.png");
@@ -289,7 +289,7 @@
 TEST_F(WebAppInstallUtilsWithShortcutsMenu,
        UpdateWebAppInfoFromManifestWithShortcuts) {
   WebApplicationInfo web_app_info;
-  web_app_info.title = base::UTF8ToUTF16(kAlternativeAppTitle);
+  web_app_info.title = kAlternativeAppTitle;
   web_app_info.start_url = GURL("http://www.notchromium.org");
   WebApplicationIconInfo info;
   const GURL kAppIcon1("fav1.png");
@@ -300,9 +300,8 @@
   for (int i = 0; i < 3; ++i) {
     WebApplicationShortcutsMenuItemInfo shortcuts_menu_item_info;
     WebApplicationShortcutsMenuItemInfo::Icon icon;
-    std::string shortcut_name = kShortcutItemName;
-    shortcut_name += base::NumberToString(i + 1);
-    shortcuts_menu_item_info.name = base::UTF8ToUTF16(shortcut_name);
+    shortcuts_menu_item_info.name =
+        kShortcutItemName + base::NumberToString16(i + 1);
     shortcuts_menu_item_info.url = kShortcutItemUrl;
 
     icon.url = GURL("http://www.chromium.org/shortcuts/icon1.png");
@@ -386,9 +385,7 @@
 
   // Test that shortcuts in the manifest replace those in |web_app_info|.
   blink::Manifest::ShortcutItem shortcut_item;
-  std::string shortcut_name = kShortcutItemName;
-  shortcut_name += base::NumberToString(4);
-  shortcut_item.name = base::UTF8ToUTF16(shortcut_name);
+  shortcut_item.name = std::u16string(kShortcutItemName) + u"4";
   shortcut_item.url = kShortcutItemUrl;
 
   const GURL kIconUrl2("http://www.chromium.org/shortcuts/icon2.png");
@@ -399,9 +396,7 @@
 
   manifest.shortcuts.push_back(shortcut_item);
 
-  shortcut_name = kShortcutItemName;
-  shortcut_name += base::NumberToString(5);
-  shortcut_item.name = base::UTF8ToUTF16(shortcut_name);
+  shortcut_item.name = std::u16string(kShortcutItemName) + u"5";
 
   const GURL kIconUrl3("http://www.chromium.org/shortcuts/icon3.png");
   icon.src = kIconUrl3;
@@ -480,9 +475,7 @@
   blink::Manifest manifest;
   for (unsigned int i = 0; i < kNumTestIcons; ++i) {
     blink::Manifest::ShortcutItem shortcut_item;
-    std::string shortcut_name = kShortcutItemName;
-    shortcut_name += base::NumberToString(i);
-    shortcut_item.name = base::UTF8ToUTF16(shortcut_name);
+    shortcut_item.name = kShortcutItemName + base::NumberToString16(i);
     shortcut_item.url = GURL("http://www.chromium.org/shortcuts/action");
 
     blink::Manifest::ImageResource icon;
@@ -536,9 +529,7 @@
   blink::Manifest manifest;
   for (int i = 1; i <= 20; ++i) {
     blink::Manifest::ShortcutItem shortcut_item;
-    std::string shortcut_name = kShortcutItemName;
-    shortcut_name += base::NumberToString(i);
-    shortcut_item.name = base::UTF8ToUTF16(shortcut_name);
+    shortcut_item.name = kShortcutItemName + base::NumberToString16(i);
     shortcut_item.url = GURL("http://www.chromium.org/shortcuts/action");
 
     blink::Manifest::ImageResource icon;
@@ -574,9 +565,7 @@
   {
     WebApplicationShortcutsMenuItemInfo shortcut_item;
     std::vector<WebApplicationShortcutsMenuItemInfo::Icon> shortcut_icon_infos;
-    std::string shortcut_name = kShortcutItemName;
-    shortcut_name += base::NumberToString(1);
-    shortcut_item.name = base::UTF8ToUTF16(shortcut_name);
+    shortcut_item.name = std::u16string(kShortcutItemName) + u"1";
     shortcut_item.url = GURL("http://www.chromium.org/shortcuts/action");
     icon.url = kIconUrl1;
     icon.square_size_px = kIconSize;
@@ -590,9 +579,7 @@
   {
     WebApplicationShortcutsMenuItemInfo shortcut_item;
     std::vector<WebApplicationShortcutsMenuItemInfo::Icon> shortcut_icon_infos;
-    std::string shortcut_name = kShortcutItemName;
-    shortcut_name += base::NumberToString(2);
-    shortcut_item.name = base::UTF8ToUTF16(shortcut_name);
+    shortcut_item.name = std::u16string(kShortcutItemName) + u"2";
     icon.url = kIconUrl1;
     icon.square_size_px = kIconSize;
     shortcut_icon_infos.push_back(icon);
@@ -773,7 +760,7 @@
   // Construct |shortcuts_menu_item_info| to add to
   // |web_app_info.shortcuts_menu_item_infos|.
   WebApplicationShortcutsMenuItemInfo shortcuts_menu_item_info;
-  shortcuts_menu_item_info.name = base::UTF8ToUTF16(kShortcutItemName);
+  shortcuts_menu_item_info.name = kShortcutItemName;
   shortcuts_menu_item_info.url =
       GURL("http://www.chromium.org/shortcuts/action");
   // Construct |icon| to add to |shortcuts_menu_item_info.shortcut_icon_infos|.
diff --git a/chrome/browser/web_applications/components/web_app_protocol_handler_registration_win.cc b/chrome/browser/web_applications/components/web_app_protocol_handler_registration_win.cc
index 07e2805..c84a015f 100644
--- a/chrome/browser/web_applications/components/web_app_protocol_handler_registration_win.cc
+++ b/chrome/browser/web_applications/components/web_app_protocol_handler_registration_win.cc
@@ -113,7 +113,7 @@
                                  const web_app::AppId& app_id,
                                  base::OnceCallback<void(bool)> callback) {
   web_app::CheckAndUpdateExternalInstallations(cur_profile_path, app_id,
-                                               base::DoNothing::Once());
+                                               base::DoNothing::Once<bool>());
   std::move(callback).Run(true);
 }
 
@@ -158,7 +158,7 @@
       base::BindOnce(&UnregisterProtocolHandlersWithOsInBackground, app_id,
                      profile->GetPath()),
       base::BindOnce(&CheckAndUpdateExternalInstallations, profile->GetPath(),
-                     app_id, base::DoNothing::Once()));
+                     app_id, base::DoNothing::Once<bool>()));
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc b/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc
index 4ae49adc..49eb540b 100644
--- a/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_run_on_os_login_win_unittest.cc
@@ -25,7 +25,7 @@
 
 namespace {
 
-constexpr char kAppTitle[] = {"app"};
+constexpr char16_t kAppTitle[] = u"app";
 }  // namespace
 
 class WebAppRunOnOsLoginWinTest : public WebAppTest {
@@ -42,7 +42,7 @@
   std::unique_ptr<ShortcutInfo> GetShortcutInfo() {
     auto shortcut_info = std::make_unique<ShortcutInfo>();
     shortcut_info->extension_id = "app-id";
-    shortcut_info->title = base::UTF8ToUTF16(kAppTitle);
+    shortcut_info->title = kAppTitle;
     shortcut_info->profile_path = profile()->GetPath();
 
     gfx::ImageFamily image_family;
@@ -64,7 +64,7 @@
 
   std::vector<base::FilePath> GetShortcuts() {
     return internals::FindAppShortcutsByProfileAndTitle(
-        GetStartupFolder(), profile()->GetPath(), base::UTF8ToUTF16(kAppTitle));
+        GetStartupFolder(), profile()->GetPath(), kAppTitle);
   }
 
   void VerifyShortcutCreated() {
@@ -116,8 +116,7 @@
   VerifyShortcutCreated();
 
   internals::UnregisterRunOnOsLogin(shortcut_info->extension_id,
-                                    profile()->GetPath(),
-                                    base::UTF8ToUTF16(kAppTitle));
+                                    profile()->GetPath(), kAppTitle);
   VerifyShortcutDeleted();
 }
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
index cba555fd..f86dff4 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -297,7 +297,8 @@
     std::unique_ptr<web_app::ShortcutInfo> old_shortcut;
     os_integration_manager().UpdateOsHooks(
         extension->id(), old_name, std::move(old_shortcut),
-        /*file_handlers_need_os_update=*/false, web_app_info);
+        /*file_handlers_need_os_update=*/
+        web_app::FileHandlerUpdateAction::kNoUpdate, web_app_info);
     registrar().NotifyWebAppManifestUpdated(extension->id(), old_name);
   }
   std::move(callback).Run(extension->id(),
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
index 371eada..fbafe67 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.cc
@@ -178,6 +178,11 @@
   return nullptr;
 }
 
+bool BookmarkAppRegistrar::IsAppFileHandlerPermissionBlocked(
+    const web_app::AppId& app_id) const {
+  return false;
+}
+
 base::Optional<GURL> BookmarkAppRegistrar::GetAppScopeInternal(
     const web_app::AppId& app_id) const {
   const Extension* extension = GetBookmarkAppDchecked(app_id);
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
index ccf6eb5..3a563a35 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_registrar.h
@@ -54,6 +54,8 @@
       const web_app::AppId& app_id) const override;
   const apps::FileHandlers* GetAppFileHandlers(
       const web_app::AppId& app_id) const override;
+  bool IsAppFileHandlerPermissionBlocked(
+      const web_app::AppId& app_id) const override;
   base::Optional<GURL> GetAppScopeInternal(
       const web_app::AppId& app_id) const override;
   web_app::DisplayMode GetAppDisplayMode(
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index 590f3a0..fa7495e 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -97,6 +97,7 @@
 )";
 
 constexpr char kAnotherShortcutsItemName[] = "Timeline";
+constexpr char16_t kAnotherShortcutsItemName16[] = u"Timeline";
 constexpr char kAnotherShortcutsItemUrl[] = "/shortcut";
 constexpr char kAnotherShortcutsItemShortName[] = "H";
 constexpr char kAnotherShortcutsItemDescription[] = "Navigate home";
@@ -1556,6 +1557,66 @@
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
+                       BlockPermissionRemoveFileHandlers) {
+  constexpr char kFileHandlerManifestTemplate[] = R"(
+    {
+      "name": "Test app name",
+      "start_url": ".",
+      "scope": "/",
+      "display": "minimal-ui",
+      "file_handlers": [
+        {
+          "action": "/?plaintext",
+          "name": "Plain Text",
+          "accept": {
+            "text/plain": ["$1"]
+          }
+        }
+      ],
+      "icons": $2
+    }
+  )";
+
+  OverrideManifest(kFileHandlerManifestTemplate,
+                   {".txt", kInstallableIconList});
+  AppId app_id = InstallWebApp();
+  const WebAppRegistrar* registrar =
+      GetProvider().registrar().AsWebAppRegistrar();
+  const WebApp* web_app = registrar->GetAppById(app_id);
+
+  EXPECT_FALSE(web_app->file_handler_permission_blocked());
+  ASSERT_FALSE(web_app->file_handlers().empty());
+  const auto& old_file_handler = web_app->file_handlers()[0];
+  ASSERT_FALSE(old_file_handler.accept.empty());
+  auto old_extensions = old_file_handler.accept[0].file_extensions;
+  EXPECT_TRUE(base::Contains(old_extensions, ".txt"));
+  auto* map =
+      HostContentSettingsMapFactory::GetForProfile(browser()->profile());
+  const GURL url = GetAppURL();
+  const GURL origin = url.GetOrigin();
+  EXPECT_EQ(CONTENT_SETTING_ASK,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+  // Set permission to BLOCK.
+  map->SetContentSettingDefaultScope(origin, origin,
+                                     ContentSettingsType::FILE_HANDLING,
+                                     CONTENT_SETTING_BLOCK);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK,
+            map->GetContentSetting(origin, origin,
+                                   ContentSettingsType::FILE_HANDLING));
+
+  // App should be updated to permission blocked by
+  // `WebAppInstallFinalizer::OnContentSettingChanged`.
+  EXPECT_TRUE(registrar->GetAppById(app_id)->file_handler_permission_blocked());
+  // Update manifest.
+  OverrideManifest(kFileHandlerManifestTemplate, {".md", kInstallableIconList});
+  EXPECT_EQ(ManifestUpdateResult::kAppUpdated,
+            GetResultAfterPageLoad(url, &app_id));
+  // Manifest update task should preserve the permission blocked state.
+  EXPECT_TRUE(registrar->GetAppById(app_id)->file_handler_permission_blocked());
+}
+
+IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
                        CheckFindsDeletedFileHandler) {
   constexpr char kFileHandlerManifestTemplate[] = R"(
     {
@@ -1765,7 +1826,7 @@
                                       ManifestUpdateResult::kAppUpdated, 1);
   EXPECT_EQ(
       GetProvider().registrar().GetAppShortcutsMenuItemInfos(app_id)[0].name,
-      base::UTF8ToUTF16(kAnotherShortcutsItemName));
+      kAnotherShortcutsItemName16);
 }
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
diff --git a/chrome/browser/web_applications/proto/web_app.proto b/chrome/browser/web_applications/proto/web_app.proto
index 0c60dda3..e4d5084 100644
--- a/chrome/browser/web_applications/proto/web_app.proto
+++ b/chrome/browser/web_applications/proto/web_app.proto
@@ -195,4 +195,6 @@
   // Last time the Badging API was used.
   // ms since the Unix epoch. See sync/base/time.h.
   optional int64 last_badging_time = 31;
+
+  optional bool file_handler_permission_blocked = 32;
 }
diff --git a/chrome/browser/web_applications/test/test_app_registrar.cc b/chrome/browser/web_applications/test/test_app_registrar.cc
index b16ac23..ed01e7a 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.cc
+++ b/chrome/browser/web_applications/test/test_app_registrar.cc
@@ -137,6 +137,11 @@
   return nullptr;
 }
 
+bool TestAppRegistrar::IsAppFileHandlerPermissionBlocked(
+    const web_app::AppId& app_id) const {
+  return false;
+}
+
 base::Optional<GURL> TestAppRegistrar::GetAppScopeInternal(
     const AppId& app_id) const {
   const auto& result = installed_apps_.find(app_id);
diff --git a/chrome/browser/web_applications/test/test_app_registrar.h b/chrome/browser/web_applications/test/test_app_registrar.h
index c5fb1361..bb72059e 100644
--- a/chrome/browser/web_applications/test/test_app_registrar.h
+++ b/chrome/browser/web_applications/test/test_app_registrar.h
@@ -70,6 +70,8 @@
       const AppId& app_id) const override;
   const apps::FileHandlers* GetAppFileHandlers(
       const AppId& app_id) const override;
+  bool IsAppFileHandlerPermissionBlocked(
+      const web_app::AppId& app_id) const override;
   base::Optional<GURL> GetAppScopeInternal(const AppId& app_id) const override;
   DisplayMode GetAppDisplayMode(const AppId& app_id) const override;
   DisplayMode GetAppUserDisplayMode(const AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/test/test_os_integration_manager.cc b/chrome/browser/web_applications/test/test_os_integration_manager.cc
index a4ce5426..b5ea932c 100644
--- a/chrome/browser/web_applications/test/test_os_integration_manager.cc
+++ b/chrome/browser/web_applications/test/test_os_integration_manager.cc
@@ -114,10 +114,10 @@
     const AppId& app_id,
     base::StringPiece old_name,
     std::unique_ptr<ShortcutInfo> old_shortcut,
-    bool file_handlers_need_os_update,
+    FileHandlerUpdateAction file_handlers_need_os_update,
     const WebApplicationInfo& web_app_info) {
-  if (file_handlers_need_os_update)
-    num_update_file_handlers_calls_++;
+  if (file_handlers_need_os_update != FileHandlerUpdateAction::kNoUpdate)
+    ++num_update_file_handlers_calls_;
 }
 
 void TestOsIntegrationManager::SetFileHandlerManager(
diff --git a/chrome/browser/web_applications/test/test_os_integration_manager.h b/chrome/browser/web_applications/test/test_os_integration_manager.h
index f51acd4..d0ac3ce 100644
--- a/chrome/browser/web_applications/test/test_os_integration_manager.h
+++ b/chrome/browser/web_applications/test/test_os_integration_manager.h
@@ -41,7 +41,7 @@
   void UpdateOsHooks(const AppId& app_id,
                      base::StringPiece old_name,
                      std::unique_ptr<ShortcutInfo> old_shortcut,
-                     bool file_handlers_need_os_update,
+                     FileHandlerUpdateAction file_handlers_need_os_update,
                      const WebApplicationInfo& web_app_info) override;
 
   size_t num_create_shortcuts_calls() const {
diff --git a/chrome/browser/web_applications/test/web_app_install_test_utils.cc b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
index 1292a6b..4c73452 100644
--- a/chrome/browser/web_applications/test/web_app_install_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_install_test_utils.cc
@@ -27,7 +27,6 @@
 
 namespace web_app {
 namespace test {
-namespace {
 
 void WaitUntilReady(WebAppProvider* provider) {
   if (provider->on_registry_ready().is_signaled())
@@ -38,8 +37,6 @@
   run_loop.Run();
 }
 
-}  // namespace
-
 void AwaitStartWebAppProviderAndSubsystems(Profile* profile) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kDisablePreinstalledApps);
diff --git a/chrome/browser/web_applications/test/web_app_install_test_utils.h b/chrome/browser/web_applications/test/web_app_install_test_utils.h
index 0d8ce7a..7d44846 100644
--- a/chrome/browser/web_applications/test/web_app_install_test_utils.h
+++ b/chrome/browser/web_applications/test/web_app_install_test_utils.h
@@ -15,6 +15,9 @@
 class Profile;
 
 namespace web_app {
+
+class WebAppProvider;
+
 namespace test {
 
 // Start the WebAppProvider and subsystems, and wait for startup to complete.
@@ -22,6 +25,9 @@
 // unit tests, not browser tests.
 void AwaitStartWebAppProviderAndSubsystems(Profile* profile);
 
+// Wait until the provided WebAppProvider is ready.
+void WaitUntilReady(WebAppProvider* provider);
+
 AppId InstallDummyWebApp(Profile* profile,
                          const std::string& app_name,
                          const GURL& app_url);
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index eba8028..1cb7841 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -329,6 +329,8 @@
     app->SetWebAppChromeOsData(std::move(chromeos_data));
   }
 
+  app->SetFileHandlerPermissionBlocked(false);
+
   WebApp::SyncFallbackData sync_fallback_data;
   sync_fallback_data.name = "Sync" + name;
   sync_fallback_data.theme_color = synced_theme_color;
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index b3f3ed8..9b127af5 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -299,6 +299,10 @@
   manifest_id_ = manifest_id;
 }
 
+void WebApp::SetFileHandlerPermissionBlocked(bool permission_blocked) {
+  file_handler_permission_blocked_ = permission_blocked;
+}
+
 WebApp::ClientData::ClientData() = default;
 
 WebApp::ClientData::~ClientData() = default;
@@ -458,6 +462,9 @@
   for (const apps::FileHandler& file_handler : app.file_handlers_)
     out << Indent(file_handler) << std::endl;
 
+  out << "file_handler_permission_blocked:"
+      << app.file_handler_permission_blocked_ << std::endl;
+
   out << "share_target:" << std::endl << Indent(app.share_target_) << std::endl;
 
   out << "additional_search_terms:" << std::endl;
@@ -545,7 +552,8 @@
         app.capture_links_,
         app.manifest_url_,
         app.manifest_id_,
-        app.client_data_.system_web_app_data
+        app.client_data_.system_web_app_data,
+        app.file_handler_permission_blocked_
         // clang-format on
     );
   };
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index 9fa792d..8bcc9bce 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -124,6 +124,10 @@
 
   const apps::FileHandlers& file_handlers() const { return file_handlers_; }
 
+  bool file_handler_permission_blocked() const {
+    return file_handler_permission_blocked_;
+  }
+
   const base::Optional<apps::ShareTarget>& share_target() const {
     return share_target_;
   }
@@ -247,6 +251,7 @@
   void SetCaptureLinks(blink::mojom::CaptureLinks capture_links);
   void SetManifestUrl(const GURL& manifest_url);
   void SetManifestId(const base::Optional<std::string>& manifest_id);
+  void SetFileHandlerPermissionBlocked(bool permission_blocked);
 
   // For logging and debug purposes.
   bool operator==(const WebApp&) const;
@@ -308,6 +313,7 @@
   ClientData client_data_;
   GURL manifest_url_;
   base::Optional<std::string> manifest_id_;
+  bool file_handler_permission_blocked_ = false;
   // New fields must be added to:
   //  - |operator==|
   //  - |operator<<|
diff --git a/chrome/browser/web_applications/web_app_database.cc b/chrome/browser/web_applications/web_app_database.cc
index 166675cb..201f5f3b 100644
--- a/chrome/browser/web_applications/web_app_database.cc
+++ b/chrome/browser/web_applications/web_app_database.cc
@@ -367,6 +367,8 @@
   if (!web_app.manifest_url().is_empty())
     local_data->set_manifest_url(web_app.manifest_url().spec());
 
+  local_data->set_file_handler_permission_blocked(
+      web_app.file_handler_permission_blocked());
   return local_data;
 }
 
@@ -744,6 +746,9 @@
     }
     web_app->SetManifestUrl(manifest_url);
   }
+  if (local_data.has_file_handler_permission_blocked())
+    web_app->SetFileHandlerPermissionBlocked(
+        local_data.file_handler_permission_blocked());
   return web_app;
 }
 
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index 47c3bd9..36601a9 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -17,6 +17,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/permission_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/app_registrar.h"
@@ -36,6 +37,7 @@
 #include "chrome/browser/web_applications/web_app_registry_update.h"
 #include "chrome/browser/web_applications/web_app_sync_bridge.h"
 #include "components/content_settings/core/common/content_settings.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "components/permissions/permission_manager.h"
 #include "components/permissions/permission_result.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
@@ -350,7 +352,7 @@
   }
 
   bool should_update_os_hooks = ShouldUpdateOsHooks(app_id);
-  bool file_handlers_need_os_update =
+  FileHandlerUpdateAction file_handlers_need_os_update =
       DoFileHandlersNeedOsUpdate(app_id, web_app_info, web_contents);
   // Grab the shortcut info before the app is removed from the database.
   os_integration_manager().GetShortcutInfoForApp(
@@ -363,6 +365,10 @@
 
 void WebAppInstallFinalizer::Start() {
   DCHECK(!started_);
+
+  content_settings_observer_.Observe(
+      HostContentSettingsMapFactory::GetForProfile(profile_));
+  DetectAndCorrectFileHandlingPermissionBlocks();
   started_ = true;
 }
 
@@ -370,6 +376,74 @@
   started_ = false;
 }
 
+bool WebAppInstallFinalizer::IsFileHandlerPermissionBlocked(const GURL& scope) {
+  permissions::PermissionManager* permission_manager =
+      PermissionManagerFactory::GetForProfile(profile_);
+  DCHECK(permission_manager);
+
+  permissions::PermissionResult status =
+      permission_manager->GetPermissionStatus(
+          ContentSettingsType::FILE_HANDLING, scope, scope);
+  return status.content_setting == CONTENT_SETTING_BLOCK;
+}
+
+void WebAppInstallFinalizer::UpdateFileHandlerPermission(
+    const AppId& app_id,
+    bool permission_blocked) {
+  ScopedRegistryUpdate update(registry_controller().AsWebAppSyncBridge());
+  WebApp* app_to_update = update->UpdateApp(app_id);
+  app_to_update->SetFileHandlerPermissionBlocked(permission_blocked);
+  FileHandlerUpdateAction file_handlers_need_os_update =
+      permission_blocked ? FileHandlerUpdateAction::kRemove
+                         : FileHandlerUpdateAction::kUpdate;
+  os_integration_manager().UpdateFileHandlers(app_id,
+                                              file_handlers_need_os_update);
+}
+
+void WebAppInstallFinalizer::DetectAndCorrectFileHandlingPermissionBlocks() {
+  DCHECK(!started_);
+
+  for (const AppId& app_id : registrar().GetAppIds()) {
+    const WebApp* app = registrar().AsWebAppRegistrar()->GetAppById(app_id);
+    if (!app || !app->is_locally_installed()) {
+      continue;
+    }
+    const GURL url = app->scope();
+    bool permission_blocked = IsFileHandlerPermissionBlocked(app->scope());
+    if (permission_blocked != app->file_handler_permission_blocked()) {
+      UpdateFileHandlerPermission(app_id, permission_blocked);
+    }
+  }
+}
+
+void WebAppInstallFinalizer::OnContentSettingChanged(
+    const ContentSettingsPattern& primary_pattern,
+    const ContentSettingsPattern& secondary_pattern,
+    ContentSettingsType content_type) {
+  if (!started_ || content_type != ContentSettingsType::FILE_HANDLING)
+    return;
+  auto* host_content_settings_map =
+      HostContentSettingsMapFactory::GetForProfile(profile_);
+  DCHECK(host_content_settings_map);
+
+  for (const AppId& app_id : registrar().GetAppIds()) {
+    const WebApp* app = registrar().AsWebAppRegistrar()->GetAppById(app_id);
+    if (!app || !app->is_locally_installed()) {
+      continue;
+    }
+    const GURL url = app->scope();
+    if (!primary_pattern.Matches(url))
+      continue;
+
+    ContentSetting setting = host_content_settings_map->GetContentSetting(
+        url, url, ContentSettingsType::FILE_HANDLING);
+    bool permission_blocked = setting == CONTENT_SETTING_BLOCK;
+    if (permission_blocked != app->file_handler_permission_blocked()) {
+      UpdateFileHandlerPermission(app_id, permission_blocked);
+    }
+  }
+}
+
 void WebAppInstallFinalizer::UninstallWebAppInternal(
     const AppId& app_id,
     webapps::WebappUninstallSource uninstall_source,
@@ -440,6 +514,8 @@
     std::unique_ptr<WebApp> web_app,
     CommitCallback commit_callback) {
   SetWebAppManifestFields(web_app_info, *web_app);
+  web_app->SetFileHandlerPermissionBlocked(
+      IsFileHandlerPermissionBlocked(web_app->scope()));
 
   AppId app_id = web_app->app_id();
   IconBitmaps icon_bitmaps;
@@ -530,7 +606,7 @@
 
 void WebAppInstallFinalizer::FinalizeUpdateWithShortcutInfo(
     bool should_update_os_hooks,
-    bool file_handlers_need_os_update,
+    FileHandlerUpdateAction file_handlers_need_os_update,
     InstallFinalizedCallback callback,
     const AppId app_id,
     const WebApplicationInfo& web_app_info,
@@ -561,18 +637,18 @@
 #endif  // defined(OS_CHROMEOS)
 }
 
-bool WebAppInstallFinalizer::DoFileHandlersNeedOsUpdate(
+FileHandlerUpdateAction WebAppInstallFinalizer::DoFileHandlersNeedOsUpdate(
     const AppId app_id,
     const WebApplicationInfo& web_app_info,
     content::WebContents* web_contents) {
   if (!base::FeatureList::IsEnabled(blink::features::kFileHandlingAPI))
-    return false;
+    return FileHandlerUpdateAction::kNoUpdate;
   // TODO(https://crbug.com/1197013): Consider trying to re-use
   // HaveFileHandlersChanged() results from the ManifestUpdateTask.
   if (!HaveFileHandlersChanged(
           /*old_handlers=*/registrar().GetAppFileHandlers(app_id),
           /*new_handlers=*/web_app_info.file_handlers)) {
-    return false;
+    return FileHandlerUpdateAction::kNoUpdate;
   }
 
   const GURL& url = web_app_info.scope;
@@ -582,12 +658,13 @@
   // Keep in sync with chromeos::kChromeUICameraAppURL.
   const char kChromeUICameraAppURL[] = "chrome://camera-app/";
 
-  // Omit file handler permission downgrade for the ChromeOS Media and Camera
-  // System Web Apps (SWAs), which have permissions granted by default.
+  // Omit file handler removal and permission downgrade for the ChromeOS Media
+  // and Camera System Web Apps (SWAs), which have permissions granted by
+  // default.
   // TODO(huangdarwin): Find a better architecture to structure this exception
   // and check relevant only in ChromeOS (outside of LaCrOS).
   if (url == kChromeUIMediaAppURL || url == kChromeUICameraAppURL) {
-    return true;
+    return FileHandlerUpdateAction::kUpdate;
   }
 
   // Downgrade file handlers permission before
@@ -610,18 +687,20 @@
   // If file handling permission is "ALLOW" during manifest update, downgrade
   // to "ASK" via reset, as the user may not want to allow newly added file
   // handlers, which may include more dangerous extensions.
-  // If the permission is "ASK" or "BLOCK", leave it as is. To avoid misuse, the
-  // user must re-install the PWA to enable previously blocked file handlers.
+  // If the permission is "ASK" or "BLOCK", leave it as is. When permission is
+  // "BLOCK", the `OnContentSettingChanged()` and
+  // `DetectAndCorrectFileHandlingPermissionBlocks()` should capture the
+  // permission change and make sure the OS and db state are in sync with the
+  // PermissionManager permission setting. Therefore, manifest update task
+  // should not update file handlers due to blocked permission state.
   if (status.content_setting == CONTENT_SETTING_ALLOW) {
     permission_manager->ResetPermission(content::PermissionType::FILE_HANDLING,
                                         url, url);
   } else if (status.content_setting == CONTENT_SETTING_BLOCK) {
-    // TODO(https://crbug.com/1194163): CONTENT_SETTING_BLOCK should block
-    // update to avoid re-registering file handlers after they're unregistered.
-    // Implement unregistration of file handlers when "BLOCK" is set.
-    return false;
+    DCHECK(registrar().IsAppFileHandlerPermissionBlocked(app_id));
+    return FileHandlerUpdateAction::kNoUpdate;
   }
-  return true;
+  return FileHandlerUpdateAction::kUpdate;
 }
 
 void WebAppInstallFinalizer::OnDatabaseCommitCompletedForUpdate(
@@ -630,7 +709,7 @@
     std::string old_name,
     std::unique_ptr<ShortcutInfo> old_shortcut,
     bool should_update_os_hooks,
-    bool file_handlers_need_os_update,
+    FileHandlerUpdateAction file_handlers_need_os_update,
     const WebApplicationInfo& web_app_info,
     bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.h b/chrome/browser/web_applications/web_app_install_finalizer.h
index 5a57c8c..5638596 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.h
+++ b/chrome/browser/web_applications/web_app_install_finalizer.h
@@ -10,10 +10,13 @@
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/os_integration_manager.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
+#include "components/content_settings/core/browser/content_settings_observer.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 
 class Profile;
@@ -28,7 +31,8 @@
 class WebAppIconManager;
 class WebAppRegistrar;
 
-class WebAppInstallFinalizer final : public InstallFinalizer {
+class WebAppInstallFinalizer final : public InstallFinalizer,
+                                     public content_settings::Observer {
  public:
   WebAppInstallFinalizer(Profile* profile, WebAppIconManager* icon_manager);
   WebAppInstallFinalizer(const WebAppInstallFinalizer&) = delete;
@@ -96,26 +100,27 @@
   // granularity we have during install.
   void FinalizeUpdateWithShortcutInfo(
       bool should_update_os_hooks,
-      bool file_handlers_need_os_update,
+      FileHandlerUpdateAction file_handlers_need_os_update,
       InstallFinalizedCallback callback,
       const AppId app_id,
       const WebApplicationInfo& web_app_info,
       std::unique_ptr<ShortcutInfo> old_shortcut);
   bool ShouldUpdateOsHooks(const AppId& app_id);
   // Checks whether OS registered file handlers need to update, taking into
-  // account permission settings, as file handlers should not update when the
-  // permission has been denied. Also, downgrades granted file handling
+  // account permission settings, as file handlers should be unregistered
+  // when the permission has been denied. Also, downgrades granted file handling
   // permissions if file handlers have changed.
-  bool DoFileHandlersNeedOsUpdate(const AppId app_id,
-                                  const WebApplicationInfo& web_app_info,
-                                  content::WebContents* web_contents);
+  FileHandlerUpdateAction DoFileHandlersNeedOsUpdate(
+      const AppId app_id,
+      const WebApplicationInfo& web_app_info,
+      content::WebContents* web_contents);
   void OnDatabaseCommitCompletedForUpdate(
       InstallFinalizedCallback callback,
       AppId app_id,
       std::string old_name,
       std::unique_ptr<ShortcutInfo> old_shortcut,
       bool should_update_os_hooks,
-      bool file_handlers_need_os_update,
+      FileHandlerUpdateAction file_handlers_need_os_update,
       const WebApplicationInfo& web_app_info,
       bool success);
   void OnUninstallOsHooks(const AppId& app_id,
@@ -125,12 +130,35 @@
 
   WebAppRegistrar& GetWebAppRegistrar() const;
 
+  // content_settings::Observer overrides.
+  // This catches permission changes occurring when browser is active, from
+  // permission prompts, site settings, and site settings padlock. When
+  // permission setting is changed to be blocked/allowed, update app's
+  // `file_handler_permission_blocked` state and update file handlers on OS.
+  void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
+                               const ContentSettingsPattern& secondary_pattern,
+                               ContentSettingsType content_type) override;
+
+  // Checks if file handling permission is blocked in settings.
+  bool IsFileHandlerPermissionBlocked(const GURL& scope);
+  // Update file handler permission state in db and OS.
+  void UpdateFileHandlerPermission(const AppId& app_id,
+                                   bool permission_blocked);
+
+  // This catches permission changes occurring when browser is not active, like
+  // enterprise policy changes. It detects any file handling permission mismatch
+  // between the app db state and permission settings, and correct the state in
+  // db as well as in OS for all apps during `WebAppInstallFinalizer::Start()`.
+  void DetectAndCorrectFileHandlingPermissionBlocks();
+
   Profile* const profile_;
   WebAppIconManager* const icon_manager_;
   bool started_ = false;
 
-  base::WeakPtrFactory<WebAppInstallFinalizer> weak_ptr_factory_{this};
+  base::ScopedObservation<HostContentSettingsMap, content_settings::Observer>
+      content_settings_observer_{this};
 
+  base::WeakPtrFactory<WebAppInstallFinalizer> weak_ptr_factory_{this};
 };
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index f7ea680..e86130f 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -152,6 +152,12 @@
   return web_app ? &web_app->file_handlers() : nullptr;
 }
 
+bool WebAppRegistrar::IsAppFileHandlerPermissionBlocked(
+    const web_app::AppId& app_id) const {
+  auto* web_app = GetAppById(app_id);
+  return web_app ? web_app->file_handler_permission_blocked() : false;
+}
+
 base::Optional<GURL> WebAppRegistrar::GetAppScopeInternal(
     const AppId& app_id) const {
   auto* web_app = GetAppById(app_id);
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index c1a322b..34165b63 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -68,6 +68,8 @@
       const AppId& app_id) const override;
   const apps::FileHandlers* GetAppFileHandlers(
       const AppId& app_id) const override;
+  bool IsAppFileHandlerPermissionBlocked(
+      const web_app::AppId& app_id) const override;
   base::Optional<GURL> GetAppScopeInternal(const AppId& app_id) const override;
   DisplayMode GetAppDisplayMode(const AppId& app_id) const override;
   DisplayMode GetAppUserDisplayMode(const AppId& app_id) const override;
diff --git a/chrome/browser/web_applications/web_app_unittest.cc b/chrome/browser/web_applications/web_app_unittest.cc
index 68b2c488..57fc66f 100644
--- a/chrome/browser/web_applications/web_app_unittest.cc
+++ b/chrome/browser/web_applications/web_app_unittest.cc
@@ -182,6 +182,7 @@
 shortcuts_menu_item_infos:
 downloaded_shortcuts_menu_icons_sizes:
 file_handlers:
+file_handler_permission_blocked:0
 share_target:
   nullopt
 additional_search_terms:
@@ -312,6 +313,7 @@
     accept:
       mime_type: application/33849121404+bar
       file_extensions: .33849121404a .33849121404b
+file_handler_permission_blocked:0
 share_target:
   action: https://example.com/path/target/1210958276
   method: POST
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 539e6de..68c6f70 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1620399564-e3ddef8c10b78b4d3ac2ca41ad6bb0fe0fe32793.profdata
+chrome-win32-master-1620410207-913598f13c4b03569954787bde44624580e02c15.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 28c0e0a..75f508a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1620399564-e2566c1bf11558c8537396818f34997ceb1f6220.profdata
+chrome-win64-master-1620410207-a29f8c31737564ce599843b1d138b938d6502247.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 70e8121..956ba5cc 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -5,7 +5,6 @@
 import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/locales.gni")
 import("//chrome/browser/buildflags.gni")
-import("//chrome/browser/resources/pdf/ink/ink.gni")
 import("//chrome/common/features.gni")
 import("//extensions/buildflags/buildflags.gni")
 import("//ui/base/ui_features.gni")
@@ -251,13 +250,6 @@
         "//ui/file_manager:resources",
       ]
 
-      if (!enable_use_media_app_ink) {
-        # TODO(crbug/1150244): Remove when enable_use_media_app_ink is default
-        # true.
-        sources += [ "$root_gen_dir/third_party/ink/ink_resources.pak" ]
-        deps += [ "//third_party/ink:ink_resources" ]
-      }
-
       if (!is_official_build) {
         sources += [
           "$root_gen_dir/chromeos/chromeos_demo_mode_app_resources.pak",
diff --git a/chrome/renderer/translate/translate_agent_browsertest.cc b/chrome/renderer/translate/translate_agent_browsertest.cc
index ad32332..bc8680dc 100644
--- a/chrome/renderer/translate/translate_agent_browsertest.cc
+++ b/chrome/renderer/translate/translate_agent_browsertest.cc
@@ -13,6 +13,7 @@
 #include "components/translate/content/common/translate.mojom.h"
 #include "components/translate/content/renderer/translate_agent.h"
 #include "components/translate/core/common/translate_constants.h"
+#include "content/public/common/url_constants.h"
 #include "content/public/renderer/render_frame.h"
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/constants.h"
@@ -30,6 +31,12 @@
 
 namespace {
 
+std::string UpdateGURLScheme(GURL url, const char scheme[]) {
+  GURL::Replacements replacements;
+  replacements.SetScheme(scheme, url::Component(0, strlen(scheme)));
+  return url.ReplaceComponents(replacements).spec();
+}
+
 class FakeContentTranslateDriver
     : public translate::mojom::ContentTranslateDriver {
  public:
@@ -530,3 +537,38 @@
   ASSERT_TRUE(fake_translate_driver_.called_new_page_);
   EXPECT_EQ("es", fake_translate_driver_.details_->adopted_language);
 }
+
+TEST_F(TranslateAgentBrowserTest, UnsupportedTranslateSchemes) {
+  GURL url("https://foo.com");
+  LoadHTMLWithUrlOverride(
+      "<html><body>A random page with random content.</body></html>",
+      UpdateGURLScheme(url, content::kChromeUIScheme).c_str());
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(fake_translate_driver_.called_new_page_);
+  EXPECT_FALSE(fake_translate_driver_.page_level_translation_critiera_met_);
+
+  LoadHTMLWithUrlOverride(
+      "<html><body>A random page with random content.</body></html>",
+      UpdateGURLScheme(url, url::kFtpScheme).c_str());
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(fake_translate_driver_.called_new_page_);
+  EXPECT_FALSE(fake_translate_driver_.page_level_translation_critiera_met_);
+
+  LoadHTMLWithUrlOverride(
+      "<html><body>A random page with random content.</body></html>",
+      url::kAboutBlankURL);
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(fake_translate_driver_.called_new_page_);
+  EXPECT_FALSE(fake_translate_driver_.page_level_translation_critiera_met_);
+
+  LoadHTMLWithUrlOverride(
+      "<html><body>A random page with random content.</body></html>",
+      UpdateGURLScheme(url, content::kChromeDevToolsScheme).c_str());
+
+  base::RunLoop().RunUntilIdle();
+  ASSERT_FALSE(fake_translate_driver_.called_new_page_);
+  EXPECT_FALSE(fake_translate_driver_.page_level_translation_critiera_met_);
+}
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc
index f3f5822..c2a7430 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -33,12 +33,12 @@
 // static
 const char
     SpeechRecognitionRecognizerImpl::kCaptionBubbleVisibleHistogramName[] =
-        "Accessibility.LiveCaption.Duration.CaptionBubbleVisible";
+        "Accessibility.LiveCaption.Duration.CaptionBubbleVisible2";
 
 // static
 const char
     SpeechRecognitionRecognizerImpl::kCaptionBubbleHiddenHistogramName[] =
-        "Accessibility.LiveCaption.Duration.CaptionBubbleHidden";
+        "Accessibility.LiveCaption.Duration.CaptionBubbleHidden2";
 
 namespace {
 
@@ -297,13 +297,13 @@
 
 void SpeechRecognitionRecognizerImpl::RecordDuration() {
   if (caption_bubble_visible_duration_ > base::TimeDelta()) {
-    base::UmaHistogramMediumTimes(kCaptionBubbleVisibleHistogramName,
-                                  caption_bubble_visible_duration_);
+    base::UmaHistogramLongTimes100(kCaptionBubbleVisibleHistogramName,
+                                   caption_bubble_visible_duration_);
   }
 
   if (caption_bubble_hidden_duration_ > base::TimeDelta()) {
-    base::UmaHistogramMediumTimes(kCaptionBubbleHiddenHistogramName,
-                                  caption_bubble_hidden_duration_);
+    base::UmaHistogramLongTimes100(kCaptionBubbleHiddenHistogramName,
+                                   caption_bubble_hidden_duration_);
   }
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b14850e..83e00df 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1830,6 +1830,12 @@
       ]
     }
 
+    if (is_chromeos_lacros) {
+      sources += [
+        "../browser/apps/app_service/web_apps_publisher_host_browsertest.cc",
+      ]
+    }
+
     if (!is_chromeos) {
       sources +=
           [ "../browser/policy/test/native_messaging_policy_browsertest.cc" ]
@@ -6830,6 +6836,7 @@
         "../browser/ui/read_later/read_later_test_utils.cc",
         "../browser/ui/read_later/read_later_test_utils.h",
         "../browser/ui/views/autofill/payments/offer_notification_bubble_views_interactive_uitest.cc",
+        "../browser/ui/views/autofill/payments/virtual_card_manual_fallback_bubble_views_interactive_uitest.cc",
         "../browser/ui/views/bookmarks/bookmark_bar_view_test.cc",
         "../browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h",
         "../browser/ui/views/certificate_selector_browsertest.cc",
diff --git a/chrome/test/chromedriver/element_util.cc b/chrome/test/chromedriver/element_util.cc
index 3a42532..2c57910 100644
--- a/chrome/test/chromedriver/element_util.cc
+++ b/chrome/test/chromedriver/element_util.cc
@@ -594,6 +594,8 @@
                                   element_id, property_name, property_value);
 }
 
+// Wrapper to JavaScript code in js/get_element_region.js. See comments near the
+// beginning of that file for what is returned.
 Status GetElementRegion(
     Session* session,
     WebView* web_view,
@@ -800,6 +802,16 @@
   return Status(kOk);
 }
 
+// Scroll a region of an element (identified by |element_id|) into view.
+// It first scrolls the element region relative to its enclosing viewport,
+// so that the region becomes visible in that viewport.
+// If that viewport is a frame, it then makes necessary scroll to make the
+// region of the frame visible in its enclosing viewport. It repeats this up
+// the frame chain until it reaches the top-level viewport.
+//
+// Upon return, |location| gives the location of the region relative to the
+// top-level viewport. If |center| is true, the location is for the center of
+// the region, otherwise it is for the upper-left corner of the region.
 Status ScrollElementRegionIntoView(
     Session* session,
     WebView* web_view,
@@ -810,6 +822,7 @@
     WebPoint* location) {
   WebPoint region_offset = region.origin;
   WebSize region_size = region.size;
+  // Scroll the element region in its enclosing viewport.
   Status status = ScrollElementRegionIntoViewHelper(
       session->GetCurrentFrameId(), web_view, element_id, region,
       center, clickable_element_id, &region_offset);
@@ -820,6 +833,9 @@
       "  return document.evaluate(xpath, document, null,"
       "      XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
       "}";
+  // If the element is in a frame, go up the frame chain (from the innermost
+  // frame up to the top-level window) and scroll each frame relative to its
+  // parent frame, so that the region becomes visible in the parent frame.
   for (auto rit = session->frames.rbegin(); rit != session->frames.rend();
        ++rit) {
     base::ListValue args;
diff --git a/chrome/test/chromedriver/js/get_element_region.js b/chrome/test/chromedriver/js/get_element_region.js
index 472db87..f40d1c1 100644
--- a/chrome/test/chromedriver/js/get_element_region.js
+++ b/chrome/test/chromedriver/js/get_element_region.js
@@ -2,6 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// Return the portion of the element that should be made visible.
+// Based on the WebDriver spec, this function only considers the first rectangle
+// returned by element.getClientRects function.
+// * When the rectangle is already partially visible in the enclosing viewport,
+//   return the portion that is currently visible. According to WebDriver spec,
+//   no scrolling should be done to bring more of the element into view.
+// * When the rectangle is completely outside of the enclosing viewport,
+//   return the entire rectangle, as WebDriver spec requires us to scroll the
+//   entire rectangle into view. (However, scrolling is NOT the responsibility
+//   of this function.)
+//
+// The returned value is an object with the following properties about the
+// region mentioned above: left, top, height, width. Note that left and top are
+// relative to the upper-left corner of the element's bounding client rect (as
+// returned by element.getBoundingClientRect).
 function getElementRegion(element) {
   // Check that node type is element.
   if (element.nodeType != 1)
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn
index 8b3f475..790325ef 100644
--- a/chrome/test/data/pdf/BUILD.gn
+++ b/chrome/test/data/pdf/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/browser/resources/pdf/ink/ink.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 
 js_type_check("closure_compile") {
@@ -49,8 +50,8 @@
     #":zoom_manager_test",
   ]
 
-  if (is_chromeos) {
-    deps += [ ":material_elements_cros_test" ]
+  if (enable_ink) {
+    deps += [ ":viewer_toolbar_dropdown_test" ]
   }
 }
 
@@ -103,8 +104,8 @@
   externs_list = [ "$externs_path/test.js" ]
 }
 
-if (is_chromeos) {
-  js_library("material_elements_cros_test") {
+if (enable_ink) {
+  js_library("viewer_toolbar_dropdown_test") {
     deps = [ "//chrome/browser/resources/pdf:pdf_viewer_wrapper" ]
     externs_list = [ "$externs_path/test.js" ]
   }
diff --git a/chrome/test/data/pdf/material_elements_cros_test.js b/chrome/test/data/pdf/viewer_toolbar_dropdown_test.js
similarity index 100%
rename from chrome/test/data/pdf/material_elements_cros_test.js
rename to chrome/test/data/pdf/viewer_toolbar_dropdown_test.js
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 1bfc887..b412bb4 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -8847,5 +8847,8 @@
         }}
       }
     ]
+  },
+  "CloudUserPolicyMerge": {
+    "note": "This policy has no pref as it is only directly read by the policy system."
   }
 }
diff --git a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
index f0d7d46..445aa93 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanning_app_test.js
@@ -7,7 +7,7 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js';
 import {setScanServiceForTesting} from 'chrome://scanning/mojo_interface_provider.js';
-import {ScannerArr} from 'chrome://scanning/scanning_app_types.js';
+import {ScannerArr, ScannerSetting, ScanSettings} from 'chrome://scanning/scanning_app_types.js';
 import {tokenToString} from 'chrome://scanning/scanning_app_util.js';
 import {ScanningBrowserProxyImpl} from 'chrome://scanning/scanning_browser_proxy.js';
 
@@ -19,6 +19,9 @@
 
 const MY_FILES_PATH = '/home/chronos/user/MyFiles';
 
+// An arbitrary date needed to replace |lastScanDate| in saved scan settings.
+const LAST_SCAN_DATE = new Date('1/1/2021');
+
 // Scanner sources names.
 const ADF_DUPLEX = 'adf_duplex';
 const ADF_SIMPLEX = 'adf_simplex';
@@ -426,6 +429,26 @@
     });
   }
 
+  /**
+   * Deep equals two ScanSettings objects.
+   * @param {!ScanSettings} expectedScanSettings
+   * @param {!ScanSettings} actualScanSettings
+   */
+  function compareSavedScanSettings(expectedScanSettings, actualScanSettings) {
+    assertEquals(
+        expectedScanSettings.lastUsedScannerName,
+        actualScanSettings.lastUsedScannerName);
+    assertEquals(
+        expectedScanSettings.scanToPath, actualScanSettings.scanToPath);
+
+    // Replace |lastScanDate|, which is a current date timestamp, with a fixed
+    // date so assertArrayEquals() can be used.
+    actualScanSettings.scanners.forEach(
+        scanner => scanner.lastScanDate = LAST_SCAN_DATE);
+    assertArrayEquals(
+        expectedScanSettings.scanners, actualScanSettings.scanners);
+  }
+
   // Verify a full scan job can be completed.
   test('Scan', () => {
     /** @type {!Array<!mojoBase.mojom.FilePath>} */
@@ -1223,7 +1246,7 @@
       return;
     }
 
-    const scanSavedSettings = {
+    const savedScanSettings = {
       lastUsedScannerName: secondScannerName,
       scanToPath: 'scan/to/path',
       scanners: [{
@@ -1236,7 +1259,7 @@
         resolutionDpi: 75,
       }],
     };
-    testBrowserProxy.setSavedSettings(JSON.stringify(scanSavedSettings));
+    testBrowserProxy.setSavedSettings(JSON.stringify(savedScanSettings));
 
     return initializeScanningApp(expectedScanners, capabilities)
         .then(() => {
@@ -1248,4 +1271,124 @@
               scanningApp.$$('#scannerSelect').$$('select').value);
         });
   });
+
+  // Verify the scan settings are sent to the Pref service to be saved.
+  test('saveScanSettings', () => {
+    if (!loadTimeData.getBoolean('scanAppStickySettingsEnabled')) {
+      return;
+    }
+
+    const scannerSetting = {
+      name: secondScannerName,
+      lastScanDate: LAST_SCAN_DATE,
+      sourceName: ADF_DUPLEX,
+      fileType: ash.scanning.mojom.FileType.kPng,
+      colorMode: ash.scanning.mojom.ColorMode.kBlackAndWhite,
+      pageSize: ash.scanning.mojom.PageSize.kMax,
+      resolutionDpi: 100,
+    };
+
+    const savedScanSettings = {
+      lastUsedScannerName: secondScannerName,
+      scanToPath: MY_FILES_PATH,
+      scanners: [scannerSetting],
+    };
+
+    return initializeScanningApp(expectedScanners, capabilities)
+        .then(() => {
+          return getScannerCapabilities();
+        })
+        .then(() => {
+          scanningApp.selectedScannerId = tokenToString(secondScannerId);
+          scanningApp.selectedSource = scannerSetting.sourceName;
+          scanningApp.selectedFileType = scannerSetting.fileType.toString();
+          scanningApp.selectedColorMode = scannerSetting.colorMode.toString();
+          scanningApp.selectedPageSize = scannerSetting.pageSize.toString();
+          scanningApp.selectedResolution =
+              scannerSetting.resolutionDpi.toString();
+
+          scanningApp.$$('#scanButton').click();
+
+          const actualSavedScanSettings = /** @type {!ScanSettings} */
+              (JSON.parse(/** @type {string} */ (
+                  testBrowserProxy.getArgs('saveScanSettings')[0])));
+          compareSavedScanSettings(savedScanSettings, actualSavedScanSettings);
+        });
+  });
+
+  // Verify that the correct scanner setting is replaced when saving scan
+  // settings to the Pref service.
+  test('replaceExistingScannerInScanSettings', () => {
+    if (!loadTimeData.getBoolean('scanAppStickySettingsEnabled')) {
+      return;
+    }
+
+    const firstScannerSetting = {
+      name: firstScannerName,
+      lastScanDate: LAST_SCAN_DATE,
+      sourceName: ADF_DUPLEX,
+      fileType: ash.scanning.mojom.FileType.kPng,
+      colorMode: ash.scanning.mojom.ColorMode.kBlackAndWhite,
+      pageSize: ash.scanning.mojom.PageSize.kMax,
+      resolutionDpi: 100,
+    };
+
+    // The saved scan settings for the second scanner. This is loaded from the
+    // Pref service when Scan app is initialized and sets the initial scan
+    // settings.
+    const initialSecondScannerSetting = {
+      name: secondScannerName,
+      lastScanDate: LAST_SCAN_DATE,
+      sourceName: ADF_DUPLEX,
+      fileType: ash.scanning.mojom.FileType.kPng,
+      colorMode: ash.scanning.mojom.ColorMode.kBlackAndWhite,
+      pageSize: ash.scanning.mojom.PageSize.kMax,
+      resolutionDpi: 100,
+    };
+
+    const savedScanSettings = {
+      lastUsedScannerName: secondScannerName,
+      scanToPath: MY_FILES_PATH,
+      scanners: [firstScannerSetting, initialSecondScannerSetting],
+    };
+
+    testBrowserProxy.setSavedSettings(JSON.stringify(savedScanSettings));
+
+    // The new scan settings for the second scanner that will replace the
+    // initial scan settings.
+    const newSecondScannerSetting = {
+      name: secondScannerName,
+      lastScanDate: LAST_SCAN_DATE,
+      sourceName: ADF_SIMPLEX,
+      fileType: ash.scanning.mojom.FileType.kJpg,
+      colorMode: ash.scanning.mojom.ColorMode.kGrayscale,
+      pageSize: ash.scanning.mojom.PageSize.kIsoA4,
+      resolutionDpi: 600,
+    };
+    savedScanSettings.scanners[1] = newSecondScannerSetting;
+
+    return initializeScanningApp(expectedScanners, capabilities)
+        .then(() => {
+          return getScannerCapabilities();
+        })
+        .then(() => {
+          scanningApp.selectedScannerId = tokenToString(secondScannerId);
+          scanningApp.selectedSource = newSecondScannerSetting.sourceName;
+          scanningApp.selectedFileType =
+              newSecondScannerSetting.fileType.toString();
+          scanningApp.selectedColorMode =
+              newSecondScannerSetting.colorMode.toString();
+          scanningApp.selectedPageSize =
+              newSecondScannerSetting.pageSize.toString();
+          scanningApp.selectedResolution =
+              newSecondScannerSetting.resolutionDpi.toString();
+
+          scanningApp.$$('#scanButton').click();
+
+          const actualSavedScanSettings = /** @type {!ScanSettings} */
+              (JSON.parse(/** @type {string} */ (
+                  testBrowserProxy.getArgs('saveScanSettings')[0])));
+          compareSavedScanSettings(savedScanSettings, actualSavedScanSettings);
+        });
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js b/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js
index e05187f0..5f6a775e 100644
--- a/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js
+++ b/chrome/test/data/webui/chromeos/scanning/test_scanning_browser_proxy.js
@@ -110,7 +110,9 @@
   }
 
   /** @override */
-  saveScanSettings(scanSettings) {}
+  saveScanSettings(scanSettings) {
+    this.methodCalled('saveScanSettings', scanSettings);
+  }
 
   /** @override */
   getScanSettings() {
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/network_siminfo_test.js b/chrome/test/data/webui/cr_components/chromeos/network/network_siminfo_test.js
index d00e4b3..93156494 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/network_siminfo_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/network_siminfo_test.js
@@ -8,6 +8,7 @@
 
 // #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 // #import {OncMojo} from 'chrome://resources/cr_components/chromeos/network/onc_mojo.m.js';
+// #import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 // clang-format on
 
 suite('NetworkSiminfoTest', function() {
@@ -25,9 +26,6 @@
     cellularNetwork.typeState.cellular.iccid = TEST_ICCID;
 
     simInfo.networkState = cellularNetwork;
-    updateDeviceState(
-        /*isPrimary=*/ true, /*lockEnabled=*/ true, /*isLocked=*/ false);
-
     document.body.appendChild(simInfo);
     await flushAsync();
   });
@@ -90,6 +88,44 @@
     assertTrue(!!getSimLockDialogElement());
   }
 
+  test('Set focus after dialog close', async function() {
+    const getSimLockDialogs = () => simInfo.$$('sim-lock-dialogs');
+    const getSimLockButton = () => simInfo.$$('#simLockButton');
+    const getUnlockPinButton = () => simInfo.$$('#unlockPinButton');
+
+    // SIM lock dialog toggle.
+    updateDeviceState(
+        /*isPrimary=*/ true, /*lockEnabled=*/ false, /*isLocked=*/ false);
+    assertTrue(!!getSimLockButton());
+    assertFalse(!!getSimLockDialogs());
+    getSimLockButton().click();
+    await flushAsync();
+
+    assertTrue(!!getSimLockDialogs());
+
+    // Simulate dialog close.
+    getSimLockDialogs().closeDialogsForTest();
+    await flushAsync();
+    assertFalse(!!getSimLockDialogs());
+    assertEquals(getSimLockButton(), getDeepActiveElement());
+
+    // SIM unlock pin button.
+    updateDeviceState(
+        /*isPrimary=*/ true, /*lockEnabled=*/ true, /*isLocked=*/ true);
+    await flushAsync();
+    assertTrue(!!getUnlockPinButton());
+    assertFalse(!!getSimLockDialogs());
+    getUnlockPinButton().click();
+    await flushAsync();
+    assertTrue(!!getSimLockDialogs());
+
+    // Simulate dialog close.
+    getSimLockDialogs().closeDialogsForTest();
+    await flushAsync();
+    assertFalse(!!getSimLockDialogs());
+    assertEquals(getUnlockPinButton(), getDeepActiveElement());
+  });
+
   test('SIM missing UI shown ', function() {
     const isSimMissingShown = (updatedUiEnabled) => {
       simInfo.isUpdatedCellularUiEnabled_ = updatedUiEnabled;
diff --git a/chrome/test/data/webui/read_later/side_panel/BUILD.gn b/chrome/test/data/webui/read_later/side_panel/BUILD.gn
index 1c3b65fb..f68d506 100644
--- a/chrome/test/data/webui/read_later/side_panel/BUILD.gn
+++ b/chrome/test/data/webui/read_later/side_panel/BUILD.gn
@@ -12,6 +12,7 @@
                     "js_module_root=./gen/chrome/test/data/webui/",
                   ]
   deps = [
+    ":bookmark_folder_test",
     ":bookmarks_list_test",
     ":test_bookmarks_api_proxy",
   ]
@@ -21,11 +22,21 @@
   deps = [
     ":test_bookmarks_api_proxy",
     "../..:chai_assert",
+    "//chrome/browser/resources/read_later/side_panel:bookmark_folder",
     "//chrome/browser/resources/read_later/side_panel:bookmarks_list",
   ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
+js_library("bookmark_folder_test") {
+  deps = [
+    "../..:chai_assert",
+    "//chrome/browser/resources/read_later/side_panel:bookmark_folder",
+    "//ui/webui/resources/js:icon.m",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
 js_library("test_bookmarks_api_proxy") {
   deps = [
     "../..:test_browser_proxy.m",
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js b/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js
new file mode 100644
index 0000000..8de6fe1
--- /dev/null
+++ b/chrome/test/data/webui/read_later/side_panel/bookmark_folder_test.js
@@ -0,0 +1,103 @@
+// 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.
+
+// ReadLaterUI is a Mojo WebUI controller and therefore needs mojo defined to
+// finish running its tests.
+import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
+
+import {BookmarkFolderElement} from 'chrome://read-later.top-chrome/side_panel/bookmark_folder.js';
+import {getFaviconForPageURL} from 'chrome://resources/js/icon.m.js';
+
+import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {flushTasks} from '../../test_util.m.js';
+
+suite('SidePanelBookmarkFolderTest', () => {
+  /** @type {!BookmarkFolderElement} */
+  let bookmarkFolder;
+
+  /** @type {!chrome.bookmarks.BookmarkTreeNode} */
+  const folder = {
+    id: '0',
+    title: 'Bookmarks bar',
+    children: [
+      {
+        id: '1',
+        title: 'Shopping list',
+        children: [],
+      },
+      {
+        id: '2',
+        title: 'Foo website',
+        url: 'http://foo/',
+      },
+      {
+        id: '3',
+        title: 'Bar website',
+        url: 'http://bar/',
+      },
+    ],
+  };
+
+  /** @return {!Array<!HTMLElement|!BookmarkFolderElement>} */
+  function getChildElements() {
+    return /** @type {!Array<!HTMLElement|!BookmarkFolderElement>} */ (
+        Array.from(bookmarkFolder.shadowRoot.querySelectorAll(
+            'bookmark-folder, .bookmark')));
+  }
+
+  setup(async () => {
+    document.body.innerHTML = '';
+
+    bookmarkFolder = /** @type {!BookmarkFolderElement} */ (
+        document.createElement('bookmark-folder'));
+    bookmarkFolder.folder = folder;
+    document.body.appendChild(bookmarkFolder);
+
+    await flushTasks();
+  });
+
+  test('UpdatesDepthVariables', () => {
+    bookmarkFolder.depth = 3;
+    assertEquals('3', bookmarkFolder.style.getPropertyValue('--node-depth'));
+    assertEquals(
+        '4',
+        bookmarkFolder.shadowRoot.querySelector('#children')
+            .style.getPropertyValue('--node-depth'));
+  });
+
+  test('RendersChildren', () => {
+    const childElements = getChildElements();
+    assertEquals(3, childElements.length);
+
+    assertTrue(childElements[0] instanceof BookmarkFolderElement);
+    assertEquals(folder.children[0], childElements[0].folder);
+
+    assertEquals(
+        folder.children[1].title,
+        childElements[1].querySelector('.title').innerText);
+    assertEquals(
+        folder.children[2].title,
+        childElements[2].querySelector('.title').innerText);
+  });
+
+  test('ShowsFaviconForBookmarks', () => {
+    const fooWebsiteElement = getChildElements()[1];
+    assertEquals(
+        getFaviconForPageURL(folder.children[1].url, false),
+        fooWebsiteElement.querySelector('.icon').style.getPropertyValue(
+            'background-image'));
+  });
+
+  test('OpensAndClosesFolder', () => {
+    const arrowIcon = bookmarkFolder.shadowRoot.querySelector('#arrowIcon');
+    const childrenElement =
+        bookmarkFolder.shadowRoot.querySelector('#children');
+    assertFalse(arrowIcon.hasAttribute('open'));
+    assertTrue(childrenElement.hasAttribute('hidden'));
+
+    bookmarkFolder.shadowRoot.querySelector('.row').click();
+    assertTrue(arrowIcon.hasAttribute('open'));
+    assertFalse(childrenElement.hasAttribute('hidden'));
+  });
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js b/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js
index d0733fe..ae35be4 100644
--- a/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js
+++ b/chrome/test/data/webui/read_later/side_panel/bookmarks_list_test.js
@@ -6,16 +6,17 @@
 // finish running its tests.
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 
+import {BookmarkFolderElement} from 'chrome://read-later.top-chrome/side_panel/bookmark_folder.js';
 import {BookmarksApiProxyImpl} from 'chrome://read-later.top-chrome/side_panel/bookmarks_api_proxy.js';
-import {BookmarksList} from 'chrome://read-later.top-chrome/side_panel/bookmarks_list.js';
+import {BookmarksListElement} from 'chrome://read-later.top-chrome/side_panel/bookmarks_list.js';
 
-import {assertEquals} from '../../chai_assert.js';
+import {assertEquals, assertTrue} from '../../chai_assert.js';
 import {flushTasks} from '../../test_util.m.js';
 
 import {TestBookmarksApiProxy} from './test_bookmarks_api_proxy.js';
 
 suite('SidePanelBookmarksListTest', () => {
-  /** @type {!BookmarksList} */
+  /** @type {!BookmarksListElement} */
   let bookmarksList;
 
   /** @type {!TestBookmarksApiProxy} */
@@ -40,6 +41,12 @@
     },
   ];
 
+  /** @return {!Array<!BookmarkFolderElement>} */
+  function getFolderElements() {
+    return /** @type {!Array<!BookmarkFolderElement>} */ (Array.from(
+        bookmarksList.shadowRoot.querySelectorAll('bookmark-folder')));
+  }
+
   setup(async () => {
     document.body.innerHTML = '';
 
@@ -47,7 +54,7 @@
     bookmarksApi.setFolders(folders);
     BookmarksApiProxyImpl.setInstance(bookmarksApi);
 
-    bookmarksList = /** @type {!BookmarksList} */ (
+    bookmarksList = /** @type {!BookmarksListElement} */ (
         document.createElement('bookmarks-list'));
     document.body.appendChild(bookmarksList);
 
@@ -56,8 +63,10 @@
 
   test('GetsAndShowsFolders', () => {
     assertEquals(1, bookmarksApi.getCallCount('getFolders'));
-    const folderElements =
-        bookmarksList.shadowRoot.querySelectorAll('bookmark-folder');
-    assertEquals(folders.length, folderElements.length);
+    assertEquals(folders.length, getFolderElements().length);
+  });
+
+  test('OpensFirstFolderByDefault', () => {
+    assertTrue(getFolderElements()[0].openByDefault);
   });
 });
\ No newline at end of file
diff --git a/chrome/test/data/webui/read_later/side_panel/side_panel_browsertest.js b/chrome/test/data/webui/read_later/side_panel/side_panel_browsertest.js
index 7a8b4263..1b3bcbe 100644
--- a/chrome/test/data/webui/read_later/side_panel/side_panel_browsertest.js
+++ b/chrome/test/data/webui/read_later/side_panel/side_panel_browsertest.js
@@ -31,3 +31,16 @@
 TEST_F('SidePanelBookmarksListTest', 'All', function() {
   mocha.run();
 });
+
+
+// eslint-disable-next-line no-var
+var SidePanelBookmarkFolderTest = class extends SidePanelBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://read-later.top-chrome/test_loader.html?module=read_later/side_panel/bookmark_folder_test.js';
+  }
+};
+
+TEST_F('SidePanelBookmarkFolderTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
index ad57b97e..f5f1030 100644
--- a/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_reset_page_test.js
@@ -84,12 +84,10 @@
 
       // Open powerwash dialog.
       assertTrue(!!resetPage);
-      resetPage.$.powerwash.click();
+      resetPage.$$('#powerwash').click();
       Polymer.dom.flush();
       const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      assertTrue(!!dialog);
-      assertTrue(dialog.$.dialog.open);
-      assertTrue(!!dialog.$$('#powerwashContainer'));
+      assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
       const onDialogClosed = new Promise(function(resolve, reject) {
         dialog.addEventListener('close', function() {
           assertFalse(dialog.$.dialog.open);
@@ -105,6 +103,25 @@
     }
 
     /**
+     * @param {boolean} shouldBeShowingESimWarning
+     */
+    function assertOpenDialogUIState(shouldBeShowingESimWarning) {
+      const dialog = resetPage.$$('os-settings-powerwash-dialog');
+      assertTrue(!!dialog);
+      assertTrue(dialog.$.dialog.open);
+
+      assertEquals(
+          !!dialog.$$('#powerwashContainer'), !shouldBeShowingESimWarning);
+      assertEquals(
+          !!dialog.$$('#powerwashContainer'), !shouldBeShowingESimWarning);
+      assertEquals(!!dialog.$$('#powerwash'), !shouldBeShowingESimWarning);
+
+      assertEquals(
+          !!dialog.$$('#profilesListContainer'), shouldBeShowingESimWarning);
+      assertEquals(!!dialog.$$('#continue'), shouldBeShowingESimWarning);
+    }
+
+    /**
      * Navigates to the deep link provided by |settingId| and returns true if
      * the focused element is |deepLinkElement|.
      * @param {!Element} deepLinkElement
@@ -136,12 +153,11 @@
       init(/*updatedCellularActivationUi=*/ false);
 
       // Open powerwash dialog.
-      resetPage.$.powerwash.click();
+      resetPage.$$('#powerwash').click();
       Polymer.dom.flush();
       const dialog = resetPage.$$('os-settings-powerwash-dialog');
-      assertTrue(!!dialog);
-      assertTrue(!!dialog.$$('#powerwashContainer'));
-      dialog.$.powerwash.click();
+      assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ false);
+      dialog.$$('#powerwash').click();
       const requestTpmFirmwareUpdate =
           await lifetimeBrowserProxy.whenCalled('factoryReset');
       assertFalse(requestTpmFirmwareUpdate);
@@ -155,7 +171,8 @@
       loadTimeData.overrideValues({isDeepLinkingEnabled: true});
       assertTrue(loadTimeData.getBoolean('isDeepLinkingEnabled'));
       assertTrue(
-          await isDeepLinkFocusedForSettingId(resetPage.$.powerwash, '1600'),
+          await isDeepLinkFocusedForSettingId(
+              resetPage.$$('#powerwash'), '1600'),
           'Powerwash should be focused for settingId=1600.');
     });
 
@@ -167,7 +184,8 @@
       loadTimeData.overrideValues({isDeepLinkingEnabled: false});
       assertFalse(loadTimeData.getBoolean('isDeepLinkingEnabled'));
       assertFalse(
-          await isDeepLinkFocusedForSettingId(resetPage.$.powerwash, '1600'),
+          await isDeepLinkFocusedForSettingId(
+              resetPage.$$('#powerwash'), '1600'),
           'Powerwash should not be focused with flag disabled.');
     });
 
@@ -179,7 +197,8 @@
       loadTimeData.overrideValues({isDeepLinkingEnabled: true});
       assertTrue(loadTimeData.getBoolean('isDeepLinkingEnabled'));
       assertFalse(
-          await isDeepLinkFocusedForSettingId(resetPage.$.powerwash, '1234'),
+          await isDeepLinkFocusedForSettingId(
+              resetPage.$$('#powerwash'), '1234'),
           'Powerwash should not be focused for settingId=1234.');
     });
 
@@ -215,14 +234,13 @@
               chromeos.cellularSetup.mojom.ProfileState.kActive;
 
           // Click the powerwash button.
-          resetPage.$.powerwash.click();
+          resetPage.$$('#powerwash').click();
           await flushAsync();
 
           // The eSIM warning should be showing.
+          assertOpenDialogUIState(/*shouldBeShowingESimWarning=*/ true);
           const dialog = resetPage.$$('os-settings-powerwash-dialog');
-          assertTrue(!!dialog);
-          assertTrue(dialog.$.dialog.open);
-          assertFalse(!!dialog.$$('#powerwashContainer'));
+          assertEquals(dialog.$$('iron-list').items.length, 1);
         });
   });
 
diff --git a/components/arc/compat_mode/arc_splash_screen_dialog_view.cc b/components/arc/compat_mode/arc_splash_screen_dialog_view.cc
index 38df8fd..3308b69 100644
--- a/components/arc/compat_mode/arc_splash_screen_dialog_view.cc
+++ b/components/arc/compat_mode/arc_splash_screen_dialog_view.cc
@@ -45,7 +45,7 @@
   auto close_button = views::CreateVectorImageButtonWithNativeTheme(
       std::move(close_callback), vector_icons::kCloseRoundedIcon);
   close_button->SetSize(kCloseButtonSize);
-  close_button->SetInkDropMode(views::Button::InkDropMode::OFF);
+  close_button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
   return close_button;
 }
 
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index e5a63aa..6e2021c 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -193,12 +193,21 @@
     const std::vector<FormStructure*>& forms) {
   if (!RendererIsAvailable())
     return;
-
+  // TODO(crbug.com/1185232) Send the FormDataPredictions object only if the
+  // debugging flag is enabled.
   std::vector<FormDataPredictions> type_predictions =
       FormStructure::GetFieldTypePredictions(forms);
   GetAutofillAgent()->FieldTypePredictionsAvailable(type_predictions);
 }
 
+void ContentAutofillDriver::SendFieldsEligibleForManualFillingToRenderer(
+    const std::vector<FieldRendererId>& fields) {
+  if (!RendererIsAvailable())
+    return;
+
+  GetAutofillAgent()->SetFieldsEligibleForManualFilling(fields);
+}
+
 void ContentAutofillDriver::RendererShouldAcceptDataListSuggestion(
     const FieldGlobalId& field,
     const std::u16string& value) {
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index ca0f934..413adf8 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -106,6 +106,8 @@
   gfx::RectF TransformBoundingBoxToViewportCoordinates(
       const gfx::RectF& bounding_box) override;
   net::IsolationInfo IsolationInfo() override;
+  void SendFieldsEligibleForManualFillingToRenderer(
+      const std::vector<FieldRendererId>& fields) override;
 
   // mojom::AutofillDriver:
   void SetFormToBeProbablySubmitted(
diff --git a/components/autofill/content/browser/content_autofill_driver_unittest.cc b/components/autofill/content/browser/content_autofill_driver_unittest.cc
index ddeafa755..080e9d9 100644
--- a/components/autofill/content/browser/content_autofill_driver_unittest.cc
+++ b/components/autofill/content/browser/content_autofill_driver_unittest.cc
@@ -235,6 +235,9 @@
 
   void SetAssistantActionState(bool running) override {}
 
+  void SetFieldsEligibleForManualFilling(
+      const std::vector<FieldRendererId>& fields) override {}
+
   mojo::AssociatedReceiverSet<mojom::AutofillAgent> receivers_;
 
   base::OnceClosure quit_closure_;
diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom
index 3bad5270..1e8bab8 100644
--- a/components/autofill/content/common/mojom/autofill_agent.mojom
+++ b/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -86,6 +86,9 @@
   // Allows heavy scraping of form data (e.g., button titles for
   // unowned forms).
   EnableHeavyFormDataScraping();
+
+  // Update fields that are eligible to show manual filling on form interaction.
+  SetFieldsEligibleForManualFilling(array<FieldRendererId> fields);
 };
 
 // There is one instance of this interface per render frame in the render
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 7d66cce..d8c31ad9 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -754,6 +754,11 @@
   is_heavy_form_data_scraping_enabled_ = true;
 }
 
+void AutofillAgent::SetFieldsEligibleForManualFilling(
+    const std::vector<FieldRendererId>& fields) {
+  form_cache_.SetFieldsEligibleForManualFilling(fields);
+}
+
 void AutofillAgent::QueryAutofillSuggestions(
     const WebFormControlElement& element,
     bool autoselect_first_suggestion) {
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index eec8d77e..e2a0d87 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -106,6 +106,8 @@
       GetElementFormAndFieldDataAtIndexCallback callback) override;
   void SetAssistantActionState(bool running) override;
   void EnableHeavyFormDataScraping() override;
+  void SetFieldsEligibleForManualFilling(
+      const std::vector<FieldRendererId>& fields) override;
 
   void FormControlElementClicked(const blink::WebFormControlElement& element,
                                  bool was_focused);
diff --git a/components/autofill/content/renderer/form_autofill_util_browsertest.cc b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
index c19c9049..0b613eb 100644
--- a/components/autofill/content/renderer/form_autofill_util_browsertest.cc
+++ b/components/autofill/content/renderer/form_autofill_util_browsertest.cc
@@ -50,7 +50,7 @@
 struct AutofillFieldUtilCase {
   const char* description;
   const char* html;
-  const char* expected_label;
+  const char16_t* expected_label;
 };
 
 const char kElevenChildren[] =
@@ -67,8 +67,8 @@
     "<div>child9</div>"
     "<div>child10</div>"
     "</div>";
-const char kElevenChildrenExpected[] =
-    "child0child1child2child3child4child5child6child7child8";
+const char16_t kElevenChildrenExpected[] =
+    u"child0child1child2child3child4child5child6child7child8";
 
 const char kElevenChildrenNested[] =
     "<div id='target'>"
@@ -85,7 +85,8 @@
     "<div>child10"
     "</div></div></div></div></div></div></div></div></div></div></div></div>";
 // Take 10 elements -1 for target element, -1 as text is a leaf element.
-const char kElevenChildrenNestedExpected[] = "child0child1child2child3child4";
+const char16_t kElevenChildrenNestedExpected[] =
+    u"child0child1child2child3child4";
 
 const char kSkipElement[] =
     "<div id='target'>"
@@ -94,13 +95,13 @@
     "<div>child2</div>"
     "</div>";
 // TODO(crbug.com/796918): Should be child0child2
-const char kSkipElementExpected[] = "child0";
+const char16_t kSkipElementExpected[] = u"child0";
 
 const char kDivTableExample1[] =
     "<div>"
     "<div>label</div><div><input id='target'/></div>"
     "</div>";
-const char kDivTableExample1Expected[] = "label";
+const char16_t kDivTableExample1Expected[] = u"label";
 
 const char kDivTableExample2[] =
     "<div>"
@@ -108,7 +109,7 @@
     "<div>should be skipped<input/></div>"
     "<div><input id='target'/></div>"
     "</div>";
-const char kDivTableExample2Expected[] = "label";
+const char16_t kDivTableExample2Expected[] = u"label";
 
 const char kDivTableExample3[] =
     "<div>"
@@ -116,7 +117,7 @@
     "<div>label</div>"
     "<div><input id='target'/></div>"
     "</div>";
-const char kDivTableExample3Expected[] = "label";
+const char16_t kDivTableExample3Expected[] = u"label";
 
 const char kDivTableExample4[] =
     "<div>"
@@ -125,21 +126,21 @@
     "<div><input id='target'/></div>"
     "</div>";
 // TODO(crbug.com/796918): Should be label
-const char kDivTableExample4Expected[] = "";
+const char16_t kDivTableExample4Expected[] = u"";
 
 const char kDivTableExample5[] =
     "<div>"
     "<div>label<div><input id='target'/></div>behind</div>"
     "</div>";
 // TODO(crbug.com/796918): Should be label
-const char kDivTableExample5Expected[] = "labelbehind";
+const char16_t kDivTableExample5Expected[] = u"labelbehind";
 
 const char kDivTableExample6[] =
     "<div>"
     "<div>label<div><div>-<div><input id='target'/></div></div>"
     "</div>";
 // TODO(crbug.com/796918): Should be "label" or "label-"
-const char kDivTableExample6Expected[] = "";
+const char16_t kDivTableExample6Expected[] = u"";
 
 void VerifyButtonTitleCache(const WebFormElement& form_target,
                             const ButtonTitleList& expected_button_titles,
@@ -163,13 +164,13 @@
 
 TEST_F(FormAutofillUtilsTest, FindChildTextTest) {
   static const AutofillFieldUtilCase test_cases[] = {
-      {"simple test", "<div id='target'>test</div>", "test"},
+      {"simple test", "<div id='target'>test</div>", u"test"},
       {"Concatenate test", "<div id='target'><span>one</span>two</div>",
-       "onetwo"},
+       u"onetwo"},
       // TODO(crbug.com/796918): should be "onetwo"
       {"Ignore input", "<div id='target'>one<input value='test'/>two</div>",
-       "one"},
-      {"Trim", "<div id='target'>   one<span>two  </span></div>", "onetwo"},
+       u"one"},
+      {"Trim", "<div id='target'>   one<span>two  </span></div>", u"onetwo"},
       {"eleven children", kElevenChildren, kElevenChildrenExpected},
       // TODO(crbug.com/796918): Depth is only 5 elements
       {"eleven children nested", kElevenChildrenNested,
@@ -182,8 +183,7 @@
     ASSERT_NE(nullptr, web_frame);
     WebElement target = web_frame->GetDocument().GetElementById("target");
     ASSERT_FALSE(target.IsNull());
-    EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label),
-              FindChildText(target));
+    EXPECT_EQ(test_case.expected_label, FindChildText(target));
   }
 }
 
@@ -205,7 +205,7 @@
       to_skip.insert(web_to_skip[i]);
     }
 
-    EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label),
+    EXPECT_EQ(test_case.expected_label,
               FindChildTextWithIgnoreListForTesting(target, to_skip));
   }
 }
@@ -234,12 +234,12 @@
         FormFieldData::LabelSource::kUnknown;
     std::u16string label;
     InferLabelForElementForTesting(form_target, &label, &label_source);
-    EXPECT_EQ(base::UTF8ToUTF16(test_case.expected_label), label);
+    EXPECT_EQ(test_case.expected_label, label);
   }
 }
 
 TEST_F(FormAutofillUtilsTest, InferLabelSourceTest) {
-  const char kLabelSourceExpectedLabel[] = "label";
+  const char16_t kLabelSourceExpectedLabel[] = u"label";
   static const AutofillFieldLabelSourceCase test_cases[] = {
       {"<div><div>label</div><div><input id='target'/></div></div>",
        FormFieldData::LabelSource::kDivTable},
@@ -279,7 +279,7 @@
     std::u16string label;
     EXPECT_TRUE(autofill::form_util::InferLabelForElementForTesting(
         form_target, &label, &label_source));
-    EXPECT_EQ(base::UTF8ToUTF16(kLabelSourceExpectedLabel), label);
+    EXPECT_EQ(kLabelSourceExpectedLabel, label);
     EXPECT_EQ(test_case.label_source, label_source);
   }
 }
@@ -441,19 +441,18 @@
       dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(),
       nullptr, EXTRACT_NONE, &target, nullptr));
   const struct {
-    const char* const name;
+    const char16_t* const name;
     bool enabled;
   } kExpectedFields[] = {
-      {"name1", true},
-      {"name2", false},
-      {"name3", true},
-      {"name4", false},
+      {u"name1", true},
+      {u"name2", false},
+      {u"name3", true},
+      {u"name4", false},
   };
   const size_t number_of_cases = base::size(kExpectedFields);
   ASSERT_EQ(number_of_cases, target.fields.size());
   for (size_t i = 0; i < number_of_cases; ++i) {
-    EXPECT_EQ(base::UTF8ToUTF16(kExpectedFields[i].name),
-              target.fields[i].name);
+    EXPECT_EQ(kExpectedFields[i].name, target.fields[i].name);
     EXPECT_EQ(kExpectedFields[i].enabled, target.fields[i].is_enabled);
   }
 }
@@ -482,19 +481,18 @@
       dummy_fieldsets, control_elements, nullptr, web_frame->GetDocument(),
       nullptr, EXTRACT_NONE, &target, nullptr));
   const struct {
-    const char* const name;
+    const char16_t* const name;
     bool readonly;
   } kExpectedFields[] = {
-      {"name1", false},
-      {"name2", true},
-      {"name3", false},
-      {"name4", true},
+      {u"name1", false},
+      {u"name2", true},
+      {u"name3", false},
+      {u"name4", true},
   };
   const size_t number_of_cases = base::size(kExpectedFields);
   ASSERT_EQ(number_of_cases, target.fields.size());
   for (size_t i = 0; i < number_of_cases; ++i) {
-    EXPECT_EQ(base::UTF8ToUTF16(kExpectedFields[i].name),
-              target.fields[i].name);
+    EXPECT_EQ(kExpectedFields[i].name, target.fields[i].name);
     EXPECT_EQ(kExpectedFields[i].readonly, target.fields[i].is_readonly);
   }
 }
diff --git a/components/autofill/content/renderer/form_cache.cc b/components/autofill/content/renderer/form_cache.cc
index 32d2eb4..4e50f9d7 100644
--- a/components/autofill/content/renderer/form_cache.cc
+++ b/components/autofill/content/renderer/form_cache.cc
@@ -227,6 +227,7 @@
   parsed_forms_.clear();
   initial_select_values_.clear();
   initial_checked_state_.clear();
+  fields_eligible_for_manual_filling_.clear();
 }
 
 void FormCache::ClearElement(WebFormControlElement& control_element,
@@ -422,6 +423,19 @@
   return true;
 }
 
+bool FormCache::IsFormElementEligibleForManualFilling(
+    const blink::WebFormControlElement& control_element) {
+  return fields_eligible_for_manual_filling_.find(
+             FieldRendererId(control_element.UniqueRendererFormControlId())) !=
+         fields_eligible_for_manual_filling_.end();
+}
+
+void FormCache::SetFieldsEligibleForManualFilling(
+    const std::vector<FieldRendererId>& fields_eligible_for_manual_filling) {
+  fields_eligible_for_manual_filling_ = base::flat_set<FieldRendererId>(
+      std::move(fields_eligible_for_manual_filling));
+}
+
 size_t FormCache::ScanFormControlElements(
     const std::vector<WebFormControlElement>& control_elements,
     bool log_deprecation_messages) {
diff --git a/components/autofill/content/renderer/form_cache.h b/components/autofill/content/renderer/form_cache.h
index c2458830..d3fd4d2 100644
--- a/components/autofill/content/renderer/form_cache.h
+++ b/components/autofill/content/renderer/form_cache.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "components/autofill/core/common/field_data_manager.h"
 #include "components/autofill/core/common/form_data.h"
+#include "components/autofill/core/common/form_data_predictions.h"
 #include "components/autofill/core/common/unique_ids.h"
 
 namespace blink {
@@ -55,6 +56,16 @@
   bool ShowPredictions(const FormDataPredictions& form,
                        bool attach_predictions_to_dom);
 
+  // For a given |control_element| check whether it is eligible for manual
+  // filling on form interaction.
+  bool IsFormElementEligibleForManualFilling(
+      const blink::WebFormControlElement& control_element);
+
+  // Stores the FieldRendererId of the fields that are eligible for manual
+  // filling in a set.
+  void SetFieldsEligibleForManualFilling(
+      const std::vector<FieldRendererId>& fields_eligible_for_manual_filling);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(FormCacheTest,
                            ShouldShowAutocompleteConsoleWarnings_Enabled);
@@ -108,6 +119,9 @@
   // keyed by the unique_renderer_form_control_id of the WebInputElements.
   std::map<FieldRendererId, bool> initial_checked_state_;
 
+  // Fields that are eligible to show manual filling on form interaction.
+  base::flat_set<FieldRendererId> fields_eligible_for_manual_filling_;
+
   DISALLOW_COPY_AND_ASSIGN(FormCache);
 };
 
diff --git a/components/autofill/content/renderer/form_cache_browsertest.cc b/components/autofill/content/renderer/form_cache_browsertest.cc
index 7ed7e51..1e21a22 100644
--- a/components/autofill/content/renderer/form_cache_browsertest.cc
+++ b/components/autofill/content/renderer/form_cache_browsertest.cc
@@ -327,4 +327,41 @@
   EXPECT_TRUE(select_month.UserHasEditedTheField());
 }
 
+TEST_F(FormCacheBrowserTest, IsFormElementEligibleForManualFilling) {
+  // Load a form.
+  LoadHTML(
+      "<html><form id='myForm'>"
+      "<label>First Name:</label><input id='fname' name='0'/><br/>"
+      "<label>Middle Name:</label> <input id='mname' name='1'/><br/>"
+      "<label>Last Name:</label> <input id='lname' name='2'/><br/>"
+      "</form></html>");
+
+  WebDocument doc = GetMainFrame()->GetDocument();
+  auto first_name_element = doc.GetElementById("fname").To<WebInputElement>();
+  auto middle_name_element = doc.GetElementById("mname").To<WebInputElement>();
+  auto last_name_element = doc.GetElementById("lname").To<WebInputElement>();
+
+  FormCache form_cache(GetMainFrame());
+  std::vector<FormData> forms =
+      form_cache.ExtractNewForms(/*field_data_manager=*/nullptr);
+  const FormData* form_data = GetFormByName(forms, "myForm");
+  EXPECT_EQ(3u, form_data->fields.size());
+
+  // Set the first_name and last_name fields as eligible for manual filling.
+  std::vector<FieldRendererId> fields_eligible_for_manual_filling;
+  fields_eligible_for_manual_filling.push_back(
+      form_data->fields[0].unique_renderer_id);
+  fields_eligible_for_manual_filling.push_back(
+      form_data->fields[2].unique_renderer_id);
+  form_cache.SetFieldsEligibleForManualFilling(
+      fields_eligible_for_manual_filling);
+
+  EXPECT_TRUE(
+      form_cache.IsFormElementEligibleForManualFilling(first_name_element));
+  EXPECT_FALSE(
+      form_cache.IsFormElementEligibleForManualFilling(middle_name_element));
+  EXPECT_TRUE(
+      form_cache.IsFormElementEligibleForManualFilling(last_name_element));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/address_rewriter_unittest.cc b/components/autofill/core/browser/address_rewriter_unittest.cc
index 327a1b7c..35df50d6 100644
--- a/components/autofill/core/browser/address_rewriter_unittest.cc
+++ b/components/autofill/core/browser/address_rewriter_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using base::UTF8ToUTF16;
 using autofill::AddressRewriter;
 
 TEST(AddressRewriterTest, InvalidCountryCode) {
@@ -41,8 +40,7 @@
 
 TEST(AddressRewriterTest, AR) {
   AddressRewriter ar = AddressRewriter::ForCountryCode(u"ar");
-  EXPECT_EQ(ar.Rewrite(UTF8ToUTF16(
-                "tierra del fuego antartida e islas del atlantico sur")),
+  EXPECT_EQ(ar.Rewrite(u"tierra del fuego antartida e islas del atlantico sur"),
             ar.Rewrite(u"tierra del fuego"));
   EXPECT_EQ(ar.Rewrite(u"ciudad autonoma de buenos aires"),
             ar.Rewrite(u"capital federal"));
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index 7e2d219..9eef8679 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -133,6 +133,11 @@
       const gfx::RectF& bounding_box) = 0;
 
   virtual net::IsolationInfo IsolationInfo() = 0;
+
+  // Tells the renderer about the form fields that are eligible for triggering
+  // manual filling on form interaction.
+  virtual void SendFieldsEligibleForManualFillingToRenderer(
+      const std::vector<FieldRendererId>& fields) = 0;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc
index d30aedd7..ec4b069 100644
--- a/components/autofill/core/browser/autofill_manager.cc
+++ b/components/autofill/core/browser/autofill_manager.cc
@@ -267,6 +267,13 @@
   // queryable forms will be updated once the field type query is complete.
   driver()->SendAutofillTypePredictionsToRenderer(non_queryable_forms);
   driver()->SendAutofillTypePredictionsToRenderer(queryable_forms);
+  // Send the fields that are eligible for manual filling to the renderer. If
+  // server predictions are not yet available for these forms, the eligible
+  // fields would be updated again once they are available.
+  driver()->SendFieldsEligibleForManualFillingToRenderer(
+      FormStructure::FindFieldsEligibleForManualFilling(non_queryable_forms));
+  driver()->SendFieldsEligibleForManualFillingToRenderer(
+      FormStructure::FindFieldsEligibleForManualFilling(queryable_forms));
   LogAutofillTypePredictionsAvailable(log_manager_, non_queryable_forms);
   LogAutofillTypePredictionsAvailable(log_manager_, queryable_forms);
 
@@ -377,7 +384,10 @@
 
   // Annotate the updated form with its predicted types.
   driver()->SendAutofillTypePredictionsToRenderer({*form_structure});
-
+  // Update the renderer with the latest set of fields eligible for manual
+  // filling.
+  driver()->SendFieldsEligibleForManualFillingToRenderer(
+      FormStructure::FindFieldsEligibleForManualFilling({*form_structure}));
   // There is no data to return if there are no auto-fillable fields.
   if (!(*form_structure)->autofill_count())
     return false;
@@ -512,6 +522,9 @@
   // annotate forms with the predicted types or add console warnings.
   driver()->SendAutofillTypePredictionsToRenderer(queried_forms);
 
+  driver()->SendFieldsEligibleForManualFillingToRenderer(
+      FormStructure::FindFieldsEligibleForManualFilling(queried_forms));
+
   LogAutofillTypePredictionsAvailable(log_manager_, queried_forms);
 
   // TODO(crbug.com/1176816): Remove the test code after initial integration.
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 2b72a5f7..f63b5ba 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -313,6 +313,8 @@
 
   MOCK_METHOD1(SendAutofillTypePredictionsToRenderer,
                void(const std::vector<FormStructure*>& forms));
+  MOCK_METHOD1(SendFieldsEligibleForManualFillingToRenderer,
+               void(const std::vector<FieldRendererId>& fields));
 };
 
 }  // namespace
@@ -923,6 +925,35 @@
   FormsSeen(forms);
 }
 
+// Test that when forms are seen, the renderer is sent the fields that are
+// eligible for manual filling.
+TEST_P(BrowserAutofillManagerStructuredProfileTest,
+       OnFormsSeen_SendFieldsEligibleForManualFillingToRenderer) {
+  // Set up a queryable form.
+  FormData form1;
+  CreateTestCreditCardFormData(&form1, true, false);
+
+  // Set up a non-queryable form.
+  FormData form2;
+  FormFieldData field;
+  test::CreateTestFormField("Querty", "qwerty", "", "text", &field);
+  form2.host_frame = test::GetLocalFrameToken();
+  form2.unique_renderer_id = test::MakeFormRendererId();
+  form2.name = u"NonQueryable";
+  form2.url = form1.url;
+  form2.action = GURL("https://myform.com/submit.html");
+  form2.fields.push_back(field);
+
+  // Package the forms for observation.
+  std::vector<FormData> forms{form1, form2};
+
+  // Set up expectations.
+  EXPECT_CALL(*autofill_driver_,
+              SendFieldsEligibleForManualFillingToRenderer(_))
+      .Times(2);
+  FormsSeen(forms);
+}
+
 // Test that no autofill suggestions are returned for a field with an
 // unrecognized autocomplete attribute.
 TEST_P(BrowserAutofillManagerStructuredProfileTest,
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
index 28e56cb..f301c95 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -56,7 +56,6 @@
 using autofill::NameInfo;
 using autofill::PhoneNumber;
 using autofill::ServerFieldType;
-using base::UTF8ToUTF16;
 
 namespace autofill {
 
@@ -102,22 +101,22 @@
     autofill::CountryNames::SetLocaleString(kLocale);
   }
 
-  NameInfo CreateNameInfo(const char* first,
-                          const char* middle,
-                          const char* last,
-                          const char* full) {
+  NameInfo CreateNameInfo(const char16_t* first,
+                          const char16_t* middle,
+                          const char16_t* last,
+                          const char16_t* full) {
     NameInfo name;
     name.SetRawInfoWithVerificationStatus(
-        NAME_FIRST, base::UTF8ToUTF16(first),
+        NAME_FIRST, first,
         autofill::structured_address::VerificationStatus::kObserved);
     name.SetRawInfoWithVerificationStatus(
-        NAME_MIDDLE, base::UTF8ToUTF16(middle),
+        NAME_MIDDLE, middle,
         autofill::structured_address::VerificationStatus::kObserved);
     name.SetRawInfoWithVerificationStatus(
-        NAME_LAST, base::UTF8ToUTF16(last),
+        NAME_LAST, last,
         autofill::structured_address::VerificationStatus::kObserved);
     name.SetRawInfoWithVerificationStatus(
-        NAME_FULL, base::UTF8ToUTF16(full),
+        NAME_FULL, full,
         autofill::structured_address::VerificationStatus::kObserved);
     return name;
   }
@@ -190,10 +189,10 @@
 
   AutofillProfile CopyAndModify(
       const AutofillProfile& profile,
-      const std::vector<std::pair<ServerFieldType, const char*>>& updates) {
+      const std::vector<std::pair<ServerFieldType, const char16_t*>>& updates) {
     AutofillProfile new_profile = profile;
     for (const auto& update : updates) {
-      new_profile.SetRawInfo(update.first, UTF8ToUTF16(update.second));
+      new_profile.SetRawInfo(update.first, update.second);
     }
     new_profile.FinalizeAfterImport();
     return new_profile;
@@ -242,7 +241,7 @@
 
   void MergePhoneNumbersAndExpect(const AutofillProfile& a,
                                   const AutofillProfile& b,
-                                  const std::string& expected_str) {
+                                  const std::u16string& expected_str) {
     AutofillProfile dummy;
 
     // Merge the phone numbers.
@@ -251,7 +250,7 @@
 
     // Construct the expected value.
     PhoneNumber expected(&dummy);
-    expected.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, UTF8ToUTF16(expected_str));
+    expected.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, expected_str);
 
     // Validate that we get what we expect.
     EXPECT_EQ(expected.GetRawInfo(PHONE_HOME_WHOLE_NUMBER),
@@ -712,20 +711,20 @@
                                                 "Carver City", "ca", "", "us");
 
   AutofillProfile differentCountry =
-      CopyAndModify(p1, {{ADDRESS_HOME_COUNTRY, "CA"}});
+      CopyAndModify(p1, {{ADDRESS_HOME_COUNTRY, u"CA"}});
   AutofillProfile differentZip =
-      CopyAndModify(p1, {{ADDRESS_HOME_ZIP, "32145"}});
+      CopyAndModify(p1, {{ADDRESS_HOME_ZIP, u"32145"}});
   AutofillProfile differentState = CopyAndModify(
-      p1, {{ADDRESS_HOME_ZIP, ""}, {ADDRESS_HOME_STATE, "Florida"}});
+      p1, {{ADDRESS_HOME_ZIP, u""}, {ADDRESS_HOME_STATE, u"Florida"}});
   AutofillProfile differentCity = CopyAndModify(
-      p1, {{ADDRESS_HOME_ZIP, ""}, {ADDRESS_HOME_CITY, "Metropolis"}});
+      p1, {{ADDRESS_HOME_ZIP, u""}, {ADDRESS_HOME_CITY, u"Metropolis"}});
   AutofillProfile differentAddress =
-      CopyAndModify(p1, {{ADDRESS_HOME_LINE1, "17 Park Lane"},
-                         {ADDRESS_HOME_LINE2, "Suite 150"}});
+      CopyAndModify(p1, {{ADDRESS_HOME_LINE1, u"17 Park Lane"},
+                         {ADDRESS_HOME_LINE2, u"Suite 150"}});
   AutofillProfile differentLocality =
-      CopyAndModify(p1, {{ADDRESS_HOME_DEPENDENT_LOCALITY, "Funky Chicken"}});
+      CopyAndModify(p1, {{ADDRESS_HOME_DEPENDENT_LOCALITY, u"Funky Chicken"}});
   AutofillProfile differentSortingCode =
-      CopyAndModify(p1, {{ADDRESS_HOME_SORTING_CODE, "98000 Monaco"}});
+      CopyAndModify(p1, {{ADDRESS_HOME_SORTING_CODE, u"98000 Monaco"}});
 
   EXPECT_TRUE(comparator_.HaveMergeableAddresses(p1, empty));
   EXPECT_TRUE(comparator_.HaveMergeableAddresses(empty, p2));
@@ -758,26 +757,26 @@
                                  "+1 (234) 567-8910", /*finalize=*/false);
 
   AutofillProfile mergeable =
-      CopyAndModify(p, {{NAME_FIRST, "MÁRÍÕÑ"},
-                        {NAME_MIDDLE, "M."},
-                        {EMAIL_ADDRESS, "MARION@ME.XYZ"},
-                        {COMPANY_NAME, "Fox Industries Inc."},
-                        {ADDRESS_HOME_LINE1, "123 zoo st. w., #5"},
-                        {ADDRESS_HOME_LINE1, ""},
-                        {ADDRESS_HOME_STATE, "california"},
-                        {PHONE_HOME_WHOLE_NUMBER, "5678910 ext. 77"}});
+      CopyAndModify(p, {{NAME_FIRST, u"MÁRÍÕÑ"},
+                        {NAME_MIDDLE, u"M."},
+                        {EMAIL_ADDRESS, u"MARION@ME.XYZ"},
+                        {COMPANY_NAME, u"Fox Industries Inc."},
+                        {ADDRESS_HOME_LINE1, u"123 zoo st. w., #5"},
+                        {ADDRESS_HOME_LINE1, u""},
+                        {ADDRESS_HOME_STATE, u"california"},
+                        {PHONE_HOME_WHOLE_NUMBER, u"5678910 ext. 77"}});
   AutofillProfile not_mergeable_by_name =
-      CopyAndModify(p, {{NAME_FIRST, "Steven"},
-                        {NAME_FULL, ""},
-                        {autofill::NAME_LAST_SECOND, ""}});
+      CopyAndModify(p, {{NAME_FIRST, u"Steven"},
+                        {NAME_FULL, u""},
+                        {autofill::NAME_LAST_SECOND, u""}});
   AutofillProfile not_mergeable_by_email_address =
-      CopyAndModify(p, {{EMAIL_ADDRESS, "marion.morrision@me.xyz"}});
+      CopyAndModify(p, {{EMAIL_ADDRESS, u"marion.morrision@me.xyz"}});
   AutofillProfile not_mergeable_by_company_name =
-      CopyAndModify(p, {{COMPANY_NAME, "Hound Corp"}});
+      CopyAndModify(p, {{COMPANY_NAME, u"Hound Corp"}});
   AutofillProfile not_mergeable_by_address =
-      CopyAndModify(p, {{ADDRESS_HOME_LINE2, "Unit 7"}});
+      CopyAndModify(p, {{ADDRESS_HOME_LINE2, u"Unit 7"}});
   AutofillProfile not_mergeable_by_phone_number =
-      CopyAndModify(p, {{PHONE_HOME_WHOLE_NUMBER, "555-1234"}});
+      CopyAndModify(p, {{PHONE_HOME_WHOLE_NUMBER, u"555-1234"}});
 
   // Finalize the initial profile.
   // Note, all other profiles are already finalized.
@@ -921,15 +920,15 @@
 
 TEST_P(AutofillProfileComparatorTest, MergeCJKNames) {
   // Korean names that are all mergeable, but constructed differently.
-  NameInfo name1 = CreateNameInfo("호", "", "이영", "이영 호");
-  NameInfo name2 = CreateNameInfo("이영호", "", "", "이영호");
-  NameInfo name3 = CreateNameInfo("영호", "", "이", "이영호");
-  NameInfo name4 = CreateNameInfo("영호", "", "이", "");
-  NameInfo name5 = CreateNameInfo("영호", "", "이", "이 영호");
+  NameInfo name1 = CreateNameInfo(u"호", u"", u"이영", u"이영 호");
+  NameInfo name2 = CreateNameInfo(u"이영호", u"", u"", u"이영호");
+  NameInfo name3 = CreateNameInfo(u"영호", u"", u"이", u"이영호");
+  NameInfo name4 = CreateNameInfo(u"영호", u"", u"이", u"");
+  NameInfo name5 = CreateNameInfo(u"영호", u"", u"이", u"이 영호");
 
   // Mergeable foreign name in Japanese with a 'KATAKANA MIDDLE DOT'.
-  NameInfo name6 = CreateNameInfo("", "", "", "ゲイツ・ビル");
-  NameInfo name7 = CreateNameInfo("ビル", "", "ゲイツ", "");
+  NameInfo name6 = CreateNameInfo(u"", u"", u"", u"ゲイツ・ビル");
+  NameInfo name7 = CreateNameInfo(u"ビル", u"", u"ゲイツ", u"");
 
   // Set the use dates for the profiles, because |MergeCJKNames()| tries to use
   // the most recent profile if there is a conflict. The ordering is
@@ -949,10 +948,10 @@
   AutofillProfile p7 = CreateProfileWithName(name7);
 
   // Because |p1| is the most recent, it always wins over others.
-  MergeNamesAndExpect(p1, p2, CreateNameInfo("호", "", "이영", "이영 호"));
-  MergeNamesAndExpect(p1, p3, CreateNameInfo("호", "", "이영", "이영 호"));
-  MergeNamesAndExpect(p1, p4, CreateNameInfo("호", "", "이영", "이영 호"));
-  MergeNamesAndExpect(p1, p5, CreateNameInfo("호", "", "이영", "이영 호"));
+  MergeNamesAndExpect(p1, p2, CreateNameInfo(u"호", u"", u"이영", u"이영 호"));
+  MergeNamesAndExpect(p1, p3, CreateNameInfo(u"호", u"", u"이영", u"이영 호"));
+  MergeNamesAndExpect(p1, p4, CreateNameInfo(u"호", u"", u"이영", u"이영 호"));
+  MergeNamesAndExpect(p1, p5, CreateNameInfo(u"호", u"", u"이영", u"이영 호"));
 
   // The following tests are not applicable to the logic of the new structured
   // name. Because we consider not having a surname a valid option for the user.
@@ -963,34 +962,36 @@
     // |p2| is more recent than |p3|, |p4|, and |p5|. However, it does not
     // have a surname entry (it was probably parsed with the old logic), so
     // the other profiles are used as the source for given/surname.
-    MergeNamesAndExpect(p2, p3, CreateNameInfo("영호", "", "이", "이영호"));
-    MergeNamesAndExpect(p2, p4, CreateNameInfo("영호", "", "이", "이영호"));
-    MergeNamesAndExpect(p2, p5, CreateNameInfo("영호", "", "이", "이영호"));
+    MergeNamesAndExpect(p2, p3, CreateNameInfo(u"영호", u"", u"이", u"이영호"));
+    MergeNamesAndExpect(p2, p4, CreateNameInfo(u"영호", u"", u"이", u"이영호"));
+    MergeNamesAndExpect(p2, p5, CreateNameInfo(u"영호", u"", u"이", u"이영호"));
   }
   // |p3| is more recent than |p4| and |p5|.
-  MergeNamesAndExpect(p3, p4, CreateNameInfo("영호", "", "이", "이영호"));
-  MergeNamesAndExpect(p3, p5, CreateNameInfo("영호", "", "이", "이영호"));
+  MergeNamesAndExpect(p3, p4, CreateNameInfo(u"영호", u"", u"이", u"이영호"));
+  MergeNamesAndExpect(p3, p5, CreateNameInfo(u"영호", u"", u"이", u"이영호"));
 
   // |p4| is more recent than |p5|. However, it does not have an explicit
   // full name, so use the one from |p5|.
-  MergeNamesAndExpect(p4, p5, CreateNameInfo("영호", "", "이", "이 영호"));
+  MergeNamesAndExpect(p4, p5, CreateNameInfo(u"영호", u"", u"이", u"이 영호"));
 
   // There is no conflict between |p6| and |p7|, so use the parts from both.
   MergeNamesAndExpect(p6, p7,
-                      CreateNameInfo("ビル", "", "ゲイツ", "ゲイツ・ビル"));
+                      CreateNameInfo(u"ビル", u"", u"ゲイツ", u"ゲイツ・ビル"));
 }
 
 TEST_P(AutofillProfileComparatorTest, MergeEmailAddresses) {
   static const char kEmailA[] = "testaccount@domain.net";
+  static const char16_t kEmailA16[] = u"testaccount@domain.net";
   static const char kEmailB[] = "TestAccount@Domain.Net";
+  static const char16_t kEmailB16[] = u"TestAccount@Domain.Net";
 
   EmailInfo email_a;
-  email_a.SetRawInfo(EMAIL_ADDRESS, UTF8ToUTF16(kEmailA));
+  email_a.SetRawInfo(EMAIL_ADDRESS, kEmailA16);
   AutofillProfile profile_a = CreateProfileWithEmail(kEmailA);
   profile_a.set_use_date(AutofillClock::Now());
 
   EmailInfo email_b;
-  email_b.SetRawInfo(EMAIL_ADDRESS, UTF8ToUTF16(kEmailB));
+  email_b.SetRawInfo(EMAIL_ADDRESS, kEmailB16);
   AutofillProfile profile_b = CreateProfileWithEmail(kEmailB);
   profile_b.set_use_date(profile_a.use_date() + base::TimeDelta::FromDays(1));
 
@@ -1002,32 +1003,36 @@
 
 TEST_P(AutofillProfileComparatorTest, MergeCompanyNames) {
   static const char kCompanyA[] = "Some Company";
+  static const char16_t kCompanyA16[] = u"Some Company";
   static const char kCompanyB[] = "SÔMÈ ÇÖMPÁÑÝ";
+  static const char16_t kCompanyB16[] = u"SÔMÈ ÇÖMPÁÑÝ";
   static const char kCompanyC[] = "SÔMÈ ÇÖMPÁÑÝ A.G.";
+  static const char16_t kCompanyC16[] = u"SÔMÈ ÇÖMPÁÑÝ A.G.";
   static const char kCompanyD[] = "1987";
+  static const char16_t kCompanyD16[] = u"1987";
 
   CompanyInfo company_a;
-  company_a.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyA));
+  company_a.SetRawInfo(COMPANY_NAME, kCompanyA16);
   AutofillProfile profile_a = CreateProfileWithCompanyName(kCompanyA);
   profile_a.set_use_date(AutofillClock::Now());
 
   // Company Name B is post_normalization identical to Company Name A. The use
   // date will be used to choose between them.
   CompanyInfo company_b;
-  company_b.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyB));
+  company_b.SetRawInfo(COMPANY_NAME, kCompanyB16);
   AutofillProfile profile_b = CreateProfileWithCompanyName(kCompanyB);
   profile_b.set_use_date(profile_a.use_date() + base::TimeDelta::FromDays(1));
 
   // Company Name C is the most complete. Even though it has the earliest use
   // date, it will be preferred to the other two.
   CompanyInfo company_c;
-  company_c.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyC));
+  company_c.SetRawInfo(COMPANY_NAME, kCompanyC16);
   AutofillProfile profile_c = CreateProfileWithCompanyName(kCompanyC);
   profile_c.set_use_date(profile_a.use_date() - base::TimeDelta::FromDays(1));
 
   // Company Name D is in the format of a birthyear, invalid and non-verified.
   CompanyInfo company_d;
-  company_d.SetRawInfo(COMPANY_NAME, UTF8ToUTF16(kCompanyD));
+  company_d.SetRawInfo(COMPANY_NAME, kCompanyD16);
   AutofillProfile profile_d = CreateProfileWithCompanyName(kCompanyD);
   profile_a.set_use_date(AutofillClock::Now());
 
@@ -1054,16 +1059,23 @@
 
 TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_NA) {
   static const char kPhoneA[] = "5550199";
+  static const char16_t kPhoneA16[] = u"5550199";
   static const char kPhoneB[] = "555.0199";
+  static const char16_t kPhoneB16[] = u"555.0199";
   static const char kPhoneC[] = "555-0199 ext321";
+  static const char16_t kPhoneC16[] = u"555-0199 ext321";
   static const char kPhoneD[] = "8005550199";
+  static const char16_t kPhoneD16[] = u"8005550199";
   static const char kPhoneE[] = "800-555-0199 #321";
+  static const char16_t kPhoneE16[] = u"800-555-0199 #321";
   static const char kPhoneF[] = "1-800-555-0199 #321";
+  static const char16_t kPhoneF16[] = u"1-800-555-0199 #321";
   static const char kPhoneG[] = "+1 (800) 555.0199;ext=321";
-  static const char kMergedShortNumber[] = "5550199";
-  static const char kMergedShortNumberExt[] = "5550199 ext. 321";
-  static const char kMergedFullNumber[] = "+1 800-555-0199";
-  static const char kMergedFullNumberExt[] = "+1 800-555-0199 ext. 321";
+  static const char16_t kPhoneG16[] = u"+1 (800) 555.0199;ext=321";
+  static const char16_t kMergedShortNumber[] = u"5550199";
+  static const char16_t kMergedShortNumberExt[] = u"5550199 ext. 321";
+  static const char16_t kMergedFullNumber[] = u"+1 800-555-0199";
+  static const char16_t kMergedFullNumberExt[] = u"+1 800-555-0199 ext. 321";
 
   AutofillProfile profile_a = CreateProfileWithPhoneNumber(kPhoneA);
   AutofillProfile profile_b = CreateProfileWithPhoneNumber(kPhoneB);
@@ -1074,7 +1086,7 @@
   AutofillProfile profile_g = CreateProfileWithPhoneNumber(kPhoneG);
 
   // Profile A
-  MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA);
+  MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA16);
   MergePhoneNumbersAndExpect(profile_a, profile_b, kMergedShortNumber);
   MergePhoneNumbersAndExpect(profile_a, profile_c, kMergedShortNumberExt);
   MergePhoneNumbersAndExpect(profile_a, profile_d, kMergedFullNumber);
@@ -1084,7 +1096,7 @@
 
   // Profile B
   MergePhoneNumbersAndExpect(profile_b, profile_a, kMergedShortNumber);
-  MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB);
+  MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB16);
   MergePhoneNumbersAndExpect(profile_b, profile_c, kMergedShortNumberExt);
   MergePhoneNumbersAndExpect(profile_b, profile_d, kMergedFullNumber);
   MergePhoneNumbersAndExpect(profile_b, profile_e, kMergedFullNumberExt);
@@ -1094,7 +1106,7 @@
   // Profile C
   MergePhoneNumbersAndExpect(profile_c, profile_a, kMergedShortNumberExt);
   MergePhoneNumbersAndExpect(profile_c, profile_b, kMergedShortNumberExt);
-  MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC);
+  MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC16);
   MergePhoneNumbersAndExpect(profile_c, profile_d, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_c, profile_e, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_c, profile_f, kMergedFullNumberExt);
@@ -1104,7 +1116,7 @@
   MergePhoneNumbersAndExpect(profile_d, profile_a, kMergedFullNumber);
   MergePhoneNumbersAndExpect(profile_d, profile_b, kMergedFullNumber);
   MergePhoneNumbersAndExpect(profile_d, profile_c, kMergedFullNumberExt);
-  MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD);
+  MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD16);
   MergePhoneNumbersAndExpect(profile_d, profile_e, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_d, profile_f, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_d, profile_g, kMergedFullNumberExt);
@@ -1114,7 +1126,7 @@
   MergePhoneNumbersAndExpect(profile_e, profile_b, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_e, profile_c, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_e, profile_d, kMergedFullNumberExt);
-  MergePhoneNumbersAndExpect(profile_e, profile_e, kPhoneE);
+  MergePhoneNumbersAndExpect(profile_e, profile_e, kPhoneE16);
   MergePhoneNumbersAndExpect(profile_e, profile_f, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_e, profile_g, kMergedFullNumberExt);
 
@@ -1124,7 +1136,7 @@
   MergePhoneNumbersAndExpect(profile_f, profile_c, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_f, profile_d, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_f, profile_e, kMergedFullNumberExt);
-  MergePhoneNumbersAndExpect(profile_f, profile_f, kPhoneF);
+  MergePhoneNumbersAndExpect(profile_f, profile_f, kPhoneF16);
   MergePhoneNumbersAndExpect(profile_f, profile_g, kMergedFullNumberExt);
 
   // Profile G
@@ -1134,7 +1146,7 @@
   MergePhoneNumbersAndExpect(profile_g, profile_d, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_g, profile_e, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_g, profile_f, kMergedFullNumberExt);
-  MergePhoneNumbersAndExpect(profile_g, profile_g, kPhoneG);
+  MergePhoneNumbersAndExpect(profile_g, profile_g, kPhoneG16);
 }
 
 TEST_P(AutofillProfileComparatorTest, MergePhoneNumbers_Intl) {
@@ -1142,11 +1154,15 @@
   const AutofillType kCountry(ADDRESS_HOME_COUNTRY);
 
   static const char kPhoneA[] = "+49492180185611";
+  static const char16_t kPhoneA16[] = u"+49492180185611";
   static const char kPhoneB[] = "+49 4921 801 856-11";
+  static const char16_t kPhoneB16[] = u"+49 4921 801 856-11";
   static const char kPhoneC[] = "+49 4921 8018 5611;ext=22";
+  static const char16_t kPhoneC16[] = u"+49 4921 8018 5611;ext=22";
   static const char kPhoneD[] = "04921 80185611";  // National Format.
-  static const char kMergedFullNumber[] = "+49 4921 80185611";
-  static const char kMergedFullNumberExt[] = "+49 4921 80185611 ext. 22";
+  static const char16_t kPhoneD16[] = u"04921 80185611";  // National Format.
+  static const char16_t kMergedFullNumber[] = u"+49 4921 80185611";
+  static const char16_t kMergedFullNumberExt[] = u"+49 4921 80185611 ext. 22";
 
   AutofillProfile profile_a = CreateProfileWithPhoneNumber(kPhoneA);
   AutofillProfile profile_b = CreateProfileWithPhoneNumber(kPhoneB);
@@ -1159,25 +1175,25 @@
   profile_d.SetInfo(kCountry, kGermany, kLocale);
 
   // Profile A
-  MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA);
+  MergePhoneNumbersAndExpect(profile_a, profile_a, kPhoneA16);
   MergePhoneNumbersAndExpect(profile_a, profile_b, kMergedFullNumber);
   MergePhoneNumbersAndExpect(profile_a, profile_c, kMergedFullNumberExt);
 
   // Profile B
   MergePhoneNumbersAndExpect(profile_b, profile_a, kMergedFullNumber);
-  MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB);
+  MergePhoneNumbersAndExpect(profile_b, profile_b, kPhoneB16);
   MergePhoneNumbersAndExpect(profile_b, profile_c, kMergedFullNumberExt);
 
   // Profile C
   MergePhoneNumbersAndExpect(profile_c, profile_a, kMergedFullNumberExt);
   MergePhoneNumbersAndExpect(profile_c, profile_b, kMergedFullNumberExt);
-  MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC);
+  MergePhoneNumbersAndExpect(profile_c, profile_c, kPhoneC16);
 
   // Profile D
   MergePhoneNumbersAndExpect(profile_d, profile_a, kMergedFullNumber);
   MergePhoneNumbersAndExpect(profile_d, profile_b, kMergedFullNumber);
   MergePhoneNumbersAndExpect(profile_d, profile_c, kMergedFullNumberExt);
-  MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD);
+  MergePhoneNumbersAndExpect(profile_d, profile_d, kPhoneD16);
 }
 
 TEST_P(AutofillProfileComparatorTest, MergeAddresses) {
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address.cc b/components/autofill/core/browser/data_model/autofill_structured_address.cc
index 0a92649..0fbedb8 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address.cc
@@ -150,9 +150,8 @@
       base::UTF16ToUTF8(GetRootNode().GetValueForType(ADDRESS_HOME_COUNTRY));
 
   if (country_code == "BR") {
-    return base::UTF8ToUTF16(
-        "${ADDRESS_HOME_STREET_NAME}${ADDRESS_HOME_HOUSE_NUMBER;, }"
-        "${ADDRESS_HOME_FLOOR;, ;º andar}${ADDRESS_HOME_APT_NUM;, apto ;}");
+    return u"${ADDRESS_HOME_STREET_NAME}${ADDRESS_HOME_HOUSE_NUMBER;, }"
+           u"${ADDRESS_HOME_FLOOR;, ;º andar}${ADDRESS_HOME_APT_NUM;, apto ;}";
   }
 
   if (country_code == "DE") {
diff --git a/components/autofill/core/browser/field_filler_unittest.cc b/components/autofill/core/browser/field_filler_unittest.cc
index e4eebbee..b359e402 100644
--- a/components/autofill/core/browser/field_filler_unittest.cc
+++ b/components/autofill/core/browser/field_filler_unittest.cc
@@ -36,9 +36,7 @@
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
 #include "third_party/libaddressinput/src/cpp/test/testdata_source.h"
 
-using base::ASCIIToUTF16;
 using base::StringToInt;
-using base::UTF8ToUTF16;
 
 namespace autofill {
 
@@ -136,10 +134,10 @@
 }
 
 struct CreditCardTestCase {
-  std::string card_number_;
+  std::u16string card_number_;
   size_t total_splits_;
   std::vector<int> splits_;
-  std::vector<std::string> expected_results_;
+  std::vector<std::u16string> expected_results_;
 };
 
 // Returns the offset to be set within the credit card number field.
@@ -539,23 +537,23 @@
 struct AutofillFieldFillerTestCase {
   HtmlFieldType field_type;
   size_t field_max_length;
-  std::string expected_value;
+  std::u16string expected_value;
 
   AutofillFieldFillerTestCase(HtmlFieldType field_type,
                               size_t field_max_length,
-                              std::string expected_value)
+                              std::u16string expected_value)
       : field_type(field_type),
         field_max_length(field_max_length),
         expected_value(expected_value) {}
 };
 
 struct AutofillPhoneFieldFillerTestCase : public AutofillFieldFillerTestCase {
-  std::string phone_home_whole_number_value;
+  std::u16string phone_home_whole_number_value;
 
   AutofillPhoneFieldFillerTestCase(HtmlFieldType field_type,
                                    size_t field_max_length,
-                                   std::string expected_value,
-                                   std::string phone_home_whole_number_value)
+                                   std::u16string expected_value,
+                                   std::u16string phone_home_whole_number_value)
       : AutofillFieldFillerTestCase(field_type,
                                     field_max_length,
                                     expected_value),
@@ -576,10 +574,10 @@
 
   AutofillProfile address;
   address.SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
-                     ASCIIToUTF16(test_case.phone_home_whole_number_value));
+                     test_case.phone_home_whole_number_value);
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string());
-  EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value);
+  EXPECT_EQ(test_case.expected_value, field.value);
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -588,43 +586,43 @@
     testing::Values(
         // Filling a prefix type field should just fill the prefix.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL_LOCAL_PREFIX,
-                                         /*field_max_length=*/0, "555",
-                                         "+15145554578"},
+                                         /*field_max_length=*/0, u"555",
+                                         u"+15145554578"},
         // Filling a suffix type field with a phone number of 7 digits should
         // just fill the suffix.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL_LOCAL_SUFFIX,
-                                         /*field_max_length=*/0, "4578",
-                                         "+15145554578"},
+                                         /*field_max_length=*/0, u"4578",
+                                         u"+15145554578"},
         // Filling a phone type field with a max length of 3 should fill only
         // the prefix.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL_LOCAL,
-                                         /*field_max_length=*/3, "555",
-                                         "+15145554578"},
+                                         /*field_max_length=*/3, u"555",
+                                         u"+15145554578"},
         // TODO(crbug.com/581485): There should be a test case where the full
         // number is requested (HTML_TYPE_TEL) but a field_max_length of 3 would
         // fill the prefix.
         // Filling a phone type field with a max length of 4 should fill only
         // the suffix.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL,
-                                         /*field_max_length=*/4, "4578",
-                                         "+15145554578"},
+                                         /*field_max_length=*/4, u"4578",
+                                         u"+15145554578"},
         // Filling a phone type field with a max length of 10 with a phone
         // number including the country code should fill the phone number
         // without the country code.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL,
-                                         /*field_max_length=*/10, "5145554578",
-                                         "+15145554578"},
+                                         /*field_max_length=*/10, u"5145554578",
+                                         u"+15145554578"},
         // Filling a phone type field with a max length of 5 with a phone number
         // should fill with the last 5 digits of that phone number.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL,
-                                         /*field_max_length=*/5, "54578",
-                                         "+15145554578"},
+                                         /*field_max_length=*/5, u"54578",
+                                         u"+15145554578"},
         // Filling a phone type field with a max length of 10 with a phone
         // number including the country code should fill the phone number
         // without the country code.
         AutofillPhoneFieldFillerTestCase{HTML_TYPE_TEL,
-                                         /*field_max_length=*/10, "123456789",
-                                         "+886123456789"}));
+                                         /*field_max_length=*/10, u"123456789",
+                                         u"+886123456789"}));
 
 class ExpirationYearTest
     : public testing::TestWithParam<AutofillFieldFillerTestCase> {
@@ -643,7 +641,7 @@
   card.SetExpirationDateFromString(u"12/2023");
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string());
-  EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value);
+  EXPECT_EQ(test_case.expected_value, field.value);
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -654,37 +652,37 @@
         // 2 digits of the expiration year if the field has an unspecified max
         // length (0) or if it's greater than 1.
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR,
-                                    /* default value */ 0, "23"},
+                                    /* default value */ 0, u"23"},
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 2,
-                                    "23"},
+                                    u"23"},
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 12,
-                                    "23"},
+                                    u"23"},
         // A field predicted as a 2 digit expiration year should fill the last
         // digit of the expiration year if the field has a max length of 1.
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_2_DIGIT_YEAR, 1,
-                                    "3"},
+                                    u"3"},
         // A field predicted as a 4 digit expiration year should fill the 4
         // digits of the expiration year if the field has an unspecified max
         // length (0) or if it's greater than 3 .
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR,
-                                    /* default value */ 0, "2023"},
+                                    /* default value */ 0, u"2023"},
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 4,
-                                    "2023"},
+                                    u"2023"},
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 12,
-                                    "2023"},
+                                    u"2023"},
         // A field predicted as a 4 digits expiration year should fill the last
         // 2 digits of the expiration year if the field has a max length of 2.
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 2,
-                                    "23"},
+                                    u"23"},
         // A field predicted as a 4 digits expiration year should fill the last
         // digit of the expiration year if the field has a max length of 1.
         AutofillFieldFillerTestCase{HTML_TYPE_CREDIT_CARD_EXP_4_DIGIT_YEAR, 1,
-                                    "3"}));
+                                    u"3"}));
 
 struct FillUtilExpirationDateTestCase {
   HtmlFieldType field_type;
   size_t field_max_length;
-  std::string expected_value;
+  std::u16string expected_value;
   bool expected_response;
 };
 
@@ -706,7 +704,7 @@
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   bool response =
       filler.FillFormField(field, &card, &field, /*cvc=*/std::u16string());
-  EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value);
+  EXPECT_EQ(test_case.expected_value, field.value);
   EXPECT_EQ(response, test_case.expected_response);
 }
 
@@ -721,30 +719,30 @@
         // 7: Use format MM/YYYY
         FillUtilExpirationDateTestCase{
             HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR,
-            /* default value */ 0, "03/22", true},
+            /* default value */ 0, u"03/22", true},
         // Unsupported max lengths of 1-3, fail
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 1, "", false},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 1, u"", false},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 2, "", false},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 2, u"", false},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 3, "", false},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 3, u"", false},
         // A max length of 4 indicates a format of MMYY.
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 4, "0322", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 4, u"0322", true},
         // A max length of 6 indicates a format of MMYYYY, the 21st century is
         // assumed.
         // Desired case of proper max length >= 5
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 5, "03/22", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 5, u"03/22", true},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 6, "032022", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 6, u"032022", true},
         // A max length of 7 indicates a format of MM/YYYY, the 21st century is
         // assumed.
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 7, "03/2022", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 7, u"03/2022", true},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 12, "03/22", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, 12, u"03/22", true},
 
         // A field predicted as a expiration date w/ 4 digit year should fill
         // with a format of MM/YYYY unless it has max-length of:
@@ -753,28 +751,29 @@
         // 6: Use format MMYYYY
         FillUtilExpirationDateTestCase{
             HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR,
-            /* default value */ 0, "03/2022", true},
+            /* default value */ 0, u"03/2022", true},
         // Unsupported max lengths of 1-3, fail
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 1, "", false},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 1, u"", false},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 2, "", false},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 2, u"", false},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 3, "", false},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 3, u"", false},
         // A max length of 4 indicates a format of MMYY.
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 4, "0322", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 4, u"0322", true},
         // A max length of 5 indicates a format of MM/YY.
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 5, "03/22", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 5, u"03/22", true},
         // A max length of 6 indicates a format of MMYYYY.
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 6, "032022", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 6, u"032022", true},
         // Desired case of proper max length >= 7
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 7, "03/2022", true},
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 7, u"03/2022", true},
         FillUtilExpirationDateTestCase{
-            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 12, "03/2022", true}));
+            HTML_TYPE_CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 12, u"03/2022",
+            true}));
 
 TEST_F(AutofillFieldFillerTest, FillSelectControlByValue) {
   std::vector<const char*> kOptions = {
@@ -823,9 +822,9 @@
 
 struct FillSelectTestCase {
   std::vector<const char*> select_values;
-  const char* input_value;
-  const char* expected_value_without_normalization;
-  const char* expected_value_with_normalization = nullptr;
+  const char16_t* input_value;
+  const char16_t* expected_value_without_normalization;
+  const char16_t* expected_value_with_normalization = nullptr;
 };
 
 class AutofillSelectWithStatesTest
@@ -880,19 +879,17 @@
 
   // Without a normalizer.
   AutofillProfile address = test::GetFullProfile();
-  address.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16(test_case.input_value));
+  address.SetRawInfo(ADDRESS_HOME_STATE, test_case.input_value);
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string());
   // nullptr means we expect them not to match without normalization.
   if (test_case.expected_value_without_normalization != nullptr) {
-    EXPECT_EQ(UTF8ToUTF16(test_case.expected_value_without_normalization),
-              field.value);
+    EXPECT_EQ(test_case.expected_value_without_normalization, field.value);
   }
 
   // With a normalizer.
   AutofillProfile canadian_address = test::GetFullCanadianProfile();
-  canadian_address.SetRawInfo(ADDRESS_HOME_STATE,
-                              UTF8ToUTF16(test_case.input_value));
+  canadian_address.SetRawInfo(ADDRESS_HOME_STATE, test_case.input_value);
   // Fill a first time without loading the rules for the region.
   FieldFiller canadian_filler(/*app_locale=*/"en-US", normalizer());
   canadian_filler.FillFormField(field, &canadian_address, &field,
@@ -900,20 +897,17 @@
   // If the expectation with normalization is nullptr, this means that the same
   // result than without a normalizer is expected.
   if (test_case.expected_value_with_normalization == nullptr) {
-    EXPECT_EQ(UTF8ToUTF16(test_case.expected_value_without_normalization),
-              field.value);
+    EXPECT_EQ(test_case.expected_value_without_normalization, field.value);
   } else {
     // We needed a normalizer with loaded rules. The first fill should have
     // failed.
-    EXPECT_NE(UTF8ToUTF16(test_case.expected_value_with_normalization),
-              field.value);
+    EXPECT_NE(test_case.expected_value_with_normalization, field.value);
 
     // Load the rules and try again.
     normalizer()->LoadRulesForRegion("CA");
     canadian_filler.FillFormField(field, &canadian_address, &field,
                                   /*cvc=*/std::u16string());
-    EXPECT_EQ(UTF8ToUTF16(test_case.expected_value_with_normalization),
-              field.value);
+    EXPECT_EQ(test_case.expected_value_with_normalization, field.value);
   }
 }
 
@@ -922,54 +916,54 @@
     AutofillSelectWithStatesTest,
     testing::Values(
         // Filling the abbreviation.
-        FillSelectTestCase{{"Alabama", "California"}, "CA", "California"},
+        FillSelectTestCase{{"Alabama", "California"}, u"CA", u"California"},
         // Attempting to fill the full name in a select full of abbreviations.
-        FillSelectTestCase{{"AL", "CA"}, "California", "CA"},
+        FillSelectTestCase{{"AL", "CA"}, u"California", u"CA"},
         // Different case and diacritics.
-        FillSelectTestCase{{"QUÉBEC", "ALBERTA"}, "Quebec", "QUÉBEC"},
+        FillSelectTestCase{{"QUÉBEC", "ALBERTA"}, u"Quebec", u"QUÉBEC"},
         // The value and the field options are different but normalize to the
         // same (NB).
         FillSelectTestCase{{"Nouveau-Brunswick", "Alberta"},
-                           "New Brunswick",
+                           u"New Brunswick",
                            nullptr,
-                           "Nouveau-Brunswick"},
-        FillSelectTestCase{{"NB", "AB"}, "New Brunswick", nullptr, "NB"},
-        FillSelectTestCase{{"NB", "AB"}, "Nouveau-Brunswick", nullptr, "NB"},
+                           u"Nouveau-Brunswick"},
+        FillSelectTestCase{{"NB", "AB"}, u"New Brunswick", nullptr, u"NB"},
+        FillSelectTestCase{{"NB", "AB"}, u"Nouveau-Brunswick", nullptr, u"NB"},
         FillSelectTestCase{{"Nouveau-Brunswick", "Alberta"},
-                           "NB",
+                           u"NB",
                            nullptr,
-                           "Nouveau-Brunswick"},
+                           u"Nouveau-Brunswick"},
         FillSelectTestCase{{"New Brunswick", "Alberta"},
-                           "NB",
+                           u"NB",
                            nullptr,
-                           "New Brunswick"},
+                           u"New Brunswick"},
         // Inexact state names.
         FillSelectTestCase{
             {"SC - South Carolina", "CA - California", "NC - North Carolina"},
-            "California",
-            "CA - California"},
+            u"California",
+            u"CA - California"},
         // Don't accidentally match "Virginia" to "West Virginia".
         FillSelectTestCase{
             {"WV - West Virginia", "VA - Virginia", "NV - North Virginia"},
-            "Virginia",
-            "VA - Virginia"},
+            u"Virginia",
+            u"VA - Virginia"},
         // Do accidentally match "Virginia" to "West Virginia".
         // TODO(crbug.com/624770): This test should not pass, but it does
         // because "Virginia" is a substring of "West Virginia".
         FillSelectTestCase{{"WV - West Virginia", "TX - Texas"},
-                           "Virginia",
-                           "WV - West Virginia"},
+                           u"Virginia",
+                           u"WV - West Virginia"},
         // Tests that substring matches work for full state names (a full token
         // match isn't required). Also tests that matches work for states with
         // whitespace in the middle.
         FillSelectTestCase{{"California.", "North Carolina."},
-                           "North Carolina",
-                           "North Carolina."},
+                           u"North Carolina",
+                           u"North Carolina."},
         FillSelectTestCase{{"NC - North Carolina", "CA - California"},
-                           "CA",
-                           "CA - California"},
+                           u"CA",
+                           u"CA - California"},
         // These are not states.
-        FillSelectTestCase{{"NCNCA", "SCNCA"}, "NC", ""}));
+        FillSelectTestCase{{"NCNCA", "SCNCA"}, u"NC", u""}));
 
 TEST_F(AutofillFieldFillerTest, FillSelectWithCountries) {
   AutofillField field;
@@ -1278,16 +1272,12 @@
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   field.set_heuristic_type(ADDRESS_HOME_STREET_ADDRESS);
 
-  std::u16string value =
-      u"123 Fake St.\n"
-      u"Apt. 42";
+  std::u16string value = u"123 Fake St.\nApt. 42";
   address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), value, "en-US");
   filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string());
   EXPECT_EQ(value, field.value);
 
-  std::u16string ja_value = UTF8ToUTF16(
-      "桜丘町26-1\n"
-      "セルリアンタワー6階");
+  std::u16string ja_value = u"桜丘町26-1\nセルリアンタワー6階";
   address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), ja_value,
                      "ja-JP");
   address()->set_language_code("ja-JP");
@@ -1301,16 +1291,12 @@
   field.set_server_type(ADDRESS_HOME_STREET_ADDRESS);
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
 
-  std::u16string value =
-      u"123 Fake St.\n"
-      u"Apt. 42";
+  std::u16string value = u"123 Fake St.\nApt. 42";
   address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), value, "en-US");
   filler.FillFormField(field, address(), &field, /*cvc=*/std::u16string());
   EXPECT_EQ(u"123 Fake St., Apt. 42", field.value);
 
-  std::u16string ja_value = UTF8ToUTF16(
-      "桜丘町26-1\n"
-      "セルリアンタワー6階");
+  std::u16string ja_value = u"桜丘町26-1\nセルリアンタワー6階";
   address()->SetInfo(AutofillType(ADDRESS_HOME_STREET_ADDRESS), ja_value,
                      "ja-JP");
   address()->set_language_code("ja-JP");
@@ -1336,13 +1322,11 @@
 TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithEqualSizeSplits) {
   // Case 2: card number broken up into four equal groups, of length 4.
   CreditCardTestCase test;
-  test.card_number_ = "5187654321098765";
+  test.card_number_ = u"5187654321098765";
   test.total_splits_ = 4;
   int splits[] = {4, 4, 4, 4};
   test.splits_ = std::vector<int>(splits, splits + base::size(splits));
-  std::string results[] = {"5187", "6543", "2109", "8765"};
-  test.expected_results_ =
-      std::vector<std::string>(results, results + base::size(results));
+  test.expected_results_ = {u"5187", u"6543", u"2109", u"8765"};
 
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   for (size_t i = 0; i < test.total_splits_; ++i) {
@@ -1352,12 +1336,12 @@
     cc_number_part.set_credit_card_number_offset(4 * i);
 
     // Fill with a card-number; should fill just the card_number_part.
-    credit_card()->SetNumber(ASCIIToUTF16(test.card_number_));
+    credit_card()->SetNumber(test.card_number_);
     filler.FillFormField(cc_number_part, credit_card(), &cc_number_part,
                          /*cvc=*/std::u16string());
 
     // Verify for expected results.
-    EXPECT_EQ(ASCIIToUTF16(test.expected_results_[i]),
+    EXPECT_EQ(test.expected_results_[i],
               cc_number_part.value.substr(0, cc_number_part.max_length));
     EXPECT_EQ(4 * i, cc_number_part.credit_card_number_offset());
   }
@@ -1366,25 +1350,23 @@
   AutofillField cc_number_full;
   cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
 
-  credit_card()->SetNumber(ASCIIToUTF16(test.card_number_));
+  credit_card()->SetNumber(test.card_number_);
   filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
                        /*cvc=*/std::u16string());
 
   // Verify for expected results.
-  EXPECT_EQ(ASCIIToUTF16(test.card_number_), cc_number_full.value);
+  EXPECT_EQ(test.card_number_, cc_number_full.value);
 }
 
 TEST_F(AutofillFieldFillerTest, FillCreditCardNumberWithUnequalSizeSplits) {
   // Case 3: card with 15 digits number, broken up into three unequal groups, of
   // lengths 4, 6, and 5.
   CreditCardTestCase test;
-  test.card_number_ = "423456789012345";
+  test.card_number_ = u"423456789012345";
   test.total_splits_ = 3;
   int splits[] = {4, 6, 5};
   test.splits_ = std::vector<int>(splits, splits + base::size(splits));
-  std::string results[] = {"4234", "567890", "12345"};
-  test.expected_results_ =
-      std::vector<std::string>(results, results + base::size(results));
+  test.expected_results_ = {u"4234", u"567890", u"12345"};
 
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   // Start executing test cases to verify parts and full credit card number.
@@ -1395,12 +1377,12 @@
     cc_number_part.set_credit_card_number_offset(GetNumberOffset(i, test));
 
     // Fill with a card-number; should fill just the card_number_part.
-    credit_card()->SetNumber(ASCIIToUTF16(test.card_number_));
+    credit_card()->SetNumber(test.card_number_);
     filler.FillFormField(cc_number_part, credit_card(), &cc_number_part,
                          /*cvc=*/std::u16string());
 
     // Verify for expected results.
-    EXPECT_EQ(ASCIIToUTF16(test.expected_results_[i]),
+    EXPECT_EQ(test.expected_results_[i],
               cc_number_part.value.substr(0, cc_number_part.max_length));
     EXPECT_EQ(GetNumberOffset(i, test),
               cc_number_part.credit_card_number_offset());
@@ -1409,12 +1391,12 @@
   // Verify that full card-number shall get fill properly as well.
   AutofillField cc_number_full;
   cc_number_full.set_heuristic_type(CREDIT_CARD_NUMBER);
-  credit_card()->SetNumber(ASCIIToUTF16(test.card_number_));
+  credit_card()->SetNumber(test.card_number_);
   filler.FillFormField(cc_number_full, credit_card(), &cc_number_full,
                        /*cvc=*/std::u16string());
 
   // Verify for expected results.
-  EXPECT_EQ(ASCIIToUTF16(test.card_number_), cc_number_full.value);
+  EXPECT_EQ(test.card_number_, cc_number_full.value);
 }
 
 TEST_F(AutofillFieldFillerTest, FindShortestSubstringMatchInSelect) {
@@ -1464,8 +1446,8 @@
 struct FillStateTextTestCase {
   HtmlFieldType field_type;
   size_t field_max_length;
-  std::string value_to_fill;
-  std::string expected_value;
+  std::u16string value_to_fill;
+  std::u16string expected_value;
   bool should_fill;
 };
 
@@ -1483,12 +1465,12 @@
 
   FieldFiller filler(/*app_locale=*/"en-US", /*address_normalizer=*/nullptr);
   AutofillProfile address = test::GetFullProfile();
-  address.SetRawInfo(ADDRESS_HOME_STATE, UTF8ToUTF16(test_case.value_to_fill));
+  address.SetRawInfo(ADDRESS_HOME_STATE, test_case.value_to_fill);
   bool has_filled =
       filler.FillFormField(field, &address, &field, /*cvc=*/std::u16string());
 
   EXPECT_EQ(test_case.should_fill, has_filled);
-  EXPECT_EQ(ASCIIToUTF16(test_case.expected_value), field.value);
+  EXPECT_EQ(test_case.expected_value, field.value);
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -1499,28 +1481,28 @@
         // should
         // fill the state value as is.
         FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, /* default value */ 0,
-                              "New York", "New York", true},
+                              u"New York", u"New York", true},
         FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, /* default value */ 0,
-                              "NY", "NY", true},
+                              u"NY", u"NY", true},
         // Filling a state to a text field with a maxlength value equal to the
         // value's length should fill the state value as is.
-        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 8, "New York",
-                              "New York", true},
+        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 8, u"New York",
+                              u"New York", true},
         // Filling a state to a text field with a maxlength value lower than the
         // value's length but higher than the value's abbreviation should fill
         // the state abbreviation.
-        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 2, "New York", "NY",
+        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 2, u"New York", u"NY",
                               true},
-        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 2, "NY", "NY", true},
+        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 2, u"NY", u"NY", true},
         // Filling a state to a text field with a maxlength value lower than the
         // value's length and the value's abbreviation should not fill at all.
-        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 1, "New York", "",
+        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 1, u"New York", u"",
                               false},
-        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 1, "NY", "", false},
+        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 1, u"NY", u"", false},
         // Filling a state to a text field with a maxlength value lower than the
         // value's length and that has no associated abbreviation should not
         // fill at all.
-        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 3, "Quebec", "",
+        FillStateTextTestCase{HTML_TYPE_ADDRESS_LEVEL1, 3, u"Quebec", u"",
                               false}));
 
 // Tests that the correct option is chosen in the selection box when one of the
diff --git a/components/autofill/core/browser/form_parsing/form_field_unittest.cc b/components/autofill/core/browser/form_parsing/form_field_unittest.cc
index 54e0c2e..2d04f20 100644
--- a/components/autofill/core/browser/form_parsing/form_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/form_field_unittest.cc
@@ -15,7 +15,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using autofill::features::kAutofillFixFillableFieldTypes;
-using base::ASCIIToUTF16;
 
 namespace autofill {
 
@@ -26,9 +25,9 @@
 }
 
 // Sets both the field label and parseable label to |label|.
-void SetFieldLabels(AutofillField* field, const std::string& label) {
-  field->label = base::UTF8ToUTF16(label);
-  field->set_parseable_label(base::UTF8ToUTF16(label));
+void SetFieldLabels(AutofillField* field, const std::u16string& label) {
+  field->label = label;
+  field->set_parseable_label(label);
 }
 
 }  // namespace
@@ -40,76 +39,76 @@
   EXPECT_TRUE(FormField::Match(&field, std::u16string(), MATCH_LABEL));
 
   // Empty pattern matches non-empty string.
-  SetFieldLabels(&field, "a");
+  SetFieldLabels(&field, u"a");
   EXPECT_TRUE(FormField::Match(&field, std::u16string(), MATCH_LABEL));
 
   // Strictly empty pattern matches empty string.
-  SetFieldLabels(&field, "");
+  SetFieldLabels(&field, u"");
   EXPECT_TRUE(FormField::Match(&field, u"^$", MATCH_LABEL));
 
   // Strictly empty pattern does not match non-empty string.
-  SetFieldLabels(&field, "a");
+  SetFieldLabels(&field, u"a");
   EXPECT_FALSE(FormField::Match(&field, u"^$", MATCH_LABEL));
 
   // Non-empty pattern doesn't match empty string.
-  SetFieldLabels(&field, "");
+  SetFieldLabels(&field, u"");
   EXPECT_FALSE(FormField::Match(&field, u"a", MATCH_LABEL));
 
   // Beginning of line.
-  SetFieldLabels(&field, "head_tail");
+  SetFieldLabels(&field, u"head_tail");
   EXPECT_TRUE(FormField::Match(&field, u"^head", MATCH_LABEL));
   EXPECT_FALSE(FormField::Match(&field, u"^tail", MATCH_LABEL));
 
   // End of line.
-  SetFieldLabels(&field, "head_tail");
+  SetFieldLabels(&field, u"head_tail");
   EXPECT_FALSE(FormField::Match(&field, u"head$", MATCH_LABEL));
   EXPECT_TRUE(FormField::Match(&field, u"tail$", MATCH_LABEL));
 
   // Exact.
-  SetFieldLabels(&field, "head_tail");
+  SetFieldLabels(&field, u"head_tail");
   EXPECT_FALSE(FormField::Match(&field, u"^head$", MATCH_LABEL));
   EXPECT_FALSE(FormField::Match(&field, u"^tail$", MATCH_LABEL));
   EXPECT_TRUE(FormField::Match(&field, u"^head_tail$", MATCH_LABEL));
 
   // Escaped dots.
-  SetFieldLabels(&field, "m.i.");
+  SetFieldLabels(&field, u"m.i.");
   // Note: This pattern is misleading as the "." characters are wild cards.
   EXPECT_TRUE(FormField::Match(&field, u"m.i.", MATCH_LABEL));
   EXPECT_TRUE(FormField::Match(&field, u"m\\.i\\.", MATCH_LABEL));
-  SetFieldLabels(&field, "mXiX");
+  SetFieldLabels(&field, u"mXiX");
   EXPECT_TRUE(FormField::Match(&field, u"m.i.", MATCH_LABEL));
   EXPECT_FALSE(FormField::Match(&field, u"m\\.i\\.", MATCH_LABEL));
 
   // Repetition.
-  SetFieldLabels(&field, "headtail");
+  SetFieldLabels(&field, u"headtail");
   EXPECT_TRUE(FormField::Match(&field, u"head.*tail", MATCH_LABEL));
-  SetFieldLabels(&field, "headXtail");
+  SetFieldLabels(&field, u"headXtail");
   EXPECT_TRUE(FormField::Match(&field, u"head.*tail", MATCH_LABEL));
-  SetFieldLabels(&field, "headXXXtail");
+  SetFieldLabels(&field, u"headXXXtail");
   EXPECT_TRUE(FormField::Match(&field, u"head.*tail", MATCH_LABEL));
-  SetFieldLabels(&field, "headtail");
+  SetFieldLabels(&field, u"headtail");
   EXPECT_FALSE(FormField::Match(&field, u"head.+tail", MATCH_LABEL));
-  SetFieldLabels(&field, "headXtail");
+  SetFieldLabels(&field, u"headXtail");
   EXPECT_TRUE(FormField::Match(&field, u"head.+tail", MATCH_LABEL));
-  SetFieldLabels(&field, "headXXXtail");
+  SetFieldLabels(&field, u"headXXXtail");
   EXPECT_TRUE(FormField::Match(&field, u"head.+tail", MATCH_LABEL));
 
   // Alternation.
-  SetFieldLabels(&field, "head_tail");
+  SetFieldLabels(&field, u"head_tail");
   EXPECT_TRUE(FormField::Match(&field, u"head|other", MATCH_LABEL));
   EXPECT_TRUE(FormField::Match(&field, u"tail|other", MATCH_LABEL));
   EXPECT_FALSE(FormField::Match(&field, u"bad|good", MATCH_LABEL));
 
   // Case sensitivity.
-  SetFieldLabels(&field, "xxxHeAd_tAiLxxx");
+  SetFieldLabels(&field, u"xxxHeAd_tAiLxxx");
   EXPECT_TRUE(FormField::Match(&field, u"head_tail", MATCH_LABEL));
 
   // Word boundaries.
-  SetFieldLabels(&field, "contains word:");
+  SetFieldLabels(&field, u"contains word:");
   EXPECT_TRUE(FormField::Match(&field, u"\\bword\\b", MATCH_LABEL));
   EXPECT_FALSE(FormField::Match(&field, u"\\bcon\\b", MATCH_LABEL));
-  // Make sure the circumflex in 'crepe' is not treated as a word boundary.
-  field.label = base::UTF8ToUTF16("cr\xC3\xAApe");
+  // Make sure the circumflex in 'crêpe' is not treated as a word boundary.
+  field.label = u"crêpe";
   EXPECT_FALSE(FormField::Match(&field, u"\\bcr\\b", MATCH_LABEL));
 }
 
diff --git a/components/autofill/core/browser/form_processing/name_processing_util.cc b/components/autofill/core/browser/form_processing/name_processing_util.cc
index 29140ba..fc78a6ca 100644
--- a/components/autofill/core/browser/form_processing/name_processing_util.cc
+++ b/components/autofill/core/browser/form_processing/name_processing_util.cc
@@ -28,7 +28,7 @@
 constexpr int kMinCommonNameLongPrefixLength = 16;
 // Regular expression for checking if |parseable_name| is valid after stripping
 // affixes.
-constexpr char kParseableNameValidationRe[] = "\\D";
+constexpr char16_t kParseableNameValidationRe[] = u"\\D";
 
 using NamePieces = std::vector<base::StringPiece16>;
 using OptionalNamePieces = base::Optional<NamePieces>;
@@ -85,7 +85,7 @@
 // is the |autofill::kParseableNameValidationRe| regex.
 bool IsValidParseableName(const base::StringPiece16 parseable_name) {
   static const std::u16string kParseableNameValidationPattern =
-      base::UTF8ToUTF16(kParseableNameValidationRe);
+      kParseableNameValidationRe;
   return MatchesPattern(parseable_name, kParseableNameValidationPattern);
 }
 
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index 0d57e55..56de269 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -952,6 +952,29 @@
   return forms;
 }
 
+// static
+std::vector<FieldRendererId> FormStructure::FindFieldsEligibleForManualFilling(
+    const std::vector<FormStructure*>& forms) {
+  std::vector<FieldRendererId> fields_eligible_for_manual_filling;
+  for (const auto* form : forms) {
+    for (const auto& field : form->fields_) {
+      FieldTypeGroup field_type_group =
+          autofill::GroupTypeOfServerFieldType(field->server_type());
+      // In order to trigger the payments bottom sheet that assists users to
+      // manually fill the form, credit card form fields are marked eligible for
+      // manual filling. Also, if a field is not classified to a type, we can
+      // assume that the prediction failed and thus mark it eligible for manual
+      // filling. As more form types support manual filling on form interaction,
+      // this list may expand in the future.
+      if (field_type_group == FieldTypeGroup::kCreditCard ||
+          field_type_group == FieldTypeGroup::kNoGroup) {
+        fields_eligible_for_manual_filling.push_back(field->unique_renderer_id);
+      }
+    }
+  }
+  return fields_eligible_for_manual_filling;
+}
+
 std::unique_ptr<FormStructure> FormStructure::CreateForPasswordManagerUpload(
     FormSignature form_signature,
     const std::vector<FieldSignature>& field_signatures) {
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index 76e5d13..e2f747bf 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -211,6 +211,11 @@
   // * NAME_LAST_SECOND heuristic predictions are unconditionally used.
   void OverrideServerPredictionsWithHeuristics();
 
+  // Returns the FieldRendererId for fields that are eligible for Manual Filling
+  // on form interaction.
+  static std::vector<FieldRendererId> FindFieldsEligibleForManualFilling(
+      const std::vector<FormStructure*>& forms);
+
   const AutofillField* field(size_t index) const;
   AutofillField* field(size_t index);
   size_t field_count() const;
@@ -332,6 +337,13 @@
     if (field_index < fields_.size() && type > 0 && type < MAX_VALID_FIELD_TYPE)
       fields_[field_index]->set_heuristic_type(type);
   }
+  // Set the server field type for |fields_[field_index]| to |type| for testing
+  // purposes.
+  void set_server_field_type_for_testing(size_t field_index,
+                                         ServerFieldType type) {
+    if (field_index < fields_.size() && type > 0 && type < MAX_VALID_FIELD_TYPE)
+      fields_[field_index]->set_server_type(type);
+  }
 #endif
 
   void set_password_symbol_vote(int noisified_symbol) {
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 11ab7c6..1d69fb87 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -8390,4 +8390,48 @@
   EXPECT_EQ("blue-shipping-default", form_structure.field(1)->section);
 }
 
+TEST_F(FormStructureTestImpl, FindFieldsEligibleForManualFilling) {
+  FormData form;
+  form.url = GURL("http://foo.com");
+  FormFieldData field;
+  field.form_control_type = "text";
+  field.max_length = 10000;
+
+  FieldRendererId full_name_renderer_id = MakeFieldRendererId();
+  field.label = u"Full Name";
+  field.name = u"fullName";
+  field.unique_renderer_id = full_name_renderer_id;
+  form.fields.push_back(field);
+
+  FieldRendererId country_renderer_id = MakeFieldRendererId();
+  field.label = u"Country";
+  field.name = u"country";
+  field.unique_renderer_id = country_renderer_id;
+  form.fields.push_back(field);
+
+  FieldRendererId unknown_renderer_id = MakeFieldRendererId();
+  field.label = u"Unknown";
+  field.name = u"unknown";
+  field.unique_renderer_id = unknown_renderer_id;
+  form.fields.push_back(field);
+
+  FormStructure form_structure(form);
+
+  form_structure.set_server_field_type_for_testing(0, CREDIT_CARD_NAME_FULL);
+  form_structure.set_server_field_type_for_testing(1, ADDRESS_HOME_COUNTRY);
+  form_structure.set_server_field_type_for_testing(2, UNKNOWN_TYPE);
+
+  std::vector<FormStructure*> forms;
+  forms.push_back(&form_structure);
+
+  form_structure.identify_sections_for_testing();
+  std::vector<FieldRendererId> expected_result;
+  // Only credit card related and unknown fields are elible for manual filling.
+  expected_result.push_back(full_name_renderer_id);
+  expected_result.push_back(unknown_renderer_id);
+
+  EXPECT_EQ(expected_result,
+            FormStructure::FindFieldsEligibleForManualFilling(forms));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc b/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc
index 5b5148f..9139a8e 100644
--- a/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc
+++ b/components/autofill/core/browser/geo/phone_number_i18n_unittest.cc
@@ -15,9 +15,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libphonenumber/phonenumber_api.h"
 
-using base::ASCIIToUTF16;
-using base::UTF8ToUTF16;
-
 namespace autofill {
 
 using i18n::ConstructPhoneNumber;
@@ -26,20 +23,15 @@
 using i18n::PhoneNumbersMatch;
 
 TEST(PhoneNumberI18NTest, NormalizePhoneNumber) {
-  // "Large" digits.
-  std::u16string phone1(
-      UTF8ToUTF16("\xEF\xBC\x91\xEF\xBC\x96\xEF\xBC\x95\xEF\xBC\x90"
-                  "\xEF\xBC\x97\xEF\xBC\x94\xEF\xBC\x99\xEF\xBC\x98"
-                  "\xEF\xBC\x93\xEF\xBC\x92\xEF\xBC\x93"));
+  // "Large" digits; these are not ASCII.
+  std::u16string phone1(u"16507498323");
   EXPECT_EQ(NormalizePhoneNumber(phone1, "US"), u"16507498323");
 
   // Devanagari script digits.
-  std::u16string phone2(
-      UTF8ToUTF16("\xD9\xA1\xD9\xA6\xD9\xA5\xD9\xA0\xD9\xA8\xD9\xA3"
-                  "\xD9\xA2\xD9\xA3\xD9\xA7\xD9\xA4\xD9\xA9"));
+  std::u16string phone2(u"١٦٥٠٨٣٢٣٧٤٩");
   EXPECT_EQ(NormalizePhoneNumber(phone2, "US"), u"16508323749");
 
-  std::u16string phone3(UTF8ToUTF16("16503334\xef\xbc\x92\x35\xd9\xa5"));
+  std::u16string phone3(u"1650333425٥");
   EXPECT_EQ(NormalizePhoneNumber(phone3, "US"), u"16503334255");
 
   std::u16string phone4(u"+1(650)2346789");
@@ -53,20 +45,20 @@
   // Expected parsing result.
   bool isPossibleNumber;
   // Inputs.
-  std::string input;
+  std::u16string input;
   std::string assumed_region;
   // Further expectations.
-  std::string number;
-  std::string city_code;
-  std::string country_code;
+  std::u16string number;
+  std::u16string city_code;
+  std::u16string country_code;
   std::string deduced_region;
 };
 
 namespace {
 
 // Returns a string which is too long to be considered a phone number.
-std::string GenerateTooLongString() {
-  return std::string(i18n::kMaxPhoneNumberSize + 1, '7');
+std::u16string GenerateTooLongString() {
+  return std::u16string(i18n::kMaxPhoneNumberSize + 1, u'7');
 }
 
 }  // namespace
@@ -75,19 +67,18 @@
 
 TEST_P(ParseNumberTest, ParsePhoneNumber) {
   auto test_case = GetParam();
-  SCOPED_TRACE("Testing phone number " + test_case.input);
+  SCOPED_TRACE(test_case.input.c_str());
 
   std::u16string country_code, city_code, number;
   std::string deduced_region;
   ::i18n::phonenumbers::PhoneNumber unused_i18n_number;
-  EXPECT_EQ(
-      test_case.isPossibleNumber,
-      ParsePhoneNumber(UTF8ToUTF16(test_case.input), test_case.assumed_region,
-                       &country_code, &city_code, &number, &deduced_region,
-                       &unused_i18n_number));
-  EXPECT_EQ(ASCIIToUTF16(test_case.number), number);
-  EXPECT_EQ(ASCIIToUTF16(test_case.city_code), city_code);
-  EXPECT_EQ(ASCIIToUTF16(test_case.country_code), country_code);
+  EXPECT_EQ(test_case.isPossibleNumber,
+            ParsePhoneNumber(test_case.input, test_case.assumed_region,
+                             &country_code, &city_code, &number,
+                             &deduced_region, &unused_i18n_number));
+  EXPECT_EQ(test_case.number, number);
+  EXPECT_EQ(test_case.city_code, city_code);
+  EXPECT_EQ(test_case.country_code, country_code);
   EXPECT_EQ(test_case.deduced_region, deduced_region);
 }
 
@@ -96,99 +87,93 @@
     ParseNumberTest,
     testing::Values(
         // Test for empty string.  Should give back empty strings.
-        ParseNumberTestCase{false, "", "US"},
+        ParseNumberTestCase{false, u"", "US"},
         // Test for string with less than 7 digits.  Should give back empty
         // strings.
-        ParseNumberTestCase{false, "1234", "US"},
+        ParseNumberTestCase{false, u"1234", "US"},
         // Too long strings should not be parsed.
         ParseNumberTestCase{false, GenerateTooLongString(), "US"},
         // Test for string with exactly 7 digits. It is too short.
         // Should fail parsing in US.
-        ParseNumberTestCase{false, "17134567", "US"},
+        ParseNumberTestCase{false, u"17134567", "US"},
         // Does not have area code, but still a possible number with
         // unknown("ZZ") deduced region.
-        ParseNumberTestCase{true, "7134567", "US", "7134567", "", "", "ZZ"},
+        ParseNumberTestCase{true, u"7134567", "US", u"7134567", u"", u"", "ZZ"},
         // Valid Canadian toll-free number.
-        ParseNumberTestCase{true, "3101234", "CA", "3101234", "", "", "ZZ"},
+        ParseNumberTestCase{true, u"3101234", "CA", u"3101234", u"", u"", "ZZ"},
         // Test for string with greater than 7 digits but less than 10 digits.
         // Should fail parsing in US.
-        ParseNumberTestCase{false, "123456789", "US"},
+        ParseNumberTestCase{false, u"123456789", "US"},
         // Test for string with greater than 7 digits but less than 10 digits
         // and
         // separators.
         // Should fail parsing in US.
-        ParseNumberTestCase{false, "12.345-6789", "US"},
+        ParseNumberTestCase{false, u"12.345-6789", "US"},
         // Non-printable ASCII.
-        ParseNumberTestCase{false, "123\x11", "US"},
-        ParseNumberTestCase{false,
-                            "123\x7F"
-                            "567",
-                            "US"},
+        ParseNumberTestCase{false, u"123", "US"},
+        ParseNumberTestCase{false, u"123\u007f567", "US"},
         // Unicode noncharacters.
-        ParseNumberTestCase{false,
-                            "1\xEF\xB7\xAF"
-                            "23",
-                            "US"},
-        // Invalid UTF8.
-        ParseNumberTestCase{false, "1\xC0", "US"},
+        ParseNumberTestCase{false, u"1\ufdef23", "US"},
+        // Invalid UTF16.
+        ParseNumberTestCase{false, u"1\xdfff", "US"},
         // Test for string with exactly 10 digits.
         // Should give back phone number and city code.
         // This one has an incorrect area code but could still be a possible
         // number with unknown("ZZ") deducted region.
-        ParseNumberTestCase{true, "1234567890", "US", "1234567890", "", "",
+        ParseNumberTestCase{true, u"1234567890", "US", u"1234567890", u"", u"",
                             "ZZ"},
         // This is actually not a valid number because the first number after
         // area code is 1. But it's still a possible number, just with deduced
         // country set to unknown("ZZ").
-        ParseNumberTestCase{true, "6501567890", "US", "1567890", "650", "",
+        ParseNumberTestCase{true, u"6501567890", "US", u"1567890", u"650", u"",
                             "ZZ"},
-        ParseNumberTestCase{true, "6504567890", "US", "4567890", "650", "",
+        ParseNumberTestCase{true, u"6504567890", "US", u"4567890", u"650", u"",
                             "US"},
         // Test for string with exactly 10 digits and separators.
         // Should give back phone number and city code.
-        ParseNumberTestCase{true, "(650) 456-7890", "US", "4567890", "650", "",
-                            "US"},
+        ParseNumberTestCase{true, u"(650) 456-7890", "US", u"4567890", u"650",
+                            u"", "US"},
         // Tests for string with over 10 digits.
         // 01 is incorrect prefix in the USA, we interpret 011 as prefix, and
         // rest is parsed as a Singapore number(country code "SG").
-        ParseNumberTestCase{true, "0116504567890", "US", "04567890", "", "65",
-                            "SG"},
+        ParseNumberTestCase{true, u"0116504567890", "US", u"04567890", u"",
+                            u"65", "SG"},
         // 011 is a correct "dial out" prefix in the USA - the parsing should
         // succeed.
-        ParseNumberTestCase{true, "01116504567890", "US", "4567890", "650", "1",
-                            "US"},
+        ParseNumberTestCase{true, u"01116504567890", "US", u"4567890", u"650",
+                            u"1", "US"},
         // 011 is a correct "dial out" prefix in the USA but the rest of the
         // number
         // can't parse as a US number.
-        ParseNumberTestCase{true, "01178124567890", "US", "4567890", "812", "7",
-                            "RU"},
+        ParseNumberTestCase{true, u"01178124567890", "US", u"4567890", u"812",
+                            u"7", "RU"},
         // Test for string with over 10 digits with separator characters.
         // Should give back phone number, city code, and country code. "011" is
         // US "dial out" code, which is discarded.
-        ParseNumberTestCase{true, "(0111) 650-456.7890", "US", "4567890", "650",
-                            "1", "US"},
+        ParseNumberTestCase{true, u"(0111) 650-456.7890", "US", u"4567890",
+                            u"650", u"1", "US"},
         // Now try phone from Czech republic - it has 00 dial out code, 420
         // country
         // code and variable length area codes.
-        ParseNumberTestCase{true, "+420 27-89.10.112", "US", "910112", "278",
-                            "420", "CZ"},
-        ParseNumberTestCase{false, "27-89.10.112", "US"},
-        ParseNumberTestCase{true, "27-89.10.112", "CZ", "910112", "278", "",
+        ParseNumberTestCase{true, u"+420 27-89.10.112", "US", u"910112", u"278",
+                            u"420", "CZ"},
+        ParseNumberTestCase{false, u"27-89.10.112", "US"},
+        ParseNumberTestCase{true, u"27-89.10.112", "CZ", u"910112", u"278", u"",
                             "CZ"},
-        ParseNumberTestCase{false, "420 57-89.10.112", "US"},
-        ParseNumberTestCase{true, "420 57-89.10.112", "CZ", "910112", "578",
-                            "420", "CZ"},
+        ParseNumberTestCase{false, u"420 57-89.10.112", "US"},
+        ParseNumberTestCase{true, u"420 57-89.10.112", "CZ", u"910112", u"578",
+                            u"420", "CZ"},
         // Parses vanity numbers.
-        ParseNumberTestCase{true, "1-650-FLOWERS", "US", "3569377", "650", "1",
-                            "US"},
+        ParseNumberTestCase{true, u"1-650-FLOWERS", "US", u"3569377", u"650",
+                            u"1", "US"},
         // 800 is not an area code, but the destination code. In our library
         // these
         // codes should be treated the same as area codes.
-        ParseNumberTestCase{true, "1-800-FLOWERS", "US", "3569377", "800", "1",
-                            "US"},
+        ParseNumberTestCase{true, u"1-800-FLOWERS", "US", u"3569377", u"800",
+                            u"1", "US"},
         // Don't add a country code where there was none.
-        ParseNumberTestCase{true, "(08) 450 777 7777", "DE", "7777777", "8450",
-                            "", "DE"}));
+        ParseNumberTestCase{true, u"(08) 450 777 7777", "DE", u"7777777",
+                            u"8450", u"", "DE"}));
 
 TEST(PhoneNumberI18NTest, ConstructPhoneNumber) {
   std::u16string number;
@@ -316,18 +301,18 @@
 
 // Test for the GetFormattedPhoneNumberForDisplay method.
 struct PhoneNumberFormatCase {
-  PhoneNumberFormatCase(const char* phone,
-                        const char* country,
-                        const char* expected_format,
+  PhoneNumberFormatCase(const char16_t* phone,
+                        const char16_t* country,
+                        const char16_t* expected_format,
                         const char* locale = "")
       : phone(phone),
         country(country),
         expected_format(expected_format),
         locale(locale) {}
 
-  const char* phone;
-  const char* country;
-  const char* expected_format;
+  const char16_t* phone;
+  const char16_t* country;
+  const char16_t* expected_format;
   const char* locale;
 };
 
@@ -337,13 +322,10 @@
 TEST_P(GetFormattedPhoneNumberForDisplayTest,
        GetFormattedPhoneNumberForDisplay) {
   AutofillProfile profile;
-  profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
-                     base::UTF8ToUTF16(GetParam().phone));
-  profile.SetRawInfo(ADDRESS_HOME_COUNTRY,
-                     base::UTF8ToUTF16(GetParam().country));
-  EXPECT_EQ(GetParam().expected_format,
-            base::UTF16ToUTF8(i18n::GetFormattedPhoneNumberForDisplay(
-                profile, GetParam().locale)));
+  profile.SetRawInfo(PHONE_HOME_WHOLE_NUMBER, GetParam().phone);
+  profile.SetRawInfo(ADDRESS_HOME_COUNTRY, GetParam().country);
+  EXPECT_EQ(GetParam().expected_format, i18n::GetFormattedPhoneNumberForDisplay(
+                                            profile, GetParam().locale));
 }
 
 INSTANTIATE_TEST_SUITE_P(
@@ -354,104 +336,104 @@
         // US phone in US.
         //////////////////////////
         // Formatted phone numbers.
-        PhoneNumberFormatCase("+1 415-555-5555", "US", "+1 415-555-5555"),
-        PhoneNumberFormatCase("1 415-555-5555", "US", "+1 415-555-5555"),
-        PhoneNumberFormatCase("415-555-5555", "US", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"+1 415-555-5555", u"US", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"1 415-555-5555", u"US", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"415-555-5555", u"US", u"+1 415-555-5555"),
         // Raw phone numbers.
-        PhoneNumberFormatCase("+14155555555", "US", "+1 415-555-5555"),
-        PhoneNumberFormatCase("14155555555", "US", "+1 415-555-5555"),
-        PhoneNumberFormatCase("4155555555", "US", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"+14155555555", u"US", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"14155555555", u"US", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"4155555555", u"US", u"+1 415-555-5555"),
 
         //////////////////////////
         // US phone in CA.
         //////////////////////////
         // Formatted phone numbers.
-        PhoneNumberFormatCase("+1 415-555-5555", "CA", "+1 415-555-5555"),
-        PhoneNumberFormatCase("1 415-555-5555", "CA", "+1 415-555-5555"),
-        PhoneNumberFormatCase("415-555-5555", "CA", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"+1 415-555-5555", u"CA", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"1 415-555-5555", u"CA", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"415-555-5555", u"CA", u"+1 415-555-5555"),
         // Raw phone numbers.
-        PhoneNumberFormatCase("+14155555555", "CA", "+1 415-555-5555"),
-        PhoneNumberFormatCase("14155555555", "CA", "+1 415-555-5555"),
-        PhoneNumberFormatCase("4155555555", "CA", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"+14155555555", u"CA", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"14155555555", u"CA", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"4155555555", u"CA", u"+1 415-555-5555"),
 
         //////////////////////////
         // US phone in AU.
         //////////////////////////
         // A US phone with the country code is correctly formatted as an US
         // number.
-        PhoneNumberFormatCase("+1 415-555-5555", "AU", "+1 415-555-5555"),
-        PhoneNumberFormatCase("1 415-555-5555", "AU", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"+1 415-555-5555", u"AU", u"+1 415-555-5555"),
+        PhoneNumberFormatCase(u"1 415-555-5555", u"AU", u"+1 415-555-5555"),
         // Without a country code, the phone is formatted for the profile's
         // country, if it's valid.
-        PhoneNumberFormatCase("2 9374 4000", "AU", "+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"2 9374 4000", u"AU", u"+61 2 9374 4000"),
         // Without a country code, formatting returns the number as entered by
         // user, if it's invalid.
-        PhoneNumberFormatCase("415-555-5555", "AU", "4155555555"),
+        PhoneNumberFormatCase(u"415-555-5555", u"AU", u"4155555555"),
 
         //////////////////////////
         // US phone in MX.
         //////////////////////////
         // A US phone with the country code is correctly formatted as an US
         // number.
-        PhoneNumberFormatCase("+1 415-555-5555", "MX", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"+1 415-555-5555", u"MX", u"+1 415-555-5555"),
         // "+52 415 555 5555" is a valid number for Mexico,
-        PhoneNumberFormatCase("1 415-555-5555", "MX", "+52 415 555 5555"),
+        PhoneNumberFormatCase(u"1 415-555-5555", u"MX", u"+52 415 555 5555"),
         // Without a country code, the phone is formatted for the profile's
         // country.
-        PhoneNumberFormatCase("415-555-5555", "MX", "+52 415 555 5555"),
+        PhoneNumberFormatCase(u"415-555-5555", u"MX", u"+52 415 555 5555"),
 
         //////////////////////////
         // AU phone in AU.
         //////////////////////////
         // Formatted phone numbers.
-        PhoneNumberFormatCase("+61 2 9374 4000", "AU", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("61 2 9374 4000", "AU", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("02 9374 4000", "AU", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("2 9374 4000", "AU", "+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"+61 2 9374 4000", u"AU", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"61 2 9374 4000", u"AU", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"02 9374 4000", u"AU", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"2 9374 4000", u"AU", u"+61 2 9374 4000"),
         // Raw phone numbers.
-        PhoneNumberFormatCase("+61293744000", "AU", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("61293744000", "AU", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("0293744000", "AU", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("293744000", "AU", "+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"+61293744000", u"AU", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"61293744000", u"AU", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"0293744000", u"AU", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"293744000", u"AU", u"+61 2 9374 4000"),
 
         //////////////////////////
         // AU phone in US.
         //////////////////////////
         // An AU phone with the country code is correctly formatted as an AU
         // number.
-        PhoneNumberFormatCase("+61 2 9374 4000", "US", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("61 2 9374 4000", "US", "+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"+61 2 9374 4000", u"US", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"61 2 9374 4000", u"US", u"+61 2 9374 4000"),
         // Without a country code, the phone is formatted for the profile's
         // country.
         // This local AU number is associated with US profile, the number is
         // not a valid US number, therefore formatting will just return what
         // user entered.
-        PhoneNumberFormatCase("02 9374 4000", "US", "0293744000"),
+        PhoneNumberFormatCase(u"02 9374 4000", u"US", u"0293744000"),
         // This local GR(Greece) number is formatted as an US number, if it's
         // valid US number.
-        PhoneNumberFormatCase("22 6800 0090", "US", "+1 226-800-0090"),
+        PhoneNumberFormatCase(u"22 6800 0090", u"US", u"+1 226-800-0090"),
 
         //////////////////////////
         // MX phone in MX.
         //////////////////////////
         // Formatted phone numbers.
-        PhoneNumberFormatCase("+52 55 5342 8400", "MX", "+52 55 5342 8400"),
-        PhoneNumberFormatCase("52 55 5342 8400", "MX", "+52 55 5342 8400"),
-        PhoneNumberFormatCase("55 5342 8400", "MX", "+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"+52 55 5342 8400", u"MX", u"+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"52 55 5342 8400", u"MX", u"+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"55 5342 8400", u"MX", u"+52 55 5342 8400"),
         // Raw phone numbers.
-        PhoneNumberFormatCase("+525553428400", "MX", "+52 55 5342 8400"),
-        PhoneNumberFormatCase("525553428400", "MX", "+52 55 5342 8400"),
-        PhoneNumberFormatCase("5553428400", "MX", "+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"+525553428400", u"MX", u"+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"525553428400", u"MX", u"+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"5553428400", u"MX", u"+52 55 5342 8400"),
 
         //////////////////////////
         // MX phone in US.
         //////////////////////////
         // A MX phone with the country code is correctly formatted as a MX
         // number.
-        PhoneNumberFormatCase("+52 55 5342 8400", "US", "+52 55 5342 8400"),
-        PhoneNumberFormatCase("52 55 5342 8400", "US", "+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"+52 55 5342 8400", u"US", u"+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"52 55 5342 8400", u"US", u"+52 55 5342 8400"),
         // This number is not a valid US number, we won't try to format.
-        PhoneNumberFormatCase("55 5342 8400", "US", "5553428400")));
+        PhoneNumberFormatCase(u"55 5342 8400", u"US", u"5553428400")));
 
 INSTANTIATE_TEST_SUITE_P(
     GetFormattedPhoneNumberForDisplay_EdgeCases,
@@ -461,31 +443,40 @@
         // No country.
         //////////////////////////
         // Fallback to locale if no country is set.
-        PhoneNumberFormatCase("52 55 5342 8400",
-                              "",
-                              "+52 55 5342 8400",
+        PhoneNumberFormatCase(u"52 55 5342 8400",
+                              u"",
+                              u"+52 55 5342 8400",
                               "es_MX"),
-        PhoneNumberFormatCase("55 5342 8400", "", "+52 55 5342 8400", "es_MX"),
-        PhoneNumberFormatCase("61 2 9374 4000", "", "+61 2 9374 4000", "en_AU"),
-        PhoneNumberFormatCase("02 9374 4000", "", "+61 2 9374 4000", "en_AU"),
+        PhoneNumberFormatCase(u"55 5342 8400",
+                              u"",
+                              u"+52 55 5342 8400",
+                              "es_MX"),
+        PhoneNumberFormatCase(u"61 2 9374 4000",
+                              u"",
+                              u"+61 2 9374 4000",
+                              "en_AU"),
+        PhoneNumberFormatCase(u"02 9374 4000",
+                              u"",
+                              u"+61 2 9374 4000",
+                              "en_AU"),
 
         // Numbers in local format yet are invalid with user locale, user might
         // be trying to enter a foreign number, calling formatting will just
         // return what the user entered.
-        PhoneNumberFormatCase("55 5342 8400", "", "5553428400", "en_US"),
-        PhoneNumberFormatCase("55 5342 8400", "", "5553428400"),
-        PhoneNumberFormatCase("226 123 1234", "", "2261231234", "en_US"),
-        PhoneNumberFormatCase("293744000", "", "293744000"),
-        PhoneNumberFormatCase("02 9374 4000", "", "0293744000"),
+        PhoneNumberFormatCase(u"55 5342 8400", u"", u"5553428400", "en_US"),
+        PhoneNumberFormatCase(u"55 5342 8400", u"", u"5553428400"),
+        PhoneNumberFormatCase(u"226 123 1234", u"", u"2261231234", "en_US"),
+        PhoneNumberFormatCase(u"293744000", u"", u"293744000"),
+        PhoneNumberFormatCase(u"02 9374 4000", u"", u"0293744000"),
 
         //////////////////////////
         // No country or locale.
         //////////////////////////
         // Format according to the country code.
-        PhoneNumberFormatCase("61 2 9374 4000", "", "+61 2 9374 4000"),
-        PhoneNumberFormatCase("52 55 5342 8400", "", "+52 55 5342 8400"),
-        PhoneNumberFormatCase("1 415 555 5555", "", "+1 415-555-5555"),
+        PhoneNumberFormatCase(u"61 2 9374 4000", u"", u"+61 2 9374 4000"),
+        PhoneNumberFormatCase(u"52 55 5342 8400", u"", u"+52 55 5342 8400"),
+        PhoneNumberFormatCase(u"1 415 555 5555", u"", u"+1 415-555-5555"),
         // If no country code is found, formats for US.
-        PhoneNumberFormatCase("415-555-5555", "", "+1 415-555-5555")));
+        PhoneNumberFormatCase(u"415-555-5555", u"", u"+1 415-555-5555")));
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 96ab3fdb..890cfee 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -82,10 +82,10 @@
 const char kTestGUID[] = "00000000-0000-0000-0000-000000000001";
 const char kTestNumber[] = "4234567890123456";  // Visa
 const char16_t kTestNumber16[] = u"4234567890123456";
-const char kTestCvc[] = "123";
 const char16_t kTestCvc16[] = u"123";
 
 #if !defined(OS_IOS)
+const char kTestCvc[] = "123";
 // Base64 encoding of "This is a test challenge".
 constexpr char kTestChallenge[] = "VGhpcyBpcyBhIHRlc3QgY2hhbGxlbmdl";
 // Base64 encoding of "This is a test Credential ID".
@@ -1844,8 +1844,8 @@
   CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false);
   CreditCard* unmasked_card =
       credit_card_access_manager_->GetCreditCard(kTestGUID);
-  credit_card_access_manager_->CacheUnmaskedCardInfo(
-      *unmasked_card, base::UTF8ToUTF16(kTestCvc));
+  credit_card_access_manager_->CacheUnmaskedCardInfo(*unmasked_card,
+                                                     kTestCvc16);
 
   CreateServerCard(kTestGUID, kTestNumber, /*masked=*/true);
   CreditCard* masked_card =
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 10e3952..a10d33c 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -78,6 +78,7 @@
 namespace {
 
 const char kPrimaryAccountEmail[] = "syncuser@example.com";
+const char16_t kPrimaryAccountEmail16[] = u"syncuser@example.com";
 const char kSyncTransportAccountEmail[] = "transport@example.com";
 
 enum UserMode { USER_MODE_NORMAL, USER_MODE_INCOGNITO };
@@ -3975,7 +3976,7 @@
 // merge logic works correctly.
 typedef struct {
   autofill::ServerFieldType field_type;
-  std::string field_value;
+  std::u16string field_value;
 } ProfileField;
 
 typedef std::vector<ProfileField> ProfileFields;
@@ -4036,7 +4037,7 @@
   // Apply changes to the original profile (if applicable).
   for (ProfileField change : test_case.changes_to_original) {
     original_profile.SetRawInfoWithVerificationStatus(
-        change.field_type, base::UTF8ToUTF16(change.field_value),
+        change.field_type, change.field_value,
         structured_address::VerificationStatus::kObserved);
   }
 
@@ -4052,7 +4053,7 @@
   // Apply changes to the second profile (if applicable).
   for (ProfileField change : test_case.changes_to_new) {
     profile2.SetRawInfoWithVerificationStatus(
-        change.field_type, base::UTF8ToUTF16(change.field_value),
+        change.field_type, change.field_value,
         structured_address::VerificationStatus::kObserved);
   }
 
@@ -4085,7 +4086,7 @@
 
     // Make sure the new information was merged correctly.
     for (ProfileField changed_field : test_case.changed_field_values) {
-      EXPECT_EQ(base::UTF8ToUTF16(changed_field.field_value),
+      EXPECT_EQ(changed_field.field_value,
                 saved_profiles.front()->GetRawInfo(changed_field.field_type));
     }
     // Verify that the merged profile's use count, use date and modification
@@ -4121,66 +4122,66 @@
             // Test that saving an identical profile except for the name results
             // in two profiles being saved.
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{NAME_FIRST, "Marionette"}}},
+                                        {{NAME_FIRST, u"Marionette"}}},
 
             // Test that saving an identical profile except with the middle name
             // initial instead of the full middle name results in the profiles
             // getting merged and the full middle name being kept.
             SaveImportedProfileTestCase{
                 ProfileFields(),
-                {{NAME_MIDDLE, "M"}},
-                {{NAME_MIDDLE, "Mitchell"},
-                 {NAME_FULL, "Marion Mitchell Morrison"}}},
+                {{NAME_MIDDLE, u"M"}},
+                {{NAME_MIDDLE, u"Mitchell"},
+                 {NAME_FULL, u"Marion Mitchell Morrison"}}},
 
             // Test that saving an identical profile except with the full middle
             // name instead of the middle name initial results in the profiles
             // getting merged and the full middle name replacing the initial.
-            SaveImportedProfileTestCase{{{NAME_MIDDLE, "M"}},
-                                        {{NAME_MIDDLE, "Mitchell"}},
-                                        {{NAME_MIDDLE, "Mitchell"}}},
+            SaveImportedProfileTestCase{{{NAME_MIDDLE, u"M"}},
+                                        {{NAME_MIDDLE, u"Mitchell"}},
+                                        {{NAME_MIDDLE, u"Mitchell"}}},
 
             // Test that saving an identical profile except with no middle name
             // results in the profiles getting merged and the full middle name
             // being kept.
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{NAME_MIDDLE, ""}},
-                                        {{NAME_MIDDLE, "Mitchell"}}},
+                                        {{NAME_MIDDLE, u""}},
+                                        {{NAME_MIDDLE, u"Mitchell"}}},
 
             // Test that saving an identical profile except with a middle name
             // initial results in the profiles getting merged and the middle
             // name initial being saved.
-            SaveImportedProfileTestCase{{{NAME_MIDDLE, ""}},
-                                        {{NAME_MIDDLE, "M"}},
-                                        {{NAME_MIDDLE, "M"}}},
+            SaveImportedProfileTestCase{{{NAME_MIDDLE, u""}},
+                                        {{NAME_MIDDLE, u"M"}},
+                                        {{NAME_MIDDLE, u"M"}}},
 
             // Test that saving an identical profile except with a middle name
             // results in the profiles getting merged and the full middle name
             // being saved.
-            SaveImportedProfileTestCase{{{NAME_MIDDLE, ""}},
-                                        {{NAME_MIDDLE, "Mitchell"}},
-                                        {{NAME_MIDDLE, "Mitchell"}}},
+            SaveImportedProfileTestCase{{{NAME_MIDDLE, u""}},
+                                        {{NAME_MIDDLE, u"Mitchell"}},
+                                        {{NAME_MIDDLE, u"Mitchell"}}},
 
             // Test that saving a identical profile except with the full name
             // set instead of the name parts results in the two profiles being
             // merged and all the name parts kept and the full name being added.
             SaveImportedProfileTestCase{
                 {
-                    {NAME_FIRST, "Marion"},
-                    {NAME_MIDDLE, "Mitchell"},
-                    {NAME_LAST, "Morrison"},
-                    {NAME_FULL, ""},
+                    {NAME_FIRST, u"Marion"},
+                    {NAME_MIDDLE, u"Mitchell"},
+                    {NAME_LAST, u"Morrison"},
+                    {NAME_FULL, u""},
                 },
                 {
-                    {NAME_FIRST, ""},
-                    {NAME_MIDDLE, ""},
-                    {NAME_LAST, ""},
-                    {NAME_FULL, "Marion Mitchell Morrison"},
+                    {NAME_FIRST, u""},
+                    {NAME_MIDDLE, u""},
+                    {NAME_LAST, u""},
+                    {NAME_FULL, u"Marion Mitchell Morrison"},
                 },
                 {
-                    {NAME_FIRST, "Marion"},
-                    {NAME_MIDDLE, "Mitchell"},
-                    {NAME_LAST, "Morrison"},
-                    {NAME_FULL, "Marion Mitchell Morrison"},
+                    {NAME_FIRST, u"Marion"},
+                    {NAME_MIDDLE, u"Mitchell"},
+                    {NAME_LAST, u"Morrison"},
+                    {NAME_FULL, u"Marion Mitchell Morrison"},
                 },
             },
 
@@ -4190,22 +4191,22 @@
             // added.
             SaveImportedProfileTestCase{
                 {
-                    {NAME_FIRST, ""},
-                    {NAME_MIDDLE, ""},
-                    {NAME_LAST, ""},
-                    {NAME_FULL, "Marion Mitchell Morrison"},
+                    {NAME_FIRST, u""},
+                    {NAME_MIDDLE, u""},
+                    {NAME_LAST, u""},
+                    {NAME_FULL, u"Marion Mitchell Morrison"},
                 },
                 {
-                    {NAME_FIRST, "Marion"},
-                    {NAME_MIDDLE, "Mitchell"},
-                    {NAME_LAST, "Morrison"},
-                    {NAME_FULL, ""},
+                    {NAME_FIRST, u"Marion"},
+                    {NAME_MIDDLE, u"Mitchell"},
+                    {NAME_LAST, u"Morrison"},
+                    {NAME_FULL, u""},
                 },
                 {
-                    {NAME_FIRST, "Marion"},
-                    {NAME_MIDDLE, "Mitchell"},
-                    {NAME_LAST, "Morrison"},
-                    {NAME_FULL, "Marion Mitchell Morrison"},
+                    {NAME_FIRST, u"Marion"},
+                    {NAME_MIDDLE, u"Mitchell"},
+                    {NAME_LAST, u"Morrison"},
+                    {NAME_FULL, u"Marion Mitchell Morrison"},
                 },
             },
 
@@ -4214,16 +4215,16 @@
             // names are different.
             SaveImportedProfileTestCase{
                 {
-                    {NAME_FIRST, "Marion"},
-                    {NAME_MIDDLE, "Mitchell"},
-                    {NAME_LAST, "Morrison"},
-                    {NAME_FULL, ""},
+                    {NAME_FIRST, u"Marion"},
+                    {NAME_MIDDLE, u"Mitchell"},
+                    {NAME_LAST, u"Morrison"},
+                    {NAME_FULL, u""},
                 },
                 {
-                    {NAME_FIRST, ""},
-                    {NAME_MIDDLE, ""},
-                    {NAME_LAST, ""},
-                    {NAME_FULL, "John Thompson Smith"},
+                    {NAME_FIRST, u""},
+                    {NAME_MIDDLE, u""},
+                    {NAME_LAST, u""},
+                    {NAME_FULL, u"John Thompson Smith"},
                 },
             },
 
@@ -4232,16 +4233,16 @@
             // names are different.
             SaveImportedProfileTestCase{
                 {
-                    {NAME_FIRST, ""},
-                    {NAME_MIDDLE, ""},
-                    {NAME_LAST, ""},
-                    {NAME_FULL, "John Thompson Smith"},
+                    {NAME_FIRST, u""},
+                    {NAME_MIDDLE, u""},
+                    {NAME_LAST, u""},
+                    {NAME_FULL, u"John Thompson Smith"},
                 },
                 {
-                    {NAME_FIRST, "Marion"},
-                    {NAME_MIDDLE, "Mitchell"},
-                    {NAME_LAST, "Morrison"},
-                    {NAME_FULL, ""},
+                    {NAME_FIRST, u"Marion"},
+                    {NAME_MIDDLE, u"Mitchell"},
+                    {NAME_LAST, u"Morrison"},
+                    {NAME_FULL, u""},
                 },
             },
 
@@ -4249,146 +4250,147 @@
             // address line results in two profiles being saved.
             SaveImportedProfileTestCase{
                 ProfileFields(),
-                {{ADDRESS_HOME_LINE1, "123 Aquarium St."}}},
+                {{ADDRESS_HOME_LINE1, u"123 Aquarium St."}}},
 
             // Test that saving an identical profile except for the second
             // address line results in two profiles being saved.
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{ADDRESS_HOME_LINE2, "unit 7"}}},
+                                        {{ADDRESS_HOME_LINE2, u"unit 7"}}},
 
             // Tests that saving an identical profile that has a new piece of
             // information (company name) results in a merge and that the
             // original empty value gets overwritten by the new information.
-            SaveImportedProfileTestCase{{{COMPANY_NAME, ""}},
+            SaveImportedProfileTestCase{{{COMPANY_NAME, u""}},
                                         ProfileFields(),
-                                        {{COMPANY_NAME, "Fox"}}},
+                                        {{COMPANY_NAME, u"Fox"}}},
 
             // Tests that saving an identical profile except a loss of
             // information results in a merge but the original value is not
             // overwritten (no information loss).
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{COMPANY_NAME, ""}},
-                                        {{COMPANY_NAME, "Fox"}}},
+                                        {{COMPANY_NAME, u""}},
+                                        {{COMPANY_NAME, u"Fox"}}},
 
             // Tests that saving an identical profile except a slightly
             // different postal code results in a merge with the new value kept.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, "R2C 0A1"}},
-                                        {{ADDRESS_HOME_ZIP, "R2C0A1"}},
-                                        {{ADDRESS_HOME_ZIP, "R2C0A1"}}},
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, "R2C0A1"}},
-                                        {{ADDRESS_HOME_ZIP, "R2C 0A1"}},
-                                        {{ADDRESS_HOME_ZIP, "R2C 0A1"}}},
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, "r2c 0a1"}},
-                                        {{ADDRESS_HOME_ZIP, "R2C0A1"}},
-                                        {{ADDRESS_HOME_ZIP, "R2C0A1"}}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, u"R2C 0A1"}},
+                                        {{ADDRESS_HOME_ZIP, u"R2C0A1"}},
+                                        {{ADDRESS_HOME_ZIP, u"R2C0A1"}}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, u"R2C0A1"}},
+                                        {{ADDRESS_HOME_ZIP, u"R2C 0A1"}},
+                                        {{ADDRESS_HOME_ZIP, u"R2C 0A1"}}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_ZIP, u"r2c 0a1"}},
+                                        {{ADDRESS_HOME_ZIP, u"R2C0A1"}},
+                                        {{ADDRESS_HOME_ZIP, u"R2C0A1"}}},
 
             // Tests that saving an identical profile plus a new piece of
             // information on the address line 2 results in a merge and that the
             // original empty value gets overwritten by the new information.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE2, ""}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE2, u""}},
                                         ProfileFields(),
-                                        {{ADDRESS_HOME_LINE2, "unit 5"}}},
+                                        {{ADDRESS_HOME_LINE2, u"unit 5"}}},
 
             // Tests that saving an identical profile except a loss of
             // information on the address line 2 results in a merge but that the
             // original value gets not overwritten (no information loss).
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{ADDRESS_HOME_LINE2, ""}},
-                                        {{ADDRESS_HOME_LINE2, "unit 5"}}},
+                                        {{ADDRESS_HOME_LINE2, u""}},
+                                        {{ADDRESS_HOME_LINE2, u"unit 5"}}},
 
             // Tests that saving an identical except with more punctuation in
             // the fist address line, while the second is empty, results in a
             // merge and that the original address gets overwritten.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE2, ""}},
-                                        {{ADDRESS_HOME_LINE2, ""},
-                                         {ADDRESS_HOME_LINE1, "123, Zoo St."}},
-                                        {{ADDRESS_HOME_LINE1, "123, Zoo St."}}},
+            SaveImportedProfileTestCase{
+                {{ADDRESS_HOME_LINE2, u""}},
+                {{ADDRESS_HOME_LINE2, u""},
+                 {ADDRESS_HOME_LINE1, u"123, Zoo St."}},
+                {{ADDRESS_HOME_LINE1, u"123, Zoo St."}}},
 
             // Tests that saving an identical profile except with less
             // punctuation in the fist address line, while the second is empty,
             // results in a merge and that the longer address is retained.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE2, ""},
-                                         {ADDRESS_HOME_LINE1, "123, Zoo St."}},
-                                        {{ADDRESS_HOME_LINE2, ""}},
-                                        {{ADDRESS_HOME_LINE1, "123 Zoo St"}}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE2, u""},
+                                         {ADDRESS_HOME_LINE1, u"123, Zoo St."}},
+                                        {{ADDRESS_HOME_LINE2, u""}},
+                                        {{ADDRESS_HOME_LINE1, u"123 Zoo St"}}},
 
             // Tests that saving an identical profile except additional
             // punctuation in the two address lines results in a merge and that
             // the newer address is retained.
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{ADDRESS_HOME_LINE1, "123, Zoo St."},
-                                         {ADDRESS_HOME_LINE2, "unit. 5"}},
-                                        {{ADDRESS_HOME_LINE1, "123, Zoo St."},
-                                         {ADDRESS_HOME_LINE2, "unit. 5"}}},
+                                        {{ADDRESS_HOME_LINE1, u"123, Zoo St."},
+                                         {ADDRESS_HOME_LINE2, u"unit. 5"}},
+                                        {{ADDRESS_HOME_LINE1, u"123, Zoo St."},
+                                         {ADDRESS_HOME_LINE2, u"unit. 5"}}},
 
             // Tests that saving an identical profile except less punctuation in
             // the two address lines results in a merge and that the newer
             // address is retained.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE1, "123, Zoo St."},
-                                         {ADDRESS_HOME_LINE2, "unit. 5"}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE1, u"123, Zoo St."},
+                                         {ADDRESS_HOME_LINE2, u"unit. 5"}},
                                         ProfileFields(),
-                                        {{ADDRESS_HOME_LINE1, "123 Zoo St"},
-                                         {ADDRESS_HOME_LINE2, "unit 5"}}},
+                                        {{ADDRESS_HOME_LINE1, u"123 Zoo St"},
+                                         {ADDRESS_HOME_LINE2, u"unit 5"}}},
 
             // Tests that saving an identical profile with accented characters
             // in the two address lines results in a merge and that the newer
             // address is retained.
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{ADDRESS_HOME_LINE1, "123 Zôö St"},
-                                         {ADDRESS_HOME_LINE2, "üñìt 5"}},
-                                        {{ADDRESS_HOME_LINE1, "123 Zôö St"},
-                                         {ADDRESS_HOME_LINE2, "üñìt 5"}}},
+                                        {{ADDRESS_HOME_LINE1, u"123 Zôö St"},
+                                         {ADDRESS_HOME_LINE2, u"üñìt 5"}},
+                                        {{ADDRESS_HOME_LINE1, u"123 Zôö St"},
+                                         {ADDRESS_HOME_LINE2, u"üñìt 5"}}},
 
             // Tests that saving an identical profile without accented
             // characters in the two address lines results in a merge and that
             // the newer address is retained.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE1, "123 Zôö St"},
-                                         {ADDRESS_HOME_LINE2, "üñìt 5"}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_LINE1, u"123 Zôö St"},
+                                         {ADDRESS_HOME_LINE2, u"üñìt 5"}},
                                         ProfileFields(),
-                                        {{ADDRESS_HOME_LINE1, "123 Zoo St"},
-                                         {ADDRESS_HOME_LINE2, "unit 5"}}},
+                                        {{ADDRESS_HOME_LINE1, u"123 Zoo St"},
+                                         {ADDRESS_HOME_LINE2, u"unit 5"}}},
 
             // Tests that saving an identical profile except that the address
             // line 1 is in the address line 2 results in a merge and that the
             // multi-lne address is retained.
             SaveImportedProfileTestCase{
                 ProfileFields(),
-                {{ADDRESS_HOME_LINE1, "123 Zoo St, unit 5"},
-                 {ADDRESS_HOME_LINE2, ""}},
-                {{ADDRESS_HOME_LINE1, "123 Zoo St"},
-                 {ADDRESS_HOME_LINE2, "unit 5"}}},
+                {{ADDRESS_HOME_LINE1, u"123 Zoo St, unit 5"},
+                 {ADDRESS_HOME_LINE2, u""}},
+                {{ADDRESS_HOME_LINE1, u"123 Zoo St"},
+                 {ADDRESS_HOME_LINE2, u"unit 5"}}},
 
             // Tests that saving an identical profile except that the address
             // line 2 contains part of the old address line 1 results in a merge
             // and that the original address lines of the reference profile get
             // overwritten.
             SaveImportedProfileTestCase{
-                {{ADDRESS_HOME_LINE1, "123 Zoo St, unit 5"},
-                 {ADDRESS_HOME_LINE2, ""}},
+                {{ADDRESS_HOME_LINE1, u"123 Zoo St, unit 5"},
+                 {ADDRESS_HOME_LINE2, u""}},
                 ProfileFields(),
-                {{ADDRESS_HOME_LINE1, "123 Zoo St"},
-                 {ADDRESS_HOME_LINE2, "unit 5"}}},
+                {{ADDRESS_HOME_LINE1, u"123 Zoo St"},
+                 {ADDRESS_HOME_LINE2, u"unit 5"}}},
 
             // Tests that saving an identical profile except that the state is
             // the abbreviation instead of the full form results in a merge and
             // that the original state gets overwritten.
-            SaveImportedProfileTestCase{{{ADDRESS_HOME_STATE, "California"}},
+            SaveImportedProfileTestCase{{{ADDRESS_HOME_STATE, u"California"}},
                                         ProfileFields(),
-                                        {{ADDRESS_HOME_STATE, "CA"}}},
+                                        {{ADDRESS_HOME_STATE, u"CA"}}},
 
             // Tests that saving an identical profile except that the state is
             // the full form instead of the abbreviation results in a merge and
             // that the abbreviated state is retained.
             SaveImportedProfileTestCase{ProfileFields(),
-                                        {{ADDRESS_HOME_STATE, "California"}},
-                                        {{ADDRESS_HOME_STATE, "CA"}}},
+                                        {{ADDRESS_HOME_STATE, u"California"}},
+                                        {{ADDRESS_HOME_STATE, u"CA"}}},
 
             // Tests that saving and identical profile except that the company
             // name has different punctuation and case results in a merge and
             // that the syntax of the new profile replaces the old one.
-            SaveImportedProfileTestCase{{{COMPANY_NAME, "Stark inc"}},
-                                        {{COMPANY_NAME, "Stark Inc."}},
-                                        {{COMPANY_NAME, "Stark Inc."}}})));
+            SaveImportedProfileTestCase{{{COMPANY_NAME, u"Stark inc"}},
+                                        {{COMPANY_NAME, u"Stark Inc."}},
+                                        {{COMPANY_NAME, u"Stark Inc."}}})));
 
 // Tests that MergeProfile tries to merge the imported profile into the
 // existing profile in decreasing order of frecency.
@@ -5667,8 +5669,7 @@
 
   // Make sure that the added address has the email address of the currently
   // signed-in user.
-  EXPECT_EQ(base::UTF8ToUTF16(kPrimaryAccountEmail),
-            profiles[0]->GetRawInfo(EMAIL_ADDRESS));
+  EXPECT_EQ(kPrimaryAccountEmail16, profiles[0]->GetRawInfo(EMAIL_ADDRESS));
 }
 
 // Tests that the converted wallet address is merged into an existing local
@@ -7839,16 +7840,16 @@
 }
 
 struct ShareNicknameTestParam {
-  std::string local_nickname;
-  std::string server_nickname;
-  std::string expected_nickname;
+  std::u16string local_nickname;
+  std::u16string server_nickname;
+  std::u16string expected_nickname;
 };
 
 const ShareNicknameTestParam kShareNicknameTestParam[] = {
-    {"", "", ""},
-    {"", "server nickname", "server nickname"},
-    {"local nickname", "", "local nickname"},
-    {"local nickname", "server nickname", "local nickname"},
+    {u"", u"", u""},
+    {u"", u"server nickname", u"server nickname"},
+    {u"local nickname", u"", u"local nickname"},
+    {u"local nickname", u"server nickname", u"local nickname"},
 };
 
 class PersonalDataManagerTestForSharingNickname
@@ -7856,9 +7857,9 @@
       public testing::WithParamInterface<ShareNicknameTestParam> {
  public:
   PersonalDataManagerTestForSharingNickname()
-      : local_nickname_(base::UTF8ToUTF16(GetParam().local_nickname)),
-        server_nickname_(base::UTF8ToUTF16(GetParam().server_nickname)),
-        expected_nickname_(base::UTF8ToUTF16(GetParam().expected_nickname)) {}
+      : local_nickname_(GetParam().local_nickname),
+        server_nickname_(GetParam().server_nickname),
+        expected_nickname_(GetParam().expected_nickname) {}
 
   CreditCard GetLocalCard() {
     CreditCard local_card("287151C8-6AB1-487C-9095-28E80BE5DA15",
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index 1577b46..683bc93 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -103,6 +103,9 @@
   return isolation_info_;
 }
 
+void TestAutofillDriver::SendFieldsEligibleForManualFillingToRenderer(
+    const std::vector<FieldRendererId>& fields) {}
+
 void TestAutofillDriver::SetIsIncognito(bool is_incognito) {
   is_incognito_ = is_incognito;
 }
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index 0f43f44..a3a298dc 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -65,6 +65,8 @@
   gfx::RectF TransformBoundingBoxToViewportCoordinates(
       const gfx::RectF& bounding_box) override;
   net::IsolationInfo IsolationInfo() override;
+  void SendFieldsEligibleForManualFillingToRenderer(
+      const std::vector<FieldRendererId>& fields) override;
 
   // Methods unique to TestAutofillDriver that tests can use to specialize
   // functionality.
diff --git a/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc b/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc
index c77fd7f..04f396f 100644
--- a/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc
+++ b/components/autofill/core/browser/ui/address_form_label_formatter_unittest.cc
@@ -175,9 +175,8 @@
       LabelFormatter::Create(profiles, "pt-BR", NAME_FIRST, GetFieldTypes());
 
   EXPECT_THAT(formatter->GetLabels(),
-              ElementsAre(base::UTF8ToUTF16(
-                  "Av. Pedro Álvares Cabral, 1301, Vila Mariana, São "
-                  "Paulo-SP, 04094-050")));
+              ElementsAre(u"Av. Pedro Álvares Cabral, 1301, Vila Mariana, São "
+                          u"Paulo-SP, 04094-050"));
 }
 
 TEST(AddressFormLabelFormatterTest, GetLabelsForFormWithoutName) {
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index c9c435351..8b3426c 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -58,6 +58,8 @@
   void RendererShouldAcceptDataListSuggestion(
       const FieldGlobalId& field,
       const std::u16string& value) override;
+  void SendFieldsEligibleForManualFillingToRenderer(
+      const std::vector<FieldRendererId>& fields) override;
 
   BrowserAutofillManager* autofill_manager() {
     return &browser_autofill_manager_;
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 69c0742..683cf140 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -143,6 +143,9 @@
     const FieldGlobalId& field,
     const std::u16string& value) {}
 
+void AutofillDriverIOS::SendFieldsEligibleForManualFillingToRenderer(
+    const std::vector<FieldRendererId>& fields) {}
+
 void AutofillDriverIOS::RendererShouldClearFilledSection() {}
 
 void AutofillDriverIOS::RendererShouldClearPreviewedForm() {
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 9201fc7..09546bf 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -471,7 +471,7 @@
     From Google Pay
   </message>
 
-  <!-- Cloud token related strings (Desktop only) -->
+  <!-- virtual cards related strings -->
   <if expr="not is_ios and not is_android">
     <message name="IDS_AUTOFILL_CLOUD_TOKEN_DROPDOWN_OPTION_LABEL" desc="Text shown in the button in the Autofill dropdown menu when a credit card form field is queried, to offer the option to use a virtual card.">
       Use a virtual card number...
@@ -492,6 +492,22 @@
     <message name="IDS_AUTOFILL_VIRTUAL_CARD_SELECTION_DIALOG_CANCEL_BUTTON_LABEL" desc="Text shown in the Cancel button in the Autofill virtual card selection dialog. This dialog offers all available virtual credit cards for users to choose.">
       Cancel
     </message>
+    <message name="IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_TITLE" desc="The title shown in the Autofill virtual card manual fallback bubble on Desktop. This bubble shows the complete information for the virtual card selected to fill the form.">
+      Virtual card number
+    </message>
+    <message name="IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CARD_NUMBER_LABEL" desc="Text shown next to the virtual card number in the virtual card manual fallback bubble on Desktop. This bubble shows the complete information for the virtual card selected to fill the form.">
+      Virtual number:
+    </message>
+    <message name="IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_EXP_DATE_LABEL" desc="Text shown next to the virtual card expiration date in the virtual card manual fallback bubble on Desktop. This bubble shows the complete information for the virtual card selected to fill the form.">
+      Month/Year:
+    </message>
+    <message name="IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CVC_LABEL" desc="Text shown next to the virtual card CVC code in the virtual card manual fallback bubble on Desktop. This bubble shows the complete information for the virtual card selected to fill the form.">
+      CVC:
+    </message>
+    <!-- TODO(crbug.com/1020740): Update tooltip message with final wording -->
+    <message name="IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_TOOLTIP" desc="The tooltip message for the omnibox icon for the virtual card manual fallback bubble on Desktop. This bubble shows the complete information for the virtual card selected to fill the form.">
+      View virtual card details
+    </message>
   </if>
 
   <message name="IDS_AUTOFILL_SAVE_UPI_PROMPT_TITLE" desc="Title text for the prompt to save a UPI ID locally, which the user used in a page. UPI is an online payment method. A UPI ID is an email-like string.">
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CARD_NUMBER_LABEL.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CARD_NUMBER_LABEL.png.sha1
new file mode 100644
index 0000000..67b260d
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CARD_NUMBER_LABEL.png.sha1
@@ -0,0 +1 @@
+162070d949f0497137ad125f4299973b3932ab79
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CVC_LABEL.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CVC_LABEL.png.sha1
new file mode 100644
index 0000000..67b260d
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_CVC_LABEL.png.sha1
@@ -0,0 +1 @@
+162070d949f0497137ad125f4299973b3932ab79
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_EXP_DATE_LABEL.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_EXP_DATE_LABEL.png.sha1
new file mode 100644
index 0000000..67b260d
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_EXP_DATE_LABEL.png.sha1
@@ -0,0 +1 @@
+162070d949f0497137ad125f4299973b3932ab79
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_TITLE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_TITLE.png.sha1
new file mode 100644
index 0000000..67b260d
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_BUBBLE_TITLE.png.sha1
@@ -0,0 +1 @@
+162070d949f0497137ad125f4299973b3932ab79
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_TOOLTIP.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_TOOLTIP.png.sha1
new file mode 100644
index 0000000..8f05168
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_VIRTUAL_CARD_MANUAL_FALLBACK_ICON_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+4b9f5a780c3a662c79041b21ccb347180cdb907a
\ No newline at end of file
diff --git a/components/download/content/internal/download_driver_impl_unittest.cc b/components/download/content/internal/download_driver_impl_unittest.cc
index c329122..7535bdc 100644
--- a/components/download/content/internal/download_driver_impl_unittest.cc
+++ b/components/download/content/internal/download_driver_impl_unittest.cc
@@ -13,7 +13,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/download/content/public/all_download_item_notifier.h"
 #include "components/download/internal/background_service/test/mock_download_driver_client.h"
-#include "components/download/public/common//mock_simple_download_manager.h"
+#include "components/download/public/common/mock_simple_download_manager.h"
 #include "content/public/test/fake_download_item.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
diff --git a/components/exo/OWNERS b/components/exo/OWNERS
index fe1ce83..af708f9 100644
--- a/components/exo/OWNERS
+++ b/components/exo/OWNERS
@@ -1,7 +1,8 @@
 oshima@chromium.org
 
-# For Input (keyboard, mouse, touch)
+# For Input (keyboard, mouse, touch, data_device)
 mukai@chromium.org
+yhanada@chromium.org
 
 # Not working on chromium anymore, but kept just in case we need his review.
 dcastagna@chromium.org
diff --git a/components/exo/shell_surface_base.cc b/components/exo/shell_surface_base.cc
index d03926e..f24bd37 100644
--- a/components/exo/shell_surface_base.cc
+++ b/components/exo/shell_surface_base.cc
@@ -27,8 +27,8 @@
 #include "build/chromeos_buildflags.h"
 #include "cc/trees/layer_tree_frame_sink.h"
 #include "chromeos/crosapi/cpp/crosapi_constants.h"
-#include "chromeos/ui/base//window_properties.h"
 #include "chromeos/ui/base/window_pin_type.h"
+#include "chromeos/ui/base/window_properties.h"
 #include "chromeos/ui/base/window_state_type.h"
 #include "chromeos/ui/frame/caption_buttons/snap_controller.h"
 #include "components/exo/shell_surface_util.h"
diff --git a/components/feed/core/v2/tasks/load_stream_task.cc b/components/feed/core/v2/tasks/load_stream_task.cc
index 8f328c02..706f469 100644
--- a/components/feed/core/v2/tasks/load_stream_task.cc
+++ b/components/feed/core/v2/tasks/load_stream_task.cc
@@ -40,8 +40,11 @@
       return stream_type.IsForYou() ? feedwire::FeedQuery::MANUAL_REFRESH
                                     : feedwire::FeedQuery::INTERACTIVE_WEB_FEED;
     case LoadType::kBackgroundRefresh:
-      return stream_type.IsForYou() ? feedwire::FeedQuery::SCHEDULED_REFRESH
-                                    : feedwire::FeedQuery::PREFETCHED_WEB_FEED;
+      return stream_type.IsForYou()
+                 ? feedwire::FeedQuery::SCHEDULED_REFRESH
+                 // TODO(b/185848601): Switch back to PREFETCHED_WEB_FEED when
+                 // the server supports it.
+                 : feedwire::FeedQuery::INTERACTIVE_WEB_FEED;
   }
 }
 
diff --git a/components/history/core/browser/history_backend_unittest.cc b/components/history/core/browser/history_backend_unittest.cc
index 3d104ea2..d519c70d4 100644
--- a/components/history/core/browser/history_backend_unittest.cc
+++ b/components/history/core/browser/history_backend_unittest.cc
@@ -568,13 +568,13 @@
       const SimulateNotificationCallback& callback);
 
   static const KeywordID kTestKeywordId;
-  static const char kTestSearchTerm1[];
-  static const char kTestSearchTerm2[];
+  static const char16_t kTestSearchTerm1[];
+  static const char16_t kTestSearchTerm2[];
 };
 
 const KeywordID InMemoryHistoryBackendTest::kTestKeywordId = 42;
-const char InMemoryHistoryBackendTest::kTestSearchTerm1[] = "banana";
-const char InMemoryHistoryBackendTest::kTestSearchTerm2[] = "orange";
+const char16_t InMemoryHistoryBackendTest::kTestSearchTerm1[] = u"banana";
+const char16_t InMemoryHistoryBackendTest::kTestSearchTerm2[] = u"orange";
 
 // http://crbug.com/114287
 #if defined(OS_WIN)
@@ -1168,7 +1168,7 @@
 // This verifies that a notification is fired. In-depth testing of logic should
 // be done in HistoryTest.SetTitle.
 TEST_F(HistoryBackendTest, SetPageTitleFiresNotificationWithCorrectDetails) {
-  const char kTestUrlTitle[] = "Google Search";
+  const char16_t kTestUrlTitle[] = u"Google Search";
 
   ASSERT_TRUE(backend_.get());
 
@@ -1186,7 +1186,7 @@
   backend_->AddPagesWithDetails(rows, history::SOURCE_BROWSED);
 
   ClearBroadcastedNotifications();
-  backend_->SetPageTitle(row2.url(), base::UTF8ToUTF16(kTestUrlTitle));
+  backend_->SetPageTitle(row2.url(), kTestUrlTitle);
 
   // Ensure that a notification was fired, and further verify that the IDs in
   // the notification are set to those that are in effect in the main database.
@@ -1197,7 +1197,7 @@
 
   const URLRows& changed_urls = urls_modified_notifications()[0];
   ASSERT_EQ(1u, changed_urls.size());
-  EXPECT_EQ(base::UTF8ToUTF16(kTestUrlTitle), changed_urls[0].title());
+  EXPECT_EQ(kTestUrlTitle, changed_urls[0].title());
   EXPECT_EQ(stored_row2.id(), changed_urls[0].id());
 }
 
@@ -2901,8 +2901,8 @@
 // between them is the type of the notification sent out.
 void InMemoryHistoryBackendTest::TestAddingAndChangingURLRows(
     const SimulateNotificationCallback& callback) {
-  const char kTestTypedURLAlternativeTitle[] = "Google Search Again";
-  const char kTestNonTypedURLAlternativeTitle[] = "Google News Again";
+  const char16_t kTestTypedURLAlternativeTitle[] = u"Google Search Again";
+  const char16_t kTestNonTypedURLAlternativeTitle[] = u"Google News Again";
 
   // Notify the in-memory database that a typed and non-typed URLRow (which were
   // never before seen by the cache) have been modified.
@@ -2919,15 +2919,14 @@
   EXPECT_EQ(row1.id(), cached_row1.id());
 
   // Try changing attributes (other than typed_count) for existing URLRows.
-  row1.set_title(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle));
-  row2.set_title(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle));
+  row1.set_title(kTestTypedURLAlternativeTitle);
+  row2.set_title(kTestNonTypedURLAlternativeTitle);
   callback.Run(&row1, &row2, nullptr);
 
   // URLRows that are cached by the in-memory database should be updated.
   EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
   EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
-  EXPECT_EQ(base::UTF8ToUTF16(kTestTypedURLAlternativeTitle),
-            cached_row1.title());
+  EXPECT_EQ(kTestTypedURLAlternativeTitle, cached_row1.title());
 
   // Now decrease the typed count for the typed URLRow, and increase it for the
   // previously non-typed URLRow.
@@ -2940,8 +2939,7 @@
   EXPECT_EQ(0, mem_backend_->db()->GetRowForURL(row1.url(), &cached_row1));
   EXPECT_NE(0, mem_backend_->db()->GetRowForURL(row2.url(), &cached_row2));
   EXPECT_EQ(row2.id(), cached_row2.id());
-  EXPECT_EQ(base::UTF8ToUTF16(kTestNonTypedURLAlternativeTitle),
-            cached_row2.title());
+  EXPECT_EQ(kTestNonTypedURLAlternativeTitle, cached_row2.title());
 }
 
 TEST_F(InMemoryHistoryBackendTest, OnURLsModified) {
@@ -3014,8 +3012,8 @@
 TEST_F(InMemoryHistoryBackendTest, SetKeywordSearchTerms) {
   URLRow row1(CreateTestTypedURL());
   URLRow row2(CreateTestNonTypedURL());
-  std::u16string term1(base::UTF8ToUTF16(kTestSearchTerm1));
-  std::u16string term2(base::UTF8ToUTF16(kTestSearchTerm2));
+  std::u16string term1(kTestSearchTerm1);
+  std::u16string term2(kTestSearchTerm2);
   PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
 
   // Both URLs now have associated search terms, so the in-memory database
@@ -3037,8 +3035,8 @@
 TEST_F(InMemoryHistoryBackendTest, DeleteKeywordSearchTerms) {
   URLRow row1(CreateTestTypedURL());
   URLRow row2(CreateTestNonTypedURL());
-  std::u16string term1(base::UTF8ToUTF16(kTestSearchTerm1));
-  std::u16string term2(base::UTF8ToUTF16(kTestSearchTerm2));
+  std::u16string term1(kTestSearchTerm1);
+  std::u16string term2(kTestSearchTerm2);
   PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
 
   // Delete both search terms. This should be reflected in the in-memory DB.
@@ -3062,8 +3060,8 @@
 TEST_F(InMemoryHistoryBackendTest, DeleteAllSearchTermsForKeyword) {
   URLRow row1(CreateTestTypedURL());
   URLRow row2(CreateTestNonTypedURL());
-  std::u16string term1(base::UTF8ToUTF16(kTestSearchTerm1));
-  std::u16string term2(base::UTF8ToUTF16(kTestSearchTerm2));
+  std::u16string term1(kTestSearchTerm1);
+  std::u16string term2(kTestSearchTerm2);
   PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
 
   // Delete all corresponding search terms from the in-memory database.
@@ -3087,8 +3085,8 @@
 TEST_F(InMemoryHistoryBackendTest, OnURLsDeletedWithSearchTerms) {
   URLRow row1(CreateTestTypedURL());
   URLRow row2(CreateTestNonTypedURL());
-  std::u16string term1(base::UTF8ToUTF16(kTestSearchTerm1));
-  std::u16string term2(base::UTF8ToUTF16(kTestSearchTerm2));
+  std::u16string term1(kTestSearchTerm1);
+  std::u16string term2(kTestSearchTerm2);
   PopulateTestURLsAndSearchTerms(&row1, &row2, term1, term2);
 
   // Notify the in-memory database that the second typed URL has been deleted.
diff --git a/components/history_clusters/core/memories.mojom b/components/history_clusters/core/memories.mojom
index b6dfa80..4f17758 100644
--- a/components/history_clusters/core/memories.mojom
+++ b/components/history_clusters/core/memories.mojom
@@ -53,7 +53,7 @@
   url.mojom.Url? thumbnail_url;
   // Time of the visit.
   mojo_base.mojom.Time time;
-  // Time of the least recent visit in the Memory to the same URL. Equals |time|
+  // Time of the least recent visit in the Memory to the same URL. Equals `time`
   // if there is only one visit in the Memory to the URL.
   mojo_base.mojom.Time first_visit_time;
   // Localized string of approximate time of visit, e.g., "2 days ago".
@@ -107,7 +107,7 @@
   string query;
 
   // The optional time threshold (exclusive) for how recent the Memories can be.
-  // If specified, Memories before |recency_threshold| and if missing, Memories
+  // If specified, Memories before `recency_threshold` and if missing, Memories
   // until the present time that match the other criteria are returned.
   mojo_base.mojom.Time? recency_threshold;
 
diff --git a/components/history_clusters/core/memories_features.cc b/components/history_clusters/core/memories_features.cc
index 044a5633..8824f1a3 100644
--- a/components/history_clusters/core/memories_features.cc
+++ b/components/history_clusters/core/memories_features.cc
@@ -8,41 +8,36 @@
 
 namespace history_clusters {
 
-GURL RemoteModelEndpointForDebugging() {
-  return GURL(base::GetFieldTrialParamValueByFeature(
-      kRemoteModelForDebugging, "MemoriesRemoteModelEndpoint"));
+namespace {
+
+const base::FeatureParam<std::string> kRemoteModelEndpoint{
+    &kRemoteModelForDebugging, "MemoriesRemoteModelEndpoint", ""};
+
+}  // namespace
+
+GURL RemoteModelEndpoint() {
+  return GURL(kRemoteModelEndpoint.Get());
 }
 
-std::string ExperimentNameForRemoteModelEndpoint() {
-  return base::GetFieldTrialParamValueByFeature(
-      kRemoteModelForDebugging, "MemoriesRemoteModelEndpointExperimentName");
-}
+const base::FeatureParam<std::string> kRemoteModelEndpointExperimentName{
+    &kRemoteModelForDebugging, "MemoriesRemoteModelEndpointExperimentName", ""};
 
-bool DebugLoggingEnabled() {
-  // Do debug logging if the kDebug feature is enabled. Additionally, remote
-  // endpoint for debugging users are implied to be in debug mode.
-  return base::FeatureList::IsEnabled(kDebug) ||
-         RemoteModelEndpointForDebugging().is_valid();
-}
+const base::FeatureParam<bool> kPersistContextAnnotationsInHistoryDb{
+    &kMemories, "MemoriesPersistContextAnnotationsInHistoryDb", false};
 
-bool StoreVisitsInHistoryDb() {
-  return base::GetFieldTrialParamByFeatureAsBool(
-      kMemories, "MemoriesStoreVisitsInHistoryDb", false);
-}
+const base::FeatureParam<int> kMaxVisitsToCluster{
+    &kMemories, "MemoriesMaxVisitsToCluster", 10};
 
-int MaxVisitsToCluster() {
-  return base::GetFieldTrialParamByFeatureAsInt(
-      kMemories, "MemoriesMaxVisitsToCluster", 10);
-}
+const base::FeatureParam<int> kMaxDaysToCluster{&kMemories,
+                                                "MemoriesMaxDaysToCluster", 9};
 
-// Enables the Chrome Memories history clustering feature.
+const base::FeatureParam<bool> kPersistClustersInHistoryDb{
+    &kMemories, "MemoriesPersistClustersInHistoryDb", false};
+
 const base::Feature kMemories{"Memories", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables debug info; e.g. shows visit metadata on chrome://history entries.
 const base::Feature kDebug{"MemoriesDebug", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Enables using a remote model endpoint for Memories clustering for debugging
-// purposes. This should not be ever enabled in production.
 const base::Feature kRemoteModelForDebugging{"MemoriesRemoteModelForDebugging",
                                              base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/history_clusters/core/memories_features.h b/components/history_clusters/core/memories_features.h
index 4f2f0ba99..9b82e781 100644
--- a/components/history_clusters/core/memories_features.h
+++ b/components/history_clusters/core/memories_features.h
@@ -6,34 +6,57 @@
 #define COMPONENTS_HISTORY_CLUSTERS_CORE_MEMORIES_FEATURES_H_
 
 #include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
 #include "url/gurl.h"
 
 namespace history_clusters {
 
+// Params & helpers functions
+
 // Returns the remote model debug endpoint used to cluster visits into memories.
 // Returns an empty GURL() when the remote model debug endpoint is disabled.
-GURL RemoteModelEndpointForDebugging();
+GURL RemoteModelEndpoint();
 
 // Returns the experiment name to pass through to the remote model debug
 // endpoint to control how the visits get clustered. Returns an empty string if
 // this client should just use be returned the default clustering or if the
 // remote model debug endpoint is disabled.
-std::string ExperimentNameForRemoteModelEndpoint();
+extern const base::FeatureParam<std::string> kRemoteModelEndpointExperimentName;
 
-// Returns true if debug logs should be generated and shown in WebUI inspector.
-bool DebugLoggingEnabled();
-
-// If enabled, completed visits are persisted to the history DB and read back
-// when clustering. If disabled, completed visits are kept in-memory and used
-// these in-memory visits are used when clustering.
-bool StoreVisitsInHistoryDb();
+// If enabled, completed visits context annotations are persisted to the history
+// DB and read back when clustering. If disabled, completed visit context
+// annotations are kept in-memory and used these in-memory visits are used when
+// clustering.
+extern const base::FeatureParam<bool> kPersistContextAnnotationsInHistoryDb;
 
 // The max number of visits to use for each clustering iteration. When using the
-// remote model, this limits the number of visits sent.
-int MaxVisitsToCluster();
+// remote model, this limits the number of visits sent.  Only applies when using
+// persisted visit context annotations; i.e.
+// `kPersistContextAnnotationsInHistoryDb` is true.
+extern const base::FeatureParam<int> kMaxVisitsToCluster;
 
+// The recency threshold controlling which visits will be clustered. This isn't
+// the only factor; i.e. visits older than `MaxDaysToCluster()` may still be
+// clustered. Only applies when using persisted visit context annotations; i.e.
+// `kPersistContextAnnotationsInHistoryDb` is true.
+extern const base::FeatureParam<int> kMaxDaysToCluster;
+
+// If enabled, updating clusters will persist the results to the history DB and
+// accessing clusters will retrieve them from the history DB. If disabled,
+// updating clusters is a no-op and accessing clusters will generate and return
+// new clusters without persisting them.
+extern const base::FeatureParam<bool> kPersistClustersInHistoryDb;
+
+// Features
+
+// Enables the Chrome Memories history clustering feature.
 extern const base::Feature kMemories;
+
+// Enables debug info; e.g. shows visit metadata on chrome://history entries.
 extern const base::Feature kDebug;
+
+// Enables using a remote model endpoint for Memories clustering for debugging
+// purposes. This should not be ever enabled in production.
 extern const base::Feature kRemoteModelForDebugging;
 
 }  // namespace history_clusters
diff --git a/components/history_clusters/core/memories_remote_model_helper.cc b/components/history_clusters/core/memories_remote_model_helper.cc
index 36ea28b..44626adc 100644
--- a/components/history_clusters/core/memories_remote_model_helper.cc
+++ b/components/history_clusters/core/memories_remote_model_helper.cc
@@ -28,7 +28,7 @@
     const std::vector<history::AnnotatedVisit>& visits,
     base::Optional<DebugLoggerCallback> debug_logger) {
   proto::GetClustersRequest request;
-  request.set_experiment_name(ExperimentNameForRemoteModelEndpoint());
+  request.set_experiment_name(kRemoteModelEndpointExperimentName.Get());
 
   base::ListValue debug_visits_list;
   for (auto& visit : visits) {
@@ -143,7 +143,7 @@
 void MemoriesRemoteModelHelper::GetMemories(
     MemoriesCallback callback,
     const std::vector<history::AnnotatedVisit>& visits) {
-  const GURL endpoint(RemoteModelEndpointForDebugging());
+  const GURL endpoint(RemoteModelEndpoint());
   if (!endpoint.is_valid() || visits.empty()) {
     std::move(callback).Run({});
     return;
diff --git a/components/history_clusters/core/memories_remote_model_helper.h b/components/history_clusters/core/memories_remote_model_helper.h
index 8b85ffb..3757b1d 100644
--- a/components/history_clusters/core/memories_remote_model_helper.h
+++ b/components/history_clusters/core/memories_remote_model_helper.h
@@ -41,7 +41,7 @@
                    const std::vector<history::AnnotatedVisit>& visits);
 
  private:
-  // Helpers for making requests used by |GetMemories()|.
+  // Helpers for making requests used by `GetMemories()`.
   static std::unique_ptr<network::ResourceRequest> CreateRequest(
       const GURL& endpoint);
   static std::unique_ptr<network::SimpleURLLoader> CreateLoader(
diff --git a/components/history_clusters/core/memories_service.cc b/components/history_clusters/core/memories_service.cc
index 4867fa49..6daca79 100644
--- a/components/history_clusters/core/memories_service.cc
+++ b/components/history_clusters/core/memories_service.cc
@@ -32,8 +32,8 @@
   // Extract query nodes from the query string.
   query_parser::QueryNodeVector query_nodes;
   query_parser::QueryParser::ParseQueryNodes(
-      base::UTF8ToUTF16(query), query_parser::MatchingAlgorithm::DEFAULT,
-      &query_nodes);
+      base::UTF8ToUTF16(query),
+      query_parser::MatchingAlgorithm::ALWAYS_PREFIX_SEARCH, &query_nodes);
 
   std::vector<history::Cluster> matching_clusters;
   base::ranges::copy_if(clusters, std::back_inserter(matching_clusters),
@@ -89,7 +89,7 @@
 // Form a `QueryMemoriesResponse` containing `clusters` and continuation query
 // params meant to be used in a follow-up request. `query_params` are the params
 // used to get `clusters` from `QueryMemories()`.
-// TODO(mahmadi): At the moment, the recency threshold of |query_params| is
+// TODO(mahmadi): At the moment, the recency threshold of `query_params` is
 //  ignored and continuation query params is set to nullptr. The service does
 //  not support paging.
 MemoriesService::QueryMemoriesResponse FormQueryMemoriesResponse(
@@ -114,14 +114,10 @@
     history::HistoryService* history_service,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
     : history_service_(history_service) {
-  base::Optional<DebugLoggerCallback> debug_logger;
-  if (DebugLoggingEnabled()) {
-    debug_logger = base::BindRepeating(&MemoriesService::NotifyDebugMessage,
-                                       weak_ptr_factory_.GetWeakPtr());
-  }
-
   remote_model_helper_ = std::make_unique<MemoriesRemoteModelHelper>(
-      url_loader_factory, debug_logger);
+      url_loader_factory,
+      base::BindRepeating(&MemoriesService::NotifyDebugMessage,
+                          weak_ptr_factory_.GetWeakPtr()));
   remote_model_helper_weak_factory_ =
       std::make_unique<base::WeakPtrFactory<MemoriesRemoteModelHelper>>(
           remote_model_helper_.get());
@@ -140,8 +136,6 @@
 }
 
 void MemoriesService::NotifyDebugMessage(const std::string& message) const {
-  DCHECK(DebugLoggingEnabled()) << "Callers must ensure logging is enabled.";
-
   for (Observer& obs : observers_) {
     obs.OnMemoriesDebugMessage(message);
   }
@@ -175,7 +169,7 @@
       (visit_context_annotations.status.ukm_page_end_signals ||
        !visit_context_annotations.status.expect_ukm_page_end_signals)) {
     if (base::FeatureList::IsEnabled(kMemories)) {
-      if (StoreVisitsInHistoryDb())
+      if (kPersistContextAnnotationsInHistoryDb.Get())
         history_service_->AddAnnotatedVisit(
             {visit_context_annotations.visit_row.visit_id,
              visit_context_annotations.context_annotations,
@@ -194,12 +188,12 @@
     mojom::QueryParamsPtr query_params,
     base::OnceCallback<void(QueryMemoriesResponse)> callback,
     base::CancelableTaskTracker* task_tracker) {
-  // |QueryMemories| has 4 steps:
+  // `QueryMemories` has 4 steps:
   // 1. Get visits either asynchronously from the history db or synchronously
-  //    from |visits_|.
-  // 2. Ask |remote_model_helper_| to convert the visits to memories.
-  // 3. Filter memories matching |query_params| and create.
-  // 4. Run |callback| with the continuation query params and matched memories.
+  //    from `visits_`.
+  // 2. Ask `remote_model_helper_` to convert the visits to memories.
+  // 3. Filter memories matching `query_params` and create.
+  // 4. Run `callback` with the continuation query params and matched memories.
 
   // Copy `query_params->query` because `query_params` is about to be moved.
   auto query_string = query_params->query;
@@ -211,11 +205,11 @@
                                               std::move(query_params)))
                          .Then(std::move(callback)));
 
-  if (StoreVisitsInHistoryDb()) {
+  if (kPersistContextAnnotationsInHistoryDb.Get()) {
     history_service_->GetAnnotatedVisits(
-        MaxVisitsToCluster(),
+        kMaxVisitsToCluster.Get(),
         base::BindOnce(
-            // This echo callback is necessary to copy the |AnnotatedVisit|
+            // This echo callback is necessary to copy the `AnnotatedVisit`
             // refs.
             [](std::vector<history::AnnotatedVisit> visits) { return visits; })
             .Then(std::move(on_visits_callback)),
diff --git a/components/history_clusters/core/memories_service.h b/components/history_clusters/core/memories_service.h
index 71b15be..2d496b3 100644
--- a/components/history_clusters/core/memories_service.h
+++ b/components/history_clusters/core/memories_service.h
@@ -56,13 +56,12 @@
   void AddObserver(Observer* obs);
   void RemoveObserver(Observer* obs);
 
-  // Notifies the observers of a debug message being available. Caller is
-  // responsible for checking that logging is enabled before calling this.
+  // Notifies the observers of a debug message being available.
   void NotifyDebugMessage(const std::string& message) const;
 
-  // TODO(manukh) |MemoriesService| should be responsible for constructing the
-  //  |AnnotatedVisit|s rather than exposing these methods which are used by
-  //  |HistoryClustersTabHelper| to construct the visits.
+  // TODO(manukh) `MemoriesService` should be responsible for constructing the
+  //  `AnnotatedVisit`s rather than exposing these methods which are used by
+  //  `HistoryClustersTabHelper` to construct the visits.
   // Gets an `IncompleteVisitContextAnnotations` after DCHECKing it exists; this
   // saves the call sites the effort.
   IncompleteVisitContextAnnotations& GetIncompleteVisitContextAnnotations(
@@ -71,24 +70,24 @@
   IncompleteVisitContextAnnotations&
   GetOrCreateIncompleteVisitContextAnnotations(int64_t nav_id);
   // Returns whether an `IncompleteVisitContextAnnotations` exists.
-  // TODO(manukh): Merge |HasIncompleteVisitContextAnnotations()| and
-  //  |GetIncompleteVisitContextAnnotations()|.
+  // TODO(manukh): Merge `HasIncompleteVisitContextAnnotations()` and
+  //  `GetIncompleteVisitContextAnnotations()`.
   bool HasIncompleteVisitContextAnnotations(int64_t nav_id);
   // Completes the `IncompleteVisitContextAnnotations` if the expected metrics
   // have been recorded. References retrieved prior will no longer be valid.
   void CompleteVisitContextAnnotationsIfReady(int64_t nav_id);
 
   // Returns the freshest Memories created from the user visit history, in
-  // reverse chronological order, based on the parameters in |query_params|
+  // reverse chronological order, based on the parameters in `query_params`
   // along with continuation query params meant to be used in the follow-up
   // request to load older Memories.
-  // Note: At the moment, this method asks |remote_model_helper_| to construct
-  // Memories from |visits_|.
+  // Note: At the moment, this method asks `remote_model_helper_` to construct
+  // Memories from `visits_`.
   void QueryMemories(mojom::QueryParamsPtr query_params,
                      base::OnceCallback<void(QueryMemoriesResponse)> callback,
                      base::CancelableTaskTracker* task_tracker);
   // Removes all visits to the specified URLs in the specified time ranges in
-  // |expire_list|. Calls |closure| when done.
+  // `expire_list`. Calls `closure` when done.
   void RemoveVisits(const std::vector<history::ExpireHistoryArgs>& expire_list,
                     base::OnceClosure closure,
                     base::CancelableTaskTracker* task_tracker);
@@ -97,14 +96,14 @@
   friend class MemoriesServiceTestApi;
 
   // If the Memories flag is enabled, this contains all the visits in-memory
-  // during the Profile lifetime. If the "MemoriesStoreVisitsInHistoryDb" param
-  // is true, this will be empty.
+  // during the Profile lifetime. If the `kPersistContextAnnotationsInHistoryDb`
+  // param is true, this will be empty.
   // TODO(tommycli): Hide this better behind a new debug flag.
   std::vector<history::AnnotatedVisit> visits_;
 
   // `VisitContextAnnotations`s are constructed stepwise; they're initially
-  // placed in |incomplete_visit_context_annotations_| and moved either to
-  // |visits_| or the history database once completed.
+  // placed in `incomplete_visit_context_annotations_` and moved either to
+  // `visits_` or the history database once completed.
   std::map<int64_t, IncompleteVisitContextAnnotations>
       incomplete_visit_context_annotations_;
 
@@ -118,7 +117,7 @@
   // A list of observers for this service.
   base::ObserverList<Observer> observers_;
 
-  // Used to asyncly call into |remote_model_helper_| after async history
+  // Used to asyncly call into `remote_model_helper_` after async history
   // request.
   std::unique_ptr<base::WeakPtrFactory<MemoriesRemoteModelHelper>>
       remote_model_helper_weak_factory_;
diff --git a/components/history_clusters/core/memories_service_unittest.cc b/components/history_clusters/core/memories_service_unittest.cc
index e765590b..8b64b63 100644
--- a/components/history_clusters/core/memories_service_unittest.cc
+++ b/components/history_clusters/core/memories_service_unittest.cc
@@ -38,7 +38,7 @@
 
 namespace {
 
-// Returns a Time that's |milliseconds| milliseconds after Windows epoch.
+// Returns a Time that's `milliseconds` milliseconds after Windows epoch.
 base::Time IntToTime(int milliseconds) {
   return base::Time::FromDeltaSinceWindowsEpoch(
       base::TimeDelta::FromMilliseconds(milliseconds));
@@ -189,7 +189,7 @@
 
   base::test::TaskEnvironment task_environment_;
 
-  // Used to construct a |MemoriesService|.
+  // Used to construct a `MemoriesService`.
   base::ScopedTempDir history_dir_;
   std::unique_ptr<history::HistoryService> history_service_;
 
@@ -214,139 +214,99 @@
 // Useless, but required by the C++14 standard. Please deliver us, C++17.
 constexpr char MemoriesServiceTest::kFakeEndpoint[];
 
-TEST_F(MemoriesServiceTest, QueryMemoriesEmptyQuery) {
+TEST_F(MemoriesServiceTest, QueryMemoriesVariousQueries) {
   std::string experiment_name = "someExperiment";
   EnableMemoriesWithEndpoint(kFakeEndpoint, experiment_name);
 
   AddVisit(0, GURL{"https://google.com"}, u"Google title", 2, IntToTime(2), 3);
   AddVisit(0, GURL{"https://github.com"}, u"Github title", 4, IntToTime(4), 5);
 
-  EXPECT_FALSE(test_url_loader_factory_.IsPending(kFakeEndpoint));
-  memories_service_->QueryMemories(
-      mojom::QueryParams::New(),
-      // This "expect" block is not run until after the fake response is sent
-      // further down in this method.
-      base::BindLambdaForTesting(
-          [&](MemoriesService::QueryMemoriesResponse response) {
-            // Verify that the continuation query params is nullptr.
-            ASSERT_FALSE(!!response.query_params);
-            // Verify the parsed response.
-            ASSERT_EQ(response.clusters.size(), 2u);
-            EXPECT_FALSE(response.clusters[0]->id.is_empty());
-            ASSERT_EQ(response.clusters[0]->top_visits.size(), 2u);
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->id, 2);
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->url,
-                      "https://google.com/");
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->time, IntToTime(2));
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->page_title,
-                      "Google title");
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->id, 4);
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->url,
-                      "https://github.com/");
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->time, IntToTime(4));
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->page_title,
-                      "Github title");
-            ASSERT_EQ(response.clusters[0]->keywords.size(), 2u);
-            EXPECT_EQ(response.clusters[0]->keywords[0], u"apples");
-            EXPECT_EQ(response.clusters[0]->keywords[1], u"Red Oranges");
-            EXPECT_FALSE(response.clusters[1]->id.is_empty());
-            ASSERT_EQ(response.clusters[1]->top_visits.size(), 1u);
-            EXPECT_EQ(response.clusters[1]->top_visits[0]->id, 4);
-            EXPECT_EQ(response.clusters[1]->top_visits[0]->url,
-                      "https://github.com/");
-            EXPECT_EQ(response.clusters[1]->top_visits[0]->time, IntToTime(4));
-            EXPECT_EQ(response.clusters[1]->top_visits[0]->page_title,
-                      "Github title");
-            EXPECT_TRUE(response.clusters[1]->keywords.empty());
-            run_loop_quit_.Run();
-          }),
-      &task_tracker_);
+  struct TestData {
+    std::string query;
+    const bool expect_first_cluster;
+    const bool expect_second_cluster;
+  } test_data[] = {
+      // Empty query should get both.
+      {"", true, true},
+      // Non matching query should get none.
+      {"non_matching_query", false, false},
+      // Query matching one cluster.
+      {"oran", true, false},
+      // This verifies the memory doesn't flicker away as the user is typing
+      // out: "red oran" one key at a time. Also tests out multi-term queries.
+      {"red", true, false},
+      {"red ", true, false},
+      {"red o", true, false},
+      {"red or", true, false},
+      {"red ora", true, false},
+      {"red oran", true, false},
+  };
 
-  VerifyHardcodedTestDataInUrlLoaderRequest(experiment_name);
-  InjectHardcodedTestDataToUrlLoaderResponse({{2, 4}, {4}});
+  for (size_t i = 0; i < base::size(test_data); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Testing case i=%d, query=%s", int(i),
+                                    test_data[i].query.c_str()));
 
-  // Verify the callback is invoked.
-  run_loop_.Run();
-}
+    base::RunLoop run_loop;
+    auto run_loop_quit = run_loop.QuitClosure();
 
-TEST_F(MemoriesServiceTest, QueryMemoriesMatchingNonEmptyQuery) {
-  EnableMemoriesWithEndpoint(kFakeEndpoint);
+    test_url_loader_factory_.ClearResponses();
+    ASSERT_FALSE(test_url_loader_factory_.IsPending(kFakeEndpoint));
 
-  AddVisit(0, GURL{"https://google.com"}, u"Google title", 2, IntToTime(2), 3);
-  AddVisit(0, GURL{"https://github.com"}, u"Github title", 4, IntToTime(4), 5);
+    auto query_params = mojom::QueryParams::New();
+    query_params->query = test_data[i].query;
+    memories_service_->QueryMemories(
+        std::move(query_params),
+        // This "expect" block is not run until after the fake response is sent
+        // further down in this method.
+        base::BindLambdaForTesting(
+            [&](MemoriesService::QueryMemoriesResponse response) {
+              // Verify that the continuation query params is nullptr.
+              ASSERT_FALSE(!!response.query_params);
 
-  auto query_params = mojom::QueryParams::New();
-  // Verify that we can match against the "Red Oranges" uppercase keyword.
-  query_params->query = "orang";
+              size_t expected_size = int(test_data[i].expect_first_cluster) +
+                                     int(test_data[i].expect_second_cluster);
+              ASSERT_EQ(response.clusters.size(), expected_size);
 
-  EXPECT_FALSE(test_url_loader_factory_.IsPending(kFakeEndpoint));
-  memories_service_->QueryMemories(
-      std::move(query_params),
-      // This "expect" block is not run until after the fake response is sent
-      // further down in this method.
-      base::BindLambdaForTesting(
-          [&](MemoriesService::QueryMemoriesResponse response) {
-            // Verify that the continuation query params is nullptr.
-            ASSERT_FALSE(!!response.query_params);
-            // Verify the parsed response.
-            ASSERT_EQ(response.clusters.size(), 1u);
-            EXPECT_FALSE(response.clusters[0]->id.is_empty());
-            ASSERT_EQ(response.clusters[0]->top_visits.size(), 2u);
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->id, 2);
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->url,
-                      "https://google.com/");
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->time, IntToTime(2));
-            EXPECT_EQ(response.clusters[0]->top_visits[0]->page_title,
-                      "Google title");
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->id, 4);
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->url,
-                      "https://github.com/");
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->time, IntToTime(4));
-            EXPECT_EQ(response.clusters[0]->top_visits[1]->page_title,
-                      "Github title");
-            ASSERT_EQ(response.clusters[0]->keywords.size(), 2u);
-            EXPECT_EQ(response.clusters[0]->keywords[0], u"apples");
-            EXPECT_EQ(response.clusters[0]->keywords[1], u"Red Oranges");
-            run_loop_quit_.Run();
-          }),
-      &task_tracker_);
+              if (test_data[i].expect_first_cluster) {
+                const auto& cluster = response.clusters[0];
+                EXPECT_FALSE(cluster->id.is_empty());
+                ASSERT_EQ(cluster->top_visits.size(), 2u);
+                EXPECT_EQ(cluster->top_visits[0]->id, 2);
+                EXPECT_EQ(cluster->top_visits[0]->url, "https://google.com/");
+                EXPECT_EQ(cluster->top_visits[0]->time, IntToTime(2));
+                EXPECT_EQ(cluster->top_visits[0]->page_title, "Google title");
+                EXPECT_EQ(cluster->top_visits[1]->id, 4);
+                EXPECT_EQ(cluster->top_visits[1]->url, "https://github.com/");
+                EXPECT_EQ(cluster->top_visits[1]->time, IntToTime(4));
+                EXPECT_EQ(cluster->top_visits[1]->page_title, "Github title");
+                ASSERT_EQ(cluster->keywords.size(), 2u);
+                EXPECT_EQ(cluster->keywords[0], u"apples");
+                EXPECT_EQ(cluster->keywords[1], u"Red Oranges");
+              }
 
-  VerifyHardcodedTestDataInUrlLoaderRequest();
-  InjectHardcodedTestDataToUrlLoaderResponse({{2, 4}, {4}});
+              if (test_data[i].expect_second_cluster) {
+                const auto& cluster = test_data[i].expect_first_cluster
+                                          ? response.clusters[1]
+                                          : response.clusters[0];
+                EXPECT_FALSE(cluster->id.is_empty());
+                ASSERT_EQ(cluster->top_visits.size(), 1u);
+                EXPECT_EQ(cluster->top_visits[0]->id, 4);
+                EXPECT_EQ(cluster->top_visits[0]->url, "https://github.com/");
+                EXPECT_EQ(cluster->top_visits[0]->time, IntToTime(4));
+                EXPECT_EQ(cluster->top_visits[0]->page_title, "Github title");
+                EXPECT_TRUE(cluster->keywords.empty());
+              }
 
-  // Verify the callback is invoked.
-  run_loop_.Run();
-}
+              run_loop_quit.Run();
+            }),
+        &task_tracker_);
 
-TEST_F(MemoriesServiceTest, QueryMemoriesNonMatchingNonEmptyQuery) {
-  EnableMemoriesWithEndpoint(kFakeEndpoint);
+    VerifyHardcodedTestDataInUrlLoaderRequest(experiment_name);
+    InjectHardcodedTestDataToUrlLoaderResponse({{2, 4}, {4}});
 
-  AddVisit(0, GURL{"https://google.com"}, u"Google title", 2, IntToTime(2), 3);
-  AddVisit(0, GURL{"https://github.com"}, u"Github title", 4, IntToTime(4), 5);
-
-  auto query_params = mojom::QueryParams::New();
-  query_params->query = "should_not_match_anything";
-
-  EXPECT_FALSE(test_url_loader_factory_.IsPending(kFakeEndpoint));
-  memories_service_->QueryMemories(
-      std::move(query_params),
-      // This "expect" block is not run until after the fake response is sent
-      // further down in this method.
-      base::BindLambdaForTesting(
-          [&](MemoriesService::QueryMemoriesResponse response) {
-            // Verify that the continuation query params is nullptr.
-            ASSERT_FALSE(!!response.query_params);
-            // Verify the parsed response.
-            EXPECT_TRUE(response.clusters.empty());
-            run_loop_quit_.Run();
-          }),
-      &task_tracker_);
-
-  VerifyHardcodedTestDataInUrlLoaderRequest();
-  InjectHardcodedTestDataToUrlLoaderResponse({{2, 4}, {4}});
-
-  // Verify the callback is invoked.
-  run_loop_.Run();
+    // Verify the callback is invoked.
+    run_loop.Run();
+  }
 }
 
 TEST_F(MemoriesServiceTest, QueryMemoriesWithEmptyVisits) {
@@ -548,7 +508,7 @@
       {
           {
               kMemories,
-              {{"MemoriesStoreVisitsInHistoryDb", "true"}},
+              {{"MemoriesPersistContextAnnotationsInHistoryDb", "true"}},
           },
           {
               kRemoteModelForDebugging,
@@ -617,7 +577,7 @@
       {
           {
               kMemories,
-              {{"MemoriesStoreVisitsInHistoryDb", "true"}},
+              {{"MemoriesPersistContextAnnotationsInHistoryDb", "true"}},
           },
           {
               kRemoteModelForDebugging,
diff --git a/components/history_clusters/core/visit_data.h b/components/history_clusters/core/visit_data.h
index 5f8927d..fa3d28b7 100644
--- a/components/history_clusters/core/visit_data.h
+++ b/components/history_clusters/core/visit_data.h
@@ -13,20 +13,20 @@
 // re-recording fields and 2) determine whether a visit is compete (i.e. has all
 // expected fields recorded).
 struct RecordingStatus {
-  // Whether |url_row| and |visit_row| have been set.
+  // Whether `url_row` and `visit_row` have been set.
   bool history_rows = false;
   // Whether a navigation has ended; i.e. another navigation has began in the
   // same tab or the navigation's tab has been closed.
   bool navigation_ended = false;
-  // Whether the |context_annotations| associated with navigation end have been
-  // set. Should only be true if both |history_rows| and |navigation_ended| are
+  // Whether the `context_annotations` associated with navigation end have been
+  // set. Should only be true if both `history_rows` and `navigation_ended` are
   // true.
   bool navigation_end_signals = false;
-  // Whether the UKM |page_end_reason| |context_annotations| is expected to be
+  // Whether the UKM `page_end_reason` `context_annotations` is expected to be
   // set.
   bool expect_ukm_page_end_signals = false;
-  // Whether the UKM |page_end_reason| |context_annotations| has been set.
-  // Should only be true if |expect_ukm_page_end_signals| is true.
+  // Whether the UKM `page_end_reason` `context_annotations` has been set.
+  // Should only be true if `expect_ukm_page_end_signals` is true.
   bool ukm_page_end_signals = false;
 };
 
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index 8a764aa..0b75dfca9 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/time/default_tick_clock.h"
 #include "base/timer/timer.h"
@@ -762,10 +763,10 @@
   views::SetImageFromVectorIcon(collapse_button_, vector_icons::kCaretUpIcon,
                                 kButtonDip, text_color);
 
-  back_to_tab_button_->SetInkDropBaseColor(text_color);
-  close_button_->SetInkDropBaseColor(text_color);
-  expand_button_->SetInkDropBaseColor(text_color);
-  collapse_button_->SetInkDropBaseColor(text_color);
+  back_to_tab_button_->ink_drop()->SetBaseColor(text_color);
+  close_button_->ink_drop()->SetBaseColor(text_color);
+  expand_button_->ink_drop()->SetBaseColor(text_color);
+  collapse_button_->ink_drop()->SetBaseColor(text_color);
 }
 
 void CaptionBubble::SetBackgroundColor() {
diff --git a/components/metrics/clean_exit_beacon.cc b/components/metrics/clean_exit_beacon.cc
index a6c0904..120d7e0 100644
--- a/components/metrics/clean_exit_beacon.cc
+++ b/components/metrics/clean_exit_beacon.cc
@@ -66,14 +66,6 @@
 CleanExitBeacon::~CleanExitBeacon() {
 }
 
-// static
-void CleanExitBeacon::RegisterPrefs(PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
-
-  registry->RegisterTimePref(prefs::kStabilityBrowserLastLiveTimeStamp,
-                             base::Time(), PrefRegistry::LOSSY_PREF);
-}
-
 void CleanExitBeacon::WriteBeaconValue(bool value) {
   UpdateLastLiveTimestamp();
   local_state_->SetBoolean(prefs::kStabilityExitedCleanly, value);
@@ -93,4 +85,17 @@
                         base::Time::Now());
 }
 
+// static
+void CleanExitBeacon::RegisterPrefs(PrefRegistrySimple* registry) {
+  registry->RegisterBooleanPref(prefs::kStabilityExitedCleanly, true);
+
+  registry->RegisterTimePref(prefs::kStabilityBrowserLastLiveTimeStamp,
+                             base::Time(), PrefRegistry::LOSSY_PREF);
+}
+
+// static
+void CleanExitBeacon::EnsureCleanShutdown(PrefService* local_state) {
+  CHECK(local_state->GetBoolean(prefs::kStabilityExitedCleanly));
+}
+
 }  // namespace metrics
diff --git a/components/metrics/clean_exit_beacon.h b/components/metrics/clean_exit_beacon.h
index 28f1ae7..c39fe59 100644
--- a/components/metrics/clean_exit_beacon.h
+++ b/components/metrics/clean_exit_beacon.h
@@ -45,6 +45,9 @@
   // Registers local state prefs used by this class.
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
+  // CHECKs that Chrome exited cleanly.
+  static void EnsureCleanShutdown(PrefService* local_state);
+
  private:
   PrefService* const local_state_;
   const bool initial_value_;
diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc
index 3623ec6..8dd54e0d 100644
--- a/components/metrics/metrics_service.cc
+++ b/components/metrics/metrics_service.cc
@@ -187,10 +187,6 @@
 }  // namespace
 
 // static
-MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ =
-    MetricsService::CLEANLY_SHUTDOWN;
-
-// static
 void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
   CleanExitBeacon::RegisterPrefs(registry);
   MetricsStateManager::RegisterPrefs(registry);
@@ -437,8 +433,6 @@
 #else
 void MetricsService::LogNeedForCleanShutdown() {
   state_manager_->clean_exit_beacon()->WriteBeaconValue(false);
-  // Redundant setting to be sure we call for a clean shutdown.
-  clean_shutdown_status_ = NEED_TO_SHUTDOWN;
 }
 #endif  // defined(OS_ANDROID) || defined(OS_IOS)
 
@@ -762,12 +756,6 @@
   return true;
 }
 
-bool MetricsService::UmaMetricsProperlyShutdown() {
-  CHECK(clean_shutdown_status_ == CLEANLY_SHUTDOWN ||
-        clean_shutdown_status_ == NEED_TO_SHUTDOWN);
-  return clean_shutdown_status_ == CLEANLY_SHUTDOWN;
-}
-
 void MetricsService::RegisterMetricsProvider(
     std::unique_ptr<MetricsProvider> provider) {
   DCHECK_EQ(CONSTRUCTED, state_);
@@ -911,9 +899,6 @@
 
 void MetricsService::LogCleanShutdown(bool end_completed) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Redundant setting to assure that we always reset this value at shutdown
-  // (and that we don't use some alternate path, and not call LogCleanShutdown).
-  clean_shutdown_status_ = CLEANLY_SHUTDOWN;
   state_manager_->clean_exit_beacon()->WriteBeaconValue(true);
   StabilityMetricsProvider(local_state_).MarkSessionEndCompleted(end_completed);
 }
diff --git a/components/metrics/metrics_service.h b/components/metrics/metrics_service.h
index 16c83f0..94f4382 100644
--- a/components/metrics/metrics_service.h
+++ b/components/metrics/metrics_service.h
@@ -144,10 +144,6 @@
   bool reporting_active() const;
   bool has_unsent_logs() const;
 
-  // Redundant test to ensure that we are notified of a clean exit.
-  // This value should be true when process has completed shutdown.
-  static bool UmaMetricsProperlyShutdown();
-
   // Register the specified |provider| to provide additional metrics into the
   // UMA log. Should be called during MetricsService initialization only.
   void RegisterMetricsProvider(std::unique_ptr<MetricsProvider> provider);
@@ -208,11 +204,6 @@
   State state() const { return state_; }
 
  private:
-  enum ShutdownCleanliness {
-    CLEANLY_SHUTDOWN = 0xdeadbeef,
-    NEED_TO_SHUTDOWN = ~CLEANLY_SHUTDOWN
-  };
-
   // The current state of recording for the MetricsService. The state is UNSET
   // until set to something else, at which point it remains INACTIVE or ACTIVE
   // for the lifetime of the object.
@@ -389,10 +380,6 @@
   bool is_in_foreground_ = false;
 #endif
 
-  // Redundant marker to check that we completed our shutdown, and set the
-  // exited-cleanly bit in the prefs.
-  static ShutdownCleanliness clean_shutdown_status_;
-
   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, ActiveFieldTrialsReported);
   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest, IsPluginProcess);
   FRIEND_TEST_ALL_PREFIXES(::ChromeMetricsServiceClientTest,
diff --git a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
index 0837c6e..5b51b432 100644
--- a/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_provider_impl_unittest.cc
@@ -124,7 +124,7 @@
 
 const char kSuggestionUrl2[] = "http://foo.com/bar";
 
-const char kTestJsonDefaultCategoryTitle[] = "Some title";
+const char16_t kTestJsonDefaultCategoryTitle[] = u"Some title";
 
 const int kOtherCategoryId = 2;
 const int kUnknownRemoteCategoryId = 1234;
@@ -592,8 +592,7 @@
 }
 
 TEST_F(RemoteSuggestionsProviderImplTest, CategoryTitle) {
-  const std::u16string test_default_title =
-      base::UTF8ToUTF16(kTestJsonDefaultCategoryTitle);
+  const std::u16string test_default_title = kTestJsonDefaultCategoryTitle;
 
   // Don't send an initial response -- we want to test what happens without any
   // server status.
diff --git a/components/ntp_tiles/custom_links_manager_impl_unittest.cc b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
index 1c36355..a273d5c 100644
--- a/components/ntp_tiles/custom_links_manager_impl_unittest.cc
+++ b/components/ntp_tiles/custom_links_manager_impl_unittest.cc
@@ -26,28 +26,29 @@
 
 struct TestCaseItem {
   const char* url;
-  const char* title;
+  const char16_t* title;
 };
 
-const TestCaseItem kTestCase1[] = {{"http://foo1.com/", "Foo1"}};
+const TestCaseItem kTestCase1[] = {{"http://foo1.com/", u"Foo1"}};
 const TestCaseItem kTestCase2[] = {
-    {"http://foo1.com/", "Foo1"},
-    {"http://foo2.com/", "Foo2"},
+    {"http://foo1.com/", u"Foo1"},
+    {"http://foo2.com/", u"Foo2"},
 };
 const TestCaseItem kTestCase3[] = {
-    {"http://foo1.com/", "Foo1"},
-    {"http://foo2.com/", "Foo2"},
-    {"http://foo3.com/", "Foo3"},
+    {"http://foo1.com/", u"Foo1"},
+    {"http://foo2.com/", u"Foo2"},
+    {"http://foo3.com/", u"Foo3"},
 };
 const TestCaseItem kTestCaseMax[] = {
-    {"http://foo1.com/", "Foo1"}, {"http://foo2.com/", "Foo2"},
-    {"http://foo3.com/", "Foo3"}, {"http://foo4.com/", "Foo4"},
-    {"http://foo5.com/", "Foo5"}, {"http://foo6.com/", "Foo6"},
-    {"http://foo7.com/", "Foo7"}, {"http://foo8.com/", "Foo8"},
-    {"http://foo9.com/", "Foo9"}, {"http://foo10.com/", "Foo10"},
+    {"http://foo1.com/", u"Foo1"}, {"http://foo2.com/", u"Foo2"},
+    {"http://foo3.com/", u"Foo3"}, {"http://foo4.com/", u"Foo4"},
+    {"http://foo5.com/", u"Foo5"}, {"http://foo6.com/", u"Foo6"},
+    {"http://foo7.com/", u"Foo7"}, {"http://foo8.com/", u"Foo8"},
+    {"http://foo9.com/", u"Foo9"}, {"http://foo10.com/", u"Foo10"},
 };
 
 const char kTestTitle[] = "Test";
+const char16_t kTestTitle16[] = u"Test";
 const char kTestUrl[] = "http://test.com/";
 
 base::Value::ListStorage FillTestListStorage(const char* url,
@@ -62,10 +63,10 @@
   return new_link_list;
 }
 
-void AddTile(NTPTilesVector* tiles, const char* url, const char* title) {
+void AddTile(NTPTilesVector* tiles, const char* url, const char16_t* title) {
   NTPTile tile;
   tile.url = GURL(url);
-  tile.title = base::UTF8ToUTF16(title);
+  tile.title = title;
   tiles->push_back(std::move(tile));
 }
 
@@ -80,8 +81,7 @@
 std::vector<Link> FillTestLinks(base::span<const TestCaseItem> test_cases) {
   std::vector<Link> links;
   for (const auto& test_case : test_cases) {
-    links.emplace_back(
-        Link{GURL(test_case.url), base::UTF8ToUTF16(test_case.title), true});
+    links.emplace_back(Link{GURL(test_case.url), test_case.title, true});
   }
   return links;
 }
@@ -160,10 +160,8 @@
 
   // Add link.
   std::vector<Link> expected_links = initial_links;
-  expected_links.emplace_back(
-      Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false});
-  EXPECT_TRUE(
-      custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+  expected_links.emplace_back(Link{GURL(kTestUrl), kTestTitle16, false});
+  EXPECT_TRUE(custom_links_->AddLink(GURL(kTestUrl), kTestTitle16));
   EXPECT_EQ(expected_links, custom_links_->GetLinks());
 }
 
@@ -174,8 +172,7 @@
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to add link. This should fail and not modify the list.
-  EXPECT_FALSE(
-      custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+  EXPECT_FALSE(custom_links_->AddLink(GURL(kTestUrl), kTestTitle16));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 }
 
@@ -186,8 +183,8 @@
   ASSERT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to add duplicate link. This should fail and not modify the list.
-  EXPECT_FALSE(custom_links_->AddLink(GURL(kTestCase1[0].url),
-                                      base::UTF8ToUTF16(kTestCase1[0].title)));
+  EXPECT_FALSE(
+      custom_links_->AddLink(GURL(kTestCase1[0].url), kTestCase1[0].title));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 }
 
@@ -200,25 +197,20 @@
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
                                         std::u16string()));
   EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestUrl),
-                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
+      std::vector<Link>({Link{GURL(kTestUrl), kTestCase1[0].title, false}}),
       custom_links_->GetLinks());
 
   // Update the link's title.
-  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
-                                        base::UTF8ToUTF16(kTestTitle)));
-  EXPECT_EQ(std::vector<Link>(
-                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(), kTestTitle16));
+  EXPECT_EQ(std::vector<Link>({Link{GURL(kTestUrl), kTestTitle16, false}}),
             custom_links_->GetLinks());
 
   // Update the link's URL and title.
-  EXPECT_TRUE(
-      custom_links_->UpdateLink(GURL(kTestUrl), GURL(kTestCase1[0].url),
-                                base::UTF8ToUTF16(kTestCase1[0].title)));
-  EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestCase1[0].url),
-                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
-      custom_links_->GetLinks());
+  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(kTestCase1[0].url),
+                                        kTestCase1[0].title));
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestCase1[0].url), kTestCase1[0].title, false}}),
+            custom_links_->GetLinks());
 }
 
 TEST_F(CustomLinksManagerImplTest, UpdateLinkWithInvalidParams) {
@@ -229,8 +221,7 @@
 
   // Try to update a link that does not exist. This should fail and not modify
   // the list.
-  EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(),
-                                         base::UTF8ToUTF16(kTestTitle)));
+  EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestUrl), GURL(), kTestTitle16));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to pass empty params. This should fail and not modify the list.
@@ -239,8 +230,7 @@
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Try to pass an invalid URL. This should fail and not modify the list.
-  EXPECT_FALSE(custom_links_->UpdateLink(GURL("test"), GURL(),
-                                         base::UTF8ToUTF16(kTestTitle)));
+  EXPECT_FALSE(custom_links_->UpdateLink(GURL("test"), GURL(), kTestTitle16));
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
   EXPECT_FALSE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL("test"),
                                          std::u16string()));
@@ -288,25 +278,19 @@
 
   // Move the last link to the front.
   EXPECT_TRUE(custom_links_->ReorderLink(GURL(kTestCase3[2].url), (size_t)0));
-  EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestCase3[2].url),
-                              base::UTF8ToUTF16(kTestCase3[2].title), true},
-                         Link{GURL(kTestCase3[0].url),
-                              base::UTF8ToUTF16(kTestCase3[0].title), true},
-                         Link{GURL(kTestCase3[1].url),
-                              base::UTF8ToUTF16(kTestCase3[1].title), true}}),
-      custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestCase3[2].url), kTestCase3[2].title, true},
+                 Link{GURL(kTestCase3[0].url), kTestCase3[0].title, true},
+                 Link{GURL(kTestCase3[1].url), kTestCase3[1].title, true}}),
+            custom_links_->GetLinks());
 
   // Move the same link to the right.
   EXPECT_TRUE(custom_links_->ReorderLink(GURL(kTestCase3[2].url), (size_t)1));
-  EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestCase3[0].url),
-                              base::UTF8ToUTF16(kTestCase3[0].title), true},
-                         Link{GURL(kTestCase3[2].url),
-                              base::UTF8ToUTF16(kTestCase3[2].title), true},
-                         Link{GURL(kTestCase3[1].url),
-                              base::UTF8ToUTF16(kTestCase3[1].title), true}}),
-      custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestCase3[0].url), kTestCase3[0].title, true},
+                 Link{GURL(kTestCase3[2].url), kTestCase3[2].title, true},
+                 Link{GURL(kTestCase3[1].url), kTestCase3[1].title, true}}),
+            custom_links_->GetLinks());
 
   // Move the same link to the end.
   EXPECT_TRUE(custom_links_->ReorderLink(GURL(kTestCase3[2].url), (size_t)2));
@@ -316,10 +300,9 @@
 TEST_F(CustomLinksManagerImplTest, DeleteLink) {
   // Initialize.
   NTPTilesVector initial_tiles;
-  AddTile(&initial_tiles, kTestUrl, kTestTitle);
+  AddTile(&initial_tiles, kTestUrl, kTestTitle16);
   ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
-  ASSERT_EQ(std::vector<Link>(
-                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}}),
+  ASSERT_EQ(std::vector<Link>({Link{GURL(kTestUrl), kTestTitle16, true}}),
             custom_links_->GetLinks());
 
   // Delete link.
@@ -348,12 +331,10 @@
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Add link.
-  EXPECT_TRUE(
-      custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+  EXPECT_TRUE(custom_links_->AddLink(GURL(kTestUrl), kTestTitle16));
   EXPECT_EQ(std::vector<Link>(
-                {Link{GURL(kTestCase1[0].url),
-                      base::UTF8ToUTF16(kTestCase1[0].title), true},
-                 {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}}),
+                {Link{GURL(kTestCase1[0].url), kTestCase1[0].title, true},
+                 {Link{GURL(kTestUrl), kTestTitle16, false}}}),
             custom_links_->GetLinks());
 
   // Undo add link.
@@ -375,8 +356,7 @@
   EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(kTestUrl),
                                         std::u16string()));
   EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestUrl),
-                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
+      std::vector<Link>({Link{GURL(kTestUrl), kTestCase1[0].title, false}}),
       custom_links_->GetLinks());
 
   // Undo update link.
@@ -384,11 +364,11 @@
   EXPECT_EQ(initial_links, custom_links_->GetLinks());
 
   // Update the link's title.
-  EXPECT_TRUE(custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(),
-                                        base::UTF8ToUTF16(kTestTitle)));
-  EXPECT_EQ(std::vector<Link>({Link{GURL(kTestCase1[0].url),
-                                    base::UTF8ToUTF16(kTestTitle), false}}),
-            custom_links_->GetLinks());
+  EXPECT_TRUE(
+      custom_links_->UpdateLink(GURL(kTestCase1[0].url), GURL(), kTestTitle16));
+  EXPECT_EQ(
+      std::vector<Link>({Link{GURL(kTestCase1[0].url), kTestTitle16, false}}),
+      custom_links_->GetLinks());
 
   // Undo update link.
   EXPECT_TRUE(custom_links_->UndoAction());
@@ -402,9 +382,8 @@
 TEST_F(CustomLinksManagerImplTest, UndoDeleteLink) {
   // Initialize.
   NTPTilesVector initial_tiles;
-  AddTile(&initial_tiles, kTestUrl, kTestTitle);
-  std::vector<Link> expected_links(
-      {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}});
+  AddTile(&initial_tiles, kTestUrl, kTestTitle16);
+  std::vector<Link> expected_links({Link{GURL(kTestUrl), kTestTitle16, true}});
   ASSERT_TRUE(custom_links_->Initialize(initial_tiles));
   ASSERT_EQ(expected_links, custom_links_->GetLinks());
 
@@ -423,10 +402,8 @@
   ASSERT_TRUE(custom_links_->GetLinks().empty());
 
   // Add link.
-  std::vector<Link> expected_links(
-      {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
-  ASSERT_TRUE(
-      custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+  std::vector<Link> expected_links({Link{GURL(kTestUrl), kTestTitle16, false}});
+  ASSERT_TRUE(custom_links_->AddLink(GURL(kTestUrl), kTestTitle16));
   ASSERT_EQ(expected_links, custom_links_->GetLinks());
 
   // Delete link.
@@ -464,10 +441,9 @@
                                 {history::URLRow(GURL(kTestCase2[1].url))},
                                 /*favicon_urls=*/std::set<GURL>(),
                                 /*restrict_urls=*/base::nullopt));
-  EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestCase2[0].url),
-                              base::UTF8ToUTF16(kTestCase2[0].title), true}}),
-      custom_links_->GetLinks());
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestCase2[0].url), kTestCase2[0].title, true}}),
+            custom_links_->GetLinks());
 
   task_environment_.RunUntilIdle();
 }
@@ -534,14 +510,12 @@
 
   // Initialize.
   std::vector<Link> links_after_add(
-      {Link{GURL(kTestCase1[0].url), base::UTF8ToUTF16(kTestCase1[0].title),
-            true},
-       Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
+      {Link{GURL(kTestCase1[0].url), kTestCase1[0].title, true},
+       Link{GURL(kTestUrl), kTestTitle16, false}});
   ASSERT_TRUE(custom_links_->Initialize(FillTestTiles(kTestCase1)));
   ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
   // Add link.
-  ASSERT_TRUE(
-      custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+  ASSERT_TRUE(custom_links_->AddLink(GURL(kTestUrl), kTestTitle16));
   ASSERT_EQ(links_after_add, custom_links_->GetLinks());
 
   // Try to delete the added link. This should fail and not modify the list.
@@ -563,8 +537,7 @@
                                 /*expired=*/false, history::URLRows(),
                                 /*favicon_urls=*/std::set<GURL>(),
                                 /*restrict_urls=*/base::nullopt));
-  EXPECT_EQ(std::vector<Link>(
-                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+  EXPECT_EQ(std::vector<Link>({Link{GURL(kTestUrl), kTestTitle16, false}}),
             custom_links_->GetLinks());
 
   task_environment_.RunUntilIdle();
@@ -636,11 +609,9 @@
   ASSERT_EQ(FillTestLinks(kTestCase1), custom_links_->GetLinks());
   // Add link.
   std::vector<Link> links_after_add(
-      {Link{GURL(kTestCase1[0].url), base::UTF8ToUTF16(kTestCase1[0].title),
-            true},
-       Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}});
-  ASSERT_TRUE(
-      custom_links_->AddLink(GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle)));
+      {Link{GURL(kTestCase1[0].url), kTestCase1[0].title, true},
+       Link{GURL(kTestUrl), kTestTitle16, false}});
+  ASSERT_TRUE(custom_links_->AddLink(GURL(kTestUrl), kTestTitle16));
   ASSERT_EQ(links_after_add, custom_links_->GetLinks());
 
   // Try an empty history deletion. This should do nothing.
@@ -669,12 +640,11 @@
 
   // Modifying ourselves should not notify.
   EXPECT_CALL(callback, Run()).Times(0);
-  EXPECT_TRUE(custom_links_->AddLink(GURL(kTestCase1[0].url),
-                                     base::UTF8ToUTF16(kTestCase1[0].title)));
-  EXPECT_EQ(
-      std::vector<Link>({Link{GURL(kTestCase1[0].url),
-                              base::UTF8ToUTF16(kTestCase1[0].title), false}}),
-      custom_links_->GetLinks());
+  EXPECT_TRUE(
+      custom_links_->AddLink(GURL(kTestCase1[0].url), kTestCase1[0].title));
+  EXPECT_EQ(std::vector<Link>(
+                {Link{GURL(kTestCase1[0].url), kTestCase1[0].title, false}}),
+            custom_links_->GetLinks());
 
   // Modify the preference. This should notify and update the current list of
   // links.
@@ -682,8 +652,7 @@
   prefs_.SetUserPref(prefs::kCustomLinksList,
                      std::make_unique<base::Value>(
                          FillTestListStorage(kTestUrl, kTestTitle, true)));
-  EXPECT_EQ(std::vector<Link>(
-                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), true}}),
+  EXPECT_EQ(std::vector<Link>({Link{GURL(kTestUrl), kTestTitle16, true}}),
             custom_links_->GetLinks());
 }
 
@@ -703,8 +672,7 @@
                      std::make_unique<base::Value>(
                          FillTestListStorage(kTestUrl, kTestTitle, false)));
   EXPECT_TRUE(custom_links_->IsInitialized());
-  EXPECT_EQ(std::vector<Link>(
-                {Link{GURL(kTestUrl), base::UTF8ToUTF16(kTestTitle), false}}),
+  EXPECT_EQ(std::vector<Link>({Link{GURL(kTestUrl), kTestTitle16, false}}),
             custom_links_->GetLinks());
 }
 
diff --git a/components/ntp_tiles/custom_links_store_unittest.cc b/components/ntp_tiles/custom_links_store_unittest.cc
index efa4cfa..36aa913 100644
--- a/components/ntp_tiles/custom_links_store_unittest.cc
+++ b/components/ntp_tiles/custom_links_store_unittest.cc
@@ -19,8 +19,8 @@
 
 namespace {
 
-const char kTestTitle1[] = "Foo1";
-const char kTestTitle2[] = "Foo2";
+const char16_t kTestTitle1[] = u"Foo1";
+const char16_t kTestTitle2[] = u"Foo2";
 const char kTestUrl1[] = "http://foo1.com/";
 const char kTestUrl2[] = "http://foo2.com/";
 
@@ -41,8 +41,8 @@
 };
 
 TEST_F(CustomLinksStoreTest, StoreAndRetrieveLinks) {
-  std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
-      GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1), true}});
+  std::vector<CustomLinksManager::Link> initial_links(
+      {CustomLinksManager::Link{GURL(kTestUrl1), kTestTitle1, true}});
 
   custom_links_store_->StoreLinks(initial_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
@@ -52,10 +52,8 @@
 
 TEST_F(CustomLinksStoreTest, StoreEmptyList) {
   std::vector<CustomLinksManager::Link> populated_links(
-      {CustomLinksManager::Link{GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1),
-                                false},
-       CustomLinksManager::Link{GURL(kTestUrl2), base::UTF8ToUTF16(kTestTitle2),
-                                true}});
+      {CustomLinksManager::Link{GURL(kTestUrl1), kTestTitle1, false},
+       CustomLinksManager::Link{GURL(kTestUrl2), kTestTitle2, true}});
 
   custom_links_store_->StoreLinks(populated_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
@@ -68,8 +66,8 @@
 }
 
 TEST_F(CustomLinksStoreTest, ClearLinks) {
-  std::vector<CustomLinksManager::Link> initial_links({CustomLinksManager::Link{
-      GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1)}});
+  std::vector<CustomLinksManager::Link> initial_links(
+      {CustomLinksManager::Link{GURL(kTestUrl1), kTestTitle1}});
 
   custom_links_store_->StoreLinks(initial_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
@@ -83,10 +81,8 @@
 
 TEST_F(CustomLinksStoreTest, LinksSavedAfterShutdown) {
   std::vector<CustomLinksManager::Link> initial_links(
-      {CustomLinksManager::Link{GURL(kTestUrl1), base::UTF8ToUTF16(kTestTitle1),
-                                false},
-       CustomLinksManager::Link{GURL(kTestUrl2), base::UTF8ToUTF16(kTestTitle2),
-                                true}});
+      {CustomLinksManager::Link{GURL(kTestUrl1), kTestTitle1, false},
+       CustomLinksManager::Link{GURL(kTestUrl2), kTestTitle2, true}});
 
   custom_links_store_->StoreLinks(initial_links);
   std::vector<CustomLinksManager::Link> retrieved_links =
diff --git a/components/omnibox/browser/autocomplete_match_type_unittest.cc b/components/omnibox/browser/autocomplete_match_type_unittest.cc
index cef395a6..5cae4c0 100644
--- a/components/omnibox/browser/autocomplete_match_type_unittest.cc
+++ b/components/omnibox/browser/autocomplete_match_type_unittest.cc
@@ -74,7 +74,6 @@
   ASSERT_TRUE(ParseAnswer(answer_json, &answer));
   match.answer = answer;
 
-  EXPECT_EQ(kSearch + base::UTF8ToUTF16(
-                          ", answer, sunny with a chance of hail, 4 of 6"),
+  EXPECT_EQ(kSearch + u", answer, sunny with a chance of hail, 4 of 6",
             AutocompleteMatchType::ToAccessibilityLabel(match, kSearch, 3, 6));
 }
diff --git a/components/omnibox/browser/clipboard_provider_unittest.cc b/components/omnibox/browser/clipboard_provider_unittest.cc
index f7c01fda..8a5fb0c 100644
--- a/components/omnibox/browser/clipboard_provider_unittest.cc
+++ b/components/omnibox/browser/clipboard_provider_unittest.cc
@@ -36,8 +36,8 @@
 
 const char kCurrentURL[] = "http://example.com/current";
 const char kClipboardURL[] = "http://example.com/clipboard";
-const char kClipboardText[] = "Search for me";
-const char kClipboardTitleText[] = "\"Search for me\"";
+const char16_t kClipboardText[] = u"Search for me";
+const char16_t kClipboardTitleText[] = u"\"Search for me\"";
 
 class CreateMatchWithContentCallbackWaiter {
  public:
@@ -185,13 +185,11 @@
   auto template_url_service = std::make_unique<TemplateURLService>(
       /*initializers=*/nullptr, /*count=*/0);
   client_->set_template_url_service(std::move(template_url_service));
-  SetClipboardText(base::UTF8ToUTF16(kClipboardText));
+  SetClipboardText(kClipboardText);
   provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
   ASSERT_GE(provider_->matches().size(), 1U);
-  EXPECT_EQ(base::UTF8ToUTF16(kClipboardTitleText),
-            provider_->matches().back().contents);
-  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText),
-            provider_->matches().back().fill_into_edit);
+  EXPECT_EQ(kClipboardTitleText, provider_->matches().back().contents);
+  EXPECT_EQ(kClipboardText, provider_->matches().back().fill_into_edit);
   EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_TEXT,
             provider_->matches().back().type);
 }
@@ -218,7 +216,7 @@
   auto template_url_service = std::make_unique<TemplateURLService>(
       /*initializers=*/nullptr, /*count=*/0);
   client_->set_template_url_service(std::move(template_url_service));
-  SetClipboardText(base::UTF8ToUTF16(kClipboardText));
+  SetClipboardText(kClipboardText);
   provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
   ASSERT_EQ(provider_->matches().size(), 1U);
 
@@ -253,7 +251,7 @@
   auto template_url_service = std::make_unique<TemplateURLService>(
       /*initializers=*/nullptr, /*count=*/0);
   client_->set_template_url_service(std::move(template_url_service));
-  SetClipboardText(base::UTF8ToUTF16(kClipboardText));
+  SetClipboardText(kClipboardText);
   provider_->Start(CreateAutocompleteInput(OmniboxFocusType::ON_FOCUS), false);
   ASSERT_GE(provider_->matches().size(), 1U);
   EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_TEXT,
@@ -299,7 +297,7 @@
 }
 
 TEST_F(ClipboardProviderTest, CreateTextMatchWithContent) {
-  SetClipboardText(base::UTF8ToUTF16(kClipboardText));
+  SetClipboardText(kClipboardText);
   auto template_url_service = std::make_unique<TemplateURLService>(
       /*initializers=*/nullptr, /*count=*/0);
   client_->set_template_url_service(std::move(template_url_service));
@@ -307,8 +305,8 @@
   CreateMatchWithContentCallbackWaiter waiter(provider_, &match);
   waiter.WaitForMatchUpdated();
 
-  EXPECT_EQ(base::UTF8ToUTF16(kClipboardTitleText), match.contents);
-  EXPECT_EQ(base::UTF8ToUTF16(kClipboardText), match.fill_into_edit);
+  EXPECT_EQ(kClipboardTitleText, match.contents);
+  EXPECT_EQ(kClipboardText, match.fill_into_edit);
   EXPECT_EQ(AutocompleteMatchType::CLIPBOARD_TEXT, match.type);
 }
 
diff --git a/components/omnibox/browser/history_quick_provider_unittest.cc b/components/omnibox/browser/history_quick_provider_unittest.cc
index 26ee153..b7610a3 100644
--- a/components/omnibox/browser/history_quick_provider_unittest.cc
+++ b/components/omnibox/browser/history_quick_provider_unittest.cc
@@ -559,9 +559,9 @@
       "%95%8C%E5%A4%A7%E6%88%A6#.E3.83.B4.E3.82.A7.E3.83.AB.E3.82.B5.E3.82."
       "A4.E3.83.A6.E4.BD.93.E5.88.B6");
   RunTest(u"第二 e3", false, expected_urls, false,
-          base::UTF8ToUTF16("ja.wikipedia.org/wiki/第二次世界大戦#.E3.83.B4.E3."
-                            "82.A7.E3.83.AB.E3.82.B5.E3.82.A4.E3.83.A6.E4.BD."
-                            "93.E5.88.B6"),
+          u"ja.wikipedia.org/wiki/第二次世界大戦#.E3.83.B4.E3."
+          u"82.A7.E3.83.AB.E3.82.B5.E3.82.A4.E3.83.A6.E4.BD."
+          u"93.E5.88.B6",
           std::u16string());
 #if DCHECK_IS_ON()
   ac_matches()[0].Validate();
@@ -813,10 +813,9 @@
 }
 
 ScoredHistoryMatch BuildScoredHistoryMatch(const std::string& url_text,
-                                           const std::string& input_term) {
+                                           const std::u16string& input_term) {
   return ScoredHistoryMatch(history::URLRow(GURL(url_text)), VisitInfoVector(),
-                            base::UTF8ToUTF16(input_term),
-                            String16Vector(1, base::UTF8ToUTF16(input_term)),
+                            input_term, String16Vector(1, input_term),
                             WordStarts(1, 0), RowWordStarts(), false, 0,
                             base::Time());
 }
@@ -827,7 +826,7 @@
                           TestSchemeClassifier());
   provider().Start(input, false);
   ScoredHistoryMatch history_match =
-      BuildScoredHistoryMatch("http://www.facebook.com", "face");
+      BuildScoredHistoryMatch("http://www.facebook.com", u"face");
 
   AutocompleteMatch match = provider().QuickMatchToACMatch(history_match, 100);
   EXPECT_EQ(u"facebook.com", match.contents);
@@ -840,7 +839,7 @@
                           TestSchemeClassifier());
   provider().Start(input, false);
   ScoredHistoryMatch history_match =
-      BuildScoredHistoryMatch("http://www.facebook.com", "http://face");
+      BuildScoredHistoryMatch("http://www.facebook.com", u"http://face");
 
   AutocompleteMatch match = provider().QuickMatchToACMatch(history_match, 100);
   EXPECT_EQ(u"http://facebook.com", match.contents);
@@ -853,7 +852,7 @@
                           TestSchemeClassifier());
   provider().Start(input, false);
   ScoredHistoryMatch history_match =
-      BuildScoredHistoryMatch("http://www.facebook.com", "ht");
+      BuildScoredHistoryMatch("http://www.facebook.com", u"ht");
   history_match.match_in_scheme = true;
 
   AutocompleteMatch match = provider().QuickMatchToACMatch(history_match, 100);
@@ -867,7 +866,7 @@
                           TestSchemeClassifier());
   provider().Start(input, false);
   ScoredHistoryMatch history_match =
-      BuildScoredHistoryMatch("https://www.facebook.com", "https://face");
+      BuildScoredHistoryMatch("https://www.facebook.com", u"https://face");
 
   AutocompleteMatch match = provider().QuickMatchToACMatch(history_match, 100);
   EXPECT_EQ(u"https://facebook.com", match.contents);
@@ -879,7 +878,7 @@
                           TestSchemeClassifier());
   provider().Start(input, false);
   ScoredHistoryMatch history_match =
-      BuildScoredHistoryMatch("https://www.facebook.com", "face");
+      BuildScoredHistoryMatch("https://www.facebook.com", u"face");
 
   AutocompleteMatch match = provider().QuickMatchToACMatch(history_match, 100);
   EXPECT_EQ(u"facebook.com", match.contents);
diff --git a/components/password_manager/core/browser/votes_uploader_unittest.cc b/components/password_manager/core/browser/votes_uploader_unittest.cc
index 3b29f72..9475312 100644
--- a/components/password_manager/core/browser/votes_uploader_unittest.cc
+++ b/components/password_manager/core/browser/votes_uploader_unittest.cc
@@ -374,8 +374,8 @@
   FormStructure form_structure(form);
   VotesUploader votes_uploader(&client_, true);
   votes_uploader.GeneratePasswordAttributesVote(
-      base::UTF8ToUTF16("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"
-                        "stuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"),
+      u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"
+      u"stuvwxyz!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
       &form_structure);
   base::Optional<std::pair<PasswordAttribute, bool>> vote =
       form_structure.get_password_attributes_vote();
@@ -386,14 +386,13 @@
   // Checks that password attributes vote is not generated if the password has
   // non-ascii characters.
   for (const auto* password :
-       {"пароль1", "パスワード", "münchen", "סיסמה-A", "Σ-12345",
-        "գաղտնաբառըTTT", "Slaptažodis", "密碼", "كلمهالسر", "mậtkhẩu!",
-        "ລະຫັດຜ່ານ-l", "စကားဝှက်ကို3", "პაროლი", "पारण शब्द"}) {
+       {u"пароль1", u"パスワード", u"münchen", u"סיסמה-A", u"Σ-12345",
+        u"գաղտնաբառըTTT", u"Slaptažodis", u"密碼", u"كلمهالسر", u"mậtkhẩu!",
+        u"ລະຫັດຜ່ານ-l", u"စကားဝှက်ကို3", u"პაროლი", u"पारण शब्द"}) {
     FormData form;
     FormStructure form_structure(form);
     VotesUploader votes_uploader(&client_, true);
-    votes_uploader.GeneratePasswordAttributesVote(base::UTF8ToUTF16(password),
-                                                  &form_structure);
+    votes_uploader.GeneratePasswordAttributesVote(password, &form_structure);
     base::Optional<std::pair<PasswordAttribute, bool>> vote =
         form_structure.get_password_attributes_vote();
 
diff --git a/components/payments/content/payment_credential_enrollment_model_unittest.cc b/components/payments/content/payment_credential_enrollment_model_unittest.cc
index 1a56b4c4..d113285 100644
--- a/components/payments/content/payment_credential_enrollment_model_unittest.cc
+++ b/components/payments/content/payment_credential_enrollment_model_unittest.cc
@@ -15,9 +15,10 @@
   PaymentCredentialEnrollmentModel model;
 
   std::u16string title(u"Use Touch ID to verify and complete your purchase?");
-  std::u16string description(base::UTF8ToUTF16(
-      "Save payment information to this device and skip bank verification next "
-      "time when you use Touch ID to verify your payment with Visa ••••4444."));
+  std::u16string description(
+      u"Save payment information to this device and skip bank verification "
+      u"next "
+      u"time when you use Touch ID to verify your payment with Visa ••••4444.");
   std::u16string accept_button_label(u"Use Touch ID");
   std::u16string cancel_button_label(u"No thanks");
 
diff --git a/components/permissions/contexts/geolocation_permission_context_android.h b/components/permissions/contexts/geolocation_permission_context_android.h
index 5b18bfa9..894ea97 100644
--- a/components/permissions/contexts/geolocation_permission_context_android.h
+++ b/components/permissions/contexts/geolocation_permission_context_android.h
@@ -28,7 +28,7 @@
 #include "components/location/android/location_settings.h"
 #include "components/location/android/location_settings_dialog_context.h"
 #include "components/location/android/location_settings_dialog_outcome.h"
-#include "components/permissions/contexts//geolocation_permission_context.h"
+#include "components/permissions/contexts/geolocation_permission_context.h"
 #include "components/permissions/permission_request_id.h"
 
 namespace content {
diff --git a/components/policy/core/browser/policy_conversions_client.cc b/components/policy/core/browser/policy_conversions_client.cc
index 26ea5e1..fd885ded 100644
--- a/components/policy/core/browser/policy_conversions_client.cc
+++ b/components/policy/core/browser/policy_conversions_client.cc
@@ -171,7 +171,9 @@
   if (policy.source == POLICY_SOURCE_MERGED) {
     bool policy_has_unmerged_source = false;
     for (const auto& conflict : policy.conflicts) {
-      if (PolicyMerger::ConflictCanBeMerged(conflict.entry(), policy))
+      if (PolicyMerger::ConflictCanBeMerged(
+              conflict.entry(), policy,
+              /*is_user_cloud_merging_enabled=*/false))
         continue;
       policy_has_unmerged_source = true;
       break;
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
index 5223b42..c69107bb 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_base.cc
@@ -62,6 +62,17 @@
 #endif
   DecodeProtoFieldsPerProfile(*payload, external_data_manager(), policy_source_,
                               policy_scope_, &policy_map_, filter);
+
+  if (policy_data->user_affiliation_ids_size() > 0) {
+    policy_map_.SetUserAffiliationIds(
+        {policy_data->user_affiliation_ids().begin(),
+         policy_data->user_affiliation_ids().end()});
+  }
+  if (policy_data->device_affiliation_ids_size() > 0) {
+    policy_map_.SetDeviceAffiliationIds(
+        {policy_data->device_affiliation_ids().begin(),
+         policy_data->device_affiliation_ids().end()});
+  }
   policy_ = std::move(policy_data);
   policy_signature_public_key_ = policy_signature_public_key;
 }
diff --git a/components/policy/core/common/policy_map.cc b/components/policy/core/common/policy_map.cc
index 6906237..42c3b9d9 100644
--- a/components/policy/core/common/policy_map.cc
+++ b/components/policy/core/common/policy_map.cc
@@ -13,6 +13,7 @@
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
+#include "components/policy/core/common/cloud/affiliation.h"
 #include "components/policy/core/common/policy_merger.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -41,6 +42,16 @@
   return result;
 }
 
+// Inserts additional user affiliation IDs to the existing set.
+base::flat_set<std::string> CombineIds(
+    const base::flat_set<std::string>& ids_first,
+    const base::flat_set<std::string>& ids_second) {
+  base::flat_set<std::string> combined_ids;
+  combined_ids.insert(ids_first.begin(), ids_first.end());
+  combined_ids.insert(ids_second.begin(), ids_second.end());
+  return combined_ids;
+}
+
 }  // namespace
 
 PolicyMap::Entry::Entry() = default;
@@ -337,6 +348,9 @@
   for (const auto& it : map_)
     clone.Set(it.first, it.second.DeepCopy());
 
+  clone.SetUserAffiliationIds(user_affiliation_ids_);
+  clone.SetDeviceAffiliationIds(device_affiliation_ids_);
+
   return clone;
 }
 
@@ -376,6 +390,11 @@
     if (other_is_higher_priority)
       *current_policy = std::move(other_policy);
   }
+
+  SetUserAffiliationIds(
+      CombineIds(GetUserAffiliationIds(), other.GetUserAffiliationIds()));
+  SetDeviceAffiliationIds(
+      CombineIds(GetDeviceAffiliationIds(), other.GetDeviceAffiliationIds()));
 }
 
 void PolicyMap::MergeValues(const std::vector<PolicyMerger*>& mergers) {
@@ -446,4 +465,26 @@
   }
 }
 
+bool PolicyMap::IsUserAffiliated() const {
+  return IsAffiliated(user_affiliation_ids_, device_affiliation_ids_);
+}
+
+void PolicyMap::SetUserAffiliationIds(
+    const base::flat_set<std::string>& user_ids) {
+  user_affiliation_ids_ = {user_ids.begin(), user_ids.end()};
+}
+
+const base::flat_set<std::string>& PolicyMap::GetUserAffiliationIds() const {
+  return user_affiliation_ids_;
+}
+
+void PolicyMap::SetDeviceAffiliationIds(
+    const base::flat_set<std::string>& device_ids) {
+  device_affiliation_ids_ = {device_ids.begin(), device_ids.end()};
+}
+
+const base::flat_set<std::string>& PolicyMap::GetDeviceAffiliationIds() const {
+  return device_affiliation_ids_;
+}
+
 }  // namespace policy
diff --git a/components/policy/core/common/policy_map.h b/components/policy/core/common/policy_map.h
index 8de7d7c..c1297dcf 100644
--- a/components/policy/core/common/policy_map.h
+++ b/components/policy/core/common/policy_map.h
@@ -13,6 +13,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/containers/flat_set.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/optional.h"
@@ -263,6 +264,22 @@
                 PolicyScope scope,
                 PolicySource source);
 
+  // Returns True if at least one shared ID is found in the user and device
+  // affiliation ID sets.
+  bool IsUserAffiliated() const;
+
+  // Populates the set containing user affiliation ID strings.
+  void SetUserAffiliationIds(const base::flat_set<std::string>& user_ids);
+
+  // Returns the set containing user affiliation ID strings.
+  const base::flat_set<std::string>& GetUserAffiliationIds() const;
+
+  // Populates the set containing device affiliation ID strings.
+  void SetDeviceAffiliationIds(const base::flat_set<std::string>& device_ids);
+
+  // Returns the set containing device affiliation ID strings.
+  const base::flat_set<std::string>& GetDeviceAffiliationIds() const;
+
   bool Equals(const PolicyMap& other) const;
   bool empty() const;
   size_t size() const;
@@ -293,8 +310,12 @@
       bool deletion_value);
 
   PolicyMapType map_;
-};
 
+  // Affiliation
+  bool is_user_affiliated_ = false;
+  base::flat_set<std::string> user_affiliation_ids_;
+  base::flat_set<std::string> device_affiliation_ids_;
+};
 }  // namespace policy
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_POLICY_MAP_H_
diff --git a/components/policy/core/common/policy_map_unittest.cc b/components/policy/core/common/policy_map_unittest.cc
index 68ebdee..ae1f0c3 100644
--- a/components/policy/core/common/policy_map_unittest.cc
+++ b/components/policy/core/common/policy_map_unittest.cc
@@ -36,7 +36,7 @@
 const char kTestPolicyName8[] = "policy.test.8";
 
 // Dummy error message.
-const char kTestError[] = "Test error message";
+const char16_t kTestError[] = u"Test error message";
 
 // Utility functions for the tests.
 void SetPolicy(PolicyMap* map, const char* name, base::Value value) {
@@ -83,20 +83,19 @@
   EXPECT_TRUE(expected_b.Equals(map.GetValue(kTestPolicyName1)));
   SetPolicy(&map, kTestPolicyName1, CreateExternalDataFetcher("dummy"));
   map.AddMessage(kTestPolicyName1, PolicyMap::MessageType::kError,
-                 IDS_POLICY_STORE_STATUS_VALIDATION_ERROR,
-                 {base::UTF8ToUTF16(kTestError)});
+                 IDS_POLICY_STORE_STATUS_VALIDATION_ERROR, {kTestError});
   EXPECT_FALSE(map.GetValue(kTestPolicyName1));
   const PolicyMap::Entry* entry = map.Get(kTestPolicyName1);
   ASSERT_TRUE(entry != nullptr);
   EXPECT_EQ(POLICY_LEVEL_MANDATORY, entry->level);
   EXPECT_EQ(POLICY_SCOPE_USER, entry->scope);
   EXPECT_EQ(POLICY_SOURCE_CLOUD, entry->source);
-  std::string error_string = base::StrCat({"Validation error: ", kTestError});
+  std::u16string error_string =
+      base::StrCat({u"Validation error: ", kTestError});
   PolicyMap::Entry::L10nLookupFunction lookup = base::BindRepeating(
       static_cast<std::u16string (*)(int)>(&base::NumberToString16));
-  EXPECT_EQ(
-      base::UTF8ToUTF16(error_string),
-      entry->GetLocalizedMessages(PolicyMap::MessageType::kError, lookup));
+  EXPECT_EQ(error_string, entry->GetLocalizedMessages(
+                              PolicyMap::MessageType::kError, lookup));
   EXPECT_TRUE(
       ExternalDataFetcher::Equals(entry->external_data_fetcher.get(),
                                   CreateExternalDataFetcher("dummy").get()));
@@ -137,28 +136,27 @@
   EXPECT_EQ(u"1234\n5678", entry1->GetLocalizedMessages(
                                PolicyMap::MessageType::kError, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("This policy is deprecated. You should use the "
-                        "SomeNewPolicy policy instead."),
+      u"This policy is deprecated. You should use the "
+      u"SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kError, lookup));
   map.AddMessage(kTestPolicyName2, PolicyMap::MessageType::kError, 1357);
   EXPECT_EQ(u"1234\n5678", entry1->GetLocalizedMessages(
                                PolicyMap::MessageType::kError, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
-                        "the SomeNewPolicy policy instead."),
+      u"1357\nThis policy is deprecated. You should use "
+      u"the SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kError, lookup));
   // Test adding Error message with placeholder replacement (two args)
   map.AddMessage(kTestPolicyName1, PolicyMap::MessageType::kError,
                  IDS_POLICY_DLP_CLIPBOARD_BLOCKED_ON_COPY_VM,
                  {u"SomeSource", u"SomeDestination"});
   EXPECT_EQ(
-      base::UTF8ToUTF16(
-          "1234\n5678\nSharing from SomeSource to SomeDestination has "
-          "been blocked by administrator policy"),
+      u"1234\n5678\nSharing from SomeSource to SomeDestination has "
+      u"been blocked by administrator policy",
       entry1->GetLocalizedMessages(PolicyMap::MessageType::kError, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
-                        "the SomeNewPolicy policy instead."),
+      u"1357\nThis policy is deprecated. You should use "
+      u"the SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kError, lookup));
 
   // Ensure other message types are empty
@@ -192,28 +190,27 @@
   EXPECT_EQ(u"1234\n5678", entry1->GetLocalizedMessages(
                                PolicyMap::MessageType::kWarning, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("This policy is deprecated. You should use the "
-                        "SomeNewPolicy policy instead."),
+      u"This policy is deprecated. You should use the "
+      u"SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kWarning, lookup));
   entry2->AddMessage(PolicyMap::MessageType::kWarning, 1357);
   EXPECT_EQ(u"1234\n5678", entry1->GetLocalizedMessages(
                                PolicyMap::MessageType::kWarning, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
-                        "the SomeNewPolicy policy instead."),
+      u"1357\nThis policy is deprecated. You should use "
+      u"the SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kWarning, lookup));
   // Test adding Warning message with placeholder replacement (two args)
   entry1->AddMessage(PolicyMap::MessageType::kWarning,
                      IDS_POLICY_DLP_CLIPBOARD_BLOCKED_ON_COPY_VM,
                      {u"SomeSource", u"SomeDestination"});
   EXPECT_EQ(
-      base::UTF8ToUTF16(
-          "1234\n5678\nSharing from SomeSource to SomeDestination has "
-          "been blocked by administrator policy"),
+      u"1234\n5678\nSharing from SomeSource to SomeDestination has "
+      u"been blocked by administrator policy",
       entry1->GetLocalizedMessages(PolicyMap::MessageType::kWarning, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
-                        "the SomeNewPolicy policy instead."),
+      u"1357\nThis policy is deprecated. You should use "
+      u"the SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kWarning, lookup));
 
   // Ensure other message types are empty
@@ -247,28 +244,27 @@
   EXPECT_EQ(u"1234\n5678", entry1->GetLocalizedMessages(
                                PolicyMap::MessageType::kInfo, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("This policy is deprecated. You should use the "
-                        "SomeNewPolicy policy instead."),
+      u"This policy is deprecated. You should use the "
+      u"SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kInfo, lookup));
   entry2->AddMessage(PolicyMap::MessageType::kInfo, 1357);
   EXPECT_EQ(u"1234\n5678", entry1->GetLocalizedMessages(
                                PolicyMap::MessageType::kInfo, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
-                        "the SomeNewPolicy policy instead."),
+      u"1357\nThis policy is deprecated. You should use "
+      u"the SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kInfo, lookup));
   // Test adding Info message with placeholder replacement (two args)
   entry1->AddMessage(PolicyMap::MessageType::kInfo,
                      IDS_POLICY_DLP_CLIPBOARD_BLOCKED_ON_COPY_VM,
                      {u"SomeSource", u"SomeDestination"});
   EXPECT_EQ(
-      base::UTF8ToUTF16(
-          "1234\n5678\nSharing from SomeSource to SomeDestination has "
-          "been blocked by administrator policy"),
+      u"1234\n5678\nSharing from SomeSource to SomeDestination has "
+      u"been blocked by administrator policy",
       entry1->GetLocalizedMessages(PolicyMap::MessageType::kInfo, lookup));
   EXPECT_EQ(
-      base::UTF8ToUTF16("1357\nThis policy is deprecated. You should use "
-                        "the SomeNewPolicy policy instead."),
+      u"1357\nThis policy is deprecated. You should use "
+      u"the SomeNewPolicy policy instead.",
       entry2->GetLocalizedMessages(PolicyMap::MessageType::kInfo, lookup));
 
   // Ensure other message types are empty
@@ -1172,4 +1168,28 @@
   EXPECT_TRUE(policies.GetUntrusted("b")->ignored());
 }
 
+TEST_F(PolicyMapTest, Affiliation) {
+  PolicyMap policies;
+  EXPECT_FALSE(policies.IsUserAffiliated());
+
+  base::flat_set<std::string> user_ids;
+  user_ids.insert("a");
+  base::flat_set<std::string> device_ids;
+  device_ids.insert("b");
+  policies.SetUserAffiliationIds(user_ids);
+  policies.SetUserAffiliationIds(device_ids);
+
+  // Affiliation check fails because user and device IDs don't have at least one
+  // ID in common.
+  EXPECT_FALSE(policies.IsUserAffiliated());
+
+  user_ids.insert("b");
+  device_ids.insert("c");
+  policies.SetUserAffiliationIds(user_ids);
+  policies.SetDeviceAffiliationIds(device_ids);
+
+  // Affiliation check succeeds now that 'a' is present in user and device IDs.
+  EXPECT_TRUE(policies.IsUserAffiliated());
+}
+
 }  // namespace policy
diff --git a/components/policy/core/common/policy_merger.cc b/components/policy/core/common/policy_merger.cc
index 98071f1a..0480ab7 100644
--- a/components/policy/core/common/policy_merger.cc
+++ b/components/policy/core/common/policy_merger.cc
@@ -27,18 +27,43 @@
 }  // namespace
 
 // static
-bool PolicyMerger::ConflictCanBeMerged(const PolicyMap::Entry& conflict,
-                                       const PolicyMap::Entry& policy) {
+bool PolicyMerger::ConflictCanBeMerged(
+    const PolicyMap::Entry& conflict,
+    const PolicyMap::Entry& policy,
+    const bool is_user_cloud_merging_enabled) {
+  if (conflict.ignored() ||
+      conflict.source == POLICY_SOURCE_ENTERPRISE_DEFAULT ||
+      conflict.level != policy.level)
+    return false;
+
+  // If the policies have matching scope and are non-user, they can be merged.
+  if (conflict.scope == policy.scope && conflict.scope != POLICY_SCOPE_USER)
+    return true;
+
   // On desktop, the user cloud policy potentially comes from a different
-  // domain than e.g. GPO policy or machine-level cloud policy, so prevent
-  // merging user cloud policy with other policy sources.
+  // domain than e.g. GPO policy or machine-level cloud policy. Merging a user
+  // cloud policy with policies from other sources is only permitted if both of
+  // the following conditions are met:
+  //   1. The CloudUserPolicyMerge is set to True.
+  //   2. The user is affiliated with the machine-level cloud policy provider.
   const bool is_conflict_user_cloud_policy =
       conflict.scope == POLICY_SCOPE_USER &&
       (conflict.source == POLICY_SOURCE_CLOUD ||
        conflict.source == POLICY_SOURCE_PRIORITY_CLOUD);
-  return !is_conflict_user_cloud_policy && !conflict.ignored() &&
-         conflict.source != POLICY_SOURCE_ENTERPRISE_DEFAULT &&
-         conflict.level == policy.level && conflict.scope == policy.scope;
+
+  // Merging of user-level GPO policies is not permitted to prevent unexpected
+  // behavior. If such merging is desired, it will be implemented in a similar
+  // way as user cloud merging.
+  const bool is_conflict_user_platform_policy =
+      conflict.scope == POLICY_SCOPE_USER &&
+      conflict.source == POLICY_SOURCE_PLATFORM;
+
+  const bool is_scope_overriden =
+      is_conflict_user_cloud_policy && is_user_cloud_merging_enabled;
+
+  return !is_conflict_user_platform_policy &&
+         (!is_conflict_user_cloud_policy || is_user_cloud_merging_enabled) &&
+         (conflict.scope == policy.scope || is_scope_overriden);
 }
 
 PolicyMerger::PolicyMerger() = default;
@@ -60,6 +85,10 @@
   }
 }
 
+void PolicyListMerger::SetAllowUserCloudPolicyMerging(bool allowed) {
+  allow_user_cloud_policy_merging_ = allowed;
+}
+
 bool PolicyListMerger::CanMerge(const std::string& policy_name,
                                 PolicyMap::Entry& policy) const {
   if (policy.source == POLICY_SOURCE_MERGED)
@@ -80,6 +109,10 @@
   return true;
 }
 
+bool PolicyListMerger::AllowUserCloudPolicyMerging() const {
+  return allow_user_cloud_policy_merging_;
+}
+
 void PolicyListMerger::DoMerge(PolicyMap::Entry* policy) const {
   std::vector<const base::Value*> merged_values;
   auto compare_value_ptr = [](const base::Value* a, const base::Value* b) {
@@ -99,7 +132,8 @@
   // Concatenates the values from accepted conflicting sources to the policy
   // value while avoiding duplicates.
   for (const auto& it : policy->conflicts) {
-    if (!PolicyMerger::ConflictCanBeMerged(it.entry(), *policy)) {
+    if (!PolicyMerger::ConflictCanBeMerged(it.entry(), *policy,
+                                           AllowUserCloudPolicyMerging())) {
       continue;
     }
 
@@ -146,6 +180,10 @@
   allowed_policies_ = std::move(allowed_policies);
 }
 
+void PolicyDictionaryMerger::SetAllowUserCloudPolicyMerging(bool allowed) {
+  allow_user_cloud_policy_merging_ = allowed;
+}
+
 bool PolicyDictionaryMerger::CanMerge(const std::string& policy_name,
                                       PolicyMap::Entry& policy) const {
   if (policy.source == POLICY_SOURCE_MERGED)
@@ -176,6 +214,10 @@
   return true;
 }
 
+bool PolicyDictionaryMerger::AllowUserCloudPolicyMerging() const {
+  return allow_user_cloud_policy_merging_;
+}
+
 void PolicyDictionaryMerger::DoMerge(PolicyMap::Entry* policy) const {
   // Keep priority sorted list of potential merge targets.
   std::vector<const PolicyMap::Entry*> policies;
@@ -193,7 +235,8 @@
 
   // Merges all the keys from the policies from different sources.
   for (const auto* it : policies) {
-    if (it != policy && !PolicyMerger::ConflictCanBeMerged(*it, *policy))
+    if (it != policy && !PolicyMerger::ConflictCanBeMerged(
+                            *it, *policy, AllowUserCloudPolicyMerging()))
       continue;
 
     const base::DictionaryValue* dict = nullptr;
diff --git a/components/policy/core/common/policy_merger.h b/components/policy/core/common/policy_merger.h
index e991ab7..b5ab830e 100644
--- a/components/policy/core/common/policy_merger.h
+++ b/components/policy/core/common/policy_merger.h
@@ -22,8 +22,13 @@
 class POLICY_EXPORT PolicyMerger {
  public:
   PolicyMerger();
+
+  // Determines if a policy value is eligible for merging depending on several
+  // factors including its scope, source, and level.
   static bool ConflictCanBeMerged(const PolicyMap::Entry& conflict,
-                                  const PolicyMap::Entry& policy);
+                                  const PolicyMap::Entry& policy,
+                                  const bool is_user_cloud_merging_enabled);
+
   virtual ~PolicyMerger();
   virtual void Merge(PolicyMap::PolicyMapType* policies) const = 0;
 };
@@ -39,17 +44,25 @@
   // Merges the list policies from |policies| that have multiple sources.
   void Merge(PolicyMap::PolicyMapType* policies) const override;
 
+  // Sets the variable used for determining if user cloud merging is enabled.
+  void SetAllowUserCloudPolicyMerging(bool allowed);
+
  private:
   // Returns True if |policy_name| is in the list of policies to merge and if
   // |policy| has values from different sources that share the same level,
   // target and scope.
   bool CanMerge(const std::string& policy_name, PolicyMap::Entry& policy) const;
 
+  // Returns True if user cloud policy merging is enabled through the
+  // CloudUserPolicyMerge policy and the current user is affiliated.
+  bool AllowUserCloudPolicyMerging() const;
+
   // Merges the values of |policy| if they come from multiple sources. Keeps
   // track of the original values by leaving them as conflicts. |policy| must
   // remain unchanged if there is nothing to merge.
   void DoMerge(PolicyMap::Entry* policy) const;
 
+  bool allow_user_cloud_policy_merging_ = false;
   const base::flat_set<std::string> policies_to_merge_;
 
   DISALLOW_COPY_AND_ASSIGN(PolicyListMerger);
@@ -70,17 +83,25 @@
   void SetAllowedPoliciesForTesting(
       base::flat_set<std::string> allowed_policies);
 
+  // Sets the variable used for determining if user cloud merging is enabled.
+  void SetAllowUserCloudPolicyMerging(bool allowed);
+
  private:
   // Returns True if |policy_name| is in the list of policies to merge and if
   // |policy| has values from different sources that share the same level,
   // target and scope.
   bool CanMerge(const std::string& policy_name, PolicyMap::Entry& policy) const;
 
+  // Returns True if user cloud policy merging is enabled through the
+  // CloudUserPolicyMerge policy and the current user is affiliated.
+  bool AllowUserCloudPolicyMerging() const;
+
   // Merges the values of |policy| if they come from multiple sources. Keeps
   // track of the original values by leaving them as conflicts. |policy| stays
   // intact if there is nothing to merge.
   void DoMerge(PolicyMap::Entry* policy) const;
 
+  bool allow_user_cloud_policy_merging_ = false;
   const base::flat_set<std::string> policies_to_merge_;
   base::flat_set<std::string> allowed_policies_;
 
diff --git a/components/policy/core/common/policy_service_impl.cc b/components/policy/core/common/policy_service_impl.cc
index 79c10d7..5a1cbd6f 100644
--- a/components/policy/core/common/policy_service_impl.cc
+++ b/components/policy/core/common/policy_service_impl.cc
@@ -408,6 +408,16 @@
   PolicyDictionaryMerger policy_dictionary_merger(
       std::move(policy_dictionaries_to_merge));
 
+  // Pass affiliation and CloudUserPolicyMerge values to both mergers.
+  const bool is_affiliated = chrome_policies.IsUserAffiliated();
+  const bool is_user_cloud_merging_enabled =
+      chrome_policies.GetValue(key::kCloudUserPolicyMerge) &&
+      chrome_policies.GetValue(key::kCloudUserPolicyMerge)->GetBool();
+  policy_list_merger.SetAllowUserCloudPolicyMerging(
+      is_affiliated && is_user_cloud_merging_enabled);
+  policy_dictionary_merger.SetAllowUserCloudPolicyMerging(
+      is_affiliated && is_user_cloud_merging_enabled);
+
   std::vector<PolicyMerger*> mergers{&policy_list_merger,
                                      &policy_dictionary_merger};
 
diff --git a/components/policy/core/common/policy_service_impl_unittest.cc b/components/policy/core/common/policy_service_impl_unittest.cc
index aaef42a..faf27ea 100644
--- a/components/policy/core/common/policy_service_impl_unittest.cc
+++ b/components/policy/core/common/policy_service_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/callback_helpers.h"
+#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
@@ -37,6 +38,12 @@
 const char kExtension[] = "extension-id";
 const char kSameLevelPolicy[] = "policy-same-level-and-scope";
 const char kDiffLevelPolicy[] = "chrome-diff-level-and-scope";
+const std::string kUrl1 = "example.com";
+const std::string kUrl2 = "gmail.com";
+const std::string kUrl3 = "google.com";
+const std::string kUrl4 = "youtube.com";
+const std::string kAffiliationId1 = "abc";
+const std::string kAffiliationId2 = "def";
 
 // Helper to compare the arguments to an EXPECT_CALL of OnPolicyUpdated() with
 // their expected values.
@@ -1117,19 +1124,20 @@
   EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
   EXPECT_TRUE(VerifyPolicies(extension_namespace, expected_extension));
 }
+
 TEST_F(PolicyServiceTest, DictionaryPoliciesMerging) {
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
 
   base::Value dict1(base::Value::Type::DICTIONARY);
-  dict1.SetBoolKey("google.com", false);
-  dict1.SetBoolKey("gmail.com", true);
+  dict1.SetBoolKey(kUrl3, false);
+  dict1.SetBoolKey(kUrl2, true);
   base::Value dict2 = base::Value(base::Value::Type::DICTIONARY);
-  dict2.SetBoolKey("example.com", true);
-  dict2.SetBoolKey("gmail.com", false);
+  dict2.SetBoolKey(kUrl1, true);
+  dict2.SetBoolKey(kUrl2, false);
   base::Value result = base::Value(base::Value::Type::DICTIONARY);
-  result.SetBoolKey("google.com", false);
-  result.SetBoolKey("gmail.com", false);
-  result.SetBoolKey("example.com", true);
+  result.SetBoolKey(kUrl3, false);
+  result.SetBoolKey(kUrl2, false);
+  result.SetBoolKey(kUrl1, true);
 
   std::unique_ptr<base::Value> policy =
       std::make_unique<base::Value>(base::Value::Type::LIST);
@@ -1176,15 +1184,15 @@
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
 
   base::Value list1(base::Value::Type::LIST);
-  list1.Append(base::Value("google.com"));
-  list1.Append(base::Value("gmail.com"));
+  list1.Append(base::Value(kUrl3));
+  list1.Append(base::Value(kUrl2));
   base::Value list2 = base::Value(base::Value::Type::LIST);
-  list2.Append(base::Value("example.com"));
-  list2.Append(base::Value("gmail.com"));
+  list2.Append(base::Value(kUrl1));
+  list2.Append(base::Value(kUrl2));
   base::Value result = base::Value(base::Value::Type::LIST);
-  result.Append(base::Value("google.com"));
-  result.Append(base::Value("gmail.com"));
-  result.Append(base::Value("example.com"));
+  result.Append(base::Value(kUrl3));
+  result.Append(base::Value(kUrl2));
+  result.Append(base::Value(kUrl1));
 
   std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
   policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
@@ -1229,14 +1237,14 @@
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
 
   base::Value list1(base::Value::Type::LIST);
-  list1.Append(base::Value("google.com"));
+  list1.Append(base::Value(kUrl3));
   base::Value list2(base::Value::Type::LIST);
-  list2.Append(base::Value("example.com"));
+  list2.Append(base::Value(kUrl1));
   base::Value list3(base::Value::Type::LIST);
-  list3.Append(base::Value("example_xyz.com"));
+  list3.Append(base::Value(kUrl4));
   base::Value result(base::Value::Type::LIST);
-  result.Append(base::Value("google.com"));
-  result.Append(base::Value("example.com"));
+  result.Append(base::Value(kUrl3));
+  result.Append(base::Value(kUrl1));
 
   std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
   policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
@@ -1296,14 +1304,14 @@
   const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
 
   base::Value list1(base::Value::Type::LIST);
-  list1.Append(base::Value("google.com"));
+  list1.Append(base::Value(kUrl3));
   base::Value list2(base::Value::Type::LIST);
-  list2.Append(base::Value("example.com"));
+  list2.Append(base::Value(kUrl1));
   base::Value list3(base::Value::Type::LIST);
-  list3.Append(base::Value("example_xyz.com"));
+  list3.Append(base::Value(kUrl4));
   base::Value result(base::Value::Type::LIST);
-  result.Append(base::Value("google.com"));
-  result.Append(base::Value("example.com"));
+  result.Append(base::Value(kUrl3));
+  result.Append(base::Value(kUrl1));
 
   std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
   policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
@@ -1360,4 +1368,431 @@
   EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
 }
 
+TEST_F(PolicyServiceTest, CloudUserListPolicyMerge_Successful) {
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+
+  // Initialize affiliation IDs. User and device ID is identical.
+  base::flat_set<std::string> affiliation_ids;
+  affiliation_ids.insert(kAffiliationId1);
+
+  // Initialize lists of URLs used for ExtensionInstallForcelist policy values.
+  base::Value list1 = base::Value(base::Value::Type::LIST);
+  list1.Append(base::Value(kUrl1));
+  list1.Append(base::Value(kUrl2));
+  base::Value list2 = base::Value(base::Value::Type::LIST);
+  list2.Append(base::Value(kUrl2));
+  list2.Append(base::Value(kUrl3));
+  base::Value list3 = base::Value(base::Value::Type::LIST);
+  list3.Append(base::Value(kUrl3));
+  list3.Append(base::Value(kUrl4));
+  base::Value result = base::Value(base::Value::Type::LIST);
+  result.Append(base::Value(kUrl1));
+  result.Append(base::Value(kUrl2));
+  result.Append(base::Value(kUrl3));
+  result.Append(base::Value(kUrl4));
+
+  // Populate separate policy bundles.
+  std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
+  policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
+
+  // policy_bundle1 is treated as a machine platform bundle. The metadata
+  // policies (PolicyListMultipleSourceMergeList, CloudUserPolicyMerge) are
+  // defined here.
+  auto policy_bundle1 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map1 = policy_bundle1->Get(chrome_namespace);
+  policy_map1.Set(key::kPolicyListMultipleSourceMergeList,
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                  POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  PolicyMap::Entry cloud_user_merge(
+      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
+      base::Value(true), nullptr);
+  PolicyMap::Entry entry_list_1(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_PLATFORM, std::move(list1),
+                                nullptr);
+  policy_map1.Set(key::kExtensionInstallForcelist, entry_list_1.DeepCopy());
+  policy_map1.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  // policy_bundle2 is treated as a machine cloud bundle. The device affiliation
+  // IDs are defined here to reflect what would happen in reality.
+  auto policy_bundle2 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map2 = policy_bundle2->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_2(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_CLOUD, std::move(list2), nullptr);
+  policy_map2.Set(key::kExtensionInstallForcelist, entry_list_2.DeepCopy());
+  policy_map2.SetDeviceAffiliationIds(affiliation_ids);
+
+  // policy_bundle3 is treated as a user cloud bundle. The user affiliation IDs
+  // are defined here to reflect what would happen in reality.
+  auto policy_bundle3 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map3 = policy_bundle3->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_3(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_CLOUD, std::move(list3), nullptr);
+  policy_map3.Set(key::kExtensionInstallForcelist, entry_list_3.DeepCopy());
+  policy_map3.SetUserAffiliationIds(affiliation_ids);
+
+  // The expected_chrome PolicyMap contains the combined URLs from all three
+  // policy bundles. The affiliation IDs don't need to be added as they're not
+  // compared in the PolicyMap equality check.
+  PolicyMap expected_chrome;
+  expected_chrome.Set(key::kPolicyListMultipleSourceMergeList,
+                      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                      POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  expected_chrome.Set("migrated", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_PLATFORM, base::Value(15), nullptr);
+
+  PolicyMap::Entry merged(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                          POLICY_SOURCE_MERGED, std::move(result), nullptr);
+  merged.AddConflictingPolicy(entry_list_2.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_3.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_1.DeepCopy());
+  expected_chrome.Set(key::kExtensionInstallForcelist, std::move(merged));
+  expected_chrome.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  provider0_.UpdatePolicy(std::move(policy_bundle1));
+  provider1_.UpdatePolicy(std::move(policy_bundle2));
+  provider2_.UpdatePolicy(std::move(policy_bundle3));
+  RunUntilIdle();
+
+  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
+}
+
+TEST_F(PolicyServiceTest, CloudUserListPolicyMerge_Unaffiliated) {
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+
+  // Initialize user and device affiliation IDs with no common ID.
+  base::flat_set<std::string> user_ids;
+  user_ids.insert(kAffiliationId1);
+  base::flat_set<std::string> device_ids;
+  device_ids.insert(kAffiliationId2);
+
+  // Initialize lists of URLs used for ExtensionInstallForcelist policy values.
+  base::Value list1 = base::Value(base::Value::Type::LIST);
+  list1.Append(base::Value(kUrl1));
+  list1.Append(base::Value(kUrl2));
+  base::Value list2 = base::Value(base::Value::Type::LIST);
+  list2.Append(base::Value(kUrl3));
+  base::Value list3 = base::Value(base::Value::Type::LIST);
+  list3.Append(base::Value(kUrl4));
+  base::Value result = base::Value(base::Value::Type::LIST);
+  result.Append(base::Value(kUrl1));
+  result.Append(base::Value(kUrl2));
+  result.Append(base::Value(kUrl3));
+
+  // Populate separate policy bundles.
+  std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
+  policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
+
+  // policy_bundle1 is treated as a machine platform bundle. The metadata
+  // policies (PolicyListMultipleSourceMergeList, CloudUserPolicyMerge) are
+  // defined here.
+  auto policy_bundle1 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map1 = policy_bundle1->Get(chrome_namespace);
+  policy_map1.Set(key::kPolicyListMultipleSourceMergeList,
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                  POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  PolicyMap::Entry cloud_user_merge(
+      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
+      base::Value(true), nullptr);
+  PolicyMap::Entry entry_list_1(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_PLATFORM, std::move(list1),
+                                nullptr);
+  policy_map1.Set(key::kExtensionInstallForcelist, entry_list_1.DeepCopy());
+  policy_map1.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  // policy_bundle2 is treated as a machine cloud bundle. The device affiliation
+  // IDs are defined here to reflect what would happen in reality.
+  auto policy_bundle2 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map2 = policy_bundle2->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_2(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_CLOUD, std::move(list2), nullptr);
+  policy_map2.Set(key::kExtensionInstallForcelist, entry_list_2.DeepCopy());
+  policy_map2.SetDeviceAffiliationIds(device_ids);
+
+  // policy_bundle3 is treated as a user cloud bundle. The user affiliation IDs
+  // are defined here to reflect what would happen in reality.
+  auto policy_bundle3 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map3 = policy_bundle3->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_3(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_CLOUD, std::move(list3), nullptr);
+  policy_map3.Set(key::kExtensionInstallForcelist, entry_list_3.DeepCopy());
+  policy_map3.SetUserAffiliationIds(user_ids);
+
+  // The expected_chrome PolicyMap contains the combined URLs from the non-user
+  // policy bundles. The policy values from the user cloud bundle aren't merged
+  // as there is no common affiliation ID between the user and device.
+  PolicyMap expected_chrome;
+  expected_chrome.Set(key::kPolicyListMultipleSourceMergeList,
+                      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                      POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  expected_chrome.Set("migrated", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_PLATFORM, base::Value(15), nullptr);
+
+  PolicyMap::Entry merged(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                          POLICY_SOURCE_MERGED, std::move(result), nullptr);
+  merged.AddConflictingPolicy(entry_list_2.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_3.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_1.DeepCopy());
+  expected_chrome.Set(key::kExtensionInstallForcelist, std::move(merged));
+  expected_chrome.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  provider0_.UpdatePolicy(std::move(policy_bundle1));
+  provider1_.UpdatePolicy(std::move(policy_bundle2));
+  provider2_.UpdatePolicy(std::move(policy_bundle3));
+  RunUntilIdle();
+
+  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
+}
+
+TEST_F(PolicyServiceTest, CloudUserListPolicyMerge_FalsePolicy) {
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+
+  // Initialize affiliation IDs. User and device ID is identical.
+  base::flat_set<std::string> affiliation_ids;
+  affiliation_ids.insert(kAffiliationId1);
+
+  // Initialize lists of URLs used for ExtensionInstallForcelist policy values.
+  base::Value list1 = base::Value(base::Value::Type::LIST);
+  list1.Append(base::Value(kUrl1));
+  base::Value list2 = base::Value(base::Value::Type::LIST);
+  list2.Append(base::Value(kUrl2));
+  base::Value list3 = base::Value(base::Value::Type::LIST);
+  list3.Append(base::Value(kUrl3));
+  base::Value result = base::Value(base::Value::Type::LIST);
+  result.Append(base::Value(kUrl1));
+  result.Append(base::Value(kUrl2));
+
+  // Populate separate policy bundles.
+  std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
+  policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
+
+  // policy_bundle1 is treated as a machine platform bundle. The metadata
+  // policies (PolicyListMultipleSourceMergeList, CloudUserPolicyMerge) are
+  // defined here. CloudUserPolicyMerge is set to false, preventing user cloud
+  // policy values from being merged with values from other sources.
+  auto policy_bundle1 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map1 = policy_bundle1->Get(chrome_namespace);
+  policy_map1.Set(key::kPolicyListMultipleSourceMergeList,
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                  POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  PolicyMap::Entry cloud_user_merge(
+      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
+      base::Value(false), nullptr);
+  PolicyMap::Entry entry_list_1(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_PLATFORM, std::move(list1),
+                                nullptr);
+  policy_map1.Set(key::kExtensionInstallForcelist, entry_list_1.DeepCopy());
+  policy_map1.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  // policy_bundle2 is treated as a machine cloud bundle. The device affiliation
+  // IDs are defined here to reflect what would happen in reality.
+  auto policy_bundle2 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map2 = policy_bundle2->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_2(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_CLOUD, std::move(list2), nullptr);
+  policy_map2.Set(key::kExtensionInstallForcelist, entry_list_2.DeepCopy());
+  policy_map2.SetDeviceAffiliationIds(affiliation_ids);
+
+  // policy_bundle3 is treated as a user cloud bundle. The user affiliation IDs
+  // are defined here to reflect what would happen in reality.
+  auto policy_bundle3 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map3 = policy_bundle3->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_3(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_CLOUD, std::move(list3), nullptr);
+  policy_map3.Set(key::kExtensionInstallForcelist, entry_list_3.DeepCopy());
+  policy_map3.SetUserAffiliationIds(affiliation_ids);
+
+  // The expected_chrome PolicyMap contains the combined URLs from the non-user
+  // policy bundles. The policy values from the user cloud bundle aren't merged
+  // because CloudUserPolicyMerge is set to false.
+  PolicyMap expected_chrome;
+  expected_chrome.Set(key::kPolicyListMultipleSourceMergeList,
+                      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                      POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  expected_chrome.Set("migrated", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_PLATFORM, base::Value(15), nullptr);
+
+  PolicyMap::Entry merged(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                          POLICY_SOURCE_MERGED, std::move(result), nullptr);
+  merged.AddConflictingPolicy(entry_list_2.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_3.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_1.DeepCopy());
+  expected_chrome.Set(key::kExtensionInstallForcelist, std::move(merged));
+  expected_chrome.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  provider0_.UpdatePolicy(std::move(policy_bundle1));
+  provider1_.UpdatePolicy(std::move(policy_bundle2));
+  provider2_.UpdatePolicy(std::move(policy_bundle3));
+  RunUntilIdle();
+
+  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
+}
+
+TEST_F(PolicyServiceTest, PlatformUserListPolicyMerge_Affiliated) {
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+
+  // Initialize affiliation IDs. User and device ID is identical.
+  base::flat_set<std::string> affiliation_ids;
+  affiliation_ids.insert(kAffiliationId1);
+
+  // Initialize lists of URLs used for ExtensionInstallForcelist policy values.
+  base::Value list1 = base::Value(base::Value::Type::LIST);
+  list1.Append(base::Value(kUrl1));
+  base::Value list2 = base::Value(base::Value::Type::LIST);
+  list2.Append(base::Value(kUrl2));
+  base::Value list3 = base::Value(base::Value::Type::LIST);
+  list3.Append(base::Value(kUrl3));
+  base::Value result = base::Value(base::Value::Type::LIST);
+  result.Append(base::Value(kUrl2));
+  result.Append(base::Value(kUrl3));
+
+  // Populate separate policy bundles.
+  std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
+  policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
+
+  // policy_bundle1 is treated as a user platform bundle. The metadata policies
+  // (PolicyListMultipleSourceMergeList, CloudUserPolicyMerge) are defined here.
+  // Policy values with a user GPO source are currently not merged with values
+  // from any other source(s).
+  auto policy_bundle1 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map1 = policy_bundle1->Get(chrome_namespace);
+  policy_map1.Set(key::kPolicyListMultipleSourceMergeList,
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                  POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  PolicyMap::Entry cloud_user_merge(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                    POLICY_SOURCE_PLATFORM, base::Value(true),
+                                    nullptr);
+  PolicyMap::Entry entry_list_1(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_PLATFORM, std::move(list1),
+                                nullptr);
+  policy_map1.Set(key::kExtensionInstallForcelist, entry_list_1.DeepCopy());
+  policy_map1.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  // policy_bundle2 is treated as a machine cloud bundle. The device affiliation
+  // IDs are defined here to reflect what would happen in reality.
+  auto policy_bundle2 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map2 = policy_bundle2->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_2(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_CLOUD, std::move(list2), nullptr);
+  policy_map2.Set(key::kExtensionInstallForcelist, entry_list_2.DeepCopy());
+  policy_map2.SetDeviceAffiliationIds(affiliation_ids);
+
+  // policy_bundle3 is treated as a user cloud bundle. The user affiliation IDs
+  // are defined here to reflect what would happen in reality.
+  auto policy_bundle3 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map3 = policy_bundle3->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_3(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_CLOUD, std::move(list3), nullptr);
+  policy_map3.Set(key::kExtensionInstallForcelist, entry_list_3.DeepCopy());
+  policy_map3.SetUserAffiliationIds(affiliation_ids);
+
+  // The expected_chrome PolicyMap contains the merged values from machine and
+  // user policy sources. User platform policy values are not merged.
+  PolicyMap expected_chrome;
+  expected_chrome.Set(key::kPolicyListMultipleSourceMergeList,
+                      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  expected_chrome.Set("migrated", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_PLATFORM, base::Value(15), nullptr);
+
+  PolicyMap::Entry merged(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                          POLICY_SOURCE_MERGED, std::move(result), nullptr);
+  merged.AddConflictingPolicy(entry_list_1.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_3.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_2.DeepCopy());
+  expected_chrome.Set(key::kExtensionInstallForcelist, std::move(merged));
+  expected_chrome.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  provider0_.UpdatePolicy(std::move(policy_bundle1));
+  provider1_.UpdatePolicy(std::move(policy_bundle2));
+  provider2_.UpdatePolicy(std::move(policy_bundle3));
+  RunUntilIdle();
+
+  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
+}
+
+TEST_F(PolicyServiceTest, PlatformUserListPolicyMerge_Unaffiliated) {
+  const PolicyNamespace chrome_namespace(POLICY_DOMAIN_CHROME, std::string());
+
+  // Initialize user affiliation IDs. This test doesn't contain a machine cloud
+  // source, so no device affiliation IDs are set.
+  base::flat_set<std::string> user_ids;
+  user_ids.insert(kAffiliationId1);
+
+  // Initialize lists of URLs used for ExtensionInstallForcelist policy values.
+  base::Value list1 = base::Value(base::Value::Type::LIST);
+  list1.Append(base::Value(kUrl1));
+  base::Value list2 = base::Value(base::Value::Type::LIST);
+  list2.Append(base::Value(kUrl2));
+  base::Value list3 = base::Value(base::Value::Type::LIST);
+  list3.Append(base::Value(kUrl3));
+  base::Value result = base::Value(base::Value::Type::LIST);
+  result.Append(base::Value(kUrl1));
+
+  // Populate separate policy bundles.
+  std::unique_ptr<base::ListValue> policy = std::make_unique<base::ListValue>();
+  policy->Append(base::Value(policy::key::kExtensionInstallForcelist));
+
+  // policy_bundle1 is treated as a machine platform bundle. The metadata
+  // policies (PolicyListMultipleSourceMergeList, CloudUserPolicyMerge) are
+  // defined here.
+  auto policy_bundle1 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map1 = policy_bundle1->Get(chrome_namespace);
+  policy_map1.Set(key::kPolicyListMultipleSourceMergeList,
+                  POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                  POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  PolicyMap::Entry cloud_user_merge(
+      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE, POLICY_SOURCE_PLATFORM,
+      base::Value(true), nullptr);
+  PolicyMap::Entry entry_list_1(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                                POLICY_SOURCE_PLATFORM, std::move(list1),
+                                nullptr);
+  policy_map1.Set(key::kExtensionInstallForcelist, entry_list_1.DeepCopy());
+  policy_map1.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  // policy_bundle2 is treated as a user platform bundle. Policy values with a
+  // user GPO source are currently not merged with values from any other
+  // source(s).
+  auto policy_bundle2 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map2 = policy_bundle2->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_2(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_PLATFORM, std::move(list2),
+                                nullptr);
+  policy_map2.Set(key::kExtensionInstallForcelist, entry_list_2.DeepCopy());
+
+  // policy_bundle3 is treated as a user cloud bundle. The user affiliation IDs
+  // are defined here to reflect what would happen in reality.
+  auto policy_bundle3 = std::make_unique<PolicyBundle>();
+  PolicyMap& policy_map3 = policy_bundle3->Get(chrome_namespace);
+  PolicyMap::Entry entry_list_3(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                                POLICY_SOURCE_CLOUD, std::move(list3), nullptr);
+  policy_map3.Set(key::kExtensionInstallForcelist, entry_list_3.DeepCopy());
+  policy_map3.SetUserAffiliationIds(user_ids);
+
+  // The expected_chrome PolicyMap only contains the URLs from the platform
+  // machine policy source. Values from the user platform policy are not
+  // mergeable. Values from the user cloud policy are not merged since the user
+  // is not affiliated (browser isn't enrolled in CBCM).
+  PolicyMap expected_chrome;
+  expected_chrome.Set(key::kPolicyListMultipleSourceMergeList,
+                      POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                      POLICY_SOURCE_PLATFORM, policy->Clone(), nullptr);
+  expected_chrome.Set("migrated", POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                      POLICY_SOURCE_PLATFORM, base::Value(15), nullptr);
+
+  PolicyMap::Entry merged(POLICY_LEVEL_MANDATORY, POLICY_SCOPE_MACHINE,
+                          POLICY_SOURCE_MERGED, std::move(result), nullptr);
+  merged.AddConflictingPolicy(entry_list_2.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_3.DeepCopy());
+  merged.AddConflictingPolicy(entry_list_1.DeepCopy());
+  expected_chrome.Set(key::kExtensionInstallForcelist, std::move(merged));
+  expected_chrome.Set(key::kCloudUserPolicyMerge, cloud_user_merge.DeepCopy());
+
+  provider0_.UpdatePolicy(std::move(policy_bundle1));
+  provider1_.UpdatePolicy(std::move(policy_bundle2));
+  provider2_.UpdatePolicy(std::move(policy_bundle3));
+  RunUntilIdle();
+
+  EXPECT_TRUE(VerifyPolicies(chrome_namespace, expected_chrome));
+}
+
 }  // namespace policy
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 4227f5a..0663751 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -8704,6 +8704,39 @@
       'label': '''Allow merging dictionary policies from different sources''',
     },
     {
+      'name': 'CloudUserPolicyMerge',
+      'owners': ['igorruvinov@google.com', 'pastarmovj@google.com'],
+      'type': 'main',
+      'schema': { 'type': 'boolean' },
+      'supported_on': ['chrome.*:92-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'items': [
+        {
+          'value': True,
+          'caption': 'Enable merging of user-level cloud policies.',
+        },
+        {
+          'value': False,
+          'caption': 'Disable merging of user-level cloud policies.',
+        },
+      ],
+      'example_value': True,
+      'default': False,
+      'id': 859,
+      'caption': '''Enables merging of user cloud policies into machine-level policies''',
+      'tags': [],
+      'desc': '''Setting the policy to Enabled allows policies associated with a <ph name="GOOGLE_WORKSPACE_PRODUCT_NAME">Google Workspace</ph> account to be merged into machine-level policies.
+
+      Only policies originating from secure users can be merged. A secure user is affiliated with the organization that manages their browser using <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>. All other user-level policies will always be ignored.
+
+      Policies that need to be merged also need to be set in either <ph name="POLICY_POLICYLISTMULTIPLESOURCEMERGELIST">PolicyListMultipleSourceMergeList</ph> or <ph name="POLICY_POLICYDICTIONARYMULTIPLESOURCEMERGELIST">PolicyDictionaryMultipleSourceMergeList</ph>. This policy will be ignored if neither of the two aforementioned policies is configured.
+
+      Leaving the policy unset or setting it to Disabled prevents user-level cloud policies from being merged with policies from any other sources.''',
+    },
+    {
       'name': 'EnableExperimentalPolicies',
       'owners': ['file://components/policy/resources/OWNERS', 'zmin@chromium.org'],
       'type': 'list',
@@ -26804,6 +26837,6 @@
   'placeholders': [],
   'deleted_policy_ids': [114, 115, 204, 205, 206, 412, 476, 544, 546, 562, 569, 578, 583, 585, 586, 587, 588, 589, 590, 591, 600, 668, 669],
   'deleted_atomic_policy_group_ids': [19],
-  'highest_id_currently_used': 858,
+  'highest_id_currently_used': 859,
   'highest_atomic_group_id_currently_used': 40
 }
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor_unittest.cc b/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor_unittest.cc
index f647c17..1df89d98 100644
--- a/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor_unittest.cc
+++ b/components/safe_browsing/content/renderer/phishing_classifier/phishing_term_feature_extractor_unittest.cc
@@ -263,19 +263,15 @@
 
   // Chinese translation of the phrase "hello goodbye hello goodbye". This tests
   // that we can correctly separate terms in languages that don't use spaces.
-  page_text = base::UTF8ToUTF16(
-      "\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x86\x8d\xe8\xa7\x81"
-      "\xe4\xbd\xa0\xe5\xa5\xbd\xe5\x86\x8d\xe8\xa7\x81");
+  page_text = u"你好再见你好再见";
   expected_features.Clear();
   expected_features.AddBooleanFeature(features::kPageTerm +
-                                      std::string("\xe4\xbd\xa0\xe5\xa5\xbd"));
+                                      std::string("你好"));
   expected_features.AddBooleanFeature(features::kPageTerm +
-                                      std::string("\xe5\x86\x8d\xe8\xa7\x81"));
+                                      std::string("再见"));
   expected_shingle_hashes.clear();
   expected_shingle_hashes.insert(
-      MurmurHash3String("\xe4\xbd\xa0\xe5\xa5\xbd \xe5\x86\x8d\xe8\xa7\x81 "
-                        "\xe4\xbd\xa0\xe5\xa5\xbd \xe5\x86\x8d\xe8\xa7\x81 ",
-                        kMurmurHash3Seed));
+      MurmurHash3String("你好 再见 你好 再见 ", kMurmurHash3Seed));
 
   features.Clear();
   shingle_hashes.clear();
diff --git a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
index 992e5c8..203a2e2 100644
--- a/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
+++ b/components/safe_browsing/content/web_ui/safe_browsing_ui.cc
@@ -849,7 +849,6 @@
   }
   dict.SetList("shingle_hashes", std::move(shingle_hashes));
 
-  dict.SetString("model_filename", cpr.model_filename());
   dict.SetKey("population", SerializeChromeUserPopulation(cpr.population()));
   if (cpr.has_screenshot_digest()) {
     dict.SetKey("screenshot_digest", base::Value(cpr.screenshot_digest()));
diff --git a/components/safe_browsing/core/proto/csd.proto b/components/safe_browsing/core/proto/csd.proto
index 8cd0721..4495d853 100644
--- a/components/safe_browsing/core/proto/csd.proto
+++ b/components/safe_browsing/core/proto/csd.proto
@@ -128,8 +128,8 @@
   // List of shingle hashes we extracted.
   repeated uint32 shingle_hashes = 12 [packed = true];
 
-  // The model filename (basename) that was used by the client.
-  optional string model_filename = 13;
+  // The model filename (basename) that was used by the client. Deprecated.
+  optional string DEPRECATED_model_filename = 13 [deprecated = true];
 
   // Population that the reporting user is part of.
   optional ChromeUserPopulation population = 14;
diff --git a/components/search_engines/template_url_unittest.cc b/components/search_engines/template_url_unittest.cc
index 79ec26e..2cbb292 100644
--- a/components/search_engines/template_url_unittest.cc
+++ b/components/search_engines/template_url_unittest.cc
@@ -522,14 +522,14 @@
     const std::string url;
     const std::string expected_result;
   } test_data[] = {
-      {"BIG5", u"\x60BD", "http://foo/?{searchTerms}{inputEncoding}",
+      {"BIG5", u"悽", "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?%B1~BIG5"},
       {"UTF-8", u"blah", "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?blahUTF-8"},
-      {"Shift_JIS", base::UTF8ToUTF16("\xe3\x81\x82"),
-       "http://foo/{searchTerms}/bar", "http://foo/%82%A0/bar"},
-      {"Shift_JIS", base::UTF8ToUTF16("\xe3\x81\x82 \xe3\x81\x84"),
-       "http://foo/{searchTerms}/bar", "http://foo/%82%A0%20%82%A2/bar"},
+      {"Shift_JIS", u"あ", "http://foo/{searchTerms}/bar",
+       "http://foo/%82%A0/bar"},
+      {"Shift_JIS", u"あ い", "http://foo/{searchTerms}/bar",
+       "http://foo/%82%A0%20%82%A2/bar"},
   };
   TemplateURLData data;
   for (size_t i = 0; i < base::size(test_data); ++i) {
@@ -558,27 +558,27 @@
   } test_data[] = {
       // First and third encodings are valid. First is used.
       {{"windows-1251", "cp-866", "UTF-8"},
-       base::UTF8ToUTF16("\xD1\x8F"),
+       u"я",
        "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?%FFwindows-1251"},
       // Second and third encodings are valid, second is used.
       {{"cp-866", "GB2312", "UTF-8"},
-       base::UTF8ToUTF16("\xE7\x8B\x97"),
+       u"狗",
        "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?%B9%B7GB2312"},
       // Second and third encodings are valid in another order, second is used.
       {{"cp-866", "UTF-8", "GB2312"},
-       base::UTF8ToUTF16("\xE7\x8B\x97"),
+       u"狗",
        "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?%E7%8B%97UTF-8"},
       // Both encodings are invalid, fallback to UTF-8.
       {{"cp-866", "windows-1251"},
-       base::UTF8ToUTF16("\xE7\x8B\x97"),
+       u"狗",
        "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?%E7%8B%97UTF-8"},
       // No encodings are given, fallback to UTF-8.
       {{},
-       base::UTF8ToUTF16("\xE7\x8B\x97"),
+       u"狗",
        "http://foo/?{searchTerms}{inputEncoding}",
        "http://foo/?%E7%8B%97UTF-8"},
   };
@@ -1854,9 +1854,7 @@
   ASSERT_EQ(u"blah", TemplateURL::GenerateKeyword(GURL("http://blah/")));
   // Don't generate the empty string.
   ASSERT_EQ(u"www.", TemplateURL::GenerateKeyword(GURL("http://www.")));
-  ASSERT_EQ(
-      base::UTF8ToUTF16("\xd0\xb0\xd0\xb1\xd0\xb2"),
-      TemplateURL::GenerateKeyword(GURL("http://xn--80acd")));
+  ASSERT_EQ(u"абв", TemplateURL::GenerateKeyword(GURL("http://xn--80acd")));
 
   // Generated keywords must always be in lowercase, because TemplateURLs always
   // converts keywords to lowercase in its constructor and TemplateURLService
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index 95f1a3c..bb6c1a9a 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -203,6 +203,17 @@
   return "";
 }
 
+std::string GetSigninStatusDescription(
+    signin::IdentityManager* identity_manager) {
+  if (!identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
+    return "Not Signed In";
+  } else if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
+    return "Signed In, Consented for Sync";
+  } else {
+    return "Signed In, Not Consented for Sync";
+  }
+}
+
 }  // anonymous namespace
 
 AboutSigninInternals::AboutSigninInternals(
@@ -615,11 +626,8 @@
       AddSection(signin_info.get(), "Basic Information");
   AddSectionEntry(basic_info, "Account Consistency",
                   GetAccountConsistencyDescription(account_consistency));
-  AddSectionEntry(
-      basic_info, "Signin Status",
-      identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)
-          ? "Signed In"
-          : "Not Signed In");
+  AddSectionEntry(basic_info, "Signin Status",
+                  GetSigninStatusDescription(identity_manager));
   signin::LoadCredentialsState load_tokens_state =
       identity_manager->GetDiagnosticsProvider()
           ->GetDetailedStateOfLoadingOfRefreshTokens();
@@ -629,9 +637,9 @@
       basic_info, "Gaia cookies state",
       GetGaiaCookiesStateAsString(GetGaiaCookiesState(signin_client)));
 
-  if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
+  if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)) {
     CoreAccountInfo account_info =
-        identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSync);
+        identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
     AddSectionEntry(basic_info,
                     SigninStatusFieldToLabel(signin_internals_util::ACCOUNT_ID),
                     account_info.account_id.ToString());
diff --git a/components/site_isolation/site_isolation_policy.cc b/components/site_isolation/site_isolation_policy.cc
index 1096c89..f36a347 100644
--- a/components/site_isolation/site_isolation_policy.cc
+++ b/components/site_isolation/site_isolation_policy.cc
@@ -9,11 +9,11 @@
 #include "base/system/sys_info.h"
 #include "build/build_config.h"
 #include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
 #include "components/site_isolation/features.h"
 #include "components/site_isolation/pref_names.h"
 #include "components/user_prefs/user_prefs.h"
 #include "content/public/browser/browser_context.h"
-#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/site_isolation_policy.h"
 
@@ -130,6 +130,24 @@
 }
 
 // static
+void SiteIsolationPolicy::PersistIsolatedOrigin(
+    content::BrowserContext* context,
+    const url::Origin& origin,
+    content::ChildProcessSecurityPolicy::IsolatedOriginSource source) {
+  DCHECK(!context->IsOffTheRecord());
+  // TODO(alexmos): Support web-triggered IsolatedOriginSources.
+  DCHECK_EQ(source, content::ChildProcessSecurityPolicy::IsolatedOriginSource::
+                        USER_TRIGGERED);
+
+  ListPrefUpdate update(user_prefs::UserPrefs::Get(context),
+                        site_isolation::prefs::kUserTriggeredIsolatedOrigins);
+  base::ListValue* list = update.Get();
+  base::Value value(origin.Serialize());
+  if (!base::Contains(list->GetList(), value))
+    list->Append(std::move(value));
+}
+
+// static
 void SiteIsolationPolicy::ApplyPersistedIsolatedOrigins(
     content::BrowserContext* browser_context) {
   // If the user turned off password-triggered isolation, don't apply any
diff --git a/components/site_isolation/site_isolation_policy.h b/components/site_isolation/site_isolation_policy.h
index 0577998..28621aa 100644
--- a/components/site_isolation/site_isolation_policy.h
+++ b/components/site_isolation/site_isolation_policy.h
@@ -5,10 +5,11 @@
 #ifndef COMPONENTS_SITE_ISOLATION_SITE_ISOLATION_POLICY_H_
 #define COMPONENTS_SITE_ISOLATION_SITE_ISOLATION_POLICY_H_
 
-#include "base/macros.h"
-
 #include <vector>
 
+#include "base/macros.h"
+#include "content/public/browser/child_process_security_policy.h"
+
 class GURL;
 
 namespace content {
@@ -42,6 +43,15 @@
   // Spectre-like attacks on such devices).
   static bool IsEnterprisePolicyApplicable();
 
+  // Saves a new dynamic isolated origin to user prefs associated with
+  // `context` so that it can be persisted across restarts. `source`
+  // specifies why the isolated origin was added; different sources may have
+  // different persistence policies.
+  static void PersistIsolatedOrigin(
+      content::BrowserContext* context,
+      const url::Origin& origin,
+      content::ChildProcessSecurityPolicy::IsolatedOriginSource source);
+
   // Reads and applies any isolated origins stored in user prefs associated with
   // |browser_context|.  This is expected to be called on startup after user
   // prefs have been loaded.
diff --git a/components/spellcheck/renderer/spellcheck_unittest.cc b/components/spellcheck/renderer/spellcheck_unittest.cc
index c2d149a..387fb378 100644
--- a/components/spellcheck/renderer/spellcheck_unittest.cc
+++ b/components/spellcheck/renderer/spellcheck_unittest.cc
@@ -1065,12 +1065,12 @@
 TEST_F(SpellCheckTest, SpellCheckParagraphLongSentence) {
   std::vector<SpellCheckResult> expected;
   // The text is taken from US constitution preamble.
-  const std::u16string text = base::UTF8ToUTF16(
-      "We the people of the United States, in order to form a more perfect "
-      "union, establish justice, insure domestic tranquility, provide for "
-      "the common defense, promote the general welfare, and secure the "
-      "blessings of liberty to ourselves and our posterity, do ordain and "
-      "establish this Constitution for the United States of America.");
+  const std::u16string text =
+      u"We the people of the United States, in order to form a more perfect "
+      u"union, establish justice, insure domestic tranquility, provide for "
+      u"the common defense, promote the general welfare, and secure the "
+      u"blessings of liberty to ourselves and our posterity, do ordain and "
+      u"establish this Constitution for the United States of America.";
 
   TestSpellCheckParagraph(text, expected);
 }
@@ -1080,12 +1080,12 @@
   std::vector<SpellCheckResult> expected;
 
   // All 'the' are converted to 'hte' in US consitition preamble.
-  const std::u16string text = base::UTF8ToUTF16(
-      "We hte people of hte United States, in order to form a more perfect "
-      "union, establish justice, insure domestic tranquility, provide for "
-      "hte common defense, promote hte general welfare, and secure hte "
-      "blessings of liberty to ourselves and our posterity, do ordain and "
-      "establish this Constitution for hte United States of America.");
+  const std::u16string text =
+      u"We hte people of hte United States, in order to form a more perfect "
+      u"union, establish justice, insure domestic tranquility, provide for "
+      u"hte common defense, promote hte general welfare, and secure hte "
+      u"blessings of liberty to ourselves and our posterity, do ordain and "
+      u"establish this Constitution for hte United States of America.";
 
   expected.push_back(SpellCheckResult(
       SpellCheckResult::SPELLING, 3, 3));
diff --git a/components/translate/content/renderer/translate_agent.cc b/components/translate/content/renderer/translate_agent.cc
index e97103d..e92b709 100644
--- a/components/translate/content/renderer/translate_agent.cc
+++ b/components/translate/content/renderer/translate_agent.cc
@@ -165,6 +165,20 @@
     return;
 
   WebDocument document = main_frame->GetDocument();
+  GURL url = GURL(document.Url());
+  // Limit detection to URLs that only detect the language of the content if the
+  // page is potentially a candidate for translation.  This should be strictly a
+  // subset of the conditions in TranslateService::IsTranslatableURL, however,
+  // due to layering they cannot be identical. Critically, this list should
+  // never filter anything that is eligible for translation. Under filtering is
+  // ok as the translate service will make the final call and only results in a
+  // slight overhead in running the model when unnecessary.
+  if (url.is_empty() || url.SchemeIs(content::kChromeUIScheme) ||
+      url.SchemeIs(content::kChromeDevToolsScheme) || url.IsAboutBlank() ||
+      url.SchemeIs(url::kFtpScheme)) {
+    return;
+  }
+
   WebLanguageDetectionDetails web_detection_details =
       WebLanguageDetectionDetails::CollectLanguageDetectionDetails(document);
   std::string content_language = web_detection_details.content_language.Utf8();
@@ -176,12 +190,6 @@
 
   std::string language;
   if (translate::IsTFLiteLanguageDetectionEnabled()) {
-    if (!document.Url().ProtocolIs(url::kHttpsScheme) &&
-        !document.Url().ProtocolIs(url::kHttpScheme)) {
-      // TFLite-based language detection only supports HTTP/HTTPS pages.
-      // Others should be ignored, for example the New Tab Page.
-      return;
-    }
     translate::LanguageDetectionModel& language_detection_model =
         GetLanguageDetectionModel();
     bool is_available = language_detection_model.IsAvailable();
diff --git a/components/viz/service/display/ca_layer_overlay.cc b/components/viz/service/display/ca_layer_overlay.cc
index f33aebb..c4506ba0 100644
--- a/components/viz/service/display/ca_layer_overlay.cc
+++ b/components/viz/service/display/ca_layer_overlay.cc
@@ -51,7 +51,7 @@
   CA_LAYER_FAILED_PICTURE_CONTENT = 12,
   // CA_LAYER_FAILED_RENDER_PASS = 13,
   CA_LAYER_FAILED_SURFACE_CONTENT = 14,
-  CA_LAYER_FAILED_YUV_VIDEO_CONTENT = 15,
+  // CA_LAYER_FAILED_YUV_VIDEO_CONTENT = 15,
   CA_LAYER_FAILED_DIFFERENT_CLIP_SETTINGS = 16,
   CA_LAYER_FAILED_DIFFERENT_VERTEX_OPACITIES = 17,
   // CA_LAYER_FAILED_RENDER_PASS_FILTER_SCALE = 18,
@@ -64,6 +64,8 @@
   CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_CLIP_MISMATCH = 25,
   CA_LAYER_FAILED_QUAD_ROUNDED_CORNER_NOT_UNIFORM = 26,
   CA_LAYER_FAILED_TOO_MANY_QUADS = 27,
+  CA_LAYER_FAILED_YUV_NOT_CANDIDATE = 28,
+  CA_LAYER_FAILED_Y_UV_TEXCOORD_MISMATCH = 29,
   CA_LAYER_FAILED_COUNT,
 };
 
@@ -167,6 +169,31 @@
   return CA_LAYER_SUCCESS;
 }
 
+CALayerResult FromYUVVideoQuad(DisplayResourceProvider* resource_provider,
+                               const YUVVideoDrawQuad* quad,
+                               CALayerOverlay* ca_layer_overlay) {
+  // For YUVVideoDrawQuads, the Y and UV planes alias the same underlying
+  // IOSurface. Ensure all planes are overlays and have the same contents
+  // rect. Then use the Y plane as the resource for the overlay.
+  ResourceId y_resource_id = quad->y_plane_resource_id();
+  if (!resource_provider->IsOverlayCandidate(y_resource_id) ||
+      !resource_provider->IsOverlayCandidate(quad->u_plane_resource_id()) ||
+      !resource_provider->IsOverlayCandidate(quad->v_plane_resource_id())) {
+    return CA_LAYER_FAILED_YUV_NOT_CANDIDATE;
+  }
+  gfx::RectF ya_contents_rect =
+      gfx::ScaleRect(quad->ya_tex_coord_rect, 1.f / quad->ya_tex_size.width(),
+                     1.f / quad->ya_tex_size.height());
+  gfx::RectF uv_contents_rect =
+      gfx::ScaleRect(quad->uv_tex_coord_rect, 1.f / quad->uv_tex_size.width(),
+                     1.f / quad->uv_tex_size.height());
+  if (ya_contents_rect != uv_contents_rect)
+    return CA_LAYER_FAILED_Y_UV_TEXCOORD_MISMATCH;
+  ca_layer_overlay->contents_resource_id = y_resource_id;
+  ca_layer_overlay->contents_rect = ya_contents_rect;
+  return CA_LAYER_SUCCESS;
+}
+
 CALayerResult FromTileQuad(DisplayResourceProvider* resource_provider,
                            const TileDrawQuad* quad,
                            CALayerOverlay* ca_layer_overlay) {
@@ -279,7 +306,9 @@
       case DrawQuad::Material::kSurfaceContent:
         return CA_LAYER_FAILED_SURFACE_CONTENT;
       case DrawQuad::Material::kYuvVideoContent:
-        return CA_LAYER_FAILED_YUV_VIDEO_CONTENT;
+        return FromYUVVideoQuad(resource_provider,
+                                YUVVideoDrawQuad::MaterialCast(quad),
+                                ca_layer_overlay);
       default:
         break;
     }
diff --git a/content/browser/accessibility/accessibility_browsertest.cc b/content/browser/accessibility/accessibility_browsertest.cc
index a17673e..209b8c22 100644
--- a/content/browser/accessibility/accessibility_browsertest.cc
+++ b/content/browser/accessibility/accessibility_browsertest.cc
@@ -102,15 +102,15 @@
   AccessibilityNotificationWaiter selection_waiter(
       shell()->web_contents(), ui::kAXModeComplete,
       ax::mojom::Event::kTextSelectionChanged);
-  ExecuteScript(base::UTF8ToUTF16(
-      "let selection=document.getSelection();"
-      "let range=document.createRange();"
-      "let editable=document.querySelector('p[contenteditable=\"true\"]');"
-      "editable.focus();"
-      "range.setStart(editable.lastChild, 0);"
-      "range.setEnd(editable.lastChild, 0);"
-      "selection.removeAllRanges();"
-      "selection.addRange(range);"));
+  ExecuteScript(
+      u"let selection=document.getSelection();"
+      u"let range=document.createRange();"
+      u"let editable=document.querySelector('p[contenteditable=\"true\"]');"
+      u"editable.focus();"
+      u"range.setStart(editable.lastChild, 0);"
+      u"range.setEnd(editable.lastChild, 0);"
+      u"selection.removeAllRanges();"
+      u"selection.addRange(range);");
   selection_waiter.WaitForNotification();
 }
 
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index b6dfe97..68b1909 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -3683,11 +3683,12 @@
                                          ui::kAXModeComplete,
                                          ax::mojom::Event::kValueChanged);
   // Place an e acute, and two emoticons in the text field.
-  ExecuteScript(base::UTF8ToUTF16(R"SCRIPT(
+  ExecuteScript(
+      uR"SCRIPT(
       const input = document.querySelector('input');
       input.value =
           'e\u0301\uD83D\uDC69\u200D\u2764\uFE0F\u200D\uD83D\uDC69\uD83D\uDC36';
-      )SCRIPT"));
+      )SCRIPT");
   waiter.WaitForNotification();
 
   LONG n_characters;
@@ -4035,10 +4036,11 @@
                                          ui::kAXModeComplete,
                                          ax::mojom::Event::kValueChanged);
   // Add a blank line at the end of the textarea.
-  ExecuteScript(base::UTF8ToUTF16(R"SCRIPT(
+  ExecuteScript(
+      uR"SCRIPT(
       const textarea = document.querySelector('textarea');
       textarea.value += '\n';
-      )SCRIPT"));
+      )SCRIPT");
   waiter.WaitForNotification();
 
   // The second last line should have an additional trailing newline. Also,
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index e71c219d..93f8ea7 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1715,6 +1715,16 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityCustomElementHidden) {
+  RunHtmlTest(FILE_PATH_LITERAL("custom-element-hidden.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
+                       AccessibilityCustomElementRemoveNodes) {
+  RunHtmlTest(FILE_PATH_LITERAL("custom-element-remove-nodes.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
                        AccessibilityCustomElementWithAriaOwnsOutside) {
   RunHtmlTest(FILE_PATH_LITERAL("custom-element-with-aria-owns-outside.html"));
 }
diff --git a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
index 0dfd86a..25403570 100644
--- a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
+++ b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
@@ -251,9 +251,7 @@
   jvalue result;
   switch (target_type.type) {
     case JavaType::TypeString: {
-      std::string string_result;
-      value->GetAsString(&string_result);
-      result.l = ConvertUTF8ToJavaString(env, string_result).Release();
+      result.l = ConvertUTF8ToJavaString(env, value->GetString()).Release();
       break;
     }
     case JavaType::TypeObject:
@@ -521,8 +519,7 @@
       length = static_cast<jsize>(length_value->GetInt());
     }
   } else if (length_value->is_double()) {
-    double double_length;
-    length_value->GetAsDouble(&double_length);
+    double double_length = length_value->GetDouble();
     if (double_length >= 0.0 &&
         double_length <= std::numeric_limits<int32_t>::max()) {
       length = static_cast<jsize>(double_length);
@@ -709,10 +706,8 @@
       return CoerceJavaScriptIntegerToJavaValue(
           env, value->GetInt(), target_type, coerce_to_string, error);
     case base::Value::Type::DOUBLE: {
-      double double_value;
-      value->GetAsDouble(&double_value);
       return CoerceJavaScriptDoubleToJavaValue(
-          env, double_value, target_type, coerce_to_string, error);
+          env, value->GetDouble(), target_type, coerce_to_string, error);
     }
     case base::Value::Type::BOOLEAN:
       return CoerceJavaScriptBooleanToJavaValue(
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index b5b658c..e4ba8e2 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -18,6 +18,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/background_fetch/background_fetch_registration_id.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_registration.h"
@@ -130,7 +131,7 @@
   {
     base::RunLoop run_loop;
     embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
-        service_worker_registration_id, origin,
+        service_worker_registration_id, storage::StorageKey(origin),
         base::BindOnce(&DidFindServiceWorkerRegistration,
                        &service_worker_registration, run_loop.QuitClosure()));
 
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 177e7cb..80e77846 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -1044,9 +1044,6 @@
   if (RenderProcessHost::run_renderer_in_process())
     RenderProcessHostImpl::ShutDownInProcessRenderer();
 
-  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI))
-    BrowserProcessIOThread::ProcessHostCleanUp();
-
   if (parts_) {
     TRACE_EVENT0("shutdown",
                  "BrowserMainLoop::Subsystem:PostMainMessageLoopRun");
@@ -1072,6 +1069,9 @@
 
   ShutDownNetworkService();
 
+  if (base::FeatureList::IsEnabled(features::kProcessHostOnUI))
+    BrowserProcessIOThread::ProcessHostCleanUp();
+
 #if defined(OS_MAC)
   BrowserCompositorMac::DisableRecyclingForShutdown();
 #endif
diff --git a/content/browser/content_index/content_index_database_unittest.cc b/content/browser/content_index/content_index_database_unittest.cc
index 3787ffb..acd0ed5 100644
--- a/content/browser/content_index/content_index_database_unittest.cc
+++ b/content/browser/content_index/content_index_database_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/public/browser/content_index_provider.h"
 #include "content/public/test/browser_task_environment.h"
@@ -258,7 +259,7 @@
     {
       base::RunLoop run_loop;
       embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
-          service_worker_registration_id, origin_,
+          service_worker_registration_id, storage::StorageKey(origin_),
           base::BindOnce(&DidFindServiceWorkerRegistration,
                          &service_worker_registration_,
                          run_loop.QuitClosure()));
diff --git a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
index ebafb3c..29f7475 100644
--- a/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/containers/flat_map.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/devtools/devtools_background_services.pb.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/public/browser/content_browser_client.h"
@@ -231,7 +232,7 @@
     {
       base::RunLoop run_loop;
       embedded_worker_test_helper_.context()->registry()->FindRegistrationForId(
-          service_worker_registration_id, origin_,
+          service_worker_registration_id, storage::StorageKey(origin_),
           base::BindOnce(&DidFindServiceWorkerRegistration,
                          &service_worker_registration_,
                          run_loop.QuitClosure()));
diff --git a/content/browser/devtools/protocol/system_info_handler.cc b/content/browser/devtools/protocol/system_info_handler.cc
index 325c5af..0a7de38f 100644
--- a/content/browser/devtools/protocol/system_info_handler.cc
+++ b/content/browser/devtools/protocol/system_info_handler.cc
@@ -230,7 +230,8 @@
   enumerator.EndAuxAttributes();
 
   std::unique_ptr<base::DictionaryValue> base_feature_status =
-      GetFeatureStatus();
+      base::DictionaryValue::From(
+          std::make_unique<base::Value>(GetFeatureStatus()));
   std::unique_ptr<protocol::DictionaryValue> feature_status =
       protocol::DictionaryValue::cast(
           protocol::toProtocolValue(base_feature_status.get(), 1000));
diff --git a/content/browser/file_system_access/file_system_chooser_unittest.cc b/content/browser/file_system_access/file_system_chooser_unittest.cc
index 3fc7b59..0b6108751 100644
--- a/content/browser/file_system_access/file_system_chooser_unittest.cc
+++ b/content/browser/file_system_access/file_system_chooser_unittest.cc
@@ -237,19 +237,19 @@
       new CancellingSelectFileDialogFactory(&dialog_params));
   std::vector<blink::mojom::ChooseFileSystemEntryAcceptsOptionPtr> accepts;
   accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
-      base::UTF8ToUTF16("Description        with \t      a  \r   lot   of  \n "
-                        "                                 spaces"),
+      u"Description        with \t      a  \r   lot   of  \n "
+      u"                                 spaces",
       std::vector<std::string>({}), std::vector<std::string>({"txt"})));
   accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
-      base::UTF8ToUTF16("Description that is very long and should be "
-                        "truncated to 64 code points if it works"),
+      u"Description that is very long and should be "
+      u"truncated to 64 code points if it works",
       std::vector<std::string>({}), std::vector<std::string>({"js"})));
   accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
-      base::UTF8ToUTF16("Unbalanced RTL \xe2\x80\xae section"),
-      std::vector<std::string>({}), std::vector<std::string>({"js"})));
+      u"Unbalanced RTL \u202e section", std::vector<std::string>({}),
+      std::vector<std::string>({"js"})));
   accepts.emplace_back(blink::mojom::ChooseFileSystemEntryAcceptsOption::New(
-      base::UTF8ToUTF16("Unbalanced RTL \xe2\x80\xae section in a otherwise "
-                        "very long description that will be truncated"),
+      u"Unbalanced RTL \u202e section in a otherwise "
+      u"very long description that will be truncated",
       std::vector<std::string>({}), std::vector<std::string>({"js"})));
   SyncShowDialog(std::move(accepts), /*include_accepts_all=*/false);
 
@@ -258,16 +258,14 @@
             dialog_params.file_types->extension_description_overrides.size());
   EXPECT_EQ(u"Description with a lot of spaces",
             dialog_params.file_types->extension_description_overrides[0]);
+  EXPECT_EQ(u"Description that is very long and should be truncated to 64 cod…",
+            dialog_params.file_types->extension_description_overrides[1]);
+  EXPECT_EQ(u"Unbalanced RTL \u202e section\u202c",
+            dialog_params.file_types->extension_description_overrides[2]);
   EXPECT_EQ(
-      base::UTF8ToUTF16(
-          "Description that is very long and should be truncated to 64 cod…"),
-      dialog_params.file_types->extension_description_overrides[1]);
-  EXPECT_EQ(
-      base::UTF8ToUTF16("Unbalanced RTL \xe2\x80\xae section\xe2\x80\xac"),
-      dialog_params.file_types->extension_description_overrides[2]);
-  EXPECT_EQ(base::UTF8ToUTF16("Unbalanced RTL \xe2\x80\xae section in a "
-                              "otherwise very long description t…\xe2\x80\xac"),
-            dialog_params.file_types->extension_description_overrides[3]);
+      u"Unbalanced RTL \u202e section in a "
+      u"otherwise very long description t…\u202c",
+      dialog_params.file_types->extension_description_overrides[3]);
 }
 
 }  // namespace content
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index 2f29c71..d3604f9 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -19,6 +19,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
 #include "base/system/sys_info.h"
+#include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "cc/base/switches.h"
@@ -195,8 +196,7 @@
   return kGpuFeatureData[index];
 }
 
-std::unique_ptr<base::DictionaryValue> GetFeatureStatusImpl(
-    GpuFeatureInfoType type) {
+base::Value GetFeatureStatusImpl(GpuFeatureInfoType type) {
   GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
   std::string gpu_access_blocked_reason;
   bool gpu_access_blocked;
@@ -214,7 +214,7 @@
         manager->IsGpuCompositingDisabledForHardwareGpu();
   }
 
-  auto feature_status_dict = std::make_unique<base::DictionaryValue>();
+  auto feature_status_dict = base::Value(base::Value::Type::DICTIONARY);
 
   bool eof = false;
   for (size_t i = 0; !eof; ++i) {
@@ -262,12 +262,12 @@
         status += "_on";
       }
     }
-    feature_status_dict->SetString(gpu_feature_data.name, status);
+    feature_status_dict.SetStringKey(gpu_feature_data.name, status);
   }
   return feature_status_dict;
 }
 
-std::unique_ptr<base::ListValue> GetProblemsImpl(GpuFeatureInfoType type) {
+base::Value GetProblemsImpl(GpuFeatureInfoType type) {
   GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance();
   std::string gpu_access_blocked_reason;
   bool gpu_access_blocked;
@@ -285,29 +285,29 @@
         manager->IsGpuCompositingDisabledForHardwareGpu();
   }
 
-  auto problem_list = std::make_unique<base::ListValue>();
+  auto problem_list = base::Value(base::Value::Type::LIST);
   if (!gpu_feature_info.applied_gpu_blocklist_entries.empty()) {
     std::unique_ptr<gpu::GpuBlocklist> blocklist(gpu::GpuBlocklist::Create());
-    blocklist->GetReasons(problem_list.get(), "disabledFeatures",
+    blocklist->GetReasons(problem_list, "disabledFeatures",
                           gpu_feature_info.applied_gpu_blocklist_entries);
   }
   if (!gpu_feature_info.applied_gpu_driver_bug_list_entries.empty()) {
     std::unique_ptr<gpu::GpuDriverBugList> bug_list(
         gpu::GpuDriverBugList::Create());
-    bug_list->GetReasons(problem_list.get(), "workarounds",
+    bug_list->GetReasons(problem_list, "workarounds",
                          gpu_feature_info.applied_gpu_driver_bug_list_entries);
   }
 
   if (gpu_access_blocked) {
-    auto problem = std::make_unique<base::DictionaryValue>();
-    problem->SetString("description", "GPU process was unable to boot: " +
-                                          gpu_access_blocked_reason);
-    problem->Set("crBugs", std::make_unique<base::ListValue>());
-    auto disabled_features = std::make_unique<base::ListValue>();
-    disabled_features->AppendString("all");
-    problem->Set("affectedGpuSettings", std::move(disabled_features));
-    problem->SetString("tag", "disabledFeatures");
-    problem_list->Insert(0, std::move(problem));
+    auto problem = base::Value(base::Value::Type::DICTIONARY);
+    problem.SetStringKey("description", "GPU process was unable to boot: " +
+                                            gpu_access_blocked_reason);
+    problem.SetKey("crBugs", base::Value(base::Value::Type::LIST));
+    auto disabled_features = base::Value(base::Value::Type::LIST);
+    disabled_features.Append("all");
+    problem.SetKey("affectedGpuSettings", std::move(disabled_features));
+    problem.SetStringKey("tag", "disabledFeatures");
+    problem_list.Insert(problem_list.GetList().begin(), std::move(problem));
   }
 
   bool eof = false;
@@ -316,15 +316,15 @@
         gpu_feature_info, i, is_gpu_compositing_disabled, &eof);
     if (gpu_feature_data.disabled &&
         gpu_feature_data.disabled_info.is_problem) {
-      auto problem = std::make_unique<base::DictionaryValue>();
-      problem->SetString("description",
-                         gpu_feature_data.disabled_info.description);
-      problem->Set("crBugs", std::make_unique<base::ListValue>());
-      auto disabled_features = std::make_unique<base::ListValue>();
-      disabled_features->AppendString(gpu_feature_data.name);
-      problem->Set("affectedGpuSettings", std::move(disabled_features));
-      problem->SetString("tag", "disabledFeatures");
-      problem_list->Append(std::move(problem));
+      auto problem = base::Value(base::Value::Type::DICTIONARY);
+      problem.SetStringKey("description",
+                           gpu_feature_data.disabled_info.description);
+      problem.SetKey("crBugs", base::Value(base::Value::Type::LIST));
+      auto disabled_features = base::Value(base::Value::Type::LIST);
+      disabled_features.Append(gpu_feature_data.name);
+      problem.SetKey("affectedGpuSettings", std::move(disabled_features));
+      problem.SetStringKey("tag", "disabledFeatures");
+      problem_list.Insert(problem_list.GetList().begin(), std::move(problem));
     }
   }
   return problem_list;
@@ -473,11 +473,11 @@
   return true;
 }
 
-std::unique_ptr<base::DictionaryValue> GetFeatureStatus() {
+base::Value GetFeatureStatus() {
   return GetFeatureStatusImpl(GpuFeatureInfoType::kCurrent);
 }
 
-std::unique_ptr<base::ListValue> GetProblems() {
+base::Value GetProblems() {
   return GetProblemsImpl(GpuFeatureInfoType::kCurrent);
 }
 
@@ -485,11 +485,11 @@
   return GetDriverBugWorkaroundsImpl(GpuFeatureInfoType::kCurrent);
 }
 
-std::unique_ptr<base::DictionaryValue> GetFeatureStatusForHardwareGpu() {
+base::Value GetFeatureStatusForHardwareGpu() {
   return GetFeatureStatusImpl(GpuFeatureInfoType::kForHardwareGpu);
 }
 
-std::unique_ptr<base::ListValue> GetProblemsForHardwareGpu() {
+base::Value GetProblemsForHardwareGpu() {
   return GetProblemsImpl(GpuFeatureInfoType::kForHardwareGpu);
 }
 
diff --git a/content/browser/gpu/compositor_util.h b/content/browser/gpu/compositor_util.h
index c7155e6..2d7ece9f 100644
--- a/content/browser/gpu/compositor_util.h
+++ b/content/browser/gpu/compositor_util.h
@@ -37,13 +37,12 @@
 // Returns true if main thread can be pipelined with activation.
 CONTENT_EXPORT bool IsMainFrameBeforeActivationEnabled();
 
-CONTENT_EXPORT std::unique_ptr<base::DictionaryValue> GetFeatureStatus();
-CONTENT_EXPORT std::unique_ptr<base::ListValue> GetProblems();
+CONTENT_EXPORT base::Value GetFeatureStatus();
+CONTENT_EXPORT base::Value GetProblems();
 CONTENT_EXPORT std::vector<std::string> GetDriverBugWorkarounds();
 
-CONTENT_EXPORT std::unique_ptr<base::DictionaryValue>
-GetFeatureStatusForHardwareGpu();
-CONTENT_EXPORT std::unique_ptr<base::ListValue> GetProblemsForHardwareGpu();
+CONTENT_EXPORT base::Value GetFeatureStatusForHardwareGpu();
+CONTENT_EXPORT base::Value GetProblemsForHardwareGpu();
 CONTENT_EXPORT std::vector<std::string> GetDriverBugWorkaroundsForHardwareGpu();
 
 }  // namespace content
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index 23dfde8..f4169cde 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -107,41 +107,38 @@
 }
 
 // Must be in sync with the copy in //ui/base/x/x11_util.cc.
-std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
-    base::StringPiece desc,
-    base::StringPiece value) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("description", desc);
-  dict->SetString("value", value);
+base::Value NewDescriptionValuePair(base::StringPiece desc,
+                                    base::StringPiece value) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("description", desc);
+  dict.SetStringKey("value", value);
   return dict;
 }
 
-std::unique_ptr<base::DictionaryValue> NewDescriptionValuePair(
-    base::StringPiece desc,
-    std::unique_ptr<base::Value> value) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
-  dict->SetString("description", desc);
-  dict->Set("value", std::move(value));
+base::Value NewDescriptionValuePair(base::StringPiece desc, base::Value value) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetStringKey("description", desc);
+  dict.SetKey("value", std::move(value));
   return dict;
 }
 
 #if defined(OS_WIN)
 // Output DxDiagNode tree as nested array of {description,value} pairs
-std::unique_ptr<base::ListValue> DxDiagNodeToList(const gpu::DxDiagNode& node) {
-  auto list = std::make_unique<base::ListValue>();
+base::Value DxDiagNodeToList(const gpu::DxDiagNode& node) {
+  base::Value list(base::Value::Type::LIST);
   for (std::map<std::string, std::string>::const_iterator it =
       node.values.begin();
       it != node.values.end();
       ++it) {
-    list->Append(NewDescriptionValuePair(it->first, it->second));
+    list.Append(NewDescriptionValuePair(it->first, it->second));
   }
 
   for (std::map<std::string, gpu::DxDiagNode>::const_iterator it =
       node.children.begin();
       it != node.children.end();
       ++it) {
-    std::unique_ptr<base::ListValue> sublist = DxDiagNodeToList(it->second);
-    list->Append(NewDescriptionValuePair(it->first, std::move(sublist)));
+    base::Value sublist(DxDiagNodeToList(it->second));
+    list.Append(NewDescriptionValuePair(it->first, std::move(sublist)));
   }
   return list;
 }
@@ -173,58 +170,54 @@
   return rt;
 }
 
-std::unique_ptr<base::ListValue> BasicGpuInfoAsListValue(
-    const gpu::GPUInfo& gpu_info,
-    const gpu::GpuFeatureInfo& gpu_feature_info,
-    const gfx::GpuExtraInfo& gpu_extra_info) {
+base::Value BasicGpuInfoAsListValue(const gpu::GPUInfo& gpu_info,
+                                    const gpu::GpuFeatureInfo& gpu_feature_info,
+                                    const gfx::GpuExtraInfo& gpu_extra_info) {
   const gpu::GPUInfo::GPUDevice& active_gpu = gpu_info.active_gpu();
-  auto basic_info = std::make_unique<base::ListValue>();
-  basic_info->Append(NewDescriptionValuePair(
+  auto basic_info = base::Value(base::Value::Type::LIST);
+  basic_info.Append(NewDescriptionValuePair(
       "Initialization time",
       base::NumberToString(gpu_info.initialization_time.InMilliseconds())));
-  basic_info->Append(NewDescriptionValuePair(
-      "In-process GPU",
-      std::make_unique<base::Value>(gpu_info.in_process_gpu)));
-  basic_info->Append(NewDescriptionValuePair(
-      "Passthrough Command Decoder",
-      std::make_unique<base::Value>(gpu_info.passthrough_cmd_decoder)));
-  basic_info->Append(NewDescriptionValuePair(
-      "Sandboxed", std::make_unique<base::Value>(gpu_info.sandboxed)));
-  basic_info->Append(
+  basic_info.Append(NewDescriptionValuePair(
+      "In-process GPU", base::Value(gpu_info.in_process_gpu)));
+  basic_info.Append(
+      NewDescriptionValuePair("Passthrough Command Decoder",
+                              base::Value(gpu_info.passthrough_cmd_decoder)));
+  basic_info.Append(
+      NewDescriptionValuePair("Sandboxed", base::Value(gpu_info.sandboxed)));
+  basic_info.Append(
       NewDescriptionValuePair("GPU0", GPUDeviceToString(gpu_info.gpu)));
   for (size_t i = 0; i < gpu_info.secondary_gpus.size(); ++i) {
-    basic_info->Append(NewDescriptionValuePair(
+    basic_info.Append(NewDescriptionValuePair(
         base::StringPrintf("GPU%d", static_cast<int>(i + 1)),
         GPUDeviceToString(gpu_info.secondary_gpus[i])));
   }
-  basic_info->Append(NewDescriptionValuePair(
-      "Optimus", std::make_unique<base::Value>(gpu_info.optimus)));
-  basic_info->Append(NewDescriptionValuePair(
-      "AMD switchable",
-      std::make_unique<base::Value>(gpu_info.amd_switchable)));
+  basic_info.Append(
+      NewDescriptionValuePair("Optimus", base::Value(gpu_info.optimus)));
+  basic_info.Append(NewDescriptionValuePair(
+      "AMD switchable", base::Value(gpu_info.amd_switchable)));
 #if defined(OS_WIN)
   std::string compositor =
       ui::win::IsAeroGlassEnabled() ? "Aero Glass" : "none";
-  basic_info->Append(
-      NewDescriptionValuePair("Desktop compositing", compositor));
+  basic_info.Append(NewDescriptionValuePair("Desktop compositing", compositor));
 
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "Direct composition",
-      std::make_unique<base::Value>(gpu_info.overlay_info.direct_composition)));
-  basic_info->Append(NewDescriptionValuePair(
+      base::Value(gpu_info.overlay_info.direct_composition)));
+  basic_info.Append(NewDescriptionValuePair(
       "Supports overlays",
-      std::make_unique<base::Value>(gpu_info.overlay_info.supports_overlays)));
-  basic_info->Append(NewDescriptionValuePair(
+      base::Value(gpu_info.overlay_info.supports_overlays)));
+  basic_info.Append(NewDescriptionValuePair(
       "YUY2 overlay support",
       gpu::OverlaySupportToString(gpu_info.overlay_info.yuy2_overlay_support)));
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "NV12 overlay support",
       gpu::OverlaySupportToString(gpu_info.overlay_info.nv12_overlay_support)));
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "BGRA8 overlay support",
       gpu::OverlaySupportToString(
           gpu_info.overlay_info.bgra8_overlay_support)));
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "RGB10A2 overlay support",
       gpu::OverlaySupportToString(
           gpu_info.overlay_info.rgb10a2_overlay_support)));
@@ -240,53 +233,51 @@
     std::string size_string = base::StringPrintf("%.1f\"", rounded_size_inches);
     std::string description_string = base::StringPrintf(
         "Diagonal Monitor Size of %s", display_size.display_name.c_str());
-    basic_info->Append(
-        NewDescriptionValuePair(description_string, size_string));
+    basic_info.Append(NewDescriptionValuePair(description_string, size_string));
   }
 
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "Driver D3D12 feature level",
       gpu::D3DFeatureLevelToString(gpu_info.d3d12_feature_level)));
 
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "Driver Vulkan API version",
       gpu::VulkanVersionToString(gpu_info.vulkan_version)));
 #endif
 
-  basic_info->Append(
+  basic_info.Append(
       NewDescriptionValuePair("Driver vendor", active_gpu.driver_vendor));
-  basic_info->Append(
+  basic_info.Append(
       NewDescriptionValuePair("Driver version", active_gpu.driver_version));
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "GPU CUDA compute capability major version",
-      std::make_unique<base::Value>(active_gpu.cuda_compute_capability_major)));
-  basic_info->Append(NewDescriptionValuePair("Pixel shader version",
-                                             gpu_info.pixel_shader_version));
-  basic_info->Append(NewDescriptionValuePair("Vertex shader version",
-                                             gpu_info.vertex_shader_version));
-  basic_info->Append(
+      base::Value(active_gpu.cuda_compute_capability_major)));
+  basic_info.Append(NewDescriptionValuePair("Pixel shader version",
+                                            gpu_info.pixel_shader_version));
+  basic_info.Append(NewDescriptionValuePair("Vertex shader version",
+                                            gpu_info.vertex_shader_version));
+  basic_info.Append(
       NewDescriptionValuePair("Max. MSAA samples", gpu_info.max_msaa_samples));
-  basic_info->Append(NewDescriptionValuePair("Machine model name",
-                                             gpu_info.machine_model_name));
-  basic_info->Append(NewDescriptionValuePair("Machine model version",
-                                             gpu_info.machine_model_version));
-  basic_info->Append(NewDescriptionValuePair("GL_VENDOR", gpu_info.gl_vendor));
-  basic_info->Append(
+  basic_info.Append(NewDescriptionValuePair("Machine model name",
+                                            gpu_info.machine_model_name));
+  basic_info.Append(NewDescriptionValuePair("Machine model version",
+                                            gpu_info.machine_model_version));
+  basic_info.Append(NewDescriptionValuePair("GL_VENDOR", gpu_info.gl_vendor));
+  basic_info.Append(
       NewDescriptionValuePair("GL_RENDERER", gpu_info.gl_renderer));
-  basic_info->Append(
-      NewDescriptionValuePair("GL_VERSION", gpu_info.gl_version));
-  basic_info->Append(
+  basic_info.Append(NewDescriptionValuePair("GL_VERSION", gpu_info.gl_version));
+  basic_info.Append(
       NewDescriptionValuePair("GL_EXTENSIONS", gpu_info.gl_extensions));
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "Disabled Extensions", gpu_feature_info.disabled_extensions));
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "Disabled WebGL Extensions", gpu_feature_info.disabled_webgl_extensions));
-  basic_info->Append(NewDescriptionValuePair("Window system binding vendor",
-                                             gpu_info.gl_ws_vendor));
-  basic_info->Append(NewDescriptionValuePair("Window system binding version",
-                                             gpu_info.gl_ws_version));
-  basic_info->Append(NewDescriptionValuePair("Window system binding extensions",
-                                             gpu_info.gl_ws_extensions));
+  basic_info.Append(NewDescriptionValuePair("Window system binding vendor",
+                                            gpu_info.gl_ws_vendor));
+  basic_info.Append(NewDescriptionValuePair("Window system binding version",
+                                            gpu_info.gl_ws_version));
+  basic_info.Append(NewDescriptionValuePair("Window system binding extensions",
+                                            gpu_info.gl_ws_extensions));
 
   {
     base::Value gpu_extra_info_as_list_value =
@@ -301,7 +292,7 @@
           LOG(WARNING) << "Unexpected item format: should have a string "
                           "description and a value.";
         }
-        basic_info->Append(std::move(pair));
+        basic_info.Append(std::move(pair));
       }
     }
   }
@@ -319,17 +310,17 @@
   } else {
     direct_rendering_version = "unknown";
   }
-  basic_info->Append(NewDescriptionValuePair("Direct rendering version",
-                                             direct_rendering_version));
+  basic_info.Append(NewDescriptionValuePair("Direct rendering version",
+                                            direct_rendering_version));
 
   std::string reset_strategy =
       base::StringPrintf("0x%04x", gpu_info.gl_reset_notification_strategy);
-  basic_info->Append(
+  basic_info.Append(
       NewDescriptionValuePair("Reset notification strategy", reset_strategy));
 
-  basic_info->Append(NewDescriptionValuePair(
-      "GPU process crash count",
-      std::make_unique<base::Value>(GpuProcessHost::GetGpuCrashCount())));
+  basic_info.Append(
+      NewDescriptionValuePair("GPU process crash count",
+                              base::Value(GpuProcessHost::GetGpuCrashCount())));
 
   std::string buffer_formats;
   for (int i = 0; i <= static_cast<int>(gfx::BufferFormat::LAST); ++i) {
@@ -342,15 +333,15 @@
         buffer_format);
     buffer_formats += supported ? ": supported" : ": not supported";
   }
-  basic_info->Append(NewDescriptionValuePair(
+  basic_info.Append(NewDescriptionValuePair(
       "gfx::BufferFormats supported for allocation and texturing",
       buffer_formats));
 
   return basic_info;
 }
 
-std::unique_ptr<base::DictionaryValue> GpuInfoAsDictionaryValue() {
-  auto info = std::make_unique<base::DictionaryValue>();
+base::Value GpuInfoAsDictionaryValue() {
+  base::Value info(base::Value::Type::DICTIONARY);
 
   const gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
   const gpu::GpuFeatureInfo gpu_feature_info =
@@ -359,40 +350,39 @@
       GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
   auto basic_info =
       BasicGpuInfoAsListValue(gpu_info, gpu_feature_info, gpu_extra_info);
-  info->Set("basicInfo", std::move(basic_info));
+  info.SetKey("basicInfo", std::move(basic_info));
 
 #if defined(OS_WIN)
-  auto dx_info = std::make_unique<base::Value>();
+  base::Value dx_info(base::Value::Type::LIST);
   if (gpu_info.dx_diagnostics.children.size())
     dx_info = DxDiagNodeToList(gpu_info.dx_diagnostics);
-  info->Set("diagnostics", std::move(dx_info));
+  info.SetKey("diagnostics", std::move(dx_info));
 #endif
 
 #if BUILDFLAG(ENABLE_VULKAN)
   if (gpu_info.vulkan_info) {
     auto blob = gpu_info.vulkan_info->Serialize();
-    info->SetString("vulkanInfo", base::Base64Encode(blob));
+    info.SetStringKey("vulkanInfo", base::Base64Encode(blob));
   }
 #endif
 
   return info;
 }
 
-std::unique_ptr<base::ListValue> CompositorInfo() {
-  auto compositor_info = std::make_unique<base::ListValue>();
+base::Value CompositorInfo() {
+  auto compositor_info = base::Value(base::Value::Type::LIST);
 
-  compositor_info->Append(NewDescriptionValuePair(
+  compositor_info.Append(NewDescriptionValuePair(
       "Tile Update Mode",
       IsZeroCopyUploadEnabled() ? "Zero-copy" : "One-copy"));
 
-  compositor_info->Append(NewDescriptionValuePair(
+  compositor_info.Append(NewDescriptionValuePair(
       "Partial Raster", IsPartialRasterEnabled() ? "Enabled" : "Disabled"));
   return compositor_info;
 }
 
-std::unique_ptr<base::ListValue> GpuMemoryBufferInfo(
-    const gfx::GpuExtraInfo& gpu_extra_info) {
-  auto gpu_memory_buffer_info = std::make_unique<base::ListValue>();
+base::Value GpuMemoryBufferInfo(const gfx::GpuExtraInfo& gpu_extra_info) {
+  auto gpu_memory_buffer_info = base::Value(base::Value::Type::LIST);
 
   gpu::GpuMemoryBufferSupport gpu_memory_buffer_support;
 
@@ -425,43 +415,43 @@
     if (native_usage_support.empty())
       native_usage_support = base::StringPrintf("Software only");
 
-    gpu_memory_buffer_info->Append(NewDescriptionValuePair(
+    gpu_memory_buffer_info.Append(NewDescriptionValuePair(
         gfx::BufferFormatToString(static_cast<gfx::BufferFormat>(format)),
         native_usage_support));
   }
   return gpu_memory_buffer_info;
 }
 
-std::unique_ptr<base::ListValue> GetDisplayInfo() {
-  auto display_info = std::make_unique<base::ListValue>();
+base::Value GetDisplayInfo() {
+  auto display_info = base::Value(base::Value::Type::LIST);
   const std::vector<display::Display> displays =
       display::Screen::GetScreen()->GetAllDisplays();
   for (const auto& display : displays) {
-    display_info->Append(NewDescriptionValuePair("Info ", display.ToString()));
+    display_info.Append(NewDescriptionValuePair("Info ", display.ToString()));
     {
       std::vector<std::string> names;
       std::vector<gfx::ColorSpace> color_spaces;
       std::vector<gfx::BufferFormat> buffer_formats;
       display.color_spaces().ToStrings(&names, &color_spaces, &buffer_formats);
       for (size_t i = 0; i < names.size(); ++i) {
-        display_info->Append(NewDescriptionValuePair(
+        display_info.Append(NewDescriptionValuePair(
             base::StringPrintf("Color space (%s)", names[i].c_str()),
             color_spaces[i].ToString()));
-        display_info->Append(NewDescriptionValuePair(
+        display_info.Append(NewDescriptionValuePair(
             base::StringPrintf("Buffer format (%s)", names[i].c_str()),
             gfx::BufferFormatToString(buffer_formats[i])));
       }
     }
-    display_info->Append(NewDescriptionValuePair(
+    display_info.Append(NewDescriptionValuePair(
         "SDR white level in nits",
         base::NumberToString(display.color_spaces().GetSDRWhiteLevel())));
-    display_info->Append(NewDescriptionValuePair(
+    display_info.Append(NewDescriptionValuePair(
         "Bits per color component",
         base::NumberToString(display.depth_per_component())));
-    display_info->Append(NewDescriptionValuePair(
+    display_info.Append(NewDescriptionValuePair(
         "Bits per pixel", base::NumberToString(display.color_depth())));
     if (display.display_frequency()) {
-      display_info->Append(NewDescriptionValuePair(
+      display_info.Append(NewDescriptionValuePair(
           "Refresh Rate in Hz",
           base::NumberToString(display.display_frequency())));
     }
@@ -512,30 +502,30 @@
 }
 #endif  // OS_WIN
 
-std::unique_ptr<base::ListValue> GetDevicePerfInfo() {
-  auto list = std::make_unique<base::ListValue>();
+base::Value GetDevicePerfInfo() {
+  auto list = base::Value(base::Value::Type::LIST);
   const base::Optional<gpu::DevicePerfInfo> device_perf_info =
       gpu::GetDevicePerfInfo();
   if (device_perf_info.has_value()) {
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "Total Physical Memory (Gb)",
         base::NumberToString(device_perf_info->total_physical_memory_mb /
                              1024)));
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "Total Disk Space (Gb)",
         base::NumberToString(device_perf_info->total_disk_space_mb / 1024)));
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "Hardware Concurrency",
         base::NumberToString(device_perf_info->hardware_concurrency)));
 
 #if defined(OS_WIN)
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "System Commit Limit (Gb)",
         base::NumberToString(device_perf_info->system_commit_limit_mb / 1024)));
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "D3D11 Feature Level",
         D3dFeatureLevelToString(device_perf_info->d3d11_feature_level)));
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "Has Discrete GPU",
         HasDiscreteGpuToString(device_perf_info->has_discrete_gpu)));
 #endif  // OS_WIN
@@ -550,10 +540,10 @@
         intel_gpu_gen = base::NumberToString(
             static_cast<int>(device_perf_info->intel_gpu_generation));
       }
-      list->Append(
+      list.Append(
           NewDescriptionValuePair("Intel GPU Generation", intel_gpu_gen));
     }
-    list->Append(NewDescriptionValuePair(
+    list.Append(NewDescriptionValuePair(
         "Software Rendering",
         device_perf_info->software_rendering ? "Yes" : "No"));
   }
@@ -627,9 +617,9 @@
   return "";
 }
 
-std::unique_ptr<base::ListValue> GetVideoAcceleratorsInfo() {
+base::Value GetVideoAcceleratorsInfo() {
   gpu::GPUInfo gpu_info = GpuDataManagerImpl::GetInstance()->GetGPUInfo();
-  auto info = std::make_unique<base::ListValue>();
+  auto info = base::Value(base::Value::Type::LIST);
 
   for (const auto& profile :
        gpu_info.video_decode_accelerator_capabilities.supported_profiles) {
@@ -639,7 +629,7 @@
         "%s to %s pixels%s", profile.min_resolution.ToString().c_str(),
         profile.max_resolution.ToString().c_str(),
         profile.encrypted_only ? " (encrypted)" : "");
-    info->Append(NewDescriptionValuePair(codec_string, resolution_string));
+    info.Append(NewDescriptionValuePair(codec_string, resolution_string));
   }
 
   for (const auto& profile :
@@ -652,24 +642,24 @@
         profile.max_resolution.ToString().c_str(),
         static_cast<double>(profile.max_framerate_numerator) /
             profile.max_framerate_denominator);
-    info->Append(NewDescriptionValuePair(codec_string, resolution_string));
+    info.Append(NewDescriptionValuePair(codec_string, resolution_string));
   }
   return info;
 }
 
-std::unique_ptr<base::ListValue> GetANGLEFeatures() {
+base::Value GetANGLEFeatures() {
   gfx::GpuExtraInfo gpu_extra_info =
       GpuDataManagerImpl::GetInstance()->GetGpuExtraInfo();
-  auto angle_features_list = std::make_unique<base::ListValue>();
+  auto angle_features_list = base::Value(base::Value::Type::LIST);
   for (const auto& feature : gpu_extra_info.angle_features) {
-    auto angle_feature = std::make_unique<base::DictionaryValue>();
-    angle_feature->SetString("name", feature.name);
-    angle_feature->SetString("category", feature.category);
-    angle_feature->SetString("description", feature.description);
-    angle_feature->SetString("bug", feature.bug);
-    angle_feature->SetString("status", feature.status);
-    angle_feature->SetString("condition", feature.condition);
-    angle_features_list->Append(std::move(angle_feature));
+    auto angle_feature = base::Value(base::Value::Type::DICTIONARY);
+    angle_feature.SetStringKey("name", feature.name);
+    angle_feature.SetStringKey("category", feature.category);
+    angle_feature.SetStringKey("description", feature.description);
+    angle_feature.SetStringKey("bug", feature.bug);
+    angle_feature.SetStringKey("status", feature.status);
+    angle_feature.SetStringKey("condition", feature.condition);
+    angle_features_list.Append(std::move(angle_feature));
   }
 
   return angle_features_list;
@@ -846,50 +836,51 @@
   auto gpu_info_val = GpuInfoAsDictionaryValue();
 
   // Add in blocklisting features
-  auto feature_status = std::make_unique<base::DictionaryValue>();
-  feature_status->Set("featureStatus", GetFeatureStatus());
-  feature_status->Set("problems", GetProblems());
-  auto workarounds = std::make_unique<base::ListValue>();
+  auto feature_status = base::Value(base::Value::Type::DICTIONARY);
+  feature_status.SetKey("featureStatus", GetFeatureStatus());
+  feature_status.SetKey("problems", GetProblems());
+  auto workarounds = base::Value(base::Value::Type::LIST);
   for (const auto& workaround : GetDriverBugWorkarounds())
-    workarounds->AppendString(workaround);
-  feature_status->Set("workarounds", std::move(workarounds));
-  gpu_info_val->Set("featureStatus", std::move(feature_status));
+    workarounds.Append(workaround);
+  feature_status.SetKey("workarounds", std::move(workarounds));
+  gpu_info_val.SetKey("featureStatus", std::move(feature_status));
   if (!GpuDataManagerImpl::GetInstance()->IsGpuProcessUsingHardwareGpu()) {
     const gpu::GPUInfo gpu_info_for_hardware_gpu =
         GpuDataManagerImpl::GetInstance()->GetGPUInfoForHardwareGpu();
     if (gpu_info_for_hardware_gpu.IsInitialized()) {
       auto feature_status_for_hardware_gpu =
-          std::make_unique<base::DictionaryValue>();
-      feature_status_for_hardware_gpu->Set("featureStatus",
-                                           GetFeatureStatusForHardwareGpu());
-      feature_status_for_hardware_gpu->Set("problems",
-                                           GetProblemsForHardwareGpu());
-      auto workarounds_for_hardware_gpu = std::make_unique<base::ListValue>();
+          base::Value(base::Value::Type::DICTIONARY);
+      feature_status_for_hardware_gpu.SetKey("featureStatus",
+                                             GetFeatureStatusForHardwareGpu());
+      feature_status_for_hardware_gpu.SetKey("problems",
+                                             GetProblemsForHardwareGpu());
+      auto workarounds_for_hardware_gpu = base::Value(base::Value::Type::LIST);
       for (const auto& workaround : GetDriverBugWorkaroundsForHardwareGpu())
-        workarounds_for_hardware_gpu->AppendString(workaround);
-      feature_status_for_hardware_gpu->Set(
+        workarounds_for_hardware_gpu.Append(workaround);
+      feature_status_for_hardware_gpu.SetKey(
           "workarounds", std::move(workarounds_for_hardware_gpu));
-      gpu_info_val->Set("featureStatusForHardwareGpu",
-                        std::move(feature_status_for_hardware_gpu));
+      gpu_info_val.SetKey("featureStatusForHardwareGpu",
+                          std::move(feature_status_for_hardware_gpu));
       const gpu::GpuFeatureInfo gpu_feature_info_for_hardware_gpu =
           GpuDataManagerImpl::GetInstance()->GetGpuFeatureInfoForHardwareGpu();
       auto gpu_info_for_hardware_gpu_val = BasicGpuInfoAsListValue(
           gpu_info_for_hardware_gpu, gpu_feature_info_for_hardware_gpu,
           gfx::GpuExtraInfo{});
-      gpu_info_val->Set("basicInfoForHardwareGpu",
-                        std::move(gpu_info_for_hardware_gpu_val));
+      gpu_info_val.SetKey("basicInfoForHardwareGpu",
+                          std::move(gpu_info_for_hardware_gpu_val));
     }
   }
-  gpu_info_val->Set("compositorInfo", CompositorInfo());
-  gpu_info_val->Set("gpuMemoryBufferInfo", GpuMemoryBufferInfo(gpu_extra_info));
-  gpu_info_val->Set("displayInfo", GetDisplayInfo());
-  gpu_info_val->Set("videoAcceleratorsInfo", GetVideoAcceleratorsInfo());
-  gpu_info_val->Set("ANGLEFeatures", GetANGLEFeatures());
-  gpu_info_val->Set("devicePerfInfo", GetDevicePerfInfo());
+  gpu_info_val.SetKey("compositorInfo", CompositorInfo());
+  gpu_info_val.SetKey("gpuMemoryBufferInfo",
+                      GpuMemoryBufferInfo(gpu_extra_info));
+  gpu_info_val.SetKey("displayInfo", GetDisplayInfo());
+  gpu_info_val.SetKey("videoAcceleratorsInfo", GetVideoAcceleratorsInfo());
+  gpu_info_val.SetKey("ANGLEFeatures", GetANGLEFeatures());
+  gpu_info_val.SetKey("devicePerfInfo", GetDevicePerfInfo());
 
   // Send GPU Info to javascript.
   web_ui()->CallJavascriptFunctionUnsafe("browserBridge.onGpuInfoUpdate",
-                                         *(gpu_info_val.get()));
+                                         gpu_info_val);
 }
 
 void GpuMessageHandler::OnGpuSwitched(gl::GpuPreference active_gpu_heuristic) {
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index 0212255..14c32e96 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -81,7 +81,7 @@
   return arg.Equals(key);
 }
 
-static const char kDatabaseName[] = "db";
+static const char16_t kDatabaseName[] = u"db";
 static const char kOrigin[] = "https://www.example.com";
 
 base::FilePath CreateAndReturnTempDir(base::ScopedTempDir* temp_dir) {
@@ -255,7 +255,7 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         connection = std::make_unique<TestDatabaseConnection>(
             context_impl_->IDBTaskRunner(), url::Origin::Create(GURL(kOrigin)),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            kDatabaseName, kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -287,7 +287,7 @@
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
-  const char kObjectStoreName[] = "os";
+  const char16_t kObjectStoreName[] = u"os";
   std::unique_ptr<TestDatabaseConnection> connection;
   IndexedDBDatabaseMetadata metadata;
   mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
@@ -297,8 +297,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -337,8 +337,7 @@
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         connection->version_change_transaction->CreateObjectStore(
-            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-            blink::IndexedDBKeyPath(), false);
+            kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
         connection->version_change_transaction->Commit(0);
       }));
   loop2.Run();
@@ -357,7 +356,7 @@
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
-  const char kObjectStoreName[] = "os";
+  const char16_t kObjectStoreName[] = u"os";
   std::unique_ptr<TestDatabaseConnection> connection1;
   mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database1;
   IndexedDBDatabaseMetadata metadata1;
@@ -368,7 +367,7 @@
         // Open connection 1, and expect the upgrade needed.
         connection1 = std::make_unique<TestDatabaseConnection>(
             context_impl_->IDBTaskRunner(), url::Origin::Create(GURL(kOrigin)),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            kDatabaseName, kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection1->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -394,8 +393,8 @@
   context_impl_->IDBTaskRunner()->PostTask(
       FROM_HERE, base::BindLambdaForTesting([&]() {
         connection2 = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, 0);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, 0);
 
         // Check that we're called in order and the second connection gets it's
         // database after the first connection completes.
@@ -425,8 +424,7 @@
 
         // Create object store.
         connection1->version_change_transaction->CreateObjectStore(
-            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-            blink::IndexedDBKeyPath(), false);
+            kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
         connection1->version_change_transaction->Commit(0);
       }));
   loop2.Run();
@@ -457,7 +455,7 @@
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
-  const char kObjectStoreName[] = "os";
+  const char16_t kObjectStoreName[] = u"os";
   std::unique_ptr<TestDatabaseConnection> connection;
   IndexedDBDatabaseMetadata metadata;
   mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
@@ -468,7 +466,7 @@
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
             context_impl_->IDBTaskRunner(), url::Origin::Create(GURL(kOrigin)),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            kDatabaseName, kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -518,8 +516,7 @@
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         connection->version_change_transaction->CreateObjectStore(
-            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-            blink::IndexedDBKeyPath(), false);
+            kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
         // Call Put with an invalid blob.
         std::vector<blink::mojom::IDBExternalObjectPtr> external_objects;
         mojo::PendingRemote<blink::mojom::Blob> blob;
@@ -571,8 +568,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -633,7 +630,7 @@
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
-  const char kObjectStoreName[] = "os";
+  const char16_t kObjectStoreName[] = u"os";
   std::unique_ptr<TestDatabaseConnection> connection;
   IndexedDBDatabaseMetadata metadata;
   mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
@@ -643,8 +640,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -689,8 +686,7 @@
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         connection->version_change_transaction->CreateObjectStore(
-            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-            blink::IndexedDBKeyPath(), false);
+            kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
         idb_mojo_factory_->AbortTransactionsAndCompactDatabase(base::BindOnce(
             &TestStatusCallback, std::move(quit_closure2), &callback_result));
       }));
@@ -719,8 +715,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
                                         IndexedDBDatabaseMetadata::NO_VERSION,
@@ -793,8 +789,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
         {
           EXPECT_CALL(*connection->open_callbacks,
                       MockedUpgradeNeeded(
@@ -859,7 +855,7 @@
   const int64_t kDBVersion = 1;
   const int64_t kTransactionId = 1;
   const int64_t kObjectStoreId = 10;
-  const char kObjectStoreName[] = "os";
+  const char16_t kObjectStoreName[] = u"os";
   std::unique_ptr<TestDatabaseConnection> connection;
   IndexedDBDatabaseMetadata metadata;
   mojo::PendingAssociatedRemote<blink::mojom::IDBDatabase> pending_database;
@@ -869,8 +865,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -916,8 +912,7 @@
         ASSERT_TRUE(connection->database.is_bound());
         ASSERT_TRUE(connection->version_change_transaction.is_bound());
         connection->version_change_transaction->CreateObjectStore(
-            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-            blink::IndexedDBKeyPath(), false);
+            kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
         idb_mojo_factory_->AbortTransactionsForDatabase(base::BindOnce(
             &TestStatusCallback, std::move(quit_closure2), &callback_result));
       }));
@@ -946,8 +941,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -1018,8 +1013,8 @@
   const int64_t kTransactionId3 = 3;
   const int64_t kObjectStoreId = 10;
   const int64_t kIndexId = 100;
-  const char kObjectStoreName[] = "os";
-  const char kIndexName[] = "index";
+  const char16_t kObjectStoreName[] = u"os";
+  const char16_t kIndexName[] = u"index";
 
   mojo::PendingReceiver<storage::mojom::IndexedDBObserver> receiver;
   mojo::PendingRemote<storage::mojom::IndexedDBObserver> remote;
@@ -1037,8 +1032,8 @@
     context_impl_->IDBTaskRunner()->PostTask(
         FROM_HERE, base::BindLambdaForTesting([&]() {
           connection1 = std::make_unique<TestDatabaseConnection>(
-              context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-              base::UTF8ToUTF16(kDatabaseName), kDBVersion1, kTransactionId1);
+              context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+              kDBVersion1, kTransactionId1);
 
           EXPECT_CALL(*connection1->open_callbacks,
                       MockedUpgradeNeeded(
@@ -1082,12 +1077,11 @@
 
           ASSERT_TRUE(connection1->database.is_bound());
           connection1->version_change_transaction->CreateObjectStore(
-              kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-              blink::IndexedDBKeyPath(), false);
-          connection1->database->CreateIndex(
-              kTransactionId1, kObjectStoreId, kIndexId,
-              base::UTF8ToUTF16(kIndexName), blink::IndexedDBKeyPath(), false,
+              kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(),
               false);
+          connection1->database->CreateIndex(
+              kTransactionId1, kObjectStoreId, kIndexId, kIndexName,
+              blink::IndexedDBKeyPath(), false, false);
           connection1->version_change_transaction->Commit(0);
         }));
     loop.Run();
@@ -1111,8 +1105,8 @@
 
           connection2 = std::make_unique<TestDatabaseConnection>(
               context_impl_->IDBTaskRunner(),
-              url::Origin::Create(GURL(kOrigin)),
-              base::UTF8ToUTF16(kDatabaseName), kDBVersion2, kTransactionId2);
+              url::Origin::Create(GURL(kOrigin)), kDatabaseName, kDBVersion2,
+              kTransactionId2);
 
           EXPECT_CALL(*connection2->open_callbacks,
                       MockedUpgradeNeeded(
@@ -1174,8 +1168,8 @@
         FROM_HERE, base::BindLambdaForTesting([&]() {
           connection2->database->Close();
           connection3 = std::make_unique<TestDatabaseConnection>(
-              context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-              base::UTF8ToUTF16(kDatabaseName), kDBVersion3, kTransactionId3);
+              context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+              kDBVersion3, kTransactionId3);
 
           EXPECT_CALL(*connection3->open_callbacks,
                       MockedUpgradeNeeded(
@@ -1249,7 +1243,7 @@
   const int64_t kTransactionId1 = 1;
   const int64_t kTransactionId2 = 2;
   const int64_t kObjectStoreId = 10;
-  const char kObjectStoreName[] = "os";
+  const char16_t kObjectStoreName[] = u"os";
 
   mojo::PendingReceiver<storage::mojom::IndexedDBObserver> receiver;
   mojo::PendingRemote<storage::mojom::IndexedDBObserver> remote;
@@ -1268,7 +1262,7 @@
         // Open connection 1.
         connection1 = std::make_unique<TestDatabaseConnection>(
             context_impl_->IDBTaskRunner(), url::Origin::Create(GURL(kOrigin)),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion1, kTransactionId1);
+            kDatabaseName, kDBVersion1, kTransactionId1);
 
         EXPECT_CALL(*connection1->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
@@ -1315,8 +1309,7 @@
         ASSERT_TRUE(connection1->database.is_bound());
         ASSERT_TRUE(connection1->version_change_transaction.is_bound());
         connection1->version_change_transaction->CreateObjectStore(
-            kObjectStoreId, base::UTF8ToUTF16(kObjectStoreName),
-            blink::IndexedDBKeyPath(), false);
+            kObjectStoreId, kObjectStoreName, blink::IndexedDBKeyPath(), false);
 
         std::string value = "value";
         const char* value_data = value.data();
@@ -1357,8 +1350,8 @@
         ::testing::InSequence dummy;
 
         connection2 = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion2, kTransactionId2);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion2, kTransactionId2);
 
         EXPECT_CALL(*connection2->open_callbacks,
                     MockedUpgradeNeeded(
@@ -1450,8 +1443,8 @@
       FROM_HERE, base::BindLambdaForTesting([&]() {
         // Open connection.
         connection = std::make_unique<TestDatabaseConnection>(
-            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin),
-            base::UTF8ToUTF16(kDatabaseName), kDBVersion, kTransactionId);
+            context_impl_->IDBTaskRunner(), ToOrigin(kOrigin), kDatabaseName,
+            kDBVersion, kTransactionId);
 
         EXPECT_CALL(*connection->open_callbacks,
                     MockedUpgradeNeeded(IsAssociatedInterfacePtrInfoValid(true),
diff --git a/content/browser/media/media_internals_unittest.cc b/content/browser/media/media_internals_unittest.cc
index ab4b44e..09a06fc 100644
--- a/content/browser/media/media_internals_unittest.cc
+++ b/content/browser/media/media_internals_unittest.cc
@@ -308,8 +308,8 @@
 namespace {
 
 // Test page titles.
-const char kTestTitle1[] = "Test Title 1";
-const char kTestTitle2[] = "Test Title 2";
+const char16_t kTestTitle1[] = u"Test Title 1";
+const char16_t kTestTitle2[] = u"Test Title 2";
 
 }  // namespace
 
@@ -415,8 +415,7 @@
 TEST_F(MediaInternalsAudioFocusTest, AudioFocusStateIsUpdated) {
   // Create a test media session and request audio focus.
   std::unique_ptr<WebContents> web_contents1 = CreateTestWebContents();
-  static_cast<TestWebContents*>(web_contents1.get())
-      ->SetTitle(base::UTF8ToUTF16(kTestTitle1));
+  static_cast<TestWebContents*>(web_contents1.get())->SetTitle(kTestTitle1);
   MediaSessionImpl* media_session1 = MediaSessionImpl::Get(web_contents1.get());
   media_session1->RequestSystemAudioFocus(AudioFocusType::kGain);
   WaitForCallbackCount(1);
@@ -438,8 +437,7 @@
 
   // Create another media session.
   std::unique_ptr<WebContents> web_contents2 = CreateTestWebContents();
-  static_cast<TestWebContents*>(web_contents2.get())
-      ->SetTitle(base::UTF8ToUTF16(kTestTitle2));
+  static_cast<TestWebContents*>(web_contents2.get())->SetTitle(kTestTitle2);
   MediaSessionImpl* media_session2 = MediaSessionImpl::Get(web_contents2.get());
   media_session2->RequestSystemAudioFocus(
       AudioFocusType::kGainTransientMayDuck);
diff --git a/content/browser/notifications/blink_notification_service_impl_unittest.cc b/content/browser/notifications/blink_notification_service_impl_unittest.cc
index e27c23e..3694fd89 100644
--- a/content/browser/notifications/blink_notification_service_impl_unittest.cc
+++ b/content/browser/notifications/blink_notification_service_impl_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/notifications/blink_notification_service_impl.h"
 #include "content/browser/notifications/platform_notification_context_impl.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
@@ -190,7 +191,7 @@
       base::RunLoop run_loop;
       embedded_worker_helper_->context()->registry()->FindRegistrationForId(
           service_worker_registration_id,
-          url::Origin::Create(GURL(kTestOrigin)),
+          storage::StorageKey(url::Origin::Create(GURL(kTestOrigin))),
           base::BindOnce(&BlinkNotificationServiceImplTest::
                              DidFindServiceWorkerRegistration,
                          base::Unretained(this), service_worker_registration,
diff --git a/content/browser/notifications/notification_storage_unittest.cc b/content/browser/notifications/notification_storage_unittest.cc
index 1582d7b..046c2bbf 100644
--- a/content/browser/notifications/notification_storage_unittest.cc
+++ b/content/browser/notifications/notification_storage_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/guid.h"
 #include "base/run_loop.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/public/test/browser_task_environment.h"
@@ -86,7 +87,7 @@
     {
       base::RunLoop run_loop;
       helper_->context()->registry()->FindRegistrationForId(
-          service_worker_registration_id_, origin_,
+          service_worker_registration_id_, storage::StorageKey(origin_),
           base::BindOnce(
               &NotificationStorageTest::DidFindServiceWorkerRegistration,
               base::Unretained(this), &service_worker_registration,
diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
index 72cc421..6a869e3a 100644
--- a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
+++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc
@@ -36,7 +36,7 @@
 namespace {
 
 struct FontExpectation {
-  const char font_name[64];
+  const char* font_name;
   uint16_t ttc_index;
 };
 
@@ -354,10 +354,10 @@
     return;
   base::FilePath result_path;
   uint32_t ttc_index;
-  std::string unavailable_font_name =
-      "Unavailable_Font_Name_56E7EA7E-2C69-4E23-99DC-750BC19B250E";
-  dwrite_font_proxy().MatchUniqueFont(base::UTF8ToUTF16(unavailable_font_name),
-                                      &result_path, &ttc_index);
+  std::u16string unavailable_font_name =
+      u"Unavailable_Font_Name_56E7EA7E-2C69-4E23-99DC-750BC19B250E";
+  dwrite_font_proxy().MatchUniqueFont(unavailable_font_name, &result_path,
+                                      &ttc_index);
   ASSERT_EQ(result_path.value().size(), 0u);
   ASSERT_EQ(ttc_index, 0u);
 }
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index a0c85c1..0a9f181 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -10,6 +10,7 @@
 #include "base/callback_helpers.h"
 #include "base/guid.h"
 #include "base/strings/stringprintf.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/service_worker/service_worker_consts.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
@@ -240,7 +241,7 @@
                            "ServiceWorkerContainerHost::GetRegistration",
                            trace_id, "Client URL", client_url.spec());
   context_->registry()->FindRegistrationForClientUrl(
-      client_url,
+      client_url, storage::StorageKey(url::Origin::Create(client_url)),
       base::AdaptCallbackForRepeating(base::BindOnce(
           &ServiceWorkerContainerHost::GetRegistrationComplete,
           weak_factory_.GetWeakPtr(), std::move(callback), trace_id)));
@@ -271,8 +272,8 @@
   TRACE_EVENT_ASYNC_BEGIN0("ServiceWorker",
                            "ServiceWorkerContainerHost::GetRegistrations",
                            trace_id);
-  context_->registry()->GetRegistrationsForOrigin(
-      url::Origin::Create(url_),
+  context_->registry()->GetRegistrationsForStorageKey(
+      storage::StorageKey(url::Origin::Create(url_)),
       base::AdaptCallbackForRepeating(base::BindOnce(
           &ServiceWorkerContainerHost::GetRegistrationsComplete,
           weak_factory_.GetWeakPtr(), std::move(callback), trace_id)));
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 5a10246..34c95e6 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -20,6 +20,7 @@
 #include "base/strings/string_util.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/services/storage/public/cpp/quota_client_callback_wrapper.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/log_console_message.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
@@ -564,8 +565,8 @@
 void ServiceWorkerContextCore::DeleteForOrigin(const url::Origin& origin,
                                                StatusCallback callback) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
-  registry()->GetRegistrationsForOrigin(
-      origin,
+  registry()->GetRegistrationsForStorageKey(
+      storage::StorageKey(origin),
       base::BindOnce(
           &ServiceWorkerContextCore::DidGetRegistrationsForDeleteForOrigin,
           AsWeakPtr(), origin, std::move(callback)));
@@ -592,7 +593,8 @@
   // unload.
   std::vector<scoped_refptr<ServiceWorkerRegistration>>
       uninstalling_registrations =
-          registry()->GetUninstallingRegistrationsForOrigin(origin);
+          registry()->GetUninstallingRegistrationsForStorageKey(
+              storage::StorageKey(origin));
   for (const auto& uninstalling_registration : uninstalling_registrations) {
     job_coordinator_->Abort(uninstalling_registration->scope());
     uninstalling_registration->DeleteAndClearImmediately();
@@ -839,9 +841,10 @@
     const GURL& url,
     ServiceWorkerContext::CheckHasServiceWorkerCallback callback) {
   registry()->FindRegistrationForClientUrl(
-      url, base::BindOnce(&ServiceWorkerContextCore::
-                              DidFindRegistrationForCheckHasServiceWorker,
-                          AsWeakPtr(), std::move(callback)));
+      url, storage::StorageKey(url::Origin::Create(url)),
+      base::BindOnce(&ServiceWorkerContextCore::
+                         DidFindRegistrationForCheckHasServiceWorker,
+                     AsWeakPtr(), std::move(callback)));
 }
 
 void ServiceWorkerContextCore::CheckOfflineCapability(
diff --git a/content/browser/service_worker/service_worker_context_core_unittest.cc b/content/browser/service_worker/service_worker_context_core_unittest.cc
index 68ecd4b..026d20f1 100644
--- a/content/browser/service_worker/service_worker_context_core_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_core_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/callback_helpers.h"
 #include "base/test/bind.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_core_observer.h"
@@ -85,11 +86,13 @@
   }
 
   // Wrapper for ServiceWorkerRegistry::FindRegistrationForScope.
-  blink::ServiceWorkerStatusCode FindRegistrationForScope(const GURL& scope) {
+  blink::ServiceWorkerStatusCode FindRegistrationForScope(
+      const GURL& scope,
+      const storage::StorageKey& key) {
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode status;
     context()->registry()->FindRegistrationForScope(
-        scope,
+        scope, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode result_status,
                 scoped_refptr<ServiceWorkerRegistration> result_registration) {
@@ -192,6 +195,7 @@
 TEST_F(ServiceWorkerContextCoreTest, DeleteForOrigin) {
   const GURL script("https://www.example.com/a/sw.js");
   const GURL scope("https://www.example.com/a");
+  const storage::StorageKey key(url::Origin::Create(scope));
   const url::Origin origin = url::Origin::Create(scope);
 
   // Register a service worker.
@@ -205,7 +209,7 @@
 
   // The registration should be deleted.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForScope(scope));
+            FindRegistrationForScope(scope, key));
 }
 
 TEST_F(ServiceWorkerContextCoreTest, DeleteForOriginAbortsQueuedJobs) {
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 98c0fc4..fd9fdaee 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/fake_embedded_worker_instance_client.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
@@ -639,7 +640,7 @@
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -689,7 +690,7 @@
   EXPECT_EQ(registration_id, notifications_[0].registration_id);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
@@ -740,7 +741,7 @@
   EXPECT_EQ(registration_id, notifications_[1].registration_id);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -774,7 +775,7 @@
   ASSERT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
@@ -873,23 +874,23 @@
   ASSERT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id1, url::Origin::Create(origin1_s1),
+      registration_id1, storage::StorageKey(url::Origin::Create(origin1_s1)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
   context()->registry()->FindRegistrationForId(
-      registration_id2, url::Origin::Create(origin1_s2),
+      registration_id2, storage::StorageKey(url::Origin::Create(origin1_s2)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, false /* expect_active */));
   context()->registry()->FindRegistrationForId(
-      registration_id3, url::Origin::Create(origin2_s1),
+      registration_id3, storage::StorageKey(url::Origin::Create(origin2_s1)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
 
   context()->registry()->FindRegistrationForId(
-      registration_id4, url::Origin::Create(origin3_s1),
+      registration_id4, storage::StorageKey(url::Origin::Create(origin3_s1)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1146,7 +1147,7 @@
   EXPECT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1157,7 +1158,7 @@
   // The storage is disabled while the recovery process is running, so the
   // operation should be aborted.
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorAbort,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1166,7 +1167,7 @@
   // The context started over and the storage was re-initialized, so the
   // registration should not be found.
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kErrorNotFound,
                      false /* expect_waiting */, true /* expect_active */));
@@ -1182,7 +1183,7 @@
   EXPECT_TRUE(called);
 
   context()->registry()->FindRegistrationForId(
-      registration_id, url::Origin::Create(scope),
+      registration_id, storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ExpectRegisteredWorkers,
                      blink::ServiceWorkerStatusCode::kOk,
                      false /* expect_waiting */, true /* expect_active */));
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 2014f05..6f38989 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -23,6 +23,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/service_worker/service_worker_storage_control_impl.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
@@ -772,6 +773,7 @@
   }
   context_core_->registry()->FindRegistrationForClientUrl(
       net::SimplifyUrlForRequest(document_url),
+      storage::StorageKey(url::Origin::Create(document_url)),
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForNavigationHint,
           this, std::move(callback_with_recording_metrics)));
@@ -894,6 +896,7 @@
   }
   context_core_->registry()->FindRegistrationForClientUrl(
       net::SimplifyUrlForRequest(client_url),
+      storage::StorageKey(url::Origin::Create(client_url)),
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
           /*include_installing_version=*/false, std::move(callback)));
@@ -911,6 +914,7 @@
   const bool include_installing_version = false;
   context_core_->registry()->FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
+      storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
           include_installing_version, std::move(callback)));
@@ -936,7 +940,7 @@
     return;
   }
   context_core_->registry()->FindRegistrationForId(
-      registration_id, origin,
+      registration_id, storage::StorageKey(origin),
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
           /*include_installing_version=*/false, std::move(callback)));
@@ -984,8 +988,8 @@
             std::vector<scoped_refptr<ServiceWorkerRegistration>>()));
     return;
   }
-  context_core_->registry()->GetRegistrationsForOrigin(origin,
-                                                       std::move(callback));
+  context_core_->registry()->GetRegistrationsForStorageKey(
+      storage::StorageKey(origin), std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::GetRegistrationUserData(
@@ -1127,7 +1131,8 @@
     return;
   }
   context_core_->registry()->StoreUserData(
-      registration_id, origin, key_value_pairs, std::move(callback));
+      registration_id, storage::StorageKey(origin), key_value_pairs,
+      std::move(callback));
 }
 
 void ServiceWorkerContextWrapper::ClearRegistrationUserData(
@@ -1309,6 +1314,7 @@
   }
   context_core_->registry()->FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
+      storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&DidFindRegistrationForStartActiveWorker,
                      std::move(callback)));
 }
@@ -1319,6 +1325,7 @@
     return;
   context_core_->registry()->FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
+      storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce([](blink::ServiceWorkerStatusCode status,
                         scoped_refptr<ServiceWorkerRegistration> registration) {
         if (status != blink::ServiceWorkerStatusCode::kOk ||
@@ -1336,6 +1343,7 @@
     return;
   context_core_->registry()->FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
+      storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(&ServiceWorkerContextWrapper::DidFindRegistrationForUpdate,
                      this));
 }
@@ -1384,6 +1392,7 @@
   }
   context_core_->registry()->FindRegistrationForScope(
       net::SimplifyUrlForRequest(scope),
+      storage::StorageKey(url::Origin::Create(scope)),
       base::BindOnce(
           &ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
           include_installing_version, std::move(callback)));
diff --git a/content/browser/service_worker/service_worker_context_wrapper_unittest.cc b/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
index 6c24448..41aaac5 100644
--- a/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/service_worker/service_worker_storage_control_impl.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_registration.h"
@@ -89,7 +90,7 @@
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
     registry()->DeleteRegistration(
-        registration, registration->scope().GetOrigin(),
+        registration, storage::StorageKey(registration->origin()),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
           loop.Quit();
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc
index 94572f5..866c5159 100644
--- a/content/browser/service_worker/service_worker_controllee_request_handler.cc
+++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc
@@ -11,6 +11,7 @@
 #include "base/optional.h"
 #include "base/trace_event/trace_event.h"
 #include "components/offline_pages/buildflags/buildflags.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/loader/navigation_url_loader_impl.h"
 #include "content/browser/navigation_subresource_loader_params.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
@@ -169,7 +170,7 @@
 
   // Look up a registration.
   context_->registry()->FindRegistrationForClientUrl(
-      stripped_url_,
+      stripped_url_, storage::StorageKey(url::Origin::Create(stripped_url_)),
       base::BindOnce(
           &ServiceWorkerControlleeRequestHandler::ContinueWithRegistration,
           weak_factory_.GetWeakPtr()));
@@ -448,7 +449,7 @@
     // Update failed. Look up the registration again since the original
     // registration was possibly unregistered in the meantime.
     context_->registry()->FindRegistrationForClientUrl(
-        stripped_url_,
+        stripped_url_, storage::StorageKey(url::Origin::Create(stripped_url_)),
         base::BindOnce(
             &ServiceWorkerControlleeRequestHandler::ContinueWithRegistration,
             weak_factory_.GetWeakPtr()));
@@ -504,7 +505,7 @@
     // continue with the incumbent version.
     // In case unregister job may have run, look up the registration again.
     context_->registry()->FindRegistrationForClientUrl(
-        stripped_url_,
+        stripped_url_, storage::StorageKey(url::Origin::Create(stripped_url_)),
         base::BindOnce(
             &ServiceWorkerControlleeRequestHandler::ContinueWithRegistration,
             weak_factory_.GetWeakPtr()));
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index ce276af..33f31686 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
@@ -196,6 +197,7 @@
                                    EmbeddedWorkerStatus running_status);
   scoped_refptr<ServiceWorkerRegistration> FindRegistrationForScope(
       const GURL& scope,
+      const storage::StorageKey& key,
       blink::ServiceWorkerStatusCode expected_status =
           blink::ServiceWorkerStatusCode::kOk);
   ServiceWorkerContainerHost* CreateControllee();
@@ -250,12 +252,14 @@
 scoped_refptr<ServiceWorkerRegistration>
 ServiceWorkerJobTest::FindRegistrationForScope(
     const GURL& scope,
+    const storage::StorageKey& key,
     blink::ServiceWorkerStatusCode expected_status) {
   scoped_refptr<ServiceWorkerRegistration> registration;
   base::RunLoop run_loop;
   registry()->FindRegistrationForScope(
-      scope, SaveFoundRegistration(expected_status, &registration,
-                                   run_loop.QuitClosure()));
+      scope, key,
+      SaveFoundRegistration(expected_status, &registration,
+                            run_loop.QuitClosure()));
   run_loop.Run();
   return registration;
 }
@@ -294,7 +298,10 @@
 
 TEST_F(ServiceWorkerJobTest, SameDocumentSameRegistration) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
-  options.scope = GURL("https://www.example.com/");
+  GURL url("https://www.example.com/");
+  storage::StorageKey key(url::Origin::Create(url));
+
+  options.scope = url;
   scoped_refptr<ServiceWorkerRegistration> original_registration =
       RunRegisterJob(GURL("https://www.example.com/service_worker.js"),
                      options);
@@ -303,12 +310,12 @@
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
   registry()->FindRegistrationForClientUrl(
-      GURL("https://www.example.com/"),
+      url, key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration1,
                             barrier_closure));
   scoped_refptr<ServiceWorkerRegistration> registration2;
   registry()->FindRegistrationForClientUrl(
-      GURL("https://www.example.com/"),
+      url, key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration2,
                             barrier_closure));
   run_loop.Run();
@@ -319,7 +326,10 @@
 
 TEST_F(ServiceWorkerJobTest, SameMatchSameRegistration) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
-  options.scope = GURL("https://www.example.com/");
+  GURL url("https://www.example.com/");
+  storage::StorageKey key(url::Origin::Create(url));
+
+  options.scope = url;
   scoped_refptr<ServiceWorkerRegistration> original_registration =
       RunRegisterJob(GURL("https://www.example.com/service_worker.js"),
                      options);
@@ -331,13 +341,13 @@
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
   registry()->FindRegistrationForClientUrl(
-      GURL("https://www.example.com/one"),
+      GURL("https://www.example.com/one"), key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration1,
                             barrier_closure));
 
   scoped_refptr<ServiceWorkerRegistration> registration2;
   registry()->FindRegistrationForClientUrl(
-      GURL("https://www.example.com/two"),
+      GURL("https://www.example.com/two"), key,
       SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration2,
                             barrier_closure));
   run_loop.Run();
@@ -349,6 +359,7 @@
   const GURL scope1("https://www.example.com/one");
   const GURL scope2("https://www.example.com/two");
   const GURL script_url("https://www.example.com/service_worker.js");
+  const storage::StorageKey key(url::Origin::Create(script_url));
   blink::mojom::ServiceWorkerRegistrationOptions options1;
   options1.scope = scope1;
   blink::mojom::ServiceWorkerRegistrationOptions options2;
@@ -362,12 +373,14 @@
   base::RepeatingClosure barrier_closure =
       base::BarrierClosure(2, run_loop.QuitClosure());
   registry()->FindRegistrationForClientUrl(
-      scope1, SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk,
-                                    &registration1, barrier_closure));
+      scope1, key,
+      SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration1,
+                            barrier_closure));
   scoped_refptr<ServiceWorkerRegistration> registration2;
   registry()->FindRegistrationForClientUrl(
-      scope2, SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk,
-                                    &registration2, barrier_closure));
+      scope2, key,
+      SaveFoundRegistration(blink::ServiceWorkerStatusCode::kOk, &registration2,
+                            barrier_closure));
 
   run_loop.Run();
   ASSERT_NE(registration1, registration2);
@@ -460,7 +473,8 @@
   EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, version->status());
 
   registration = FindRegistrationForScope(
-      options.scope, blink::ServiceWorkerStatusCode::kErrorNotFound);
+      options.scope, storage::StorageKey(url::Origin::Create(options.scope)),
+      blink::ServiceWorkerStatusCode::kErrorNotFound);
 
   EXPECT_FALSE(registration);
 }
@@ -499,6 +513,7 @@
 TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
+  storage::StorageKey key(url::Origin::Create(options.scope));
   scoped_refptr<ServiceWorkerRegistration> old_registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
   auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
@@ -507,7 +522,7 @@
   observer.RunUntilActivated(old_registration->installing_version(), runner);
 
   scoped_refptr<ServiceWorkerRegistration> old_registration_by_scope =
-      FindRegistrationForScope(options.scope);
+      FindRegistrationForScope(options.scope, key);
 
   ASSERT_EQ(old_registration, old_registration_by_scope);
   old_registration_by_scope = nullptr;
@@ -518,7 +533,7 @@
   ASSERT_EQ(old_registration, new_registration);
 
   scoped_refptr<ServiceWorkerRegistration> new_registration_by_scope =
-      FindRegistrationForScope(options.scope);
+      FindRegistrationForScope(options.scope, key);
 
   ASSERT_EQ(new_registration, new_registration_by_scope);
 }
@@ -529,6 +544,7 @@
   GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
+  storage::StorageKey key(url::Origin::Create(options.scope));
 
   scoped_refptr<ServiceWorkerRegistration> old_registration =
       RunRegisterJob(script_url, options);
@@ -552,7 +568,7 @@
   ASSERT_TRUE(old_registration->HasOneRef());
 
   scoped_refptr<ServiceWorkerRegistration> old_registration_by_scope =
-      FindRegistrationForScope(options.scope);
+      FindRegistrationForScope(options.scope, key);
 
   ASSERT_TRUE(old_registration_by_scope.get());
 
@@ -564,7 +580,7 @@
   ASSERT_FALSE(old_registration->HasOneRef());
 
   scoped_refptr<ServiceWorkerRegistration> new_registration_by_scope =
-      FindRegistrationForScope(options.scope);
+      FindRegistrationForScope(options.scope, key);
 
   EXPECT_EQ(new_registration_by_scope, old_registration);
 }
@@ -611,7 +627,8 @@
   RunUnregisterJob(options.scope);
 
   registration = FindRegistrationForScope(
-      options.scope, blink::ServiceWorkerStatusCode::kErrorNotFound);
+      options.scope, storage::StorageKey(url::Origin::Create(options.scope)),
+      blink::ServiceWorkerStatusCode::kErrorNotFound);
 
   ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
 }
@@ -635,7 +652,8 @@
                        blink::mojom::ServiceWorkerUpdateViaCache::kAll));
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      FindRegistrationForScope(scope);
+      FindRegistrationForScope(scope,
+                               storage::StorageKey(url::Origin::Create(scope)));
 
   ASSERT_EQ(registration2, registration);
 }
@@ -657,7 +675,9 @@
   ASSERT_EQ(registration1, registration2);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      FindRegistrationForScope(options.scope);
+      FindRegistrationForScope(
+          options.scope,
+          storage::StorageKey(url::Origin::Create(options.scope)));
 
   ASSERT_EQ(registration, registration1);
 }
@@ -676,6 +696,7 @@
   // crashing.
   scoped_refptr<ServiceWorkerRegistration> registration =
       FindRegistrationForScope(scope,
+                               storage::StorageKey(url::Origin::Create(scope)),
                                blink::ServiceWorkerStatusCode::kErrorNotFound);
 
   ASSERT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
@@ -710,10 +731,12 @@
   run_loop.Run();
 
   registration1 = FindRegistrationForScope(
-      options1.scope, blink::ServiceWorkerStatusCode::kErrorNotFound);
+      options1.scope, storage::StorageKey(url::Origin::Create(options1.scope)),
+      blink::ServiceWorkerStatusCode::kErrorNotFound);
 
   registration2 = FindRegistrationForScope(
-      options2.scope, blink::ServiceWorkerStatusCode::kErrorNotFound);
+      options2.scope, storage::StorageKey(url::Origin::Create(options2.scope)),
+      blink::ServiceWorkerStatusCode::kErrorNotFound);
 
   EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration1);
   EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration2);
@@ -766,7 +789,8 @@
   run_loop.Run();
 
   registration = FindRegistrationForScope(
-      options.scope, blink::ServiceWorkerStatusCode::kErrorNotFound);
+      options.scope, storage::StorageKey(url::Origin::Create(options.scope)),
+      blink::ServiceWorkerStatusCode::kErrorNotFound);
 
   EXPECT_EQ(scoped_refptr<ServiceWorkerRegistration>(), registration);
 }
@@ -798,11 +822,13 @@
   run_loop.Run();
 
   registration1 = FindRegistrationForScope(
-      options1.scope, blink::ServiceWorkerStatusCode::kErrorNotFound);
+      options1.scope, storage::StorageKey(url::Origin::Create(options1.scope)),
+      blink::ServiceWorkerStatusCode::kErrorNotFound);
   EXPECT_EQ(nullptr, registration1);
 
-  registration2 = FindRegistrationForScope(options2.scope,
-                                           blink::ServiceWorkerStatusCode::kOk);
+  registration2 = FindRegistrationForScope(
+      options2.scope, storage::StorageKey(url::Origin::Create(options2.scope)),
+      blink::ServiceWorkerStatusCode::kOk);
   EXPECT_NE(nullptr, registration2);
 }
 
@@ -966,6 +992,7 @@
   GURL script2("https://www.example.com/service_worker.js?new");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/one/");
+  storage::StorageKey key(url::Origin::Create(options.scope));
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       RunRegisterJob(script1, options);
@@ -983,7 +1010,7 @@
   // Wait until the worker becomes installed.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(registration, FindRegistrationForScope(options.scope));
+  EXPECT_EQ(registration, FindRegistrationForScope(options.scope, key));
   scoped_refptr<ServiceWorkerVersion> new_version =
       registration->waiting_version();
   ASSERT_TRUE(new_version);
@@ -994,7 +1021,7 @@
   RunUnregisterJob(options.scope,
                    blink::ServiceWorkerStatusCode::kErrorNotFound);
 
-  FindRegistrationForScope(options.scope,
+  FindRegistrationForScope(options.scope, key,
                            blink::ServiceWorkerStatusCode::kErrorNotFound);
   EXPECT_TRUE(registration->is_uninstalling());
   EXPECT_EQ(old_version, registration->active_version());
@@ -1101,6 +1128,7 @@
   GURL script("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
+  storage::StorageKey key(url::Origin::Create(options.scope));
   scoped_refptr<ServiceWorkerRegistration> registration;
 
   auto* fetch_handler_worker =
@@ -1110,7 +1138,7 @@
   RunRegisterJob(script, options);
   // Wait until the worker becomes active.
   base::RunLoop().RunUntilIdle();
-  registration = FindRegistrationForScope(options.scope);
+  registration = FindRegistrationForScope(options.scope, key);
   EXPECT_EQ(ServiceWorkerVersion::FetchHandlerExistence::EXISTS,
             registration->active_version()->fetch_handler_existence());
   RunUnregisterJob(options.scope);
@@ -1122,7 +1150,7 @@
   RunRegisterJob(script, options);
   // Wait until the worker becomes active.
   base::RunLoop().RunUntilIdle();
-  registration = FindRegistrationForScope(options.scope);
+  registration = FindRegistrationForScope(options.scope, key);
   EXPECT_EQ(ServiceWorkerVersion::FetchHandlerExistence::DOES_NOT_EXIST,
             registration->active_version()->fetch_handler_existence());
   RunUnregisterJob(options.scope);
@@ -1455,6 +1483,7 @@
   const GURL script_url("https://www.example.com/service_worker.js");
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
+  storage::StorageKey key(url::Origin::Create(options.scope));
 
   scoped_refptr<ServiceWorkerRegistration> old_registration =
       RunRegisterJob(script_url, options);
@@ -1480,7 +1509,7 @@
   EXPECT_EQ(0UL, container_host->registration_object_hosts_.size());
   EXPECT_TRUE(old_registration->HasOneRef());
 
-  EXPECT_TRUE(FindRegistrationForScope(options.scope));
+  EXPECT_TRUE(FindRegistrationForScope(options.scope, key));
 
   base::HistogramTester histogram_tester;
   options.update_via_cache = blink::mojom::ServiceWorkerUpdateViaCache::kNone;
@@ -1493,7 +1522,7 @@
             new_registration->update_via_cache());
 
   scoped_refptr<ServiceWorkerRegistration> new_registration_by_scope =
-      FindRegistrationForScope(options.scope);
+      FindRegistrationForScope(options.scope, key);
 
   EXPECT_EQ(new_registration_by_scope, old_registration);
 }
diff --git a/content/browser/service_worker/service_worker_offline_capability_check_browsertest.cc b/content/browser/service_worker/service_worker_offline_capability_check_browsertest.cc
index 103e767..c3a5b61 100644
--- a/content/browser/service_worker/service_worker_offline_capability_check_browsertest.cc
+++ b/content/browser/service_worker/service_worker_offline_capability_check_browsertest.cc
@@ -13,6 +13,7 @@
 #include "base/task/task_traits.h"
 #include "base/test/bind.h"
 #include "build/build_config.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
 #include "content/browser/service_worker/service_worker_version.h"
@@ -229,8 +230,10 @@
   void SetupFetchEventDispatchTargetVersionOnCoreThread(
       base::OnceClosure done) {
     DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
+    GURL url = embedded_test_server()->GetURL("/service_worker/");
+    storage::StorageKey key = storage::StorageKey(url::Origin::Create(url));
     wrapper()->context()->registry()->FindRegistrationForScope(
-        embedded_test_server()->GetURL("/service_worker/"),
+        embedded_test_server()->GetURL("/service_worker/"), key,
         base::BindOnce(&ServiceWorkerOfflineCapabilityCheckBrowserTest::
                            DidFindRegistration,
                        base::Unretained(this), std::move(done)));
diff --git a/content/browser/service_worker/service_worker_offline_capability_checker.cc b/content/browser/service_worker/service_worker_offline_capability_checker.cc
index 8395c69..2abaae6c 100644
--- a/content/browser/service_worker/service_worker_offline_capability_checker.cc
+++ b/content/browser/service_worker/service_worker_offline_capability_checker.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/guid.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/browser/service_worker/service_worker_registration.h"
@@ -33,11 +34,12 @@
     ServiceWorkerContext::CheckOfflineCapabilityCallback callback) {
   callback_ = std::move(callback);
   registry->FindRegistrationForClientUrl(
-      url_, base::BindOnce(
-                &ServiceWorkerOfflineCapabilityChecker::DidFindRegistration,
-                // We can use base::Unretained(this) because |this| is expected
-                // to be alive until the |callback_| is called.
-                base::Unretained(this)));
+      url_, storage::StorageKey(url::Origin::Create(url_)),
+      base::BindOnce(
+          &ServiceWorkerOfflineCapabilityChecker::DidFindRegistration,
+          // We can use base::Unretained(this) because |this| is expected
+          // to be alive until the |callback_| is called.
+          base::Unretained(this)));
 }
 
 void ServiceWorkerOfflineCapabilityChecker::DidFindRegistration(
diff --git a/content/browser/service_worker/service_worker_quota_client.cc b/content/browser/service_worker/service_worker_quota_client.cc
index ddb4265..bb0e8748 100644
--- a/content/browser/service_worker/service_worker_quota_client.cc
+++ b/content/browser/service_worker/service_worker_quota_client.cc
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/sequence_checker.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_usage_info.h"
@@ -49,8 +50,9 @@
                                               GetOriginUsageCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(type, StorageType::kTemporary);
-  context_->registry()->GetStorageUsageForOrigin(
-      origin, base::BindOnce(&FindUsageForOrigin, std::move(callback)));
+  context_->registry()->GetStorageUsageForStorageKey(
+      storage::StorageKey(origin),
+      base::BindOnce(&FindUsageForOrigin, std::move(callback)));
 }
 
 void ServiceWorkerQuotaClient::GetOriginsForType(
diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc
index 0cd91682..256a57d 100644
--- a/content/browser/service_worker/service_worker_register_job.cc
+++ b/content/browser/service_worker/service_worker_register_job.cc
@@ -15,6 +15,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_instance.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_consts.h"
@@ -143,15 +144,17 @@
   }
 
   scoped_refptr<ServiceWorkerRegistration> registration =
-      context_->registry()->GetUninstallingRegistration(scope_);
+      context_->registry()->GetUninstallingRegistration(
+          scope_, storage::StorageKey(url::Origin::Create(scope_)));
   if (registration.get())
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::BindOnce(std::move(next_step),
                        blink::ServiceWorkerStatusCode::kOk, registration));
   else
-    context_->registry()->FindRegistrationForScope(scope_,
-                                                   std::move(next_step));
+    context_->registry()->FindRegistrationForScope(
+        scope_, storage::StorageKey(url::Origin::Create(scope_)),
+        std::move(next_step));
 }
 
 void ServiceWorkerRegisterJob::Abort() {
@@ -371,6 +374,7 @@
   context_->registry()->NotifyInstallingRegistration(registration());
   context_->registry()->CreateNewVersion(
       registration(), script_url_, worker_script_type_,
+      storage::StorageKey(url::Origin::Create(script_url_)),
       base::BindOnce(&ServiceWorkerRegisterJob::StartWorkerForUpdate,
                      weak_factory_.GetWeakPtr()));
 }
@@ -541,7 +545,9 @@
                          weak_factory_.GetWeakPtr());
     }
     context_->registry()->CreateNewVersion(
-        registration(), script_url_, worker_script_type_, std::move(next_task));
+        registration(), script_url_, worker_script_type_,
+        storage::StorageKey(url::Origin::Create(script_url_)),
+        std::move(next_task));
     return;
   }
 
@@ -757,7 +763,7 @@
         registration()->NotifyRegistrationFailed();
         if (!registration()->is_deleted()) {
           context_->registry()->DeleteRegistration(
-              registration(), registration()->scope().GetOrigin(),
+              registration(), storage::StorageKey(registration()->origin()),
               base::DoNothing());
           context_->registry()->NotifyDoneUninstallingRegistration(
               registration(), ServiceWorkerRegistration::Status::kUninstalled);
@@ -861,7 +867,7 @@
 
     if (registration()->newest_installed_version()) {
       context_->registry()->UpdateLastUpdateCheckTime(
-          registration()->id(), registration()->scope().GetOrigin(),
+          registration()->id(), storage::StorageKey(registration()->origin()),
           registration()->last_update_check(),
           base::BindOnce([](blink::ServiceWorkerStatusCode status) {
             // Ignore errors; bumping the update check time is just best-effort.
diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc
index 71d55d2f5..df52910 100644
--- a/content/browser/service_worker/service_worker_registration.cc
+++ b/content/browser/service_worker/service_worker_registration.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
@@ -334,7 +335,7 @@
   }
 
   context_->registry()->DeleteRegistration(
-      this, scope().GetOrigin(),
+      this, storage::StorageKey(origin()),
       AdaptCallbackForRepeating(
           base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, this)));
 
@@ -346,7 +347,7 @@
   DCHECK(context_);
   if (!is_deleted()) {
     context_->registry()->DeleteRegistration(
-        this, scope().GetOrigin(),
+        this, storage::StorageKey(origin()),
         AdaptCallbackForRepeating(base::BindOnce(
             &ServiceWorkerRegistration::OnDeleteFinished, this)));
   }
@@ -588,7 +589,7 @@
   // Delete the registration and its state from storage.
   if (status() == Status::kIntact) {
     context_->registry()->DeleteRegistration(
-        this, scope().GetOrigin(),
+        this, storage::StorageKey(origin()),
         base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, protect));
   }
   DCHECK(is_uninstalling());
@@ -681,7 +682,7 @@
   // "Run the Update State algorithm passing registration's active worker and
   // 'activated' as the arguments."
   activating_version->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  context_->registry()->UpdateToActiveState(id(), scope().GetOrigin(),
+  context_->registry()->UpdateToActiveState(id(), storage::StorageKey(origin()),
                                             base::DoNothing());
 }
 
diff --git a/content/browser/service_worker/service_worker_registration_object_host.cc b/content/browser/service_worker/service_worker_registration_object_host.cc
index 35b94725..b33aa54 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.cc
+++ b/content/browser/service_worker/service_worker_registration_object_host.cc
@@ -8,6 +8,7 @@
 #include "base/callback_helpers.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/service_worker_consts.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
@@ -285,7 +286,7 @@
   }
 
   context_->registry()->UpdateNavigationPreloadEnabled(
-      registration_->id(), registration_->scope().GetOrigin(), enable,
+      registration_->id(), storage::StorageKey(registration_->origin()), enable,
       base::AdaptCallbackForRepeating(base::BindOnce(
           &ServiceWorkerRegistrationObjectHost::
               DidUpdateNavigationPreloadEnabled,
@@ -335,7 +336,7 @@
   }
 
   context_->registry()->UpdateNavigationPreloadHeader(
-      registration_->id(), registration_->scope().GetOrigin(), value,
+      registration_->id(), storage::StorageKey(registration_->origin()), value,
       base::AdaptCallbackForRepeating(base::BindOnce(
           &ServiceWorkerRegistrationObjectHost::
               DidUpdateNavigationPreloadHeader,
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index f6c3ec93..f9bba43 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -21,6 +21,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
 #include "content/browser/service_worker/fake_embedded_worker_instance_client.h"
@@ -854,10 +855,10 @@
 
   blink::ServiceWorkerStatusCode FindRegistrationInStorage(
       int64_t registration_id,
-      const GURL& scope) {
+      const storage::StorageKey& key) {
     base::Optional<blink::ServiceWorkerStatusCode> status;
     registry()->FindRegistrationForId(
-        registration_id, url::Origin::Create(scope),
+        registration_id, key,
         base::AdaptCallbackForRepeating(base::BindOnce(
             [](base::Optional<blink::ServiceWorkerStatusCode>* out_status,
                blink::ServiceWorkerStatusCode status,
@@ -1178,13 +1179,17 @@
   info->receiver.reset();
   info->waiting->receiver.reset();
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationInStorage(registration_id, kScope));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationInStorage(
+          registration_id, storage::StorageKey(url::Origin::Create(kScope))));
   EXPECT_EQ(blink::mojom::ServiceWorkerErrorType::kNone,
             CallUnregister(registration_host.get()));
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationInStorage(registration_id, kScope));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kErrorNotFound,
+      FindRegistrationInStorage(
+          registration_id, storage::StorageKey(url::Origin::Create(kScope))));
   EXPECT_EQ(blink::mojom::ServiceWorkerErrorType::kNotFound,
             CallUnregister(registration_host.get()));
 }
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc
index 9b2d1ad..d78cece 100644
--- a/content/browser/service_worker/service_worker_registry.cc
+++ b/content/browser/service_worker/service_worker_registry.cc
@@ -12,7 +12,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/trace_event/trace_event.h"
-#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/public/mojom/storage_policy_update.mojom.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
@@ -84,13 +83,13 @@
 void MaybeNotifyWriteFailed(
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
     storage::mojom::ServiceWorkerDatabaseStatus status,
-    const url::Origin& origin) {
+    const storage::StorageKey& key) {
   if (!quota_manager_proxy)
     return;
 
   if (status == storage::mojom::ServiceWorkerDatabaseStatus::kErrorFailed ||
       status == storage::mojom::ServiceWorkerDatabaseStatus::kErrorIOError) {
-    quota_manager_proxy->NotifyWriteFailed(origin);
+    quota_manager_proxy->NotifyWriteFailed(key.origin());
   }
 }
 
@@ -177,6 +176,7 @@
     scoped_refptr<ServiceWorkerRegistration> registration,
     const GURL& script_url,
     blink::mojom::ScriptType script_type,
+    const storage::StorageKey& key,
     NewVersionCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(registration);
@@ -184,11 +184,12 @@
       &storage::mojom::ServiceWorkerStorageControl::GetNewVersionId,
       base::BindOnce(&ServiceWorkerRegistry::DidGetNewVersionId,
                      weak_factory_.GetWeakPtr(), registration, script_url,
-                     script_type, std::move(callback)));
+                     script_type, key, std::move(callback)));
 }
 
 void ServiceWorkerRegistry::FindRegistrationForClientUrl(
     const GURL& client_url,
+    const storage::StorageKey& key,
     FindRegistrationCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // To connect this TRACE_EVENT with the callback, Time::Now() is used as a
@@ -202,13 +203,14 @@
       &storage::mojom::ServiceWorkerStorageControl::
           FindRegistrationForClientUrl,
       base::BindOnce(&ServiceWorkerRegistry::DidFindRegistrationForClientUrl,
-                     weak_factory_.GetWeakPtr(), client_url, trace_event_id,
-                     std::move(callback)),
-      client_url, storage::StorageKey(url::Origin::Create(client_url)));
+                     weak_factory_.GetWeakPtr(), client_url, key,
+                     trace_event_id, std::move(callback)),
+      client_url, key);
 }
 
 void ServiceWorkerRegistry::FindRegistrationForScope(
     const GURL& scope,
+    const storage::StorageKey& key,
     FindRegistrationCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (is_storage_disabled_) {
@@ -221,7 +223,7 @@
 
   // Look up installing registration before checking storage.
   scoped_refptr<ServiceWorkerRegistration> installing_registration =
-      FindInstallingRegistrationForScope(scope);
+      FindInstallingRegistrationForScope(scope, key);
   if (installing_registration && !installing_registration->is_deleted()) {
     CompleteFindSoon(FROM_HERE, std::move(installing_registration),
                      blink::ServiceWorkerStatusCode::kOk, std::move(callback));
@@ -232,14 +234,14 @@
       &storage::mojom::ServiceWorkerStorageControl::FindRegistrationForScope,
       base::BindOnce(&ServiceWorkerRegistry::DidFindRegistrationForScope,
                      weak_factory_.GetWeakPtr(), std::move(callback)),
-      scope, storage::StorageKey(url::Origin::Create(scope)));
+      scope, key);
 }
 
 void ServiceWorkerRegistry::FindRegistrationForId(
     int64_t registration_id,
-    const url::Origin& origin,
+    const storage::StorageKey& key,
     FindRegistrationCallback callback) {
-  FindRegistrationForIdInternal(registration_id, origin, std::move(callback));
+  FindRegistrationForIdInternal(registration_id, key, std::move(callback));
 }
 
 void ServiceWorkerRegistry::FindRegistrationForIdOnly(
@@ -249,29 +251,29 @@
                                 std::move(callback));
 }
 
-void ServiceWorkerRegistry::GetRegistrationsForOrigin(
-    const url::Origin& origin,
+void ServiceWorkerRegistry::GetRegistrationsForStorageKey(
+    const storage::StorageKey& key,
     GetRegistrationsCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::
           GetRegistrationsForStorageKey,
-      base::BindOnce(&ServiceWorkerRegistry::DidGetRegistrationsForOrigin,
-                     weak_factory_.GetWeakPtr(), std::move(callback), origin),
-      storage::StorageKey(origin));
+      base::BindOnce(&ServiceWorkerRegistry::DidGetRegistrationsForStorageKey,
+                     weak_factory_.GetWeakPtr(), std::move(callback), key),
+      key);
 }
 
-void ServiceWorkerRegistry::GetStorageUsageForOrigin(
-    const url::Origin& origin,
+void ServiceWorkerRegistry::GetStorageUsageForStorageKey(
+    const storage::StorageKey& key,
     GetStorageUsageForOriginCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   auto wrapped_callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
       std::move(callback), blink::ServiceWorkerStatusCode::kErrorFailed, 0);
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::GetUsageForStorageKey,
-      base::BindOnce(&ServiceWorkerRegistry::DidGetStorageUsageForOrigin,
+      base::BindOnce(&ServiceWorkerRegistry::DidGetStorageUsageForStorageKey,
                      weak_factory_.GetWeakPtr(), std::move(wrapped_callback)),
-      storage::StorageKey(origin));
+      key);
 }
 
 void ServiceWorkerRegistry::GetAllRegistrationsInfos(
@@ -284,8 +286,11 @@
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+// TODO(crbug.com/1199077): Use `key` once ServiceWorkerRegistration
+// implements StorageKey.
 ServiceWorkerRegistration* ServiceWorkerRegistry::GetUninstallingRegistration(
-    const GURL& scope) {
+    const GURL& scope,
+    const storage::StorageKey& key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // TODO(bashi): Should we check state of ServiceWorkerStorage?
   for (const auto& registration : uninstalling_registrations_) {
@@ -298,12 +303,14 @@
 }
 
 std::vector<scoped_refptr<ServiceWorkerRegistration>>
-ServiceWorkerRegistry::GetUninstallingRegistrationsForOrigin(
-    const url::Origin& origin) {
+ServiceWorkerRegistry::GetUninstallingRegistrationsForStorageKey(
+    const storage::StorageKey& key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   std::vector<scoped_refptr<ServiceWorkerRegistration>> results;
   for (const auto& registration : uninstalling_registrations_) {
-    if (url::Origin::Create(registration.second->scope()) == origin) {
+    // TODO(crbug/1199077): Use full `key` once ServiceWorkerRegistration
+    // implements StorageKey.
+    if (url::Origin::Create(registration.second->scope()) == key.origin()) {
       results.push_back(registration.second);
     }
   }
@@ -375,18 +382,22 @@
   }
   data->resources_total_size_bytes = resources_total_size_bytes;
 
+  // TODO(crbug/1199077): Use `key` once ServiceWorkerRegistration implements
+  // StorageKey.
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::StoreRegistration,
-      base::BindOnce(&ServiceWorkerRegistry::DidStoreRegistration,
-                     weak_factory_.GetWeakPtr(), registration->id(),
-                     resources_total_size_bytes, registration->scope(),
-                     std::move(callback)),
+      base::BindOnce(
+          &ServiceWorkerRegistry::DidStoreRegistration,
+          weak_factory_.GetWeakPtr(), registration->id(),
+          resources_total_size_bytes, registration->scope(),
+          storage::StorageKey(url::Origin::Create(registration->scope())),
+          std::move(callback)),
       std::move(data), std::move(resources));
 }
 
 void ServiceWorkerRegistry::DeleteRegistration(
     scoped_refptr<ServiceWorkerRegistration> registration,
-    const GURL& origin,
+    const storage::StorageKey& key,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (is_storage_disabled_) {
@@ -402,9 +413,9 @@
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::DeleteRegistration,
       base::BindOnce(&ServiceWorkerRegistry::DidDeleteRegistration,
-                     weak_factory_.GetWeakPtr(), registration->id(), origin,
+                     weak_factory_.GetWeakPtr(), registration->id(), key,
                      std::move(callback)),
-      registration->id(), storage::StorageKey(url::Origin::Create(origin)));
+      registration->id(), key);
 
   DCHECK(!base::Contains(uninstalling_registrations_, registration->id()));
   uninstalling_registrations_[registration->id()] = registration;
@@ -445,21 +456,19 @@
 }
 
 void ServiceWorkerRegistry::UpdateToActiveState(int64_t registration_id,
-                                                const GURL& origin,
+                                                const storage::StorageKey& key,
                                                 StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::UpdateToActiveState,
       base::BindOnce(&ServiceWorkerRegistry::DidUpdateToActiveState,
-                     weak_factory_.GetWeakPtr(), url::Origin::Create(origin),
-                     std::move(callback)),
-      static_cast<const int64_t>(registration_id),
-      storage::StorageKey(url::Origin::Create(origin)));
+                     weak_factory_.GetWeakPtr(), key, std::move(callback)),
+      static_cast<const int64_t>(registration_id), key);
 }
 
 void ServiceWorkerRegistry::UpdateLastUpdateCheckTime(
     int64_t registration_id,
-    const GURL& origin,
+    const storage::StorageKey& key,
     base::Time last_update_check_time,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -467,14 +476,13 @@
       &storage::mojom::ServiceWorkerStorageControl::UpdateLastUpdateCheckTime,
       base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
                      weak_factory_.GetWeakPtr(), std::move(callback)),
-      static_cast<const int64_t>(registration_id),
-      storage::StorageKey(url::Origin::Create(origin)),
+      static_cast<const int64_t>(registration_id), key,
       static_cast<const base::Time&>(last_update_check_time));
 }
 
 void ServiceWorkerRegistry::UpdateNavigationPreloadEnabled(
     int64_t registration_id,
-    const GURL& origin,
+    const storage::StorageKey& key,
     bool enable,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -483,14 +491,13 @@
           UpdateNavigationPreloadEnabled,
       base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
                      weak_factory_.GetWeakPtr(), std::move(callback)),
-      static_cast<const int64_t>(registration_id),
-      storage::StorageKey(url::Origin::Create(origin)),
+      static_cast<const int64_t>(registration_id), key,
       static_cast<const bool>(enable));
 }
 
 void ServiceWorkerRegistry::UpdateNavigationPreloadHeader(
     int64_t registration_id,
-    const GURL& origin,
+    const storage::StorageKey& key,
     const std::string& value,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -499,17 +506,17 @@
           UpdateNavigationPreloadHeader,
       base::BindOnce(&ServiceWorkerRegistry::DidUpdateRegistration,
                      weak_factory_.GetWeakPtr(), std::move(callback)),
-      static_cast<const int64_t>(registration_id),
-      storage::StorageKey(url::Origin::Create(origin)), value);
+      static_cast<const int64_t>(registration_id), key, value);
 }
 
-void ServiceWorkerRegistry::StoreUncommittedResourceId(int64_t resource_id,
-                                                       const GURL& origin) {
+void ServiceWorkerRegistry::StoreUncommittedResourceId(
+    int64_t resource_id,
+    const storage::StorageKey& key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::StoreUncommittedResourceId,
       base::BindOnce(&ServiceWorkerRegistry::DidWriteUncommittedResourceIds,
-                     weak_factory_.GetWeakPtr(), url::Origin::Create(origin)),
+                     weak_factory_.GetWeakPtr(), key),
       static_cast<const int64_t>(resource_id));
 }
 
@@ -557,7 +564,7 @@
 
 void ServiceWorkerRegistry::StoreUserData(
     int64_t registration_id,
-    const url::Origin& origin,
+    const storage::StorageKey& key,
     const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
     StatusCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
@@ -583,8 +590,8 @@
       &storage::mojom::ServiceWorkerStorageControl::StoreUserData,
       base::BindOnce(&ServiceWorkerRegistry::DidStoreUserData,
                      weak_factory_.GetWeakPtr(), std::move(wrapped_callback),
-                     origin),
-      registration_id, storage::StorageKey(origin), std::move(user_data));
+                     key),
+      registration_id, key, std::move(user_data));
 }
 
 void ServiceWorkerRegistry::ClearUserData(int64_t registration_id,
@@ -717,7 +724,7 @@
 
 void ServiceWorkerRegistry::FindRegistrationForIdInternal(
     int64_t registration_id,
-    const base::Optional<url::Origin>& origin,
+    const base::Optional<storage::StorageKey>& key,
     FindRegistrationCallback callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Registration lookup is expected to abort when storage is disabled.
@@ -739,9 +746,6 @@
     return;
   }
 
-  const base::Optional<storage::StorageKey> key =
-      origin.has_value() ? base::make_optional(storage::StorageKey(*origin))
-                         : base::nullopt;
   CreateInvokerAndStartRemoteCall(
       &storage::mojom::ServiceWorkerStorageControl::FindRegistrationForId,
       base::BindOnce(&ServiceWorkerRegistry::DidFindRegistrationForId,
@@ -750,9 +754,12 @@
       static_cast<const int64_t>(registration_id), key);
 }
 
+// TODO(crbug.com/1199077): Use `key` once ServiceWorkerRegistration
+// implements StorageKey.
 ServiceWorkerRegistration*
 ServiceWorkerRegistry::FindInstallingRegistrationForClientUrl(
-    const GURL& client_url) {
+    const GURL& client_url,
+    const storage::StorageKey& key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!client_url.has_ref());
 
@@ -767,8 +774,12 @@
   return match;
 }
 
+// TODO(crbug.com/1199077): Use `key` once ServiceWorkerRegistration
+// implements StorageKey.
 ServiceWorkerRegistration*
-ServiceWorkerRegistry::FindInstallingRegistrationForScope(const GURL& scope) {
+ServiceWorkerRegistry::FindInstallingRegistrationForScope(
+    const GURL& scope,
+    const storage::StorageKey& key) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   for (const auto& registration : installing_registrations_)
     if (registration.second->scope() == scope)
@@ -878,6 +889,7 @@
 
 void ServiceWorkerRegistry::DidFindRegistrationForClientUrl(
     const GURL& client_url,
+    const storage::StorageKey& key,
     int64_t trace_event_id,
     FindRegistrationCallback callback,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
@@ -895,7 +907,7 @@
   if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
     // Look for something currently being installed.
     scoped_refptr<ServiceWorkerRegistration> installing_registration =
-        FindInstallingRegistrationForClientUrl(client_url);
+        FindInstallingRegistrationForClientUrl(client_url, key);
     if (installing_registration) {
       blink::ServiceWorkerStatusCode installing_status =
           installing_registration->is_deleted()
@@ -996,9 +1008,9 @@
   CompleteFindNow(std::move(registration), status, std::move(callback));
 }
 
-void ServiceWorkerRegistry::DidGetRegistrationsForOrigin(
+void ServiceWorkerRegistry::DidGetRegistrationsForStorageKey(
     GetRegistrationsCallback callback,
-    const url::Origin& origin_filter,
+    const storage::StorageKey& key_filter,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     std::vector<storage::mojom::ServiceWorkerFindRegistrationResultPtr>
         entries) {
@@ -1028,8 +1040,11 @@
   }
 
   // Add unstored registrations that are being installed.
+  // TODO(crbug/1199077): Use full `key` once ServiceWorkerRegistration
+  // implements StorageKey.
   for (const auto& registration : installing_registrations_) {
-    if (url::Origin::Create(registration.second->scope()) != origin_filter)
+    if (url::Origin::Create(registration.second->scope()) !=
+        key_filter.origin())
       continue;
     if (registration_ids.insert(registration.first).second)
       registrations.push_back(registration.second);
@@ -1133,7 +1148,7 @@
   std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk, infos);
 }
 
-void ServiceWorkerRegistry::DidGetStorageUsageForOrigin(
+void ServiceWorkerRegistry::DidGetStorageUsageForStorageKey(
     GetStorageUsageForOriginCallback callback,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     int64_t usage) {
@@ -1147,15 +1162,16 @@
     int64_t stored_registration_id,
     uint64_t stored_resources_total_size_bytes,
     const GURL& stored_scope,
+    const storage::StorageKey& key,
     StatusCallback callback,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     uint64_t deleted_resources_size) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   blink::ServiceWorkerStatusCode status =
       DatabaseStatusToStatusCode(database_status);
-  url::Origin origin = url::Origin::Create(stored_scope);
+  url::Origin origin = key.origin();
 
-  MaybeNotifyWriteFailed(quota_manager_proxy_, database_status, origin);
+  MaybeNotifyWriteFailed(quota_manager_proxy_, database_status, key);
 
   if (status != blink::ServiceWorkerStatusCode::kOk) {
     ScheduleDeleteAndStartOver();
@@ -1189,7 +1205,7 @@
 
 void ServiceWorkerRegistry::DidDeleteRegistration(
     int64_t registration_id,
-    const GURL& origin,
+    const storage::StorageKey& key,
     StatusCallback callback,
     storage::mojom::ServiceWorkerDatabaseStatus database_status,
     uint64_t deleted_resources_size,
@@ -1207,7 +1223,7 @@
   if (quota_manager_proxy_) {
     // Can be nullptr in tests.
     quota_manager_proxy_->NotifyStorageModified(
-        storage::QuotaClientType::kServiceWorker, url::Origin::Create(origin),
+        storage::QuotaClientType::kServiceWorker, key.origin(),
         blink::mojom::StorageType::kTemporary, -deleted_resources_size,
         base::Time::Now());
   }
@@ -1219,10 +1235,9 @@
 
   if (storage_key_state ==
       storage::mojom::ServiceWorkerStorageStorageKeyState::kDelete) {
-    context_->NotifyAllRegistrationsDeletedForOrigin(
-        url::Origin::Create(origin));
+    context_->NotifyAllRegistrationsDeletedForOrigin(key.origin());
     if (storage_policy_observer_)
-      storage_policy_observer_->StopTrackingOrigin(url::Origin::Create(origin));
+      storage_policy_observer_->StopTrackingOrigin(key.origin());
   }
 
   std::move(callback).Run(status);
@@ -1240,18 +1255,18 @@
 }
 
 void ServiceWorkerRegistry::DidUpdateToActiveState(
-    const url::Origin& origin,
+    const storage::StorageKey& key,
     StatusCallback callback,
     storage::mojom::ServiceWorkerDatabaseStatus status) {
-  MaybeNotifyWriteFailed(quota_manager_proxy_, status, origin);
+  MaybeNotifyWriteFailed(quota_manager_proxy_, status, key);
   DidUpdateRegistration(std::move(callback), status);
 }
 
 void ServiceWorkerRegistry::DidWriteUncommittedResourceIds(
-    const url::Origin& origin,
+    const storage::StorageKey& key,
     storage::mojom::ServiceWorkerDatabaseStatus status) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  MaybeNotifyWriteFailed(quota_manager_proxy_, status, origin);
+  MaybeNotifyWriteFailed(quota_manager_proxy_, status, key);
   if (status != storage::mojom::ServiceWorkerDatabaseStatus::kOk)
     ScheduleDeleteAndStartOver();
 }
@@ -1289,10 +1304,10 @@
 
 void ServiceWorkerRegistry::DidStoreUserData(
     StatusCallback callback,
-    const url::Origin& origin,
+    const storage::StorageKey& key,
     storage::mojom::ServiceWorkerDatabaseStatus status) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  MaybeNotifyWriteFailed(quota_manager_proxy_, status, origin);
+  MaybeNotifyWriteFailed(quota_manager_proxy_, status, key);
   // |status| can be NOT_FOUND when the associated registration did not exist in
   // the database. In the case, we don't have to schedule the corruption
   // recovery.
@@ -1346,6 +1361,7 @@
     scoped_refptr<ServiceWorkerRegistration> registration,
     const GURL& script_url,
     blink::mojom::ScriptType script_type,
+    const storage::StorageKey& key,
     NewVersionCallback callback,
     int64_t version_id,
     mojo::PendingRemote<storage::mojom::ServiceWorkerLiveVersionRef>
@@ -1355,6 +1371,8 @@
     std::move(callback).Run(nullptr);
     return;
   }
+  // TODO(crbug/1199077): Once ServiceWorkerVersion supports StorageKey use
+  // `key` with it.
   auto version = base::MakeRefCounted<ServiceWorkerVersion>(
       registration.get(), script_url, script_type, version_id,
       std::move(version_reference), context_->AsWeakPtr());
@@ -1436,11 +1454,11 @@
 }
 
 bool ServiceWorkerRegistry::ShouldPurgeOnShutdownForTesting(
-    const url::Origin& origin) {
+    const storage::StorageKey& key) {
   if (!storage_policy_observer_)
     return false;
   return storage_policy_observer_->ShouldPurgeOnShutdownForTesting(  // IN-TEST
-      origin);
+      key.origin());
 }
 
 mojo::Remote<storage::mojom::ServiceWorkerStorageControl>&
diff --git a/content/browser/service_worker/service_worker_registry.h b/content/browser/service_worker/service_worker_registry.h
index 1696c3c..fc0e2c3 100644
--- a/content/browser/service_worker/service_worker_registry.h
+++ b/content/browser/service_worker/service_worker_registry.h
@@ -11,6 +11,7 @@
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/threading/sequence_bound.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "components/services/storage/public/mojom/service_worker_storage_control.mojom.h"
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/common/content_export.h"
@@ -55,8 +56,8 @@
   using FindRegistrationCallback = base::OnceCallback<void(
       blink::ServiceWorkerStatusCode status,
       scoped_refptr<ServiceWorkerRegistration> registration)>;
-  // TODO(http://crbug.com/1199077) Edit this once upstream code knows about
-  // StorageKey.
+  // TODO(crbug.com/1199077) Update this and associated functions once
+  // quota_client.mojom::GetOriginsForType is modified for StorageKey.
   using GetRegisteredOriginsCallback =
       base::OnceCallback<void(const std::vector<url::Origin>& origins)>;
   using GetRegistrationsCallback = base::OnceCallback<void(
@@ -75,6 +76,8 @@
   using GetUserDataForAllRegistrationsCallback = base::OnceCallback<void(
       const std::vector<std::pair<int64_t, std::string>>& user_data,
       blink::ServiceWorkerStatusCode status)>;
+  // TODO(crbug.com/1199077): Update this and associated functions once
+  // quota_client.mojom::GetOriginUsage is modified for StorageKey.
   using GetStorageUsageForOriginCallback =
       base::OnceCallback<void(blink::ServiceWorkerStatusCode status,
                               int64_t usage)>;
@@ -109,28 +112,31 @@
   void CreateNewVersion(scoped_refptr<ServiceWorkerRegistration> registration,
                         const GURL& script_url,
                         blink::mojom::ScriptType script_type,
+                        const storage::StorageKey& key,
                         NewVersionCallback callback);
 
-  // Finds registration for |client_url| or |scope| or |registration_id|.
-  // The Find methods will find stored and initially installing registrations.
-  // Returns blink::ServiceWorkerStatusCode::kOk with non-null
-  // registration if registration is found, or returns
+  // Finds registration for `client_url`, `scope`, or `registration_id` with the
+  // associated `key`. The Find methods will find stored and initially
+  // installing registrations. Returns blink::ServiceWorkerStatusCode::kOk with
+  // non-null registration if registration is found, or returns
   // blink::ServiceWorkerStatusCode::kErrorNotFound if no
   // matching registration is found.  The FindRegistrationForScope method is
   // guaranteed to return asynchronously. However, the methods to find
-  // for |client_url| or |registration_id| may complete immediately
+  // for `client_url` or `registration_id` may complete immediately
   // (the callback may be called prior to the method returning) or
   // asynchronously.
   void FindRegistrationForClientUrl(const GURL& client_url,
+                                    const storage::StorageKey& key,
                                     FindRegistrationCallback callback);
   void FindRegistrationForScope(const GURL& scope,
+                                const storage::StorageKey& key,
                                 FindRegistrationCallback callback);
   // These FindRegistrationForId() methods look up live registrations and may
   // return a "findable" registration without looking up storage. A registration
   // is considered as "findable" when the registration is stored or in the
   // installing state.
   void FindRegistrationForId(int64_t registration_id,
-                             const url::Origin& origin,
+                             const storage::StorageKey& key,
                              FindRegistrationCallback callback);
   // Generally |FindRegistrationForId| should be used to look up a registration
   // by |registration_id| since it's more efficient. But if a |registration_id|
@@ -139,24 +145,22 @@
   // callback may be called prior to the method returning) or asynchronously.
   void FindRegistrationForIdOnly(int64_t registration_id,
                                  FindRegistrationCallback callback);
-
-  // Returns all stored and installing registrations for a given origin.
-  void GetRegistrationsForOrigin(const url::Origin& origin,
-                                 GetRegistrationsCallback callback);
-
+  // Returns all stored and installing registrations for a given StorageKey.
+  void GetRegistrationsForStorageKey(const storage::StorageKey& key,
+                                     GetRegistrationsCallback callback);
   // Reads the total resource size stored in the storage for a given origin.
-  void GetStorageUsageForOrigin(const url::Origin& origin,
-                                GetStorageUsageForOriginCallback callback);
+  void GetStorageUsageForStorageKey(const storage::StorageKey& key,
+                                    GetStorageUsageForOriginCallback callback);
 
   // Returns info about all stored and initially installing registrations.
   // TODO(crbug.com/807440,1055677): Consider removing this method. Getting all
   // registrations at once might not be a good idea.
   void GetAllRegistrationsInfos(GetRegistrationsInfosCallback callback);
-
-  ServiceWorkerRegistration* GetUninstallingRegistration(const GURL& scope);
-
+  ServiceWorkerRegistration* GetUninstallingRegistration(
+      const GURL& scope,
+      const storage::StorageKey& key);
   std::vector<scoped_refptr<ServiceWorkerRegistration>>
-  GetUninstallingRegistrationsForOrigin(const url::Origin& origin);
+  GetUninstallingRegistrationsForStorageKey(const storage::StorageKey& key);
 
   // Commits |registration| with the installed but not activated |version|
   // to storage, overwriting any pre-existing registration data for the scope.
@@ -167,7 +171,7 @@
                          ServiceWorkerVersion* version,
                          StatusCallback callback);
 
-  // Deletes the registration data for |registration|. The live registration is
+  // Deletes the registration data for `registration`. The live registration is
   // still findable via GetUninstallingRegistration(), and versions are usable
   // because their script resources have not been deleted. After calling this,
   // the caller should later:
@@ -177,7 +181,7 @@
   //   ServiceWorkerStorage::PurgeResources() to delete their script resources.
   // If these aren't called, on the next profile session the cleanup occurs.
   void DeleteRegistration(scoped_refptr<ServiceWorkerRegistration> registration,
-                          const GURL& origin,
+                          const storage::StorageKey& key,
                           StatusCallback callback);
 
   // Intended for use only by ServiceWorkerRegisterJob and
@@ -193,21 +197,22 @@
   // Wrapper functions of ServiceWorkerStorage. These wrappers provide error
   // recovering mechanism when database operations fail.
   void UpdateToActiveState(int64_t registration_id,
-                           const GURL& origin,
+                           const storage::StorageKey& key,
                            StatusCallback callback);
   void UpdateLastUpdateCheckTime(int64_t registration_id,
-                                 const GURL& origin,
+                                 const storage::StorageKey& key,
                                  base::Time last_update_check_time,
                                  StatusCallback callback);
   void UpdateNavigationPreloadEnabled(int64_t registration_id,
-                                      const GURL& origin,
+                                      const storage::StorageKey& key,
                                       bool enable,
                                       StatusCallback callback);
   void UpdateNavigationPreloadHeader(int64_t registration_id,
-                                     const GURL& origin,
+                                     const storage::StorageKey& key,
                                      const std::string& value,
                                      StatusCallback callback);
-  void StoreUncommittedResourceId(int64_t resource_id, const GURL& origin);
+  void StoreUncommittedResourceId(int64_t resource_id,
+                                  const storage::StorageKey& key);
   void DoomUncommittedResource(int64_t resource_id);
   void GetUserData(int64_t registration_id,
                    const std::vector<std::string>& keys,
@@ -220,7 +225,7 @@
                                      GetUserKeysAndDataCallback callback);
   void StoreUserData(
       int64_t registration_id,
-      const url::Origin& origin,
+      const storage::StorageKey& key,
       const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
       StatusCallback callback);
   void ClearUserData(int64_t registration_id,
@@ -269,15 +274,16 @@
                            RetryInflightCalls_ApplyPolicyUpdates);
 
   void Start();
-
-  void FindRegistrationForIdInternal(int64_t registration_id,
-                                     const base::Optional<url::Origin>& origin,
-                                     FindRegistrationCallback callback);
-
+  void FindRegistrationForIdInternal(
+      int64_t registration_id,
+      const base::Optional<storage::StorageKey>& key,
+      FindRegistrationCallback callback);
   ServiceWorkerRegistration* FindInstallingRegistrationForClientUrl(
-      const GURL& client_url);
+      const GURL& client_url,
+      const storage::StorageKey& key);
   ServiceWorkerRegistration* FindInstallingRegistrationForScope(
-      const GURL& scope);
+      const GURL& scope,
+      const storage::StorageKey& key);
   ServiceWorkerRegistration* FindInstallingRegistrationForId(
       int64_t registration_id);
 
@@ -294,9 +300,9 @@
   FindFromLiveRegistrationsForId(int64_t registration_id);
 
   void DoomUncommittedResources(const std::vector<int64_t>& resource_ids);
-
   void DidFindRegistrationForClientUrl(
       const GURL& client_url,
+      const storage::StorageKey& key,
       int64_t trace_event_id,
       FindRegistrationCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
@@ -310,10 +316,9 @@
       FindRegistrationCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       storage::mojom::ServiceWorkerFindRegistrationResultPtr result);
-
-  void DidGetRegistrationsForOrigin(
+  void DidGetRegistrationsForStorageKey(
       GetRegistrationsCallback callback,
-      const url::Origin& origin_filter,
+      const storage::StorageKey& key_filter,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       std::vector<storage::mojom::ServiceWorkerFindRegistrationResultPtr>
           entries);
@@ -321,35 +326,34 @@
       GetRegistrationsInfosCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       RegistrationList registration_data_list);
-  void DidGetStorageUsageForOrigin(
+  void DidGetStorageUsageForStorageKey(
       GetStorageUsageForOriginCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       int64_t usage);
-
   void DidStoreRegistration(
       int64_t stored_registration_id,
       uint64_t stored_resources_total_size_bytes,
       const GURL& stored_scope,
+      const storage::StorageKey& key,
       StatusCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       uint64_t deleted_resources_size);
   void DidDeleteRegistration(
       int64_t registration_id,
-      const GURL& origin,
+      const storage::StorageKey& key,
       StatusCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus database_status,
       uint64_t deleted_resources_size,
       storage::mojom::ServiceWorkerStorageStorageKeyState storage_key_state);
-
   void DidUpdateRegistration(
       StatusCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus status);
   void DidUpdateToActiveState(
-      const url::Origin& origin,
+      const storage::StorageKey& key,
       StatusCallback callback,
       storage::mojom::ServiceWorkerDatabaseStatus status);
   void DidWriteUncommittedResourceIds(
-      const url::Origin& origin,
+      const storage::StorageKey& key,
       storage::mojom::ServiceWorkerDatabaseStatus status);
   void DidDoomUncommittedResourceIds(
       storage::mojom::ServiceWorkerDatabaseStatus status);
@@ -361,7 +365,7 @@
       storage::mojom::ServiceWorkerDatabaseStatus status,
       const base::flat_map<std::string, std::string>& data_map);
   void DidStoreUserData(StatusCallback callback,
-                        const url::Origin& origin,
+                        const storage::StorageKey& key,
                         storage::mojom::ServiceWorkerDatabaseStatus status);
   void DidClearUserData(StatusCallback callback,
                         storage::mojom::ServiceWorkerDatabaseStatus status);
@@ -374,10 +378,13 @@
       blink::mojom::ServiceWorkerRegistrationOptions options,
       NewRegistrationCallback callback,
       int64_t registration_id);
+  // TODO(http://crbug.com/1199077): Update function when ServiceWorkerVersion
+  // supports StorageKey
   void DidGetNewVersionId(
       scoped_refptr<ServiceWorkerRegistration> registration,
       const GURL& script_url,
       blink::mojom::ScriptType script_type,
+      const storage::StorageKey& key,
       NewVersionCallback callback,
       int64_t version_id,
       mojo::PendingRemote<storage::mojom::ServiceWorkerLiveVersionRef>
@@ -394,12 +401,11 @@
   void DidDisable();
   void DidApplyPolicyUpdates(
       storage::mojom::ServiceWorkerDatabaseStatus status);
-
   void DidGetRegisteredOriginsOnStartup(
       const std::vector<url::Origin>& origins);
   void ApplyPolicyUpdates(
       std::vector<storage::mojom::StoragePolicyUpdatePtr> policy_updates);
-  bool ShouldPurgeOnShutdownForTesting(const url::Origin& origin);
+  bool ShouldPurgeOnShutdownForTesting(const storage::StorageKey& key);
 
   void OnRemoteStorageDisconnected();
 
diff --git a/content/browser/service_worker/service_worker_registry_unittest.cc b/content/browser/service_worker/service_worker_registry_unittest.cc
index c2f6f34c..e4683fa 100644
--- a/content/browser/service_worker/service_worker_registry_unittest.cc
+++ b/content/browser/service_worker/service_worker_registry_unittest.cc
@@ -33,7 +33,7 @@
   base::Optional<mojo_base::BigBuffer> metadata;
 };
 
-struct GetStorageUsageForOriginResult {
+struct GetStorageUsageForStorageKeyResult {
   blink::ServiceWorkerStatusCode status;
   int64_t usage;
 };
@@ -327,11 +327,12 @@
 
   blink::ServiceWorkerStatusCode FindRegistrationForClientUrl(
       const GURL& document_url,
+      const storage::StorageKey& key,
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
     registry()->FindRegistrationForClientUrl(
-        document_url,
+        document_url, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
                 scoped_refptr<ServiceWorkerRegistration> registration) {
@@ -345,29 +346,31 @@
 
   blink::ServiceWorkerStatusCode FindRegistrationForScope(
       const GURL& scope,
+      const storage::StorageKey& key,
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
     registry()->FindRegistrationForScope(
-        scope, base::BindLambdaForTesting(
-                   [&](blink::ServiceWorkerStatusCode status,
-                       scoped_refptr<ServiceWorkerRegistration> registration) {
-                     result = status;
-                     out_registration = std::move(registration);
-                     loop.Quit();
-                   }));
+        scope, key,
+        base::BindLambdaForTesting(
+            [&](blink::ServiceWorkerStatusCode status,
+                scoped_refptr<ServiceWorkerRegistration> registration) {
+              result = status;
+              out_registration = std::move(registration);
+              loop.Quit();
+            }));
     loop.Run();
     return result;
   }
 
   blink::ServiceWorkerStatusCode FindRegistrationForId(
       int64_t registration_id,
-      const url::Origin& origin,
+      const storage::StorageKey& key,
       scoped_refptr<ServiceWorkerRegistration>& out_registration) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
     registry()->FindRegistrationForId(
-        registration_id, origin,
+        registration_id, key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
                 scoped_refptr<ServiceWorkerRegistration> registration) {
@@ -414,11 +417,11 @@
 
   blink::ServiceWorkerStatusCode DeleteRegistration(
       scoped_refptr<ServiceWorkerRegistration> registration,
-      const GURL& origin) {
+      const storage::StorageKey& key) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
     registry()->DeleteRegistration(
-        registration, origin,
+        registration, key,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
           loop.Quit();
@@ -432,7 +435,7 @@
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode result;
     registry()->UpdateToActiveState(
-        registration->id(), registration->scope().GetOrigin(),
+        registration->id(), storage::StorageKey(registration->origin()),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
           loop.Quit();
@@ -446,7 +449,7 @@
     base::RunLoop loop;
     blink::ServiceWorkerStatusCode result;
     registry()->UpdateLastUpdateCheckTime(
-        registration->id(), registration->scope().GetOrigin(),
+        registration->id(), storage::StorageKey(registration->origin()),
         registration->last_update_check(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           result = status;
@@ -456,17 +459,17 @@
     return result;
   }
 
-  GetStorageUsageForOriginResult GetStorageUsageForOrigin(
-      const url::Origin& origin) {
-    GetStorageUsageForOriginResult result;
+  GetStorageUsageForStorageKeyResult GetStorageUsageForStorageKey(
+      const storage::StorageKey& key) {
+    GetStorageUsageForStorageKeyResult result;
     base::RunLoop loop;
-    registry()->GetStorageUsageForOrigin(
-        origin, base::BindLambdaForTesting(
-                    [&](blink::ServiceWorkerStatusCode status, int64_t usage) {
-                      result.status = status;
-                      result.usage = usage;
-                      loop.Quit();
-                    }));
+    registry()->GetStorageUsageForStorageKey(
+        key, base::BindLambdaForTesting(
+                 [&](blink::ServiceWorkerStatusCode status, int64_t usage) {
+                   result.status = status;
+                   result.usage = usage;
+                   loop.Quit();
+                 }));
     loop.Run();
     return result;
   }
@@ -487,13 +490,13 @@
     return result.value();
   }
 
-  blink::ServiceWorkerStatusCode GetRegistrationsForOrigin(
-      const url::Origin& origin,
+  blink::ServiceWorkerStatusCode GetRegistrationsForStorageKey(
+      const storage::StorageKey& key,
       std::vector<scoped_refptr<ServiceWorkerRegistration>>& registrations) {
     blink::ServiceWorkerStatusCode result;
     base::RunLoop loop;
-    registry()->GetRegistrationsForOrigin(
-        origin,
+    registry()->GetRegistrationsForStorageKey(
+        key,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
                 const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
@@ -614,6 +617,7 @@
 
 TEST_F(ServiceWorkerRegistryTest, StoreFindUpdateDeleteRegistration) {
   const GURL kScope("http://www.test.not/scope/");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
   const GURL kDocumentUrl("http://www.test.not/scope/document.html");
   const GURL kResource1("http://www.test.not/scope/resource1.js");
   const int64_t kResource1Size = 1591234;
@@ -631,17 +635,17 @@
   scoped_refptr<ServiceWorkerRegistration> found_registration;
 
   // We shouldn't find anything without having stored anything.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kErrorNotFound,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForScope(kScope, found_registration));
+            FindRegistrationForScope(kScope, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
-                                  found_registration));
+            FindRegistrationForId(kRegistrationId, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources;
@@ -675,8 +679,9 @@
             StoreRegistration(live_registration, live_version));
 
   // Now we should find it and get the live ptr back immediately.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_EQ(live_registration, found_registration);
   EXPECT_EQ(kResource1Size + kResource2Size,
             live_registration->resources_total_size_bytes());
@@ -691,14 +696,13 @@
 
   // But FindRegistrationForScope is always async.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForScope(kScope, found_registration));
+            FindRegistrationForScope(kScope, kKey, found_registration));
   EXPECT_EQ(live_registration, found_registration);
   found_registration = nullptr;
 
   // Can be found by id too.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
-                                  found_registration));
+            FindRegistrationForId(kRegistrationId, kKey, found_registration));
   ASSERT_TRUE(found_registration.get());
   EXPECT_EQ(kRegistrationId, found_registration->id());
   EXPECT_EQ(live_registration, found_registration);
@@ -716,8 +720,9 @@
   live_registration = nullptr;
 
   // Now FindRegistrationForClientUrl should be async.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   ASSERT_TRUE(found_registration.get());
   EXPECT_EQ(kRegistrationId, found_registration->id());
   EXPECT_TRUE(found_registration->HasOneRef());
@@ -734,20 +739,21 @@
   EXPECT_EQ(kResource1Size + kResource2Size, info.stored_version_size_bytes);
   all_registrations.clear();
 
-  // Finding by origin should provide the same result if origin is kScope.
+  // Finding by StorageKey should provide the same result if the StorageKey's
+  // origin is kScope.
   std::vector<scoped_refptr<ServiceWorkerRegistration>>
-      registrations_for_origin;
+      registrations_for_storage_key;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(url::Origin::Create(kScope),
-                                      registrations_for_origin));
-  EXPECT_EQ(1u, registrations_for_origin.size());
-  registrations_for_origin.clear();
+            GetRegistrationsForStorageKey(kKey, registrations_for_storage_key));
+  EXPECT_EQ(1u, registrations_for_storage_key.size());
+  registrations_for_storage_key.clear();
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(
-                url::Origin::Create(GURL("http://example.com/")),
-                registrations_for_origin));
-  EXPECT_TRUE(registrations_for_origin.empty());
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      GetRegistrationsForStorageKey(
+          storage::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          registrations_for_storage_key));
+  EXPECT_TRUE(registrations_for_storage_key.empty());
 
   found_registration = nullptr;
 
@@ -756,7 +762,7 @@
 
   // And FindRegistrationForScope is always async.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForScope(kScope, found_registration));
+            FindRegistrationForScope(kScope, kKey, found_registration));
   ASSERT_TRUE(found_registration.get());
   EXPECT_EQ(kRegistrationId, found_registration->id());
   EXPECT_TRUE(found_registration->HasOneRef());
@@ -789,8 +795,9 @@
 
   // The Find methods should return a registration with an active version
   // and the expected update time.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   ASSERT_TRUE(found_registration.get());
   EXPECT_EQ(kRegistrationId, found_registration->id());
   EXPECT_TRUE(found_registration->HasOneRef());
@@ -803,6 +810,7 @@
 
 TEST_F(ServiceWorkerRegistryTest, InstallingRegistrationsAreFindable) {
   const GURL kScope("http://www.test.not/scope/");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
   const GURL kScript("http://www.test.not/script.js");
   const GURL kDocumentUrl("http://www.test.not/scope/document.html");
   const int64_t kVersionId = 0;
@@ -826,20 +834,20 @@
 
   // Should not be findable, including by GetAllRegistrationsInfos.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
-                                  found_registration));
+            FindRegistrationForId(kRegistrationId, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
             FindRegistrationForIdOnly(kRegistrationId, found_registration));
   EXPECT_FALSE(found_registration.get());
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kErrorNotFound,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForScope(kScope, found_registration));
+            FindRegistrationForScope(kScope, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   std::vector<ServiceWorkerRegistrationInfo> all_registrations;
@@ -848,25 +856,24 @@
   EXPECT_TRUE(all_registrations.empty());
 
   std::vector<scoped_refptr<ServiceWorkerRegistration>>
-      registrations_for_origin;
+      registrations_for_storage_key;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(url::Origin::Create(kScope),
-                                      registrations_for_origin));
-  EXPECT_TRUE(registrations_for_origin.empty());
+            GetRegistrationsForStorageKey(kKey, registrations_for_storage_key));
+  EXPECT_TRUE(registrations_for_storage_key.empty());
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(
-                url::Origin::Create(GURL("http://example.com/")),
-                registrations_for_origin));
-  EXPECT_TRUE(registrations_for_origin.empty());
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      GetRegistrationsForStorageKey(
+          storage::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          registrations_for_storage_key));
+  EXPECT_TRUE(registrations_for_storage_key.empty());
 
   // Notify storage of it being installed.
   registry()->NotifyInstallingRegistration(live_registration.get());
 
   // Now should be findable.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
-                                  found_registration));
+            FindRegistrationForId(kRegistrationId, kKey, found_registration));
   EXPECT_EQ(live_registration, found_registration);
   found_registration = nullptr;
 
@@ -875,13 +882,14 @@
   EXPECT_EQ(live_registration, found_registration);
   found_registration = nullptr;
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_EQ(live_registration, found_registration);
   found_registration = nullptr;
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForScope(kScope, found_registration));
+            FindRegistrationForScope(kScope, kKey, found_registration));
   EXPECT_EQ(live_registration, found_registration);
   found_registration = nullptr;
 
@@ -890,18 +898,19 @@
   EXPECT_EQ(1u, all_registrations.size());
   all_registrations.clear();
 
-  // Finding by origin should provide the same result if origin is kScope.
+  // Finding by StorageKey should provide the same result if the StorageKey's
+  // origin is kScope.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(url::Origin::Create(kScope),
-                                      registrations_for_origin));
-  EXPECT_EQ(1u, registrations_for_origin.size());
-  registrations_for_origin.clear();
+            GetRegistrationsForStorageKey(kKey, registrations_for_storage_key));
+  EXPECT_EQ(1u, registrations_for_storage_key.size());
+  registrations_for_storage_key.clear();
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(
-                url::Origin::Create(GURL("http://example.com/")),
-                registrations_for_origin));
-  EXPECT_TRUE(registrations_for_origin.empty());
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      GetRegistrationsForStorageKey(
+          storage::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          registrations_for_storage_key));
+  EXPECT_TRUE(registrations_for_storage_key.empty());
 
   // Notify storage of installation no longer happening.
   registry()->NotifyDoneInstallingRegistration(
@@ -909,20 +918,20 @@
 
   // Once again, should not be findable.
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForId(kRegistrationId, url::Origin::Create(kScope),
-                                  found_registration));
+            FindRegistrationForId(kRegistrationId, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
             FindRegistrationForIdOnly(kRegistrationId, found_registration));
   EXPECT_FALSE(found_registration.get());
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kErrorNotFound,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kErrorNotFound,
-            FindRegistrationForScope(kScope, found_registration));
+            FindRegistrationForScope(kScope, kKey, found_registration));
   EXPECT_FALSE(found_registration.get());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
@@ -930,19 +939,20 @@
   EXPECT_TRUE(all_registrations.empty());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(url::Origin::Create(kScope),
-                                      registrations_for_origin));
-  EXPECT_TRUE(registrations_for_origin.empty());
+            GetRegistrationsForStorageKey(kKey, registrations_for_storage_key));
+  EXPECT_TRUE(registrations_for_storage_key.empty());
 
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            GetRegistrationsForOrigin(
-                url::Origin::Create(GURL("http://example.com/")),
-                registrations_for_origin));
-  EXPECT_TRUE(registrations_for_origin.empty());
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      GetRegistrationsForStorageKey(
+          storage::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          registrations_for_storage_key));
+  EXPECT_TRUE(registrations_for_storage_key.empty());
 }
 
 TEST_F(ServiceWorkerRegistryTest, FindRegistration_LongestScopeMatch) {
   const GURL kDocumentUrl("http://www.example.com/scope/foo");
+  const storage::StorageKey kKey(url::Origin::Create(kDocumentUrl));
   scoped_refptr<ServiceWorkerRegistration> found_registration;
 
   // Registration for "/scope/".
@@ -972,8 +982,9 @@
   registry()->NotifyInstallingRegistration(live_registration3.get());
 
   // Find a registration among installing ones.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_EQ(live_registration2, found_registration);
   found_registration = nullptr;
 
@@ -997,8 +1008,9 @@
       live_registration3.get(), nullptr, blink::ServiceWorkerStatusCode::kOk);
 
   // Find a registration among installed ones.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kDocumentUrl, found_registration));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      FindRegistrationForClientUrl(kDocumentUrl, kKey, found_registration));
   EXPECT_EQ(live_registration2, found_registration);
 }
 
@@ -1038,6 +1050,7 @@
 
 TEST_F(ServiceWorkerRegistryTest, OriginTrialsAbsentEntryAndEmptyEntry) {
   const GURL origin1("http://www1.example.com");
+  const storage::StorageKey key1(url::Origin::Create(origin1));
   const GURL scope1("http://www1.example.com/foo/");
   const GURL script1(origin1.spec() + "/script.js");
   std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources1;
@@ -1052,6 +1065,7 @@
   StoreRegistrationData(std::move(data1), std::move(resources1));
 
   const GURL origin2("http://www2.example.com");
+  const storage::StorageKey key2(url::Origin::Create(origin2));
   const GURL scope2("http://www2.example.com/foo/");
   const GURL script2(origin2.spec() + "/script.js");
   std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources2;
@@ -1069,13 +1083,13 @@
   scoped_refptr<ServiceWorkerRegistration> found_registration;
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(scope1, found_registration));
+            FindRegistrationForClientUrl(scope1, key1, found_registration));
   ASSERT_TRUE(found_registration->active_version());
   // origin_trial_tokens must be unset.
   EXPECT_FALSE(found_registration->active_version()->origin_trial_tokens());
 
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(scope2, found_registration));
+            FindRegistrationForClientUrl(scope2, key2, found_registration));
   ASSERT_TRUE(found_registration->active_version());
   // Empty origin_trial_tokens must exist.
   ASSERT_TRUE(found_registration->active_version()->origin_trial_tokens());
@@ -1086,6 +1100,7 @@
 // Tests loading a registration that has no navigation preload state.
 TEST_F(ServiceWorkerRegistryTest, AbsentNavigationPreloadState) {
   const GURL origin1("http://www1.example.com");
+  const storage::StorageKey key1(url::Origin::Create(origin1));
   const GURL scope1("http://www1.example.com/foo/");
   const GURL script1(origin1.spec() + "/script.js");
   std::vector<storage::mojom::ServiceWorkerResourceRecordPtr> resources1;
@@ -1101,7 +1116,7 @@
 
   scoped_refptr<ServiceWorkerRegistration> found_registration;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(scope1, found_registration));
+            FindRegistrationForClientUrl(scope1, key1, found_registration));
   const blink::mojom::NavigationPreloadState& registration_state =
       found_registration->navigation_preload_state();
   EXPECT_FALSE(registration_state.enabled);
@@ -1117,6 +1132,7 @@
 // well as a custom header value.
 TEST_F(ServiceWorkerRegistryTest, EnabledNavigationPreloadState) {
   const GURL kScope("https://valid.example.com/scope");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
   const GURL kScript("https://valid.example.com/script.js");
   const std::string kHeaderValue("custom header value");
 
@@ -1138,7 +1154,7 @@
   SimulateRestart();
 
   scoped_refptr<ServiceWorkerRegistration> found_registration;
-  EXPECT_EQ(FindRegistrationForClientUrl(kScope, found_registration),
+  EXPECT_EQ(FindRegistrationForClientUrl(kScope, kKey, found_registration),
             blink::ServiceWorkerStatusCode::kOk);
   const blink::mojom::NavigationPreloadState& registration_state =
       found_registration->navigation_preload_state();
@@ -1155,6 +1171,7 @@
 TEST_F(ServiceWorkerRegistryTest, ScriptResponseTime) {
   // Make a registration.
   const GURL kScope("https://example.com/scope");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
   const GURL kScript("https://example.com/script.js");
   scoped_refptr<ServiceWorkerRegistration> registration =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope, kScript,
@@ -1187,7 +1204,7 @@
   // Read the registration. The main script's response time should be gettable.
   scoped_refptr<ServiceWorkerRegistration> found_registration;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kScope, found_registration));
+            FindRegistrationForClientUrl(kScope, kKey, found_registration));
   ASSERT_TRUE(found_registration);
   auto* waiting_version = found_registration->waiting_version();
   ASSERT_TRUE(waiting_version);
@@ -1202,6 +1219,7 @@
 // state.
 TEST_F(ServiceWorkerRegistryTest, DisabledNavigationPreloadState) {
   const GURL kScope("https://valid.example.com/scope");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
   const GURL kScript("https://valid.example.com/script.js");
   scoped_refptr<ServiceWorkerRegistration> registration =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope, kScript,
@@ -1221,7 +1239,7 @@
 
   scoped_refptr<ServiceWorkerRegistration> found_registration;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kScope, found_registration));
+            FindRegistrationForClientUrl(kScope, kKey, found_registration));
   const blink::mojom::NavigationPreloadState& registration_state =
       found_registration->navigation_preload_state();
   EXPECT_FALSE(registration_state.enabled);
@@ -1238,6 +1256,7 @@
   const GURL kScope("http://www.example.com/scope/");
   const GURL kScriptUrl("http://www.example.com/script.js");
   const auto kOrigin(url::Origin::Create(kScope));
+  const storage::StorageKey kKey(kOrigin);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope, kScriptUrl,
@@ -1245,7 +1264,7 @@
 
   ASSERT_EQ(StoreRegistration(registration, registration->waiting_version()),
             blink::ServiceWorkerStatusCode::kOk);
-  EXPECT_FALSE(registry()->ShouldPurgeOnShutdownForTesting(kOrigin));
+  EXPECT_FALSE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
 
   {
     // Update storage policy to mark the origin should be purged on shutdown.
@@ -1254,7 +1273,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  EXPECT_TRUE(registry()->ShouldPurgeOnShutdownForTesting(kOrigin));
+  EXPECT_TRUE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
 }
 
 // Tests that callbacks of storage operations are always called even when the
@@ -1263,6 +1282,7 @@
   const GURL kScope("http://www.example.com/scope/");
   const GURL kScriptUrl("http://www.example.com/script.js");
   const auto kOrigin(url::Origin::Create(kScope));
+  const storage::StorageKey kKey(kOrigin);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope, kScriptUrl,
@@ -1271,7 +1291,8 @@
   ASSERT_EQ(StoreRegistration(registration, registration->waiting_version()),
             blink::ServiceWorkerStatusCode::kOk);
 
-  GetStorageUsageForOriginResult result = GetStorageUsageForOrigin(kOrigin);
+  GetStorageUsageForStorageKeyResult result =
+      GetStorageUsageForStorageKey(kKey);
   ASSERT_EQ(result.status, blink::ServiceWorkerStatusCode::kOk);
 
   // This will disconnect mojo connection of the remote storage.
@@ -1279,9 +1300,9 @@
 
   // The connection should be recovered and inflight calls should be retried
   // automatically.
-  result = GetStorageUsageForOrigin(kOrigin);
+  result = GetStorageUsageForStorageKey(kKey);
   ASSERT_EQ(result.status, blink::ServiceWorkerStatusCode::kOk);
-  result = GetStorageUsageForOrigin(kOrigin);
+  result = GetStorageUsageForStorageKey(kKey);
   ASSERT_EQ(result.status, blink::ServiceWorkerStatusCode::kOk);
 }
 
@@ -1293,6 +1314,7 @@
   const GURL kScope1("https://www.example.com/scope/");
   const GURL kScriptUrl1("https://www.example.com/script.js");
   const auto kOrigin1(url::Origin::Create(kScope1));
+  const storage::StorageKey kKey1(kOrigin1);
   scoped_refptr<ServiceWorkerRegistration> registration1 =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope1, kScriptUrl1,
                                                 /*resource_id=*/1);
@@ -1300,6 +1322,7 @@
   const GURL kScope2("https://www2.example.com/scope/foo");
   const GURL kScriptUrl2("https://www2.example.com/foo/script.js");
   const auto kOrigin2(url::Origin::Create(kScope2));
+  const storage::StorageKey kKey2(kOrigin2);
   scoped_refptr<ServiceWorkerRegistration> registration2 =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope2, kScriptUrl2,
                                                 /*resource_id=*/2);
@@ -1355,7 +1378,7 @@
   {
     base::RunLoop loop1;
     registry()->FindRegistrationForClientUrl(
-        kScope1,
+        kScope1, kKey1,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
                 scoped_refptr<ServiceWorkerRegistration> found_registration) {
@@ -1365,8 +1388,9 @@
             }));
 
     base::RunLoop loop2;
+    const GURL kNotInScope("http://www.example.com/not-in-scope");
     registry()->FindRegistrationForScope(
-        GURL("http://www.example.com/not-in-scope"),
+        kNotInScope, storage::StorageKey(url::Origin::Create(kNotInScope)),
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
                 scoped_refptr<ServiceWorkerRegistration> found_registration) {
@@ -1386,8 +1410,8 @@
   // Get both of the registrations by these APIs.
   {
     base::RunLoop loop1;
-    registry()->GetRegistrationsForOrigin(
-        kOrigin1,
+    registry()->GetRegistrationsForStorageKey(
+        kKey1,
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status,
                 const std::vector<scoped_refptr<ServiceWorkerRegistration>>&
@@ -1418,7 +1442,7 @@
   {
     base::RunLoop loop;
     registry()->DeleteRegistration(
-        registration2, kScope2.GetOrigin(),
+        registration2, kKey2,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
           loop.Quit();
@@ -1435,7 +1459,7 @@
   {
     base::RunLoop loop1;
     registry()->UpdateToActiveState(
-        registration1->id(), kScope1.GetOrigin(),
+        registration1->id(), kKey1,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
           loop1.Quit();
@@ -1443,7 +1467,7 @@
 
     base::RunLoop loop2;
     registry()->UpdateLastUpdateCheckTime(
-        registration1->id(), kScope1.GetOrigin(), base::Time::Now(),
+        registration1->id(), kKey1, base::Time::Now(),
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
           loop2.Quit();
@@ -1451,7 +1475,7 @@
 
     base::RunLoop loop3;
     registry()->UpdateNavigationPreloadEnabled(
-        registration1->id(), kScope1.GetOrigin(), /*enable=*/true,
+        registration1->id(), kKey1, /*enable=*/true,
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
           loop3.Quit();
@@ -1459,7 +1483,7 @@
 
     base::RunLoop loop4;
     registry()->UpdateNavigationPreloadHeader(
-        registration1->id(), kScope1.GetOrigin(), "header",
+        registration1->id(), kKey1, "header",
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
           loop4.Quit();
@@ -1509,7 +1533,8 @@
 
   base::RunLoop loop1;
   registry()->FindRegistrationForId(
-      registration_id1, url::Origin::Create(origin1.GetOrigin()),
+      registration_id1,
+      storage::StorageKey(url::Origin::Create(origin1.GetOrigin())),
       base::BindLambdaForTesting(
           [&](blink::ServiceWorkerStatusCode status,
               scoped_refptr<ServiceWorkerRegistration> found_registration) {
@@ -1543,6 +1568,7 @@
 
   const GURL kScope("http://www.example.com/scope/");
   const GURL kScriptUrl("http://www.example.com/script.js");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
 
   scoped_refptr<ServiceWorkerRegistration> registration;
 
@@ -1569,7 +1595,7 @@
   {
     base::RunLoop loop;
     registry()->CreateNewVersion(
-        registration, kScriptUrl, blink::mojom::ScriptType::kClassic,
+        registration, kScriptUrl, blink::mojom::ScriptType::kClassic, kKey,
         base::BindLambdaForTesting(
             [&](scoped_refptr<ServiceWorkerVersion> new_version) {
               EXPECT_EQ(new_version->script_url(), kScriptUrl);
@@ -1590,6 +1616,7 @@
   const GURL kScope1("http://www.example.com/scope/");
   const GURL kScriptUrl1("http://www.example.com/script.js");
   const auto kOrigin1(url::Origin::Create(kScope1));
+  const storage::StorageKey kKey1(kOrigin1);
   scoped_refptr<ServiceWorkerRegistration> registration1 =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope1, kScriptUrl1,
                                                 /*resource_id=*/1);
@@ -1599,6 +1626,7 @@
   const GURL kScope2("http://www.example.com/scope/foo");
   const GURL kScriptUrl2("http://www.example.com/fooscript.js");
   const auto kOrigin2(url::Origin::Create(kScope2));
+  const storage::StorageKey kKey2(kOrigin2);
   scoped_refptr<ServiceWorkerRegistration> registration2 =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope2, kScriptUrl2,
                                                 /*resource_id=*/2);
@@ -1609,7 +1637,7 @@
   {
     base::RunLoop loop1;
     registry()->StoreUserData(
-        registration1->id(), kOrigin1,
+        registration1->id(), kKey1,
         {{"key1", "value1"}, {"prefixed_key1", "prefixed_value1"}},
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -1618,7 +1646,7 @@
 
     base::RunLoop loop2;
     registry()->StoreUserData(
-        registration2->id(), kOrigin2,
+        registration2->id(), kKey2,
         {{"key2", "value2"}, {"prefixed_key2", "prefixed_value2"}},
         base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
           EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kOk);
@@ -1813,6 +1841,7 @@
   const GURL kScope("http://www.example.com/scope/");
   const GURL kScriptUrl("http://www.example.com/script.js");
   const auto kOrigin(url::Origin::Create(kScope));
+  const storage::StorageKey kKey(kOrigin);
 
   scoped_refptr<ServiceWorkerRegistration> registration =
       CreateServiceWorkerRegistrationAndVersion(context(), kScope, kScriptUrl,
@@ -1820,7 +1849,7 @@
 
   ASSERT_EQ(StoreRegistration(registration, registration->waiting_version()),
             blink::ServiceWorkerStatusCode::kOk);
-  EXPECT_FALSE(registry()->ShouldPurgeOnShutdownForTesting(kOrigin));
+  EXPECT_FALSE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
 
   // Update storage policy to mark the origin should be purged on shutdown.
   special_storage_policy()->AddSessionOnly(kOrigin.GetURL());
@@ -1832,7 +1861,7 @@
   // All Mojo calls must be done at this point.
   EXPECT_EQ(inflight_call_count(), 0U);
 
-  EXPECT_TRUE(registry()->ShouldPurgeOnShutdownForTesting(kOrigin));
+  EXPECT_TRUE(registry()->ShouldPurgeOnShutdownForTesting(kKey));
 }
 
 // Regression test for https://crbug.com/1165784.
@@ -1853,8 +1882,8 @@
 
   {
     base::RunLoop loop;
-    registry()->GetStorageUsageForOrigin(
-        url::Origin::Create(GURL("https://example.com/")),
+    registry()->GetStorageUsageForStorageKey(
+        storage::StorageKey(url::Origin::Create(GURL("https://example.com/"))),
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status, int64_t usage) {
               EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
@@ -1876,7 +1905,8 @@
        DestroyRegistryDuringInflightCall_StoreUserData) {
   base::RunLoop loop;
   registry()->StoreUserData(
-      /*registration_id=*/1, url::Origin::Create(GURL("https://example.com/")),
+      /*registration_id=*/1,
+      storage::StorageKey(url::Origin::Create(GURL("https://example.com/"))),
       {{"key", "value"}},
       base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
         EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
@@ -1997,6 +2027,7 @@
 
 TEST_F(ServiceWorkerRegistryOriginTrialsTest, FromMainScript) {
   const GURL kScope("https://valid.example.com/scope");
+  const storage::StorageKey kKey(url::Origin::Create(kScope));
   const GURL kScript("https://valid.example.com/script.js");
   const int64_t kRegistrationId = 1;
   const int64_t kVersionId = 1;
@@ -2084,7 +2115,7 @@
 
   scoped_refptr<ServiceWorkerRegistration> found_registration;
   EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            FindRegistrationForClientUrl(kScope, found_registration));
+            FindRegistrationForClientUrl(kScope, kKey, found_registration));
   ASSERT_TRUE(found_registration->active_version());
   const blink::TrialTokenValidator::FeatureToTokensMap& found_tokens =
       *found_registration->active_version()->origin_trial_tokens();
@@ -2133,8 +2164,9 @@
     version_id_ = version->version_id();
 
     // Add the resources ids to the uncommitted list.
-    registry()->StoreUncommittedResourceId(resource_id1_, scope_);
-    registry()->StoreUncommittedResourceId(resource_id2_, scope_);
+    storage::StorageKey key(url::Origin::Create(scope_));
+    registry()->StoreUncommittedResourceId(resource_id1_, key);
+    registry()->StoreUncommittedResourceId(resource_id2_, key);
     EnsureRemoteCallsAreExecuted();
 
     std::vector<int64_t> verify_ids = GetUncommittedResourceIds();
@@ -2257,8 +2289,10 @@
   // list.
   base::RunLoop loop;
   storage_control()->SetPurgingCompleteCallbackForTest(loop.QuitClosure());
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            DeleteRegistration(registration_, scope_.GetOrigin()));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      DeleteRegistration(registration_,
+                         storage::StorageKey(url::Origin::Create(scope_))));
   // At this point registration_->waiting_version() has a remote reference, so
   // the resources should be in the purgeable list.
   EXPECT_EQ(2u, GetPurgeableResourceIds().size());
@@ -2277,8 +2311,10 @@
   // Deleting the registration should result in the resources being added to the
   // purgeable list and then doomed in the disk cache and removed from that
   // list.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            DeleteRegistration(registration_, scope_.GetOrigin()));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      DeleteRegistration(registration_,
+                         storage::StorageKey(url::Origin::Create(scope_))));
   EXPECT_EQ(2u, GetPurgeableResourceIds().size());
 
   EXPECT_TRUE(VerifyBasicResponse(storage_control(), resource_id1_, false));
@@ -2299,9 +2335,10 @@
   // Promote the worker to active and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  registry()->UpdateToActiveState(registration_->id(),
-                                  registration_->scope().GetOrigin(),
-                                  base::DoNothing());
+  registry()->UpdateToActiveState(
+      registration_->id(),
+      storage::StorageKey(url::Origin::Create(registration_->scope())),
+      base::DoNothing());
   ServiceWorkerRemoteContainerEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerContainerHost> container_host =
       CreateContainerHostForWindow(33 /* dummy render process id */,
@@ -2311,8 +2348,10 @@
 
   // Deleting the registration should move the resources to the purgeable list
   // but keep them available.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            DeleteRegistration(registration_, scope_.GetOrigin()));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      DeleteRegistration(registration_,
+                         storage::StorageKey(url::Origin::Create(scope_))));
   EXPECT_EQ(2u, GetPurgeableResourceIds().size());
 
   EXPECT_TRUE(VerifyBasicResponse(storage_control(), resource_id1_, true));
@@ -2336,7 +2375,7 @@
   registration_->SetActiveVersion(registration_->waiting_version());
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registry()->UpdateToActiveState(registration_->id(),
-                                  registration_->scope().GetOrigin(),
+                                  storage::StorageKey(registration_->origin()),
                                   base::DoNothing());
   ServiceWorkerRemoteContainerEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerContainerHost> container_host =
@@ -2389,7 +2428,7 @@
   // Promote the worker to active worker and add a controllee.
   registration_->SetActiveVersion(registration_->waiting_version());
   registry()->UpdateToActiveState(registration_->id(),
-                                  registration_->scope().GetOrigin(),
+                                  storage::StorageKey(registration_->origin()),
                                   base::DoNothing());
 
   // Make an updated registration.
@@ -2429,7 +2468,7 @@
   registration_->active_version()->SetStatus(ServiceWorkerVersion::ACTIVATED);
   registration_->SetWaitingVersion(nullptr);
   registry()->UpdateToActiveState(registration_->id(),
-                                  registration_->scope().GetOrigin(),
+                                  storage::StorageKey(registration_->origin()),
                                   base::DoNothing());
   ServiceWorkerRemoteContainerEndpoint remote_endpoint;
   base::WeakPtr<ServiceWorkerContainerHost> container_host =
@@ -2440,8 +2479,10 @@
 
   // Deleting the registration should move the resources to the purgeable list
   // but keep them available.
-  EXPECT_EQ(blink::ServiceWorkerStatusCode::kOk,
-            DeleteRegistration(registration_, scope_.GetOrigin()));
+  EXPECT_EQ(
+      blink::ServiceWorkerStatusCode::kOk,
+      DeleteRegistration(registration_,
+                         storage::StorageKey(url::Origin::Create(scope_))));
   std::vector<int64_t> verify_ids = GetPurgeableResourceIds();
   EXPECT_EQ(2u, verify_ids.size());
 
@@ -2450,8 +2491,9 @@
 
   // Also add an uncommitted resource.
   int64_t kStaleUncommittedResourceId = GetNewResourceIdSync(storage_control());
-  registry()->StoreUncommittedResourceId(kStaleUncommittedResourceId,
-                                         registration_->scope());
+  registry()->StoreUncommittedResourceId(
+      kStaleUncommittedResourceId,
+      storage::StorageKey(registration_->origin()));
   EnsureRemoteCallsAreExecuted();
   verify_ids = GetUncommittedResourceIds();
   EXPECT_EQ(1u, verify_ids.size());
@@ -2468,8 +2510,8 @@
   storage_control()->SetPurgingCompleteCallbackForTest(loop.QuitClosure());
   int64_t kNewResourceId = GetNewResourceIdSync(storage_control());
   WriteBasicResponse(storage_control(), kNewResourceId);
-  registry()->StoreUncommittedResourceId(kNewResourceId,
-                                         registration_->scope());
+  registry()->StoreUncommittedResourceId(
+      kNewResourceId, storage::StorageKey(registration_->origin()));
   loop.Run();
 
   // The stale resources should be purged, but the new resource should persist.
@@ -2500,7 +2542,8 @@
 
   // Delete the registration. The resources should be on the purgeable list but
   // should not be purged yet.
-  ASSERT_EQ(DeleteRegistration(registration_, scope_.GetOrigin()),
+  ASSERT_EQ(DeleteRegistration(registration_, storage::StorageKey(
+                                                  url::Origin::Create(scope_))),
             blink::ServiceWorkerStatusCode::kOk);
 
   EXPECT_THAT(GetPurgeableResourceIds(), testing::UnorderedElementsAreArray(
@@ -2536,7 +2579,8 @@
 TEST_F(ServiceWorkerRegistryResourceTest, RetryInflightCalls_Resources) {
   const int64_t kResourceId = GetNewResourceIdSync(storage_control());
 
-  registry()->StoreUncommittedResourceId(kResourceId, registration_->scope());
+  registry()->StoreUncommittedResourceId(
+      kResourceId, storage::StorageKey(registration_->origin()));
   EXPECT_EQ(inflight_call_count(), 1U);
 
   helper()->SimulateStorageRestartForTesting();
diff --git a/content/browser/service_worker/service_worker_script_cache_map.cc b/content/browser/service_worker/service_worker_script_cache_map.cc
index b024723..f215974 100644
--- a/content/browser/service_worker/service_worker_script_cache_map.cc
+++ b/content/browser/service_worker/service_worker_script_cache_map.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/check_op.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/service_worker_consts.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_version.h"
@@ -41,8 +42,8 @@
     return;  // Our storage has been wiped via DeleteAndStartOver.
   resource_map_[url] =
       storage::mojom::ServiceWorkerResourceRecord::New(resource_id, url, -1);
-  context_->registry()->StoreUncommittedResourceId(resource_id,
-                                                   owner_->scope().GetOrigin());
+  context_->registry()->StoreUncommittedResourceId(
+      resource_id, storage::StorageKey(url::Origin::Create(owner_->scope())));
 }
 
 void ServiceWorkerScriptCacheMap::NotifyFinishedCaching(
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc
index f1b396f..4f6fcd3d 100644
--- a/content/browser/service_worker/service_worker_test_utils.cc
+++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -384,6 +384,8 @@
   return registration;
 }
 
+// TODO(http://crbug.com/1199077): Update after ServiceWorkerVersion supports
+// StorageKey.
 scoped_refptr<ServiceWorkerVersion> CreateNewServiceWorkerVersion(
     ServiceWorkerRegistry* registry,
     scoped_refptr<ServiceWorkerRegistration> registration,
@@ -395,6 +397,7 @@
   base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
   registry->CreateNewVersion(
       std::move(registration), script_url, script_type,
+      storage::StorageKey(url::Origin::Create(script_url)),
       base::BindLambdaForTesting(
           [&](scoped_refptr<ServiceWorkerVersion> new_version) {
             version = std::move(new_version);
diff --git a/content/browser/service_worker/service_worker_unregister_job.cc b/content/browser/service_worker/service_worker_unregister_job.cc
index 4a92247..753e82c 100644
--- a/content/browser/service_worker/service_worker_unregister_job.cc
+++ b/content/browser/service_worker/service_worker_unregister_job.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_job_coordinator.h"
 #include "content/browser/service_worker/service_worker_registration.h"
@@ -35,8 +36,9 @@
 
 void ServiceWorkerUnregisterJob::Start() {
   context_->registry()->FindRegistrationForScope(
-      scope_, base::BindOnce(&ServiceWorkerUnregisterJob::OnRegistrationFound,
-                             weak_factory_.GetWeakPtr()));
+      scope_, storage::StorageKey(url::Origin::Create(scope_)),
+      base::BindOnce(&ServiceWorkerUnregisterJob::OnRegistrationFound,
+                     weak_factory_.GetWeakPtr()));
 }
 
 void ServiceWorkerUnregisterJob::Abort() {
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 0eea147..4f10416 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -27,6 +27,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/default_clock.h"
 #include "base/time/default_tick_clock.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/renderer_host/back_forward_cache_can_store_document_result.h"
@@ -464,7 +465,7 @@
   // get associated with it in
   // ServiceWorkerHost::CompleteStartWorkerPreparation.
   context_->registry()->FindRegistrationForId(
-      registration_id_, origin_,
+      registration_id_, storage::StorageKey(origin_),
       base::BindOnce(
           &ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker,
           weak_factory_.GetWeakPtr(), purpose, status_,
@@ -582,7 +583,7 @@
   if (!context_)
     return;
   context_->registry()->FindRegistrationForId(
-      registration_id_, origin_,
+      registration_id_, storage::StorageKey(origin_),
       base::BindOnce(&ServiceWorkerVersion::FoundRegistrationForUpdate,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/content/browser/service_worker/service_worker_version_browsertest.cc b/content/browser/service_worker/service_worker_version_browsertest.cc
index 16a976e..a122d6b8 100644
--- a/content/browser/service_worker/service_worker_version_browsertest.cc
+++ b/content/browser/service_worker/service_worker_version_browsertest.cc
@@ -24,6 +24,7 @@
 #include "base/test/bind.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/service_worker/embedded_worker_instance.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_container_host.h"
@@ -503,14 +504,14 @@
   }
 
   void FindRegistrationForId(int64_t id,
-                             const url::Origin& origin,
+                             const storage::StorageKey& key,
                              blink::ServiceWorkerStatusCode expected_status) {
     ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
     blink::ServiceWorkerStatusCode status =
         blink::ServiceWorkerStatusCode::kErrorFailed;
     base::RunLoop run_loop;
     wrapper()->context()->registry()->FindRegistrationForId(
-        id, origin,
+        id, key,
         CreateFindRegistrationReceiver(BrowserThread::UI,
                                        run_loop.QuitClosure(), &status));
     run_loop.Run();
@@ -868,7 +869,8 @@
   EXPECT_EQ(ServiceWorkerVersion::REDUNDANT, version_->status());
 
   // The registration should be deleted from storage.
-  FindRegistrationForId(registration_->id(), registration_->origin(),
+  FindRegistrationForId(registration_->id(),
+                        storage::StorageKey(registration_->origin()),
                         blink::ServiceWorkerStatusCode::kErrorNotFound);
   EXPECT_TRUE(registration_->is_uninstalled());
 }
@@ -910,7 +912,8 @@
 
   // The whole registration should be deleted from storage even though the
   // waiting version was not the broken one.
-  FindRegistrationForId(registration_->id(), registration_->origin(),
+  FindRegistrationForId(registration_->id(),
+                        storage::StorageKey(registration_->origin()),
                         blink::ServiceWorkerStatusCode::kErrorNotFound);
   EXPECT_TRUE(registration_->is_uninstalled());
 }
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 8f61829..02521228 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "base/time/time.h"
+#include "components/services/storage/public/cpp/storage_key.h"
 #include "content/browser/renderer_host/frame_tree_node.h"
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/embedded_worker_test_helper.h"
@@ -348,7 +349,8 @@
   base::Optional<blink::ServiceWorkerStatusCode> status;
   base::RunLoop run_loop;
   helper_->context()->registry()->DeleteRegistration(
-      registration_, registration_->scope().GetOrigin(),
+      registration_,
+      storage::StorageKey(url::Origin::Create(registration_->scope())),
       ReceiveServiceWorkerStatus(&status, run_loop.QuitClosure()));
   run_loop.Run();
   ASSERT_EQ(blink::ServiceWorkerStatusCode::kOk, status.value());
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 4ca99ce..a2f1859 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -1801,8 +1801,10 @@
   // This function currently assumes the new isolated site should persist
   // across restarts, so ask the embedder to save it, excluding off-the-record
   // profiles.
-  if (!context->IsOffTheRecord() && should_persist)
-    GetContentClient()->browser()->PersistIsolatedOrigin(context, site_origin);
+  if (!context->IsOffTheRecord() && should_persist) {
+    GetContentClient()->browser()->PersistIsolatedOrigin(context, site_origin,
+                                                         source);
+  }
 }
 
 void SiteInstanceImpl::WriteIntoTrace(perfetto::TracedValue context) {
diff --git a/content/browser/sms/sms_metrics.cc b/content/browser/sms/sms_metrics.cc
index 386c1ee..552faf0 100644
--- a/content/browser/sms/sms_metrics.cc
+++ b/content/browser/sms/sms_metrics.cc
@@ -89,4 +89,9 @@
   builder.Record(ukm_recorder);
 }
 
+void RecordWebContentsVisibilityOnReceive(bool is_visible) {
+  base::UmaHistogramBoolean("Blink.Sms.WebContentsVisibleOnReceive",
+                            is_visible);
+}
+
 }  // namespace content
diff --git a/content/browser/sms/sms_metrics.h b/content/browser/sms/sms_metrics.h
index 15873e8..e1b171a 100644
--- a/content/browser/sms/sms_metrics.h
+++ b/content/browser/sms/sms_metrics.h
@@ -61,6 +61,10 @@
 void RecordSmsUserCancelTime(base::TimeDelta duration,
                              ukm::SourceId source_id,
                              ukm::UkmRecorder* ukm_recorder);
+
+// Records whether the web contents that receives the OTP is visible or not.
+void RecordWebContentsVisibilityOnReceive(bool is_visible);
+
 }  // namespace content
 
 #endif  // CONTENT_BROWSER_SMS_SMS_METRICS_H_
diff --git a/content/browser/sms/webotp_service.cc b/content/browser/sms/webotp_service.cc
index dfc3cb7d..45fa320 100644
--- a/content/browser/sms/webotp_service.cc
+++ b/content/browser/sms/webotp_service.cc
@@ -186,6 +186,18 @@
 
   one_time_code_ = one_time_code;
 
+  WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(render_frame_host());
+  // With UserConsent API, users can see and interact with the permission prompt
+  // when they are on the different page other than the one that calls WebOTP.
+  // This is considered as a bad UX and we should measure how many successful
+  // verifications are exercising the UserConsent backend which is implied by
+  // UserConsent::kObtained.
+  if (consent_requirement == UserConsent::kObtained) {
+    RecordWebContentsVisibilityOnReceive(web_contents->GetVisibility() ==
+                                         Visibility::VISIBLE);
+  }
+
   // Create a new consent handler for each OTP request. While we could
   // potentially cache these across request but they are lightweight enought to
   // not be worth the complexity associate with caching them.
diff --git a/content/browser/sms/webotp_service_unittest.cc b/content/browser/sms/webotp_service_unittest.cc
index 1950ceb..2252050 100644
--- a/content/browser/sms/webotp_service_unittest.cc
+++ b/content/browser/sms/webotp_service_unittest.cc
@@ -1069,4 +1069,57 @@
   ExpectOutcomeUKM(url, blink::WebOTPServiceOutcome::kUserCancelled);
 }
 
+TEST_F(WebOTPServiceTest, RecordWebContentsVisibilityForUserConsentAPI) {
+  NavigateAndCommit(GURL(kTestUrl));
+  base::HistogramTester histogram_tester;
+
+  // Sets the WebContents to visible
+  WebContentsImpl* web_contents_impl =
+      static_cast<WebContentsImpl*>(web_contents());
+  web_contents_impl->UpdateWebContentsVisibility(Visibility::VISIBLE);
+  ASSERT_EQ(web_contents_impl->GetVisibility(), Visibility::VISIBLE);
+  Service service1(web_contents_impl);
+
+  base::RunLoop loop1;
+
+  EXPECT_CALL(*service1.provider(), Retrieve(_, _))
+      .WillOnce(Invoke([&service1]() {
+        service1.NotifyReceive(GURL(kTestUrl), "ABC", UserConsent::kObtained);
+      }));
+
+  service1.MakeRequest(BindLambdaForTesting(
+      [&loop1](SmsStatus status, const Optional<string>& otp) {
+        loop1.Quit();
+      }));
+
+  loop1.Run();
+
+  histogram_tester.ExpectBucketCount("Blink.Sms.WebContentsVisibleOnReceive", 1,
+                                     1);
+  histogram_tester.ExpectTotalCount("Blink.Sms.WebContentsVisibleOnReceive", 1);
+
+  // Sets the WebContents to invisible
+  web_contents_impl->UpdateWebContentsVisibility(Visibility::HIDDEN);
+  ASSERT_NE(web_contents_impl->GetVisibility(), Visibility::VISIBLE);
+
+  Service service2(web_contents_impl);
+
+  base::RunLoop loop2;
+  EXPECT_CALL(*service2.provider(), Retrieve(_, _))
+      .WillOnce(Invoke([&service2]() {
+        service2.NotifyReceive(GURL(kTestUrl), "ABC", UserConsent::kObtained);
+      }));
+
+  service2.MakeRequest(BindLambdaForTesting(
+      [&loop2](SmsStatus status, const Optional<string>& otp) {
+        loop2.Quit();
+      }));
+
+  loop2.Run();
+
+  histogram_tester.ExpectBucketCount("Blink.Sms.WebContentsVisibleOnReceive", 0,
+                                     1);
+  histogram_tester.ExpectTotalCount("Blink.Sms.WebContentsVisibleOnReceive", 2);
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index bce76c9..3f9925b6 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -337,7 +337,9 @@
   metadata_dict->SetString("gpu-gl-vendor", gpu_info.gl_vendor);
   metadata_dict->SetString("gpu-gl-renderer", gpu_info.gl_renderer);
 #endif
-  metadata_dict->SetDictionary("gpu-features", GetFeatureStatus());
+  metadata_dict->SetDictionary(
+      "gpu-features", base::DictionaryValue::From(
+                          std::make_unique<base::Value>(GetFeatureStatus())));
 
   metadata_dict->SetString("clock-domain", GetClockString());
   metadata_dict->SetBoolean("highres-ticks",
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 7823b5e..1836c65 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -3783,6 +3783,7 @@
 }
 
 static constexpr char kTestPIN[] = "1234";
+static constexpr char16_t kTestPIN16[] = u"1234";
 
 class UVTestAuthenticatorClientDelegate
     : public AuthenticatorRequestClientDelegate {
@@ -3807,8 +3808,7 @@
     *collected_pin_ = true;
     *min_pin_length_ = options.min_pin_length;
     base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(provide_pin_cb), base::UTF8ToUTF16(kTestPIN)));
+        FROM_HERE, base::BindOnce(std::move(provide_pin_cb), kTestPIN16));
   }
 
   void StartBioEnrollment(base::OnceClosure next_callback) override {
@@ -3933,7 +3933,7 @@
 // the PIN to answer with.
 struct PINExpectation {
   PINReason reason;
-  std::string pin;
+  std::u16string pin;
   int attempts;
   uint32_t min_pin_length = device::kMinPinLength;
   PINError error = PINError::kNoError;
@@ -3969,12 +3969,11 @@
     DCHECK_EQ(expected_.front().min_pin_length, options.min_pin_length);
     DCHECK_EQ(expected_.front().reason, options.reason);
     DCHECK_EQ(expected_.front().error, options.error);
-    std::string pin = std::move(expected_.front().pin);
+    std::u16string pin = std::move(expected_.front().pin);
     expected_.pop_front();
 
     base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(provide_pin_cb),
-                                  base::UTF8ToUTF16(std::move(pin))));
+        FROM_HERE, base::BindOnce(std::move(provide_pin_cb), std::move(pin)));
   }
 
   void FinishCollectToken() override {}
@@ -4160,12 +4159,13 @@
 
               case kSetPIN:
                 // A single PIN prompt to set a PIN is expected.
-                test_client_.expected = {{PINReason::kSet, kTestPIN}};
+                test_client_.expected = {{PINReason::kSet, kTestPIN16}};
                 break;
 
               case kUsePIN:
                 // A single PIN prompt to get the PIN is expected.
-                test_client_.expected = {{PINReason::kChallenge, kTestPIN, 8}};
+                test_client_.expected = {
+                    {PINReason::kChallenge, kTestPIN16, 8}};
                 break;
 
               default:
@@ -4210,10 +4210,10 @@
   virtual_device_factory_->mutable_state()->pin_retries =
       device::kMaxPinRetries;
 
-  test_client_.expected = {{PINReason::kChallenge, "wrong", 8},
-                           {PINReason::kChallenge, "wrong", 7,
+  test_client_.expected = {{PINReason::kChallenge, u"wrong", 8},
+                           {PINReason::kChallenge, u"wrong", 7,
                             device::kMinPinLength, PINError::kWrongPIN},
-                           {PINReason::kChallenge, "wrong", 6,
+                           {PINReason::kChallenge, u"wrong", 6,
                             device::kMinPinLength, PINError::kWrongPIN}};
   EXPECT_EQ(AuthenticatorMakeCredential(make_credential_options()).status,
             AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -4228,7 +4228,7 @@
   virtual_device_factory_->mutable_state()->pin = kTestPIN;
   virtual_device_factory_->mutable_state()->pin_retries = 1;
 
-  test_client_.expected = {{PINReason::kChallenge, "wrong", 1}};
+  test_client_.expected = {{PINReason::kChallenge, u"wrong", 1}};
   EXPECT_EQ(AuthenticatorMakeCredential().status,
             AuthenticatorStatus::NOT_ALLOWED_ERROR);
   EXPECT_EQ(0, virtual_device_factory_->mutable_state()->pin_retries);
@@ -4243,8 +4243,8 @@
       device::kMaxPinRetries;
 
   // Test that we can successfully get a PIN token after a failure.
-  test_client_.expected = {{PINReason::kChallenge, "wrong", 8},
-                           {PINReason::kChallenge, kTestPIN, 7,
+  test_client_.expected = {{PINReason::kChallenge, u"wrong", 8},
+                           {PINReason::kChallenge, kTestPIN16, 7,
                             device::kMinPinLength, PINError::kWrongPIN}};
   EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
   EXPECT_EQ(static_cast<int>(device::kMaxPinRetries),
@@ -4262,7 +4262,7 @@
   virtual_device_factory_->mutable_state()->pin_retries =
       device::kMaxPinRetries;
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
   EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
   EXPECT_EQ(taps, 1);
 }
@@ -4293,7 +4293,7 @@
       ->ReplaceDefaultDiscoveryFactoryForTesting(std::move(discovery));
 
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
   EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
   EXPECT_EQ(taps, 2);
 }
@@ -4312,7 +4312,7 @@
   virtual_device_factory_->SetCtap2Config(config);
   virtual_device_factory_->mutable_state()->pin = kTestPIN;
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
 
   MakeCredentialResult result =
       AuthenticatorMakeCredential(make_credential_options(
@@ -4365,7 +4365,7 @@
   config.ctap2_versions = {device::Ctap2Version::kCtap2_1};
   virtual_device_factory_->SetCtap2Config(config);
   virtual_device_factory_->mutable_state()->min_pin_length = 6;
-  test_client_.expected = {{PINReason::kSet, "123456", 0, 6}};
+  test_client_.expected = {{PINReason::kSet, u"123456", 0, 6}};
 
   MakeCredentialResult result =
       AuthenticatorMakeCredential(make_credential_options());
@@ -4385,7 +4385,7 @@
   virtual_device_factory_->mutable_state()->min_pin_length = 6;
   virtual_device_factory_->mutable_state()->pin = "123456";
   test_client_.expected = {
-      {PINReason::kChallenge, "123456", device::kMaxPinRetries, 6}};
+      {PINReason::kChallenge, u"123456", device::kMaxPinRetries, 6}};
 
   MakeCredentialResult result =
       AuthenticatorMakeCredential(make_credential_options());
@@ -4408,9 +4408,9 @@
   virtual_device_factory_->mutable_state()->pin_retries =
       device::kMaxPinRetries;
   virtual_device_factory_->mutable_state()->min_pin_length = 6;
-  test_client_.expected = {{PINReason::kChallenge, kTestPIN,
+  test_client_.expected = {{PINReason::kChallenge, kTestPIN16,
                             device::kMaxPinRetries, device::kMinPinLength},
-                           {PINReason::kChange, "567890", 0, 6}};
+                           {PINReason::kChange, u"567890", 0, 6}};
 
   MakeCredentialResult result =
       AuthenticatorMakeCredential(make_credential_options());
@@ -4440,7 +4440,7 @@
       // PIN is still required for discoverable credentials, or if the caller
       // requests it.
       if (discoverable || request_uv) {
-        test_client_.expected = {{PINReason::kChallenge, kTestPIN,
+        test_client_.expected = {{PINReason::kChallenge, kTestPIN16,
                                   device::kMaxPinRetries,
                                   device::kMinPinLength}};
       } else {
@@ -4485,10 +4485,10 @@
     virtual_device_factory_->SetCtap2Config(config);
     if (pin_set) {
       virtual_device_factory_->mutable_state()->pin = kTestPIN;
-      test_client_.expected = {{PINReason::kChallenge, kTestPIN,
+      test_client_.expected = {{PINReason::kChallenge, kTestPIN16,
                                 device::kMaxPinRetries, device::kMinPinLength}};
     } else {
-      test_client_.expected = {{PINReason::kSet, kTestPIN,
+      test_client_.expected = {{PINReason::kSet, kTestPIN16,
                                 device::kMaxPinRetries, device::kMinPinLength}};
     }
 
@@ -4556,7 +4556,8 @@
 
               case kUsePIN:
                 // A single prompt to get the PIN is expected.
-                test_client_.expected = {{PINReason::kChallenge, kTestPIN, 8}};
+                test_client_.expected = {
+                    {PINReason::kChallenge, kTestPIN16, 8}};
                 break;
 
               default:
@@ -4603,10 +4604,10 @@
   ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
       options->allow_credentials[0].id(), kTestRelyingPartyId));
 
-  test_client_.expected = {{PINReason::kChallenge, "wrong", 8},
-                           {PINReason::kChallenge, "wrong", 7,
+  test_client_.expected = {{PINReason::kChallenge, u"wrong", 8},
+                           {PINReason::kChallenge, u"wrong", 7,
                             device::kMinPinLength, PINError::kWrongPIN},
-                           {PINReason::kChallenge, "wrong", 6,
+                           {PINReason::kChallenge, u"wrong", 6,
                             device::kMinPinLength, PINError::kWrongPIN}};
   EXPECT_EQ(AuthenticatorGetAssertion(std::move(options)).status,
             AuthenticatorStatus::NOT_ALLOWED_ERROR);
@@ -4625,7 +4626,7 @@
   ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
       options->allow_credentials[0].id(), kTestRelyingPartyId));
 
-  test_client_.expected = {{PINReason::kChallenge, "wrong", 1}};
+  test_client_.expected = {{PINReason::kChallenge, u"wrong", 1}};
   EXPECT_EQ(AuthenticatorGetAssertion(std::move(options)).status,
             AuthenticatorStatus::NOT_ALLOWED_ERROR);
   EXPECT_EQ(0, virtual_device_factory_->mutable_state()->pin_retries);
@@ -4646,7 +4647,7 @@
   ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
       options->allow_credentials[0].id(), kTestRelyingPartyId));
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
   EXPECT_EQ(AuthenticatorGetAssertion(std::move(options)).status,
             AuthenticatorStatus::SUCCESS);
   EXPECT_EQ(taps, 1);
@@ -4681,7 +4682,7 @@
       ->ReplaceDefaultDiscoveryFactoryForTesting(std::move(discovery));
 
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
   EXPECT_EQ(AuthenticatorGetAssertion(std::move(options)).status,
             AuthenticatorStatus::SUCCESS);
   EXPECT_EQ(taps, 2);
@@ -4701,7 +4702,7 @@
   ASSERT_TRUE(virtual_device_factory_->mutable_state()->InjectRegistration(
       options->allow_credentials[0].id(), kTestRelyingPartyId));
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
 
   GetAssertionResult result = AuthenticatorGetAssertion(std::move(options));
   EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
@@ -4772,7 +4773,7 @@
           device::kMaxPinRetries;
       virtual_device_factory_->SetCtap2Config(config);
       test_client_.expected = {
-          {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+          {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
       // Since converting to U2F isn't possible, this will trigger a PIN prompt
       // and succeed because the device does actually support the algorithm.
       expected_to_succeed = true;
@@ -4832,7 +4833,7 @@
       // test infrastructure will CHECK if |expected| is set and not used.)
       options->prf_enable = true;
       test_client_.expected = {
-          {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+          {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
     } else {
       // If PRF is requested, but the authenticator doesn't support it, then we
       // should still use U2F.
@@ -4870,7 +4871,7 @@
       device::kMaxPinRetries;
 
   test_client_.expected = {
-      {PINReason::kChallenge, kTestPIN, device::kMaxPinRetries}};
+      {PINReason::kChallenge, kTestPIN16, device::kMaxPinRetries}};
 
   // Craft an exclude list that is large enough to trigger batched probing and
   // includes one match for a credProtect=uvRequired credential.
@@ -5479,8 +5480,7 @@
       CollectPINOptions options,
       base::OnceCallback<void(std::u16string)> provide_pin_cb) override {
     base::SequencedTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(std::move(provide_pin_cb), base::UTF8ToUTF16(kTestPIN)));
+        FROM_HERE, base::BindOnce(std::move(provide_pin_cb), kTestPIN16));
   }
 
   void FinishCollectToken() override {}
diff --git a/content/browser/webui/web_ui_data_source_unittest.cc b/content/browser/webui/web_ui_data_source_unittest.cc
index ae07bfa..ffdf3cd 100644
--- a/content/browser/webui/web_ui_data_source_unittest.cc
+++ b/content/browser/webui/web_ui_data_source_unittest.cc
@@ -20,7 +20,7 @@
 const int kDummyResourceId = 789;
 const int kDummyJSResourceId = 790;
 
-const char kDummyString[] = "foo";
+const char16_t kDummyString[] = u"foo";
 const char kDummyDefaultResource[] = "<html>foo</html>";
 const char kDummyResource[] = "<html>blah</html>";
 const char kDummyJSResource[] = "export const bar = 5;";
@@ -31,7 +31,7 @@
 
   std::u16string GetLocalizedString(int message_id) override {
     if (message_id == kDummyStringId)
-      return base::UTF8ToUTF16(kDummyString);
+      return kDummyString;
     return std::u16string();
   }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 802e4f28..ccd0247 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -29,6 +29,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/allow_service_worker_result.h"
 #include "content/public/browser/certificate_request_result_type.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/generated_code_cache_settings.h"
 #include "content/public/browser/mojo_binder_policy_map.h"
 #include "content/public/browser/storage_partition_config.h"
@@ -589,8 +590,10 @@
   // Called when a new dynamic isolated origin was added in |context|, and the
   // origin desires to be persisted across restarts, to give the embedder an
   // opportunity to save this isolated origin to disk.
-  virtual void PersistIsolatedOrigin(BrowserContext* context,
-                                     const url::Origin& origin) {}
+  virtual void PersistIsolatedOrigin(
+      BrowserContext* context,
+      const url::Origin& origin,
+      ChildProcessSecurityPolicy::IsolatedOriginSource source) {}
 
   // Indicates whether a file path should be accessible via file URL given a
   // request from a browser context which lives within |profile_path|.
diff --git a/content/renderer/media/android/stream_texture_factory.cc b/content/renderer/media/android/stream_texture_factory.cc
index 15eec24..29a3760 100644
--- a/content/renderer/media/android/stream_texture_factory.cc
+++ b/content/renderer/media/android/stream_texture_factory.cc
@@ -137,10 +137,9 @@
 unsigned StreamTextureFactory::CreateStreamTexture() {
   int32_t stream_id = channel_->GenerateRouteID();
   bool succeeded = false;
-  mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync;
-  channel_->GetGpuChannel().CreateStreamTexture(stream_id, &succeeded);
+  channel_->Send(new GpuChannelMsg_CreateStreamTexture(stream_id, &succeeded));
   if (!succeeded) {
-    DLOG(ERROR) << "CreateStreamTexture returned failure";
+    DLOG(ERROR) << "GpuChannelMsg_CreateStreamTexture returned failure";
     return 0;
   }
   return stream_id;
diff --git a/content/renderer/media/media_factory.cc b/content/renderer/media/media_factory.cc
index fd6e474..48217e8c8 100644
--- a/content/renderer/media/media_factory.cc
+++ b/content/renderer/media/media_factory.cc
@@ -587,7 +587,7 @@
     media::DecoderFactory* decoder_factory,
     std::unique_ptr<media::RemotePlaybackClientWrapper> client_wrapper,
     base::WeakPtr<media::MediaObserver>* out_media_observer) {
-  using FactoryType = media::RendererFactoryType;
+  using media::RendererType;
 
   RenderThreadImpl* render_thread = RenderThreadImpl::current();
   // Render thread may not exist in tests, returning nullptr if it does not.
@@ -616,13 +616,13 @@
               render_frame_->GetTaskRunner(blink::TaskType::kInternalMedia)));
 
   if (use_media_player_renderer) {
-    factory_selector->AddBaseFactory(FactoryType::kMediaPlayer,
+    factory_selector->AddBaseFactory(RendererType::kMediaPlayer,
                                      std::move(media_player_factory));
     use_default_renderer_factory = false;
   } else {
     // Always give |factory_selector| a MediaPlayerRendererClient factory. WMPI
     // might fallback to it if the final redirected URL is an HLS url.
-    factory_selector->AddFactory(FactoryType::kMediaPlayer,
+    factory_selector->AddFactory(RendererType::kMediaPlayer,
                                  std::move(media_player_factory));
   }
 
@@ -642,7 +642,7 @@
       base::BindRepeating(&FlingingRendererClientFactory::IsFlingingActive,
                           base::Unretained(flinging_factory.get()));
   factory_selector->AddConditionalFactory(
-      FactoryType::kFlinging, std::move(flinging_factory), is_flinging_cb);
+      RendererType::kFlinging, std::move(flinging_factory), is_flinging_cb);
 #endif  // defined(OS_ANDROID)
 
 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
@@ -651,16 +651,16 @@
     use_default_renderer_factory = false;
 #if BUILDFLAG(ENABLE_CAST_RENDERER)
     factory_selector->AddBaseFactory(
-        FactoryType::kCast, std::make_unique<CastRendererClientFactory>(
-                                media_log, CreateMojoRendererFactory()));
+        RendererType::kCast, std::make_unique<CastRendererClientFactory>(
+                                 media_log, CreateMojoRendererFactory()));
 #else
     // The "default" MojoRendererFactory can be wrapped by a
     // DecryptingRendererFactory without changing any behavior.
-    // TODO(tguilbert/xhwang): Add "FactoryType::DECRYPTING" if ever we need to
+    // TODO(tguilbert/xhwang): Add "RendererType::DECRYPTING" if ever we need to
     // distinguish between a "pure" and "decrypting" MojoRenderer.
     factory_selector->AddBaseFactory(
-        FactoryType::kMojo, std::make_unique<media::DecryptingRendererFactory>(
-                                media_log, CreateMojoRendererFactory()));
+        RendererType::kMojo, std::make_unique<media::DecryptingRendererFactory>(
+                                 media_log, CreateMojoRendererFactory()));
 #endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
   }
 #endif  // BUILDFLAG(ENABLE_MOJO_RENDERER)
@@ -668,7 +668,7 @@
 #if defined(OS_FUCHSIA)
   use_default_renderer_factory = false;
   factory_selector->AddBaseFactory(
-      FactoryType::kFuchsia,
+      RendererType::kFuchsia,
       std::make_unique<FuchsiaRendererFactory>(
           media_log, decoder_factory,
           base::BindRepeating(&RenderThreadImpl::GetGpuFactories,
@@ -680,7 +680,7 @@
     DCHECK(!use_media_player_renderer);
     auto default_factory = CreateDefaultRendererFactory(
         media_log, decoder_factory, render_thread, render_frame_);
-    factory_selector->AddBaseFactory(FactoryType::kDefault,
+    factory_selector->AddBaseFactory(RendererType::kDefault,
                                      std::move(default_factory));
   }
 
@@ -706,14 +706,14 @@
       &media::remoting::CourierRendererFactory::IsRemotingActive,
       base::Unretained(courier_factory.get()));
   factory_selector->AddConditionalFactory(
-      FactoryType::kCourier, std::move(courier_factory), is_remoting_cb);
+      RendererType::kCourier, std::move(courier_factory), is_remoting_cb);
 #endif
 
 #if defined(OS_WIN)
   // Only use MediaFoundationRenderer when MediaFoundationCdm is available.
   if (media::MediaFoundationCdm::IsAvailable()) {
     factory_selector->AddFactory(
-        FactoryType::kMediaFoundation,
+        RendererType::kMediaFoundation,
         std::make_unique<media::MediaFoundationRendererClientFactory>(
             render_thread->compositor_task_runner(),
             CreateMojoRendererFactory()));
@@ -741,7 +741,7 @@
         },
         url);
     factory_selector->AddConditionalFactory(
-        FactoryType::kRemoting, std::move(remoting_renderer_factory),
+        RendererType::kRemoting, std::move(remoting_renderer_factory),
         is_remoting_media);
   }
 #endif  // BUILDFLAG(IS_CHROMECAST)
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index df7e323..b000033f 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -2375,8 +2375,7 @@
       0, base::DoNothing());
 
   // Non surrogate pair unicode character.
-  const std::u16string unicode_composition = base::UTF8ToUTF16(
-      "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
+  const std::u16string unicode_composition = u"あいうえお";
   widget_input_handler->ImeSetComposition(unicode_composition,
                                           empty_ime_text_span,
                                           gfx::Range::InvalidRange(), 0, 0);
@@ -2389,8 +2388,7 @@
                                       base::DoNothing());
 
   // Surrogate pair character.
-  const std::u16string surrogate_pair_char =
-      base::UTF8ToUTF16("\xF0\xA0\xAE\x9F");
+  const std::u16string surrogate_pair_char = u"𠮟";
   widget_input_handler->ImeSetComposition(surrogate_pair_char,
                                           empty_ime_text_span,
                                           gfx::Range::InvalidRange(), 0, 0);
@@ -2404,8 +2402,8 @@
 
   // Mixed string.
   const std::u16string surrogate_pair_mixed_composition =
-      surrogate_pair_char + base::UTF8ToUTF16("\xE3\x81\x82") +
-      surrogate_pair_char + u"b" + surrogate_pair_char;
+      surrogate_pair_char + u"あ" + surrogate_pair_char + u"b" +
+      surrogate_pair_char;
   const size_t utf16_length = 8UL;
   const bool is_surrogate_pair_empty_rect[8] = {false, true,  false, false,
                                                 true,  false, false, true};
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index bb60e5d..753c4c9 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -508,12 +508,6 @@
   return webview_->GetRendererPreferences();
 }
 
-void RenderViewImpl::OnPageVisibilityChanged(PageVisibilityState visibility) {
-#if defined(OS_ANDROID)
-  SuspendVideoCaptureDevices(visibility != PageVisibilityState::kVisible);
-#endif
-}
-
 void RenderViewImpl::OnPageFrozenChanged(bool frozen) {
   if (frozen) {
     // Make sure browser has the latest info before the page is frozen. If the
@@ -554,21 +548,4 @@
 #endif
 }
 
-#if defined(OS_ANDROID)
-void RenderViewImpl::SuspendVideoCaptureDevices(bool suspend) {
-  if (!main_render_frame_)
-    return;
-
-  blink::WebMediaStreamDeviceObserver* media_stream_device_observer =
-      main_render_frame_->MediaStreamDeviceObserver();
-  if (!media_stream_device_observer)
-    return;
-
-  blink::MediaStreamDevices video_devices =
-      media_stream_device_observer->GetNonScreenCaptureDevices();
-  RenderThreadImpl::current()->video_capture_impl_manager()->SuspendDevices(
-      video_devices, suspend);
-}
-#endif  // defined(OS_ANDROID)
-
 }  // namespace content
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index b855cc7..c08c71c 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -61,7 +61,6 @@
 namespace content {
 class AgentSchedulingGroup;
 class RenderViewImplTest;
-class RenderViewObserver;
 class RenderViewTest;
 
 namespace mojom {
@@ -122,10 +121,6 @@
 
   CompositorDependencies* compositor_deps() const { return compositor_deps_; }
 
-  // Functions to add and remove observers for this object.
-  void AddObserver(RenderViewObserver* observer);
-  void RemoveObserver(RenderViewObserver* observer);
-
   // Passes along the page zoom to the WebView to set it on a newly attached
   // LocalFrame.
   void PropagatePageZoomToNewlyAttachedFrame(bool use_zoom_for_dsf,
@@ -160,7 +155,6 @@
   void PrintPage(blink::WebLocalFrame* frame) override;
   bool AcceptsLoadDrops() override;
   bool CanUpdateLayout() override;
-  void OnPageVisibilityChanged(PageVisibilityState visibility) override;
   void OnPageFrozenChanged(bool frozen) override;
   void DidUpdateRendererPreferences() override;
 
@@ -208,13 +202,6 @@
 
   // Misc private functions ----------------------------------------------------
 
-#if defined(OS_ANDROID)
-  // Make the video capture devices (e.g. webcam) stop/resume delivering video
-  // frames to their clients, depending on flag |suspend|. This is called in
-  // response to a RenderView PageHidden/Shown().
-  void SuspendVideoCaptureDevices(bool suspend);
-#endif
-
   // In OOPIF-enabled modes, this tells each RenderFrame with a pending state
   // update to inform the browser process.
   void SendFrameStateUpdates();
@@ -287,12 +274,6 @@
   bool was_created_by_renderer_ = false;
 #endif
 
-  // Misc ----------------------------------------------------------------------
-
-  // All the registered observers.  We expect this list to be small, so vector
-  // is fine.
-  base::ObserverList<RenderViewObserver>::Unchecked observers_;
-
   // ---------------------------------------------------------------------------
   // ADDING NEW DATA? Please see if it fits appropriately in one of the above
   // sections rather than throwing it randomly at the end. If you're adding a
diff --git a/content/renderer/stream_texture_host_android.cc b/content/renderer/stream_texture_host_android.cc
index 49583c7..f56e4bf3 100644
--- a/content/renderer/stream_texture_host_android.cc
+++ b/content/renderer/stream_texture_host_android.cc
@@ -8,7 +8,6 @@
 #include "content/renderer/render_thread_impl.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/command_buffer_id.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/common/vulkan_ycbcr_info.h"
 #include "ipc/ipc_message_macros.h"
@@ -28,7 +27,7 @@
     // to ensure this is ordered correctly with regards to previous deferred
     // messages, such as CreateSharedImage.
     uint32_t flush_id = channel_->EnqueueDeferredMessage(
-        gpu::mojom::DeferredRequestParams::NewDestroyStreamTexture(route_id_));
+        GpuStreamTextureMsg_Destroy(route_id_));
     channel_->EnsureFlush(flush_id);
     channel_->RemoveRoute(route_id_);
   }
diff --git a/content/services/auction_worklet/auction_runner.cc b/content/services/auction_worklet/auction_runner.cc
index 8aa3b2a..1b5f699 100644
--- a/content/services/auction_worklet/auction_runner.cc
+++ b/content/services/auction_worklet/auction_runner.cc
@@ -11,7 +11,6 @@
 #include "content/services/auction_worklet/bidder_worklet.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "content/services/auction_worklet/seller_worklet.h"
-#include "content/services/auction_worklet/trusted_bidding_signals.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
 #include "url/gurl.h"
@@ -65,23 +64,8 @@
         browser_signals_->top_frame_origin,
         browser_signals_->seller.Serialize(), auction_start_time_,
         &auction_v8_helper_,
-        base::BindOnce(&AuctionRunner::OnBidderScriptLoaded,
+        base::BindOnce(&AuctionRunner::OnGenerateBidComplete,
                        base::Unretained(this), bid_state));
-    if (bidder->group->trusted_bidding_signals_url.has_value()) {
-      bid_state->trusted_bidding_signals =
-          std::make_unique<TrustedBiddingSignals>(
-              url_loader_factory_.get(),
-              bidder->group->trusted_bidding_signals_keys.has_value()
-                  ? bidder->group->trusted_bidding_signals_keys.value()
-                  : std::vector<std::string>(),
-              browser_signals_->top_frame_origin.host(),
-              bidder->group->trusted_bidding_signals_url.value_or(GURL()),
-              &auction_v8_helper_,
-              base::BindOnce(&AuctionRunner::OnTrustedSignalsLoaded,
-                             base::Unretained(this), bid_state));
-    } else {
-      OnTrustedSignalsLoaded(bid_state, false, base::nullopt);
-    }
   }
 
   // Also initiate the script fetch for the seller script.
@@ -92,66 +76,22 @@
                      base::Unretained(this)));
 }
 
-void AuctionRunner::OnBidderScriptLoaded(
-    BidState* state,
-    bool load_result,
-    base::Optional<std::string> error_msg) {
-  DCHECK(!state->bidder_script_loaded);
-  DCHECK(!state->failed);
-
-  if (error_msg.has_value())
-    errors_.push_back(std::move(error_msg).value());
-
-  state->bidder_script_loaded = true;
-  if (!load_result) {
-    state->failed = true;
-
-    // No point in waiting for trusted signals (if any) if we don't have a
-    // bidder script that looks at them.
-    state->trusted_bidding_signals.reset();
-    state->trusted_signals_loaded = true;
-  }
-
-  MaybeRunBid(state);
-}
-
-void AuctionRunner::OnTrustedSignalsLoaded(
-    BidState* state,
-    bool load_result,
-    base::Optional<std::string> error_msg) {
-  DCHECK(!state->trusted_signals_loaded);
-
-  if (error_msg.has_value())
-    errors_.push_back(std::move(error_msg).value());
-
-  state->trusted_signals_loaded = true;
-  if (!load_result)
-    state->trusted_bidding_signals.reset();
-
-  MaybeRunBid(state);
-}
-
-void AuctionRunner::MaybeRunBid(BidState* state) {
-  if (!state->trusted_signals_loaded || !state->bidder_script_loaded)
-    return;
+void AuctionRunner::OnGenerateBidComplete(BidState* state,
+                                          BidderWorklet::BidResult bid_result) {
+  DCHECK(!state->bid_generate_complete);
+  DCHECK_GT(outstanding_bids_, 0);
 
   --outstanding_bids_;
-  if (!state->failed)
-    RunBid(state);
+
+  errors_.insert(errors_.end(), bid_result.error_msgs.begin(),
+                 bid_result.error_msgs.end());
+  state->bid_generate_complete = true;
+  state->bid_result = bid_result;
 
   if (ReadyToScore())
     ScoreOne();
 }
 
-void AuctionRunner::RunBid(BidState* state) {
-  base::TimeTicks start = base::TimeTicks::Now();
-  state->bid_result =
-      state->bidder_worklet->GenerateBid(state->trusted_bidding_signals.get());
-  if (state->bid_result.error_msg.has_value())
-    errors_.push_back(std::move(state->bid_result.error_msg).value());
-  state->bid_duration = base::TimeTicks::Now() - start;
-}
-
 void AuctionRunner::OnSellerWorkletLoaded(
     bool load_result,
     base::Optional<std::string> error_msg) {
@@ -174,8 +114,7 @@
 
   // Skip over failed ones.
   while (seller_considering_ < num_bidders &&
-         (bid_states_[seller_considering_].failed ||
-          !bid_states_[seller_considering_].bid_result.success)) {
+         !bid_states_[seller_considering_].bid_result.success) {
     ++seller_considering_;
   }
 
@@ -201,7 +140,7 @@
   SellerWorklet::ScoreResult result = seller_worklet_->ScoreAd(
       state->bid_result.ad, state->bid_result.bid, *auction_config_,
       browser_signals_->top_frame_origin.host(), state->bidder->group->owner,
-      AdRenderFingerprint(state), state->bid_duration);
+      AdRenderFingerprint(state), state->bid_result.bid_duration);
   if (result.error_msg.has_value())
     errors_.push_back(std::move(result.error_msg).value());
   return result;
diff --git a/content/services/auction_worklet/auction_runner.h b/content/services/auction_worklet/auction_runner.h
index f72a3ae..4ce2c21 100644
--- a/content/services/auction_worklet/auction_runner.h
+++ b/content/services/auction_worklet/auction_runner.h
@@ -16,15 +16,12 @@
 #include "content/services/auction_worklet/bidder_worklet.h"
 #include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
 #include "content/services/auction_worklet/seller_worklet.h"
-#include "content/services/auction_worklet/trusted_bidding_signals.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
 #include "third_party/blink/public/mojom/interest_group/interest_group_types.mojom.h"
 #include "url/gurl.h"
 
 namespace auction_worklet {
 
-class TrustedBiddingSignals;
-
 // An AuctionRunner loads and runs the bidder and seller worklets, along with
 // their reporting phases and produces the result via a callback.
 //
@@ -57,23 +54,13 @@
 
     mojom::BiddingInterestGroup* bidder = nullptr;
 
-    // true if loading of the bidder script failed, meaning that no bidding
-    // will actually be done.
-    bool failed = false;
+    // true if the generateBid() callback passed to the BidderWorklet's
+    // constructor has been invoked. This may indicated either successful
+    // generation of a bid, or failure to load or run the script.
+    bool bid_generate_complete = false;
 
-    // true if there is no outstanding load of trusted bidding signals pending
-    // for this bidder (including if none were configured or it failed; in such
-    // cases `trusted_bidding_signals` will be null).
-    bool trusted_signals_loaded = false;
-
-    // true if there is no outstanding load of the bidder script pending
-    // (including if the load failed).
-    bool bidder_script_loaded = false;
-
-    std::unique_ptr<TrustedBiddingSignals> trusted_bidding_signals;
     std::unique_ptr<BidderWorklet> bidder_worklet;
     BidderWorklet::BidResult bid_result;
-    base::TimeDelta bid_duration;
     SellerWorklet::ScoreResult score_result;
   };
 
@@ -86,14 +73,8 @@
   ~AuctionRunner();
 
   void StartBidding();
-  void OnBidderScriptLoaded(BidState* state,
-                            bool load_result,
-                            base::Optional<std::string> error_msg);
-  void OnTrustedSignalsLoaded(BidState* state,
-                              bool load_result,
-                              base::Optional<std::string> error_msg);
-  void MaybeRunBid(BidState* state);
-  void RunBid(BidState* state);
+  void OnGenerateBidComplete(BidState* state,
+                             BidderWorklet::BidResult bid_result);
 
   // True if all bid results and the seller script load are complete.
   bool ReadyToScore() const { return outstanding_bids_ == 0 && seller_loaded_; }
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index 2846f2b..7634b33 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -51,17 +51,31 @@
 
 BidderWorklet::BidResult::BidResult() = default;
 
-BidderWorklet::BidResult::BidResult(std::string ad, double bid, GURL render_url)
+BidderWorklet::BidResult::BidResult(
+    std::string ad,
+    double bid,
+    GURL render_url,
+    base::TimeDelta bid_duration,
+    base::Optional<std::string> trusted_bidding_signals_error_msg)
     : success(true),
       ad(std::move(ad)),
       bid(bid),
-      render_url(std::move(render_url)) {
+      render_url(std::move(render_url)),
+      bid_duration(bid_duration) {
   DCHECK_GT(this->bid, 0);
   DCHECK(this->render_url.is_valid());
+  if (trusted_bidding_signals_error_msg)
+    error_msgs.push_back(std::move(trusted_bidding_signals_error_msg).value());
 }
 
-BidderWorklet::BidResult::BidResult(base::Optional<std::string> error_msg)
-    : error_msg(std::move(error_msg)) {}
+BidderWorklet::BidResult::BidResult(
+    base::Optional<std::string> error_msg,
+    base::Optional<std::string> trusted_bidding_signals_error_msg) {
+  if (error_msg)
+    error_msgs.push_back(std::move(error_msg).value());
+  if (trusted_bidding_signals_error_msg)
+    error_msgs.push_back(std::move(trusted_bidding_signals_error_msg).value());
+}
 
 BidderWorklet::BidResult::BidResult(const BidResult& other) = default;
 BidderWorklet::BidResult::BidResult(BidResult&& other) = default;
@@ -101,184 +115,45 @@
     const std::string& browser_signal_seller,
     base::Time auction_start_time,
     AuctionV8Helper* v8_helper,
-    LoadWorkletCallback load_worklet_callback)
+    LoadScriptAndGenerateBidCallback load_script_and_generate_bid_callback)
     : script_source_url_(
           bidding_interest_group->group->bidding_url.value_or(GURL())),
       v8_helper_(v8_helper),
       bidding_interest_group_(std::move(bidding_interest_group)),
+      load_script_and_generate_bid_callback_(
+          std::move(load_script_and_generate_bid_callback)),
       auction_signals_json_(auction_signals_json),
       per_buyer_signals_json_(per_buyer_signals_json),
       browser_signal_top_window_hostname_(
           browser_signal_top_window_origin.host()),
       browser_signal_seller_(browser_signal_seller),
       auction_start_time_(auction_start_time) {
-  DCHECK(load_worklet_callback);
+  DCHECK(load_script_and_generate_bid_callback_);
+
   // TODO(mmenke): Remove up the value_or() for script_source_url_- auction
   // worklets shouldn't be created when there's no bidding URL.
   worklet_loader_ = std::make_unique<WorkletLoader>(
       url_loader_factory, script_source_url_, v8_helper,
-      base::BindOnce(&BidderWorklet::OnDownloadComplete, base::Unretained(this),
-                     std::move(load_worklet_callback)));
+      base::BindOnce(&BidderWorklet::OnScriptDownloaded,
+                     base::Unretained(this)));
+
+  if (bidding_interest_group_->group->trusted_bidding_signals_url.has_value() &&
+      bidding_interest_group_->group->trusted_bidding_signals_keys
+          .has_value() &&
+      !bidding_interest_group_->group->trusted_bidding_signals_keys->empty()) {
+    trusted_bidding_signals_loading_ = true;
+    trusted_bidding_signals_ = std::make_unique<TrustedBiddingSignals>(
+        url_loader_factory,
+        *bidding_interest_group_->group->trusted_bidding_signals_keys,
+        browser_signal_top_window_origin.host(),
+        *bidding_interest_group_->group->trusted_bidding_signals_url, v8_helper,
+        base::BindOnce(&BidderWorklet::OnTrustedBiddingSignalsDownloaded,
+                       base::Unretained(this)));
+  }
 }
 
 BidderWorklet::~BidderWorklet() = default;
 
-BidderWorklet::BidResult BidderWorklet::GenerateBid(
-    TrustedBiddingSignals* trusted_bidding_signals) {
-  const blink::mojom::InterestGroup& interest_group =
-      *bidding_interest_group_->group;
-  // Can't make a bid without any ads.
-  if (!interest_group.ads)
-    return BidResult();
-
-  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_);
-  v8::Isolate* isolate = v8_helper_->isolate();
-  // Short lived context, to avoid leaking data at global scope between either
-  // repeated calls to this worklet, or to calls to any other worklet.
-  v8::Local<v8::Context> context = v8_helper_->CreateContext();
-  v8::Context::Scope context_scope(context);
-
-  std::vector<v8::Local<v8::Value>> args;
-  v8::Local<v8::Object> interest_group_object = v8::Object::New(isolate);
-  gin::Dictionary interest_group_dict(isolate, interest_group_object);
-  if (!interest_group_dict.Set("owner", interest_group.owner.Serialize()) ||
-      !interest_group_dict.Set("name", interest_group.name) ||
-      (interest_group.user_bidding_signals &&
-       !v8_helper_->InsertJsonValue(context, "userBiddingSignals",
-                                    *interest_group.user_bidding_signals,
-                                    interest_group_object))) {
-    return BidResult();
-  }
-
-  if (interest_group.ads) {
-    std::vector<v8::Local<v8::Value>> ads_vector;
-    for (const auto& ad : *interest_group.ads) {
-      v8::Local<v8::Object> ad_object = v8::Object::New(isolate);
-      gin::Dictionary ad_dict(isolate, ad_object);
-      if (!ad_dict.Set("renderUrl", ad->render_url.spec()) ||
-          (ad->metadata &&
-           !v8_helper_->InsertJsonValue(context, "metadata", *ad->metadata,
-                                        ad_object))) {
-        return BidResult();
-      }
-      ads_vector.emplace_back(std::move(ad_object));
-    }
-    if (!v8_helper_->InsertValue(
-            "ads",
-            v8::Array::New(isolate, ads_vector.data(), ads_vector.size()),
-            interest_group_object)) {
-      return BidResult();
-    }
-  }
-
-  args.push_back(std::move(interest_group_object));
-
-  if (!AppendJsonValueOrNull(v8_helper_, context, auction_signals_json_,
-                             &args) ||
-      !AppendJsonValueOrNull(v8_helper_, context, per_buyer_signals_json_,
-                             &args)) {
-    return BidResult();
-  }
-
-  v8::Local<v8::Value> trusted_signals;
-  if (!trusted_bidding_signals ||
-      !interest_group.trusted_bidding_signals_keys ||
-      interest_group.trusted_bidding_signals_keys->empty()) {
-    trusted_signals = v8::Null(isolate);
-  } else {
-    trusted_signals = trusted_bidding_signals->GetSignals(
-        context, *interest_group.trusted_bidding_signals_keys);
-  }
-  args.push_back(trusted_signals);
-
-  v8::Local<v8::Object> browser_signals = v8::Object::New(isolate);
-  gin::Dictionary browser_signals_dict(isolate, browser_signals);
-  if (!browser_signals_dict.Set("topWindowHostname",
-                                browser_signal_top_window_hostname_) ||
-      !browser_signals_dict.Set("seller", browser_signal_seller_) ||
-      !browser_signals_dict.Set("joinCount",
-                                bidding_interest_group_->signals->join_count) ||
-      !browser_signals_dict.Set("bidCount",
-                                bidding_interest_group_->signals->bid_count)) {
-    return BidResult();
-  }
-
-  std::vector<v8::Local<v8::Value>> prev_wins_v8;
-  for (const auto& prev_win : bidding_interest_group_->signals->prev_wins) {
-    int64_t time_delta = (auction_start_time_ - prev_win->time).InSeconds();
-    // Don't give negative times if clock has changed since last auction win.
-    // Clock changes do mean times can be out of numerical order, despite being
-    // in chronological order.
-    if (time_delta < 0)
-      time_delta = 0;
-    v8::Local<v8::Value> win_values[2];
-    win_values[0] = v8::Number::New(isolate, time_delta);
-    if (!v8_helper_->CreateValueFromJson(context, prev_win->ad_json)
-             .ToLocal(&win_values[1])) {
-      return BidResult();
-    }
-    prev_wins_v8.push_back(
-        v8::Array::New(isolate, win_values, base::size(win_values)));
-  }
-  v8::Maybe<bool> result = browser_signals->Set(
-      context, gin::StringToV8(isolate, "prevWins"),
-      v8::Array::New(isolate, prev_wins_v8.data(), prev_wins_v8.size()));
-  if (result.IsNothing() || !result.FromJust())
-    return BidResult();
-
-  args.push_back(browser_signals);
-
-  v8::Local<v8::Value> generate_bid_result;
-  base::Optional<std::string> error_msg_out;
-  if (!v8_helper_
-           ->RunScript(context, worklet_script_->Get(isolate), "generateBid",
-                       args, error_msg_out)
-           .ToLocal(&generate_bid_result)) {
-    return BidResult(std::move(error_msg_out));
-  }
-
-  if (!generate_bid_result->IsObject()) {
-    return BidResult(
-        base::StrCat({script_source_url_.spec(),
-                      " generateBid() return value not an object."}));
-  }
-
-  gin::Dictionary result_dict(isolate, generate_bid_result.As<v8::Object>());
-
-  v8::Local<v8::Value> ad_object;
-  std::string ad_json;
-  double bid;
-  std::string render_url_string;
-  // Parse and validate values.
-  if (!result_dict.Get("ad", &ad_object) ||
-      !v8_helper_->ExtractJson(context, ad_object, &ad_json) ||
-      !result_dict.Get("bid", &bid) ||
-      !result_dict.Get("render", &render_url_string)) {
-    return BidResult(
-        base::StrCat({script_source_url_.spec(),
-                      " generateBid() return value has incorrect structure."}));
-  }
-
-  if (bid <= 0 || std::isnan(bid) || !std::isfinite(bid))
-    return BidResult();
-
-  GURL render_url(render_url_string);
-  if (!render_url.is_valid() || !render_url.SchemeIs(url::kHttpsScheme)) {
-    return BidResult(base::StrCat(
-        {script_source_url_.spec(),
-         " generateBid() returned render_url isn't a valid https:// URL."}));
-  }
-
-  // `render_url` must be in `ad_render_urls`.
-  for (const auto& ad : *interest_group.ads) {
-    if (render_url == ad->render_url)
-      return BidResult(std::move(ad_json), bid, std::move(render_url));
-  }
-  return BidResult(base::StrCat({script_source_url_.spec(),
-                                 " generateBid() returned render_url isn't one "
-                                 "of the registered creative URLs."}));
-}
-
 BidderWorklet::ReportWinResult BidderWorklet::ReportWin(
     const std::string& seller_signals_json,
     const GURL& browser_signal_render_url,
@@ -339,14 +214,220 @@
   return ReportWinResult(report_bindings.report_url());
 }
 
-void BidderWorklet::OnDownloadComplete(
-    LoadWorkletCallback load_worklet_callback,
+void BidderWorklet::OnScriptDownloaded(
     std::unique_ptr<v8::Global<v8::UnboundScript>> worklet_script,
     base::Optional<std::string> error_msg) {
+  DCHECK(load_script_and_generate_bid_callback_);
+
+  if (worklet_script == nullptr) {
+    // Abort loading trusted bidding signals, if it hasn't completed already.
+    trusted_bidding_signals_.reset();
+    InvokeBidCallbackOnError(std::move(error_msg));
+    return;
+  }
+
   worklet_loader_.reset();
   worklet_script_ = std::move(worklet_script);
-  std::move(load_worklet_callback)
-      .Run(worklet_script_ != nullptr, std::move(error_msg));
+  GenerateBidIfReady();
+}
+
+void BidderWorklet::OnTrustedBiddingSignalsDownloaded(
+    bool load_result,
+    base::Optional<std::string> error_msg) {
+  // Worklet results should still be pending.
+  DCHECK(load_script_and_generate_bid_callback_);
+  DCHECK(trusted_bidding_signals_loading_);
+
+  trusted_bidding_signals_error_msg_ = std::move(error_msg);
+  if (load_result == false)
+    trusted_bidding_signals_.reset();
+  trusted_bidding_signals_loading_ = false;
+
+  GenerateBidIfReady();
+}
+
+void BidderWorklet::GenerateBidIfReady() {
+  DCHECK(load_script_and_generate_bid_callback_);
+  if (trusted_bidding_signals_loading_ || !worklet_script_)
+    return;
+
+  const blink::mojom::InterestGroup& interest_group =
+      *bidding_interest_group_->group;
+  // Can't make a bid without any ads.
+  if (!interest_group.ads) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  base::TimeTicks start = base::TimeTicks::Now();
+
+  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_);
+  v8::Isolate* isolate = v8_helper_->isolate();
+  // Short lived context, to avoid leaking data at global scope between either
+  // repeated calls to this worklet, or to calls to any other worklet.
+  v8::Local<v8::Context> context = v8_helper_->CreateContext();
+  v8::Context::Scope context_scope(context);
+
+  std::vector<v8::Local<v8::Value>> args;
+  v8::Local<v8::Object> interest_group_object = v8::Object::New(isolate);
+  gin::Dictionary interest_group_dict(isolate, interest_group_object);
+  if (!interest_group_dict.Set("owner", interest_group.owner.Serialize()) ||
+      !interest_group_dict.Set("name", interest_group.name) ||
+      (interest_group.user_bidding_signals &&
+       !v8_helper_->InsertJsonValue(context, "userBiddingSignals",
+                                    *interest_group.user_bidding_signals,
+                                    interest_group_object))) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  std::vector<v8::Local<v8::Value>> ads_vector;
+  for (const auto& ad : *interest_group.ads) {
+    v8::Local<v8::Object> ad_object = v8::Object::New(isolate);
+    gin::Dictionary ad_dict(isolate, ad_object);
+    if (!ad_dict.Set("renderUrl", ad->render_url.spec()) ||
+        (ad->metadata && !v8_helper_->InsertJsonValue(
+                             context, "metadata", *ad->metadata, ad_object))) {
+      InvokeBidCallbackOnError();
+      return;
+    }
+    ads_vector.emplace_back(std::move(ad_object));
+  }
+  if (!v8_helper_->InsertValue(
+          "ads", v8::Array::New(isolate, ads_vector.data(), ads_vector.size()),
+          interest_group_object)) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  args.push_back(std::move(interest_group_object));
+
+  if (!AppendJsonValueOrNull(v8_helper_, context, auction_signals_json_,
+                             &args) ||
+      !AppendJsonValueOrNull(v8_helper_, context, per_buyer_signals_json_,
+                             &args)) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  v8::Local<v8::Value> trusted_signals;
+  if (!trusted_bidding_signals_) {
+    trusted_signals = v8::Null(isolate);
+  } else {
+    trusted_signals = trusted_bidding_signals_->GetSignals(
+        context, *interest_group.trusted_bidding_signals_keys);
+  }
+  args.push_back(trusted_signals);
+
+  v8::Local<v8::Object> browser_signals = v8::Object::New(isolate);
+  gin::Dictionary browser_signals_dict(isolate, browser_signals);
+  if (!browser_signals_dict.Set("topWindowHostname",
+                                browser_signal_top_window_hostname_) ||
+      !browser_signals_dict.Set("seller", browser_signal_seller_) ||
+      !browser_signals_dict.Set("joinCount",
+                                bidding_interest_group_->signals->join_count) ||
+      !browser_signals_dict.Set("bidCount",
+                                bidding_interest_group_->signals->bid_count)) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  std::vector<v8::Local<v8::Value>> prev_wins_v8;
+  for (const auto& prev_win : bidding_interest_group_->signals->prev_wins) {
+    int64_t time_delta = (auction_start_time_ - prev_win->time).InSeconds();
+    // Don't give negative times if clock has changed since last auction win.
+    // Clock changes do mean times can be out of numerical order, despite being
+    // in chronological order.
+    if (time_delta < 0)
+      time_delta = 0;
+    v8::Local<v8::Value> win_values[2];
+    win_values[0] = v8::Number::New(isolate, time_delta);
+    if (!v8_helper_->CreateValueFromJson(context, prev_win->ad_json)
+             .ToLocal(&win_values[1])) {
+      InvokeBidCallbackOnError();
+      return;
+    }
+    prev_wins_v8.push_back(
+        v8::Array::New(isolate, win_values, base::size(win_values)));
+  }
+  v8::Maybe<bool> result = browser_signals->Set(
+      context, gin::StringToV8(isolate, "prevWins"),
+      v8::Array::New(isolate, prev_wins_v8.data(), prev_wins_v8.size()));
+  if (result.IsNothing() || !result.FromJust()) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  args.push_back(browser_signals);
+
+  v8::Local<v8::Value> generate_bid_result;
+  base::Optional<std::string> error_msg_out;
+  if (!v8_helper_
+           ->RunScript(context, worklet_script_->Get(isolate), "generateBid",
+                       args, error_msg_out)
+           .ToLocal(&generate_bid_result)) {
+    InvokeBidCallbackOnError(std::move(error_msg_out));
+    return;
+  }
+
+  if (!generate_bid_result->IsObject()) {
+    InvokeBidCallbackOnError(
+        base::StrCat({script_source_url_.spec(),
+                      " generateBid() return value not an object."}));
+    return;
+  }
+
+  gin::Dictionary result_dict(isolate, generate_bid_result.As<v8::Object>());
+
+  v8::Local<v8::Value> ad_object;
+  std::string ad_json;
+  double bid;
+  std::string render_url_string;
+  // Parse and validate values.
+  if (!result_dict.Get("ad", &ad_object) ||
+      !v8_helper_->ExtractJson(context, ad_object, &ad_json) ||
+      !result_dict.Get("bid", &bid) ||
+      !result_dict.Get("render", &render_url_string)) {
+    InvokeBidCallbackOnError(
+        base::StrCat({script_source_url_.spec(),
+                      " generateBid() return value has incorrect structure."}));
+    return;
+  }
+
+  if (bid <= 0 || std::isnan(bid) || !std::isfinite(bid)) {
+    InvokeBidCallbackOnError();
+    return;
+  }
+
+  GURL render_url(render_url_string);
+  if (!render_url.is_valid() || !render_url.SchemeIs(url::kHttpsScheme)) {
+    return InvokeBidCallbackOnError(base::StrCat(
+        {script_source_url_.spec(),
+         " generateBid() returned render_url isn't a valid https:// URL."}));
+    return;
+  }
+
+  // `render_url` must be in `ad_render_urls`.
+  for (const auto& ad : *interest_group.ads) {
+    if (render_url == ad->render_url) {
+      std::move(load_script_and_generate_bid_callback_)
+          .Run(BidResult(std::move(ad_json), bid, std::move(render_url),
+                         base::TimeTicks::Now() - start /* bid_duration */,
+                         std::move(trusted_bidding_signals_error_msg_)));
+      return;
+    }
+  }
+  InvokeBidCallbackOnError(
+      base::StrCat({script_source_url_.spec(),
+                    " generateBid() returned render_url isn't one "
+                    "of the registered creative URLs."}));
+}
+
+void BidderWorklet::InvokeBidCallbackOnError(
+    base::Optional<std::string> error_msg) {
+  std::move(load_script_and_generate_bid_callback_)
+      .Run(BidResult(std::move(error_msg),
+                     std::move(trusted_bidding_signals_error_msg_)));
 }
 
 }  // namespace auction_worklet
diff --git a/content/services/auction_worklet/bidder_worklet.h b/content/services/auction_worklet/bidder_worklet.h
index 85fec55c..0d074e0 100644
--- a/content/services/auction_worklet/bidder_worklet.h
+++ b/content/services/auction_worklet/bidder_worklet.h
@@ -47,11 +47,16 @@
 
     // Constructor when a bid is made. `bid` must be > 0, and the URL must be
     // valid.
-    BidResult(std::string ad, double bid, GURL render_url);
+    BidResult(std::string ad,
+              double bid,
+              GURL render_url,
+              base::TimeDelta bid_duration,
+              base::Optional<std::string> trusted_bidding_signals_error_msg);
 
     // Constructor when there is no bid due to an error and an error message
     // may be available.
-    explicit BidResult(base::Optional<std::string> error_msg);
+    BidResult(base::Optional<std::string> error_msg,
+              base::Optional<std::string> trusted_bidding_signals_error_msg);
 
     BidResult(const BidResult& other);
     BidResult(BidResult&& other);
@@ -76,9 +81,16 @@
     // Render URL, if any bid was made.
     GURL render_url;
 
-    // Error message for debugging. This isn't guaranteed to be produced for all
-    // failures, so don't check this instead of `success`.
-    base::Optional<std::string> error_msg;
+    // How long it took to run the script that generated the bid, if a bid was
+    // made.
+    base::TimeDelta bid_duration;
+
+    // Error messages for debugging. This isn't guaranteed to be produced for
+    // all failures, so don't check this instead of `success`. It's possible for
+    // there to be an error message on success, in the case the trusted bidding
+    // signals failed to load - auctions will still be run without it, but
+    // `error_msgs` will be populated with information about the load failure.
+    std::vector<std::string> error_msgs;
   };
 
   struct ReportWinResult {
@@ -113,32 +125,30 @@
     base::Optional<std::string> error_msg;
   };
 
-  using LoadWorkletCallback =
-      base::OnceCallback<void(bool success,
-                              base::Optional<std::string> error_msg)>;
+  using LoadScriptAndGenerateBidCallback =
+      base::OnceCallback<void(BidResult bid_result)>;
 
-  // Starts loading the worklet script on construction. Callback will be invoked
-  // asynchronously once the data has been fetched or an error has occurred.
-  // Must be destroyed before `v8_helper`.
+  // Starts loading the worklet script on construction, as well as the trusted
+  // bidding data, if necessary. Will then call the script's generateBid()
+  // function and invoke the callback with the results. Callback will always be
+  // invoked asynchronously, once a bid has been generated or a fatal error has
+  // occurred. Must be destroyed before `v8_helper`.
   //
-  // Data is cached and reused in GenerateBid() and ReportWin().
-  BidderWorklet(network::mojom::URLLoaderFactory* url_loader_factory,
-                mojom::BiddingInterestGroupPtr bidding_interest_group,
-                const base::Optional<std::string>& auction_signals_json,
-                const base::Optional<std::string>& per_buyer_signals_json,
-                const url::Origin& browser_signal_top_window_origin,
-                const std::string& browser_signal_seller,
-                base::Time auction_start_time,
-                AuctionV8Helper* v8_helper,
-                LoadWorkletCallback load_worklet_callback);
+  // Data is cached and will be reused ReportWin().
+  BidderWorklet(
+      network::mojom::URLLoaderFactory* url_loader_factory,
+      mojom::BiddingInterestGroupPtr bidding_interest_group,
+      const base::Optional<std::string>& auction_signals_json,
+      const base::Optional<std::string>& per_buyer_signals_json,
+      const url::Origin& browser_signal_top_window_origin,
+      const std::string& browser_signal_seller,
+      base::Time auction_start_time,
+      AuctionV8Helper* v8_helper,
+      LoadScriptAndGenerateBidCallback load_script_and_generate_bid_callback);
   explicit BidderWorklet(const BidderWorklet&) = delete;
   BidderWorklet& operator=(const BidderWorklet&) = delete;
   ~BidderWorklet();
 
-  // Calls generateBid(), and returns resulting bid, if any. May only be called
-  // once BidderWorklet has successfully loaded.
-  BidResult GenerateBid(TrustedBiddingSignals* trusted_bidding_signals);
-
   // Calls reportWin(), and returns reporting information. May only be called
   // once the worklet has successfully loaded.
   ReportWinResult ReportWin(
@@ -148,15 +158,31 @@
       double browser_signal_bid);
 
  private:
-  void OnDownloadComplete(
-      LoadWorkletCallback load_worklet_callback,
+  void OnScriptDownloaded(
       std::unique_ptr<v8::Global<v8::UnboundScript>> worklet_script,
       base::Optional<std::string> error_msg);
 
+  void OnTrustedBiddingSignalsDownloaded(bool load_result,
+                                         base::Optional<std::string> error_msg);
+
+  // Checks if the script has been loaded successfully, and the
+  // TrustedBiddingSignals load has finished (successfully or not). If so, calls
+  // generateBid(), and invokes `load_script_and_generate_bid_callback_` with
+  // the resulting bid, if any. May only be called once BidderWorklet has
+  // successfully loaded.
+  void GenerateBidIfReady();
+
+  // Utility function to invoke `load_script_and_generate_bid_callback_` with
+  // `error_msg` and `trusted_bidding_signals_error_msg_`.
+  void InvokeBidCallbackOnError(
+      base::Optional<std::string> error_msg = base::nullopt);
+
   const GURL script_source_url_;
   AuctionV8Helper* const v8_helper_;
   const mojom::BiddingInterestGroupPtr bidding_interest_group_;
 
+  LoadScriptAndGenerateBidCallback load_script_and_generate_bid_callback_;
+
   const base::Optional<std::string> auction_signals_json_;
   const base::Optional<std::string> per_buyer_signals_json_;
   const std::string browser_signal_top_window_hostname_;
@@ -165,6 +191,13 @@
 
   std::unique_ptr<WorkletLoader> worklet_loader_;
 
+  bool trusted_bidding_signals_loading_ = false;
+  std::unique_ptr<TrustedBiddingSignals> trusted_bidding_signals_;
+  // Error message returned by attempt to load `trusted_bidding_signals_`.
+  // Errors loading it are not fatal, so such errors are cached here and only
+  // reported on bid completion.
+  base::Optional<std::string> trusted_bidding_signals_error_msg_;
+
   // Compiled script, not bound to any context. Can be repeatedly bound to
   // different context and executed, without persisting any state.
   std::unique_ptr<v8::Global<v8::UnboundScript>> worklet_script_;
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index 99d9fd8..bafd7e2 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -38,7 +38,7 @@
 std::string CreateGenerateBidScript(const std::string& raw_return_value) {
   constexpr char kGenerateBidScript[] = R"(
     function generateBid(interestGroup, auctionSignals, perBuyerSignals,
-                          trustedBiddingSignals, browserSignals) {
+                         trustedBiddingSignals, browserSignals) {
       return %s;
     }
   )";
@@ -52,7 +52,8 @@
       R"({ad: ["ad"], bid:1, render:"https://response.test/"})");
 }
 
-// Creates reportWin() scripts with the specified body.
+// Creates bidder worklet script with a reportWin() function with the specified
+// body, and the default generateBid() function.
 std::string CreateReportWinScript(const std::string& function_body) {
   constexpr char kReportWinScript[] = R"(
     function reportWin(auctionSignals, perBuyerSignals, sellerSignals,
@@ -60,7 +61,8 @@
       %s;
     }
   )";
-  return base::StringPrintf(kReportWinScript, function_body.c_str());
+  return CreateBasicGenerateBidScript() +
+         base::StringPrintf(kReportWinScript, function_body.c_str());
 }
 
 class BidderWorkletTest : public testing::Test {
@@ -76,9 +78,14 @@
     interest_group_owner_ = url::Origin::Create(GURL("https://foo.test"));
     interest_group_name_ = "Fred";
     interest_group_user_bidding_signals_ = std::string();
+
     interest_group_ads_.clear();
     interest_group_ads_.push_back(blink::mojom::InterestGroupAd::New(
         GURL("https://response.test/"), base::nullopt /* metadata */));
+
+    interest_group_trusted_bidding_signals_url_.reset();
+    interest_group_trusted_bidding_signals_keys_.reset();
+
     browser_signal_join_count_ = 2;
     browser_signal_bid_count_ = 3;
     browser_signal_prev_wins_.clear();
@@ -121,16 +128,8 @@
   // expecting the provided result.
   void RunGenerateBidExpectingResult(
       const BidderWorklet::BidResult& expected_result) {
-    auto bidder_worklet = CreateWorklet();
-    ASSERT_TRUE(bidder_worklet);
-
-    BidderWorklet::BidResult actual_result =
-        RunGenerateBid(bidder_worklet.get());
-    ExpectBidResultsEqual(expected_result, actual_result);
-  }
-
-  BidderWorklet::BidResult RunGenerateBid(BidderWorklet* bidder_worket) {
-    return bidder_worket->GenerateBid(trusted_bidding_signals_.get());
+    auto bidder_worklet = CreateWorkletAndGenerateBid();
+    ExpectBidResultsEqual(expected_result, bid_result_);
   }
 
   void ExpectBidResultsEqual(const BidderWorklet::BidResult& expected_result,
@@ -139,10 +138,7 @@
     EXPECT_EQ(expected_result.ad, actual_result.ad);
     EXPECT_EQ(expected_result.bid, actual_result.bid);
     EXPECT_EQ(expected_result.render_url, actual_result.render_url);
-    EXPECT_EQ(expected_result.error_msg.has_value(),
-              actual_result.error_msg.has_value());
-    EXPECT_EQ(expected_result.error_msg.value_or("Not an error"),
-              actual_result.error_msg.value_or("Not an error"));
+    EXPECT_EQ(expected_result.error_msgs, actual_result.error_msgs);
   }
 
   // Configures `url_loader_factory_` to return a reportWin() script with the
@@ -174,7 +170,7 @@
   void RunReportWinExpectingResult(
       const GURL& expected_report_url,
       base::Optional<std::string> expected_error_msg = base::nullopt) {
-    auto bidder_worket = CreateWorklet();
+    auto bidder_worket = CreateWorkletAndGenerateBid();
     ASSERT_TRUE(bidder_worket);
 
     BidderWorklet::ReportWinResult actual_result = bidder_worket->ReportWin(
@@ -190,7 +186,7 @@
 
   // Create a BidderWorklet, waiting for the URLLoader to complete. Returns
   // nullptr on failure.
-  std::unique_ptr<BidderWorklet> CreateWorklet() {
+  std::unique_ptr<BidderWorklet> CreateWorkletAndGenerateBid() {
     CHECK(!load_script_run_loop_);
 
     blink::mojom::InterestGroupPtr interest_group =
@@ -203,6 +199,8 @@
       interest_group->user_bidding_signals =
           interest_group_user_bidding_signals_;
     }
+    interest_group->trusted_bidding_signals_url =
+        interest_group_trusted_bidding_signals_url_;
     interest_group->trusted_bidding_signals_keys =
         interest_group_trusted_bidding_signals_keys_;
     interest_group->ads = std::vector<blink::mojom::InterestGroupAdPtr>();
@@ -218,7 +216,6 @@
         mojom::BiddingInterestGroup::New(std::move(interest_group),
                                          std::move(bidding_browser_signals));
 
-    create_worklet_succeeded_ = false;
     auto bidder_worket = std::make_unique<BidderWorklet>(
         &url_loader_factory_, std::move(bidding_interest_group),
         null_auction_signals_
@@ -234,11 +231,13 @@
     load_script_run_loop_ = std::make_unique<base::RunLoop>();
     load_script_run_loop_->Run();
     load_script_run_loop_.reset();
-    if (!create_worklet_succeeded_)
+    if (!bid_result_.success)
       return nullptr;
     return bidder_worket;
   }
 
+  const BidderWorklet::BidResult& bid_result() const { return bid_result_; }
+
  protected:
   std::vector<mojo::StructPtr<mojom::PreviousWin>> CloneWinList(
       const std::vector<mojo::StructPtr<mojom::PreviousWin>>& prev_win_list) {
@@ -249,19 +248,11 @@
     return out;
   }
 
-  void CreateWorkletCallback(bool success,
-                             base::Optional<std::string> error_msg) {
-    create_worklet_succeeded_ = success;
-    error_msg_ = std::move(error_msg);
-    if (success)
-      EXPECT_FALSE(error_msg_.has_value());
+  void CreateWorkletCallback(BidderWorklet::BidResult bid_result) {
+    bid_result_ = bid_result;
     load_script_run_loop_->Quit();
   }
 
-  std::string last_error_msg() const {
-    return error_msg_.value_or("Not an error");
-  }
-
   base::test::TaskEnvironment task_environment_;
 
   // Values used to construct the BiddingInterestGroup passed to the
@@ -273,7 +264,9 @@
   // string. An empty string means nullptr.
   std::string interest_group_user_bidding_signals_;
   std::vector<blink::mojom::InterestGroupAdPtr> interest_group_ads_;
-  std::vector<std::string> interest_group_trusted_bidding_signals_keys_;
+  base::Optional<GURL> interest_group_trusted_bidding_signals_url_;
+  base::Optional<std::vector<std::string>>
+      interest_group_trusted_bidding_signals_keys_;
   int browser_signal_join_count_;
   int browser_signal_bid_count_;
   std::vector<mojo::StructPtr<mojom::PreviousWin>> browser_signal_prev_wins_;
@@ -288,7 +281,6 @@
 
   url::Origin browser_signal_top_window_origin_;
   std::string browser_signal_seller_;
-  std::unique_ptr<TrustedBiddingSignals> trusted_bidding_signals_;
   std::string seller_signals_;
   GURL browser_signal_render_url_;
   std::string browser_signal_ad_render_fingerprint_;
@@ -302,8 +294,7 @@
   // creating the worklet, to cause a crash if the callback is invoked
   // synchronously.
   std::unique_ptr<base::RunLoop> load_script_run_loop_;
-  bool create_worklet_succeeded_ = false;
-  base::Optional<std::string> error_msg_;
+  BidderWorklet::BidResult bid_result_;
 
   network::TestURLLoaderFactory url_loader_factory_;
   AuctionV8Helper v8_helper_;
@@ -313,18 +304,19 @@
   url_loader_factory_.AddResponse(interest_group_bidding_url_.spec(),
                                   CreateBasicGenerateBidScript(),
                                   net::HTTP_NOT_FOUND);
-  EXPECT_FALSE(CreateWorklet());
-  EXPECT_EQ("Failed to load https://url.test/ HTTP status = 404 Not Found.",
-            last_error_msg());
+  RunGenerateBidExpectingResult(BidderWorklet::BidResult(
+      "Failed to load https://url.test/ HTTP status = 404 Not Found.",
+      base::nullopt));
 }
 
 TEST_F(BidderWorkletTest, CompileError) {
   AddJavascriptResponse(&url_loader_factory_, interest_group_bidding_url_,
                         "Invalid Javascript");
-  EXPECT_FALSE(CreateWorklet());
+  EXPECT_FALSE(CreateWorkletAndGenerateBid());
 
-  EXPECT_THAT(last_error_msg(), StartsWith("https://url.test/:1 "));
-  EXPECT_THAT(last_error_msg(), HasSubstr("SyntaxError"));
+  ASSERT_EQ(1u, bid_result().error_msgs.size());
+  EXPECT_THAT(bid_result().error_msgs[0], StartsWith("https://url.test/:1 "));
+  EXPECT_THAT(bid_result().error_msgs[0], HasSubstr("SyntaxError"));
 }
 
 // Test parsing of return values.
@@ -333,7 +325,8 @@
   // CreateBasicGenerateBidScript() does indeed work.
   RunGenerateBidWithJavascriptExpectingResult(
       CreateBasicGenerateBidScript(),
-      BidderWorklet::BidResult("[\"ad\"]", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("[\"ad\"]", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 
   // --------
   // Vary ad
@@ -342,37 +335,46 @@
   // Make sure "ad" can be of a variety of JS object types.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("\"ad\"", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("\"ad\"", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: {a:1,b:null}, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(R"({"a":1,"b":null})", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: [2.5,[]], bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("[2.5,[]]", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("[2.5,[]]", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: -5, bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("-5", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("-5", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   // Some values that can't be represented in JSON become null.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: 0/0, bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("null", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("null", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: [globalThis.not_defined], bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("[null]", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("[null]", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: [function() {return 1;}], bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("[null]", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("[null]", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 
   // Other values JSON can't represent result in failing instead of null.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: globalThis.not_defined, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: function() {return 1;}, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
 
   // Make sure recursive structures aren't allowed in ad field.
   RunGenerateBidWithJavascriptExpectingResult(
@@ -384,7 +386,8 @@
         }
       )",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
 
   // --------
   // Vary bid
@@ -393,14 +396,16 @@
   // Valid positive bid values.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:1.5, render:"https://response.test/"})",
-      BidderWorklet::BidResult("\"ad\"", 1.5, GURL("https://response.test/")));
+      BidderWorklet::BidResult("\"ad\"", 1.5, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:2, render:"https://response.test/"})",
-      BidderWorklet::BidResult("\"ad\"", 2, GURL("https://response.test/")));
+      BidderWorklet::BidResult("\"ad\"", 2, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:0.001, render:"https://response.test/"})",
-      BidderWorklet::BidResult("\"ad\"", 0.001,
-                               GURL("https://response.test/")));
+      BidderWorklet::BidResult("\"ad\"", 0.001, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 
   // Bids <= 0.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -428,11 +433,13 @@
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:"1", render:"https://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:[1], render:"https://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
 
   // ---------
   // Vary URL.
@@ -440,43 +447,52 @@
 
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("[\"ad\"]", 1, GURL("https://response.test/")));
+      BidderWorklet::BidResult("[\"ad\"]", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 
   // Disallowed schemes.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"http://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() returned "
-                               "render_url isn't a valid https:// URL."));
+                               "render_url isn't a valid https:// URL.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"chrome-extension://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() returned "
-                               "render_url isn't a valid https:// URL."));
+                               "render_url isn't a valid https:// URL.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"about:blank"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() returned "
-                               "render_url isn't a valid https:// URL."));
+                               "render_url isn't a valid https:// URL.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"data:,foo"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() returned "
-                               "render_url isn't a valid https:// URL."));
+                               "render_url isn't a valid https:// URL.",
+                               base::nullopt));
 
   // Invalid URLs.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"test"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() returned "
-                               "render_url isn't a valid https:// URL."));
+                               "render_url isn't a valid https:// URL.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"http://"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() returned "
-                               "render_url isn't a valid https:// URL."));
+                               "render_url isn't a valid https:// URL.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:["http://response.test/"]})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:9})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
 
   // ------------
   // Other cases.
@@ -485,21 +501,25 @@
   // No return value.
   RunGenerateBidWithReturnValueExpectingResult(
       "", BidderWorklet::BidResult(
-              "https://url.test/ generateBid() return value not an object."));
+              "https://url.test/ generateBid() return value not an object.",
+              base::nullopt));
 
   // Missing value.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({bid:"a", render:"https://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], render:"https://response.test/"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:"a"})",
       BidderWorklet::BidResult("https://url.test/ generateBid() return value "
-                               "has incorrect structure."));
+                               "has incorrect structure.",
+                               base::nullopt));
 
   // Valid JS, but missing function.
   RunGenerateBidWithJavascriptExpectingResult(
@@ -509,19 +529,22 @@
         }
       )",
       BidderWorklet::BidResult(
-          "https://url.test/ `generateBid` is not a function."));
+          "https://url.test/ `generateBid` is not a function.", base::nullopt));
   RunGenerateBidWithJavascriptExpectingResult(
-      "", BidderWorklet::BidResult(
-              "https://url.test/ `generateBid` is not a function."));
+      "",
+      BidderWorklet::BidResult(
+          "https://url.test/ `generateBid` is not a function.", base::nullopt));
   RunGenerateBidWithJavascriptExpectingResult(
-      "5", BidderWorklet::BidResult(
-               "https://url.test/ `generateBid` is not a function."));
+      "5",
+      BidderWorklet::BidResult(
+          "https://url.test/ `generateBid` is not a function.", base::nullopt));
 
   // Throw exception.
   RunGenerateBidWithJavascriptExpectingResult(
       "shrimp",
       BidderWorklet::BidResult("https://url.test/:1 Uncaught ReferenceError: "
-                               "shrimp is not defined."));
+                               "shrimp is not defined.",
+                               base::nullopt));
 }
 
 // Make sure Date() is not available when running generateBid().
@@ -529,7 +552,8 @@
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: Date().toString(), bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(
-          "https://url.test/:4 Uncaught ReferenceError: Date is not defined."));
+          "https://url.test/:4 Uncaught ReferenceError: Date is not defined.",
+          base::nullopt));
 }
 
 // Checks that most input parameters are correctly passed in, and each is parsed
@@ -581,7 +605,8 @@
             test_case.name),
         test_case.is_json ? BidderWorklet::BidResult()
                           : BidderWorklet::BidResult(
-                                R"("foo")", 1, GURL("https://response.test/")));
+                                R"("foo")", 1, GURL("https://response.test/"),
+                                base::TimeDelta(), base::nullopt));
 
     *test_case.value_ptr = R"("foo")";
     RunGenerateBidWithReturnValueExpectingResult(
@@ -590,9 +615,11 @@
             test_case.name),
         test_case.is_json
             ? BidderWorklet::BidResult(R"("foo")", 1,
-                                       GURL("https://response.test/"))
+                                       GURL("https://response.test/"),
+                                       base::TimeDelta(), base::nullopt)
             : BidderWorklet::BidResult(R"("\"foo\"")", 1,
-                                       GURL("https://response.test/")));
+                                       GURL("https://response.test/"),
+                                       base::TimeDelta(), base::nullopt));
 
     *test_case.value_ptr = "[1]";
     RunGenerateBidWithReturnValueExpectingResult(
@@ -600,9 +627,11 @@
             R"({ad: %s[0], bid:1, render:"https://response.test/"})",
             test_case.name),
         test_case.is_json
-            ? BidderWorklet::BidResult("1", 1, GURL("https://response.test/"))
+            ? BidderWorklet::BidResult("1", 1, GURL("https://response.test/"),
+                                       base::TimeDelta(), base::nullopt)
             : BidderWorklet::BidResult(R"("[")", 1,
-                                       GURL("https://response.test/")));
+                                       GURL("https://response.test/"),
+                                       base::TimeDelta(), base::nullopt));
     SetDefaultParameters();
   }
 
@@ -610,13 +639,15 @@
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: interestGroup.owner, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(R"("https://foo.test")", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 
   interest_group_owner_ = url::Origin::Create(GURL("https://[::1]:40000/"));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: interestGroup.owner, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(R"("https://[::1]:40000")", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   SetDefaultParameters();
 
   // Test the empty `userBiddingSignals` case, too. It's actually an optional
@@ -627,7 +658,8 @@
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad:typeof interestGroup.userBiddingSignals, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(R"("undefined")", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   SetDefaultParameters();
 
   browser_signal_top_window_origin_ =
@@ -635,7 +667,8 @@
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: browserSignals.topWindowHostname, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(R"("top.window.test")", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
   SetDefaultParameters();
 
   const struct IntegerTestCase {
@@ -655,14 +688,16 @@
         base::StringPrintf(
             R"({ad: %s, bid:1, render:"https://response.test/"})",
             test_case.name),
-        BidderWorklet::BidResult("0", 1, GURL("https://response.test/")));
+        BidderWorklet::BidResult("0", 1, GURL("https://response.test/"),
+                                 base::TimeDelta(), base::nullopt));
 
     *test_case.value_ptr = 10;
     RunGenerateBidWithReturnValueExpectingResult(
         base::StringPrintf(
             R"({ad: %s, bid:1, render:"https://response.test/"})",
             test_case.name),
-        BidderWorklet::BidResult("10", 1, GURL("https://response.test/")));
+        BidderWorklet::BidResult("10", 1, GURL("https://response.test/"),
+                                 base::TimeDelta(), base::nullopt));
     SetDefaultParameters();
   }
 
@@ -673,7 +708,8 @@
       R"({ad: 0, bid:1, render:"https://response2.test/"})",
       BidderWorklet::BidResult(
           "https://url.test/ generateBid() returned render_url isn't one of "
-          "the registered creative URLs."));
+          "the registered creative URLs.",
+          base::nullopt));
 
   // Adding an ad with a corresponding `renderUrl` should result in success.
   // Also check the `interestGroup.ads` field passed to Javascript.
@@ -684,13 +720,15 @@
       BidderWorklet::BidResult("[{\"renderUrl\":\"https://response.test/\"},"
                                "{\"renderUrl\":\"https://response2.test/"
                                "\",\"metadata\":[\"metadata\"]}]",
-                               1, GURL("https://response2.test/")));
+                               1, GURL("https://response2.test/"),
+                               base::TimeDelta(), base::nullopt));
 
   // Make sure `metadata` is treated as an object, instead of a raw string.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: interestGroup.ads[1].metadata[0], bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult("\"metadata\"", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 }
 
 // Test handling of null auctionSignals and perBuyerSignals to generateBid.
@@ -706,28 +744,32 @@
   null_per_buyer_signals_ = false;
   RunGenerateBidWithReturnValueExpectingResult(
       kRetVal, BidderWorklet::BidResult("[false,false]", 1,
-                                        GURL("https://response.test/")));
+                                        GURL("https://response.test/"),
+                                        base::TimeDelta(), base::nullopt));
 
   SetDefaultParameters();
   null_auction_signals_ = false;
   null_per_buyer_signals_ = true;
   RunGenerateBidWithReturnValueExpectingResult(
       kRetVal, BidderWorklet::BidResult("[false,true]", 1,
-                                        GURL("https://response.test/")));
+                                        GURL("https://response.test/"),
+                                        base::TimeDelta(), base::nullopt));
 
   SetDefaultParameters();
   null_auction_signals_ = true;
   null_per_buyer_signals_ = false;
   RunGenerateBidWithReturnValueExpectingResult(
       kRetVal, BidderWorklet::BidResult("[true,false]", 1,
-                                        GURL("https://response.test/")));
+                                        GURL("https://response.test/"),
+                                        base::TimeDelta(), base::nullopt));
 
   SetDefaultParameters();
   null_auction_signals_ = true;
   null_per_buyer_signals_ = true;
   RunGenerateBidWithReturnValueExpectingResult(
-      kRetVal, BidderWorklet::BidResult("[true,true]", 1,
-                                        GURL("https://response.test/")));
+      kRetVal,
+      BidderWorklet::BidResult("[true,true]", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 }
 
 // Utility methods to create vectors of PreviousWin. Needed because StructPtr's
@@ -819,14 +861,15 @@
             R"({ad: %s, bid:1, render:"https://response.test/"})",
             test_case.ad),
         BidderWorklet::BidResult(test_case.expected_ad, 1,
-                                 GURL("https://response.test/")));
+                                 GURL("https://response.test/"),
+                                 base::TimeDelta(), base::nullopt));
   }
 }
 
 TEST_F(BidderWorkletTest, GenerateBidTrustedBiddingSignals) {
   const GURL kBaseSignalsUrl("https://signals.test/");
   const GURL kFullSignalsUrl(
-      "https://signals.test/?hostname=hostname&keys=key1,key2");
+      "https://signals.test/?hostname=top.window.test&keys=key1,key2");
 
   const char kJson[] = R"(
     {
@@ -835,50 +878,61 @@
     }
   )";
 
+  // Request with null TrustedBiddingSignals keys and URL. No request should be
+  // made.
+  RunGenerateBidWithReturnValueExpectingResult(
+      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
+      BidderWorklet::BidResult("null", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
+
+  // Request with TrustedBiddingSignals keys and null URL. No request should be
+  // made.
+  interest_group_trusted_bidding_signals_keys_ =
+      std::vector<std::string>({"key1", "key2"});
+  RunGenerateBidWithReturnValueExpectingResult(
+      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
+      BidderWorklet::BidResult("null", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
+
+  // Request with TrustedBiddingSignals URL and null keys. No request should be
+  // made.
+  interest_group_trusted_bidding_signals_url_ = kBaseSignalsUrl;
+  interest_group_trusted_bidding_signals_keys_.reset();
+  RunGenerateBidWithReturnValueExpectingResult(
+      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
+      BidderWorklet::BidResult("null", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
+
+  // Request with TrustedBiddingSignals URL and empty keys. No request should be
+  // made.
+  interest_group_trusted_bidding_signals_keys_ = std::vector<std::string>();
+  RunGenerateBidWithReturnValueExpectingResult(
+      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
+      BidderWorklet::BidResult("null", 1, GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
+
+  // Request with valid TrustedBiddingSignals URL and non-empty keys. Request
+  // should be made. The request fails.
+  interest_group_trusted_bidding_signals_keys_ =
+      std::vector<std::string>({"key1", "key2"});
+  url_loader_factory_.AddResponse(kFullSignalsUrl.spec(), kJson,
+                                  net::HTTP_NOT_FOUND);
+  RunGenerateBidWithReturnValueExpectingResult(
+      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
+      BidderWorklet::BidResult(
+          "null", 1, GURL("https://response.test/"), base::TimeDelta(),
+          "Failed to load "
+          "https://signals.test/?hostname=top.window.test&keys=key1,key2 HTTP "
+          "status = 404 Not Found."));
+
+  // Request with valid TrustedBiddingSignals URL and non-empty keys. Request
+  // should be made. The request succeeds.
   AddJsonResponse(&url_loader_factory_, kFullSignalsUrl, kJson);
-
-  // Request with null TrustedBiddingSignals. This results
-  RunGenerateBidWithReturnValueExpectingResult(
-      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("null", 1, GURL("https://response.test/")));
-
-  base::RunLoop run_loop;
-  bool signals_loaded_successfully = false;
-  trusted_bidding_signals_ = std::make_unique<TrustedBiddingSignals>(
-      &url_loader_factory_, std::vector<std::string>({"key1", "key2"}),
-      "hostname", kBaseSignalsUrl, &v8_helper_,
-      base::BindLambdaForTesting(
-          [&](bool success, base::Optional<std::string> signals_error_msg) {
-            signals_loaded_successfully = success;
-            EXPECT_FALSE(signals_error_msg.has_value());
-            run_loop.Quit();
-          }));
-  run_loop.Run();
-  ASSERT_TRUE(signals_loaded_successfully);
-
-  // Request with no keys, but non-empty `trustedBiddingSignals`. Probably
-  // best not to load the signals if it happens, but whether or not that's done,
-  // `trustedBiddingSignals` should be null.
-  RunGenerateBidWithReturnValueExpectingResult(
-      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("null", 1, GURL("https://response.test/")));
-
-  interest_group_trusted_bidding_signals_keys_ = {"key1"};
-  RunGenerateBidWithReturnValueExpectingResult(
-      R"({ad: trustedBiddingSignals["key1"], bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult("1", 1, GURL("https://response.test/")));
-
-  interest_group_trusted_bidding_signals_keys_ = {"key2"};
-  RunGenerateBidWithReturnValueExpectingResult(
-      R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
-      BidderWorklet::BidResult(R"({"key2":[2]})", 1,
-                               GURL("https://response.test/")));
-
-  interest_group_trusted_bidding_signals_keys_ = {"key1", "key2"};
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: trustedBiddingSignals, bid:1, render:"https://response.test/"})",
       BidderWorklet::BidResult(R"({"key1":1,"key2":[2]})", 1,
-                               GURL("https://response.test/")));
+                               GURL("https://response.test/"),
+                               base::TimeDelta(), base::nullopt));
 }
 
 TEST_F(BidderWorkletTest, ReportWin) {
@@ -893,22 +947,22 @@
 
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo("http://http.not.allowed.test"))", GURL(),
-      "https://url.test/:4 Uncaught TypeError: sendReportTo must be passed a "
+      "https://url.test/:9 Uncaught TypeError: sendReportTo must be passed a "
       "valid HTTPS url.");
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo("file:///file.not.allowed.test"))", GURL(),
-      "https://url.test/:4 Uncaught TypeError: sendReportTo must be passed a "
+      "https://url.test/:9 Uncaught TypeError: sendReportTo must be passed a "
       "valid HTTPS url.");
 
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo(""))", GURL(),
-      "https://url.test/:4 Uncaught TypeError: sendReportTo must be passed a "
+      "https://url.test/:9 Uncaught TypeError: sendReportTo must be passed a "
       "valid HTTPS url.");
 
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo("https://foo.test");sendReportTo("https://foo.test"))",
       GURL(),
-      "https://url.test/:4 Uncaught TypeError: sendReportTo may be called at "
+      "https://url.test/:9 Uncaught TypeError: sendReportTo may be called at "
       "most once.");
 }
 
@@ -916,7 +970,7 @@
 TEST_F(BidderWorkletTest, ReportWinDateNotAvailable) {
   RunReportWinWithFunctionBodyExpectingResult(
       R"(sendReportTo("https://foo.test/" + Date().toString()))", GURL(),
-      "https://url.test/:4 Uncaught ReferenceError: Date is not defined.");
+      "https://url.test/:9 Uncaught ReferenceError: Date is not defined.");
 }
 
 TEST_F(BidderWorkletTest, ReportWinParameters) {
@@ -925,8 +979,15 @@
     // String used in JS to access the parameter.
     const char* name;
     bool is_json;
+
+    // Whether a value is also passed to generateBid(). Important because in the
+    // tests for passing non-JSON data as JSON, creating the worklet fails if
+    // generateBid() also takes the value as an argument.
+    bool passed_to_generate_bid;
+
     // Pointer to location at which the string can be modified.
     std::string* value_ptr;
+
     // Whether to expect an error. This can be empty when call fails in case
     // it's due to something like passing non-JSON to JSON parameter which user
     // code should be unable to trigger, and for which we thus do not produce
@@ -937,6 +998,7 @@
       {
           "auctionSignals",
           true /* is_json */,
+          true /* passed_to_generate_bid */,
           &auction_signals_,
           base::nullopt,
           base::nullopt,
@@ -944,6 +1006,7 @@
       {
           "perBuyerSignals",
           true /* is_json */,
+          true /* passed_to_generate_bid */,
           &per_buyer_signals_,
           base::nullopt,
           base::nullopt,
@@ -951,6 +1014,7 @@
       {
           "sellerSignals",
           true /* is_json */,
+          false /* passed_to_generate_bid */,
           &seller_signals_,
           base::nullopt,
           base::nullopt,
@@ -958,17 +1022,19 @@
       {
           "browserSignals.interestGroupName",
           false /* is_json */,
+          true /* passed_to_generate_bid */,
           &interest_group_name_,
           base::nullopt,
-          "https://url.test/:4 Uncaught TypeError: sendReportTo must be passed "
+          "https://url.test/:9 Uncaught TypeError: sendReportTo must be passed "
           "a valid HTTPS url.",
       },
       {
           "browserSignals.adRenderFingerprint",
           false /* is_json */,
+          false /* passed_to_generate_bid */,
           &browser_signal_ad_render_fingerprint_,
           base::nullopt,
-          "https://url.test/:4 Uncaught TypeError: sendReportTo must be passed "
+          "https://url.test/:9 Uncaught TypeError: sendReportTo must be passed "
           "a valid HTTPS url.",
       },
   };
@@ -977,10 +1043,17 @@
     SCOPED_TRACE(test_case.name);
 
     *test_case.value_ptr = "https://foo.test/";
-    RunReportWinWithFunctionBodyExpectingResult(
-        base::StringPrintf("sendReportTo(%s)", test_case.name),
-        test_case.is_json ? GURL() : GURL("https://foo.test/"),
-        test_case.expect_error_msg);
+    if (!test_case.is_json || !test_case.passed_to_generate_bid) {
+      RunReportWinWithFunctionBodyExpectingResult(
+          base::StringPrintf("sendReportTo(%s)", test_case.name),
+          test_case.is_json ? GURL() : GURL("https://foo.test/"),
+          test_case.expect_error_msg);
+    } else {
+      // JSON values passed the generateBid() result in failures there, before
+      // reportWin is called.
+      RunGenerateBidWithJavascriptExpectingResult(
+          CreateBasicGenerateBidScript(), BidderWorklet::BidResult());
+    }
 
     *test_case.value_ptr = R"(["https://foo.test/"])";
     RunReportWinWithFunctionBodyExpectingResult(
@@ -1060,6 +1133,11 @@
 
 // Subsequent runs of the same script should not affect each other. Same is true
 // for different scripts, but it follows from the single script case.
+//
+// TODO(mmenke): The current API only allows each generateBid() method to be
+// called once, but each ReportWin() to be called multiple times. When the API
+// is updated to allow multiple calls to generateBid(), update this method to
+// invoke it multiple times.
 TEST_F(BidderWorkletTest, ScriptIsolation) {
   // Use arrays so that all values are references, to catch both the case where
   // variables are persisted, and the case where what they refer to is
@@ -1078,20 +1156,27 @@
                 ad: [++globalThis.var1[0], ++var2[0]],
                 bid: 1,
                 render:"https://response.test/"
-            };
-          }
+            }
+          };
         }();
+
+        function reportWin() {
+          // Reuse generateBid() to check same potential cases for leaks between
+          // successive calls.
+          var ad = generateBid().ad;
+          sendReportTo("https://" + ad[0] + ad[1] + ".test/");
+        }
       )");
-  auto bidder_worket = CreateWorklet();
+  auto bidder_worket = CreateWorkletAndGenerateBid();
   ASSERT_TRUE(bidder_worket);
 
   for (int i = 0; i < 3; ++i) {
-    BidderWorklet::BidResult actual_result =
-        RunGenerateBid(bidder_worket.get());
-    // "ad" value should be the same every time the script is run.
-    ExpectBidResultsEqual(
-        BidderWorklet::BidResult("[2,3]", 1, GURL("https://response.test/")),
-        actual_result);
+    BidderWorklet::ReportWinResult actual_result = bidder_worket->ReportWin(
+        seller_signals_, browser_signal_render_url_,
+        browser_signal_ad_render_fingerprint_, browser_signal_bid_);
+    EXPECT_TRUE(actual_result.success);
+    EXPECT_EQ(GURL("https://23.test/"), actual_result.report_url);
+    EXPECT_FALSE(actual_result.error_msg);
   }
 }
 
diff --git a/content/test/data/accessibility/html/custom-element-hidden-expected-blink.txt b/content/test/data/accessibility/html/custom-element-hidden-expected-blink.txt
new file mode 100644
index 0000000..63ac8e70
--- /dev/null
+++ b/content/test/data/accessibility/html/custom-element-hidden-expected-blink.txt
@@ -0,0 +1,9 @@
+rootWebArea htmlTag='#document'
+++genericContainer ignored htmlTag='html'
+++++genericContainer htmlTag='body'
+++++++genericContainer ignored invisible htmlTag='template'
+++++++genericContainer ignored invisible htmlTag='my-element'
+++++++++genericContainer ignored invisible htmlTag='div'
+++++++++++genericContainer ignored invisible htmlTag='slot'
+++++++++++++genericContainer ignored invisible htmlTag='span'
+++++++button htmlTag='button' name='Done'
diff --git a/content/test/data/accessibility/html/custom-element-hidden.html b/content/test/data/accessibility/html/custom-element-hidden.html
new file mode 100644
index 0000000..1f90691
--- /dev/null
+++ b/content/test/data/accessibility/html/custom-element-hidden.html
@@ -0,0 +1,42 @@
+<!--
+@BLINK-ALLOW:htmlTag=*
+@WAIT-FOR:Done
+-->
+<!DOCTYPE html>
+<html>
+<body>
+  <template id="template">
+    <div><slot name="my-slot"></slot></div>
+  </template>
+
+  <div hidden>
+    <my-element>
+      <span slot="my-slot">Slot contents</span>
+    </my-element>
+  </div>
+
+  <button id="status"></button>
+
+  <script>
+    // After a delay, make "my-element" into a custom element using
+    // the template defined above. That will cause the template to be
+    // rendered inside <my-element>, and the slot contents to be reparented
+    // to the <slot> element.
+    window.setTimeout(() => {
+        customElements.define(
+            'my-element',
+            class extends HTMLElement {
+                constructor() {
+                    super();
+                    let template = document.getElementById('template');
+                    let templateContent = template.content;
+
+                    const shadowRoot = this.attachShadow({mode: 'open'})
+                          .appendChild(templateContent.cloneNode(true));
+                }
+            }
+        );
+        document.getElementById('status').setAttribute('aria-label', 'Done');
+    }, 500);
+  </script>
+</body>
diff --git a/content/test/data/accessibility/html/custom-element-remove-nodes-expected-blink.txt b/content/test/data/accessibility/html/custom-element-remove-nodes-expected-blink.txt
new file mode 100644
index 0000000..7f60f6f
--- /dev/null
+++ b/content/test/data/accessibility/html/custom-element-remove-nodes-expected-blink.txt
@@ -0,0 +1,51 @@
+rootWebArea htmlTag='#document' name='done'
+++genericContainer ignored htmlTag='html'
+++++genericContainer ignored htmlTag='body'
+++++++genericContainer ignored className='dt1' htmlTag='x-details'
+++++++++genericContainer ignored htmlTag='slot'
+++++++++++genericContainer description='Details' htmlTag='summary' descriptionFrom=summary
+++++++++++++listMarker htmlTag='::marker' name='%E2%96%B8 '
+++++++++++++++staticText name='%E2%96%B8 '
+++++++++++++++++inlineTextBox name='%E2%96%B8 '
+++++++++++++staticText name='Details'
+++++++++++++++inlineTextBox name='Details'
+++++++genericContainer ignored className='dt2' htmlTag='x-details'
+++++++++genericContainer ignored htmlTag='slot'
+++++++++++genericContainer description='Details' htmlTag='summary' descriptionFrom=summary
+++++++++++++listMarker htmlTag='::marker' name='%E2%96%B8 '
+++++++++++++++staticText name='%E2%96%B8 '
+++++++++++++++++inlineTextBox name='%E2%96%B8 '
+++++++++++++staticText name='Details'
+++++++++++++++inlineTextBox name='Details'
+++++++genericContainer ignored className='dt3' htmlTag='x-details'
+++++++++genericContainer ignored htmlTag='slot'
+++++++++++genericContainer description='Details' htmlTag='summary' descriptionFrom=summary
+++++++++++++listMarker htmlTag='::marker' name='%E2%96%B8 '
+++++++++++++++staticText name='%E2%96%B8 '
+++++++++++++++++inlineTextBox name='%E2%96%B8 '
+++++++++++++staticText name='Details'
+++++++++++++++inlineTextBox name='Details'
+++++++genericContainer ignored className='dt4' htmlTag='x-details'
+++++++++genericContainer ignored htmlTag='slot'
+++++++++++genericContainer description='Details' htmlTag='summary' descriptionFrom=summary
+++++++++++++listMarker htmlTag='::marker' name='%E2%96%BE '
+++++++++++++++staticText name='%E2%96%BE '
+++++++++++++++++inlineTextBox name='%E2%96%BE '
+++++++++++++staticText name='Details'
+++++++++++++++inlineTextBox name='Details'
+++++++genericContainer ignored className='dt5' htmlTag='x-details'
+++++++++genericContainer ignored htmlTag='slot'
+++++++++++genericContainer description='Details' htmlTag='summary' descriptionFrom=summary
+++++++++++++listMarker htmlTag='::marker' name='%E2%96%BE '
+++++++++++++++staticText name='%E2%96%BE '
+++++++++++++++++inlineTextBox name='%E2%96%BE '
+++++++++++++staticText name='Details'
+++++++++++++++inlineTextBox name='Details'
+++++++genericContainer ignored className='dt6' htmlTag='x-details'
+++++++++genericContainer ignored htmlTag='slot'
+++++++++++genericContainer description='Details' htmlTag='summary' descriptionFrom=summary
+++++++++++++listMarker htmlTag='::marker' name='%E2%96%BE '
+++++++++++++++staticText name='%E2%96%BE '
+++++++++++++++++inlineTextBox name='%E2%96%BE '
+++++++++++++staticText name='Details'
+++++++++++++++inlineTextBox name='Details'
diff --git a/content/test/data/accessibility/html/custom-element-remove-nodes.html b/content/test/data/accessibility/html/custom-element-remove-nodes.html
new file mode 100644
index 0000000..5689e46
--- /dev/null
+++ b/content/test/data/accessibility/html/custom-element-remove-nodes.html
@@ -0,0 +1,114 @@
+<!--
+@BLINK-ALLOW:htmlTag*
+@BLINK-ALLOW:className*
+@WAIT-FOR:done
+-->
+<script>
+class CustomDetails extends HTMLElement {
+  constructor() {
+    super();
+    const shadowRoot = this.attachShadow({mode: 'open', slotAssignment: 'manual'});
+
+    this.summarySlot = document.createElement('slot');
+    this.summarySlot.id = 'details-summary';
+    shadowRoot.appendChild(this.summarySlot);
+
+    const defaultSummary = document.createElement('summary');
+    defaultSummary.textContent = 'Details';
+    this.summarySlot.appendChild(defaultSummary);
+
+    this.contentSlot = document.createElement('slot');
+    this.contentSlot.id = 'details-content';
+    this.contentSlot.style = `content-visibility:hidden; display:block;`;
+    shadowRoot.appendChild(this.contentSlot);
+
+    const style = document.createElement('style');
+    style.textContent = `
+:host summary {
+  display: list-item;
+  counter-increment: list-item 0;
+  list-style: disclosure-closed inside;
+}
+:host([open]) summary {
+  list-style-type: disclosure-open;
+}
+`;
+    shadowRoot.appendChild(style);
+  }
+
+  connectedCallback() {
+    this.updateOpen();
+    this.updateAssignment();
+    this.addEventListener('DOMNodeInserted', this.updateAssignment);
+    this.addEventListener('DOMNodeRemoved', this.updateAssignment);
+  }
+
+  disconnectedCallback() {
+    this.removeEventListener('DOMNodeInserted', this.updateAssignment);
+    this.removeEventListener('DOMNodeRemoved', this.updateAssignment);
+  }
+
+  attributeChangedCallback(name, oldValue, newValue) {
+    this.updateOpen();
+  }
+
+  static get observedAttributes() {
+    return ['open'];
+  }
+
+  updateOpen() {
+    if (this.hasAttribute('open')) {
+      this.contentSlot.style = ``;
+    } else {
+      this.contentSlot.style = `content-visibility: hidden; display:block`;
+    }
+  }
+
+  updateAssignment() {
+    let summary = null;
+    const content = [];
+
+    for (let child = this.firstChild; child; child = child.nextSibling) {
+      if (!summary && child.tagName === 'SUMMARY') {
+        summary = child;
+      } else {
+        content.push(child);
+      }
+    }
+
+    if (summary) {
+      this.summarySlot.assign([summary]);
+    } else {
+      this.summarySlot.assign([]);
+    }
+    this.contentSlot.assign(content);
+  }
+};
+customElements.define('x-details', CustomDetails);
+</script>
+
+<script>
+const $ = document.querySelector.bind(document);
+
+document.addEventListener('DOMContentLoaded', () => {
+  setTimeout(() => {
+  $('#dt1').removeChild($('#dt1 > summary'));
+  $('#dt2').removeChild($('#dt2 > summary'));
+  $('#dt3').removeChild($('#dt3 > summary:last-of-type'));
+  $('#dt4').removeChild($('#dt4 > summary'));
+  $('#dt5').removeChild($('#dt5 > summary'));
+  $('#dt6').removeChild($('#dt6 > summary:last-of-type'));
+
+    document.title = 'done';
+  }, 200);
+});
+
+</script>
+<body>
+<x-details id="dt1" class="dt1"><summary>summary</summary></x-details>
+<x-details id="dt2" class="dt2"><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt3" class="dt3"><summary>summary 1</summary><summary>summary <b>2</b></summary></x-details>
+<x-details id="dt4" class="dt4" open><summary>summary</summary></x-details>
+<x-details id="dt5" class="dt5" open><summary>summary 1</summary><summary>summary <mark>2</mark></summary></x-details>
+<x-details id="dt6" class="dt6" open><summary>summary 1</summary><summary>summary <mark>2</mark></summary></x-details>
+</body>
diff --git a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
index 2dc6367..b5c4560b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/mediapipe_expectations.txt
@@ -58,3 +58,10 @@
 # results: [ Failure RetryOnFailure Skip ]
 # END TAG HEADER
 
+crbug.com/1206463 [ android android-nexus-5x ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ android android-pixel-4 ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ chromeos chromeos-board-kevin ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ linux display-server-wayland intel ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ linux display-server-x intel ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ linux display-server-x nvidia ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
+crbug.com/1206463 [ win nvidia ] MediaPipe_mediapipe_face_mesh_test [ Failure ]
diff --git a/content/web_test/renderer/test_plugin.cc b/content/web_test/renderer/test_plugin.cc
index 4ad54df..b00db5c 100644
--- a/content/web_test/renderer/test_plugin.cc
+++ b/content/web_test/renderer/test_plugin.cc
@@ -23,7 +23,7 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/shared_image_interface.h"
-#include "gpu/command_buffer/common//shared_image_usage.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
 #include "third_party/blink/public/common/input/web_gesture_event.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
diff --git a/device/fido/auth_token_requester_unittest.cc b/device/fido/auth_token_requester_unittest.cc
index 4111ee2..1bbcc77 100644
--- a/device/fido/auth_token_requester_unittest.cc
+++ b/device/fido/auth_token_requester_unittest.cc
@@ -34,14 +34,15 @@
     device::AuthenticatorSupportedOptions::UserVerificationAvailability;
 
 constexpr char kTestPIN[] = "1234";
-constexpr char kNewPIN[] = "5678";
+constexpr char16_t kTestPIN16[] = u"1234";
+constexpr char16_t kNewPIN[] = u"5678";
 
 struct TestExpectation {
   pin::PINEntryReason reason;
   pin::PINEntryError error = pin::PINEntryError::kNoError;
   uint32_t min_pin_length = kMinPinLength;
   int attempts = 8;
-  std::u16string pin = base::UTF8ToUTF16(kTestPIN);
+  std::u16string pin = kTestPIN16;
 };
 
 struct TestCase {
@@ -471,7 +472,7 @@
                         {
                             .reason = pin::PINEntryReason::kChange,
                             .attempts = 0,
-                            .pin = base::UTF8ToUTF16(kNewPIN),
+                            .pin = kNewPIN,
                         }}});
 
   EXPECT_EQ(*delegate_->result(), AuthTokenRequester::Result::kSuccess);
@@ -500,7 +501,7 @@
                             .reason = pin::PINEntryReason::kChange,
                             .error = pin::PINEntryError::kSameAsCurrentPIN,
                             .attempts = 0,
-                            .pin = base::UTF8ToUTF16(kNewPIN),
+                            .pin = kNewPIN,
                         }}});
 
   EXPECT_EQ(*delegate_->result(), AuthTokenRequester::Result::kSuccess);
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index 09247a5..915292e8 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -241,6 +241,8 @@
   void SetExtensionDisabled(const std::string& extension_id,
                             int disable_reasons);
 
+  // TODO(crbug.com/1180996): Move SetExtensionBlocklistState and
+  // GetExtensionBlocklistState to the blocklist_extension_prefs file.
   void SetExtensionBlocklistState(const std::string& extension_id,
                                   BlocklistState state);
 
diff --git a/extensions/common/stack_frame.cc b/extensions/common/stack_frame.cc
index 9ec229bb..157492d 100644
--- a/extensions/common/stack_frame.cc
+++ b/extensions/common/stack_frame.cc
@@ -13,7 +13,7 @@
 namespace extensions {
 
 namespace {
-const char kAnonymousFunction[] = "(anonymous function)";
+const char16_t kAnonymousFunction[] = u"(anonymous function)";
 }
 
 StackFrame::StackFrame() : line_number(1), column_number(1) {
@@ -33,8 +33,7 @@
     : line_number(line_number),
       column_number(column_number),
       source(source),
-      function(function.empty() ? base::UTF8ToUTF16(kAnonymousFunction)
-                                : function) {}
+      function(function.empty() ? kAnonymousFunction : function) {}
 
 StackFrame::~StackFrame() {
 }
diff --git a/extensions/renderer/extensions_render_frame_observer.cc b/extensions/renderer/extensions_render_frame_observer.cc
index 63e85e3..4adc86c 100644
--- a/extensions/renderer/extensions_render_frame_observer.cc
+++ b/extensions/renderer/extensions_render_frame_observer.cc
@@ -26,7 +26,7 @@
 namespace {
 
 // The delimiter for a stack trace provided by WebKit.
-const char kStackFrameDelimiter[] = "\n    at ";
+const char16_t kStackFrameDelimiter[] = u"\n    at ";
 
 // Get a stack trace from a WebKit console message.
 // There are three possible scenarios:
@@ -46,17 +46,16 @@
   std::vector<std::u16string> pieces;
   size_t index = 0;
 
-  if (message->find(base::UTF8ToUTF16(kStackFrameDelimiter)) !=
-      std::u16string::npos) {
-    pieces = base::SplitStringUsingSubstr(
-        *message, base::UTF8ToUTF16(kStackFrameDelimiter),
-        base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  if (message->find(kStackFrameDelimiter) != std::u16string::npos) {
+    pieces = base::SplitStringUsingSubstr(*message, kStackFrameDelimiter,
+                                          base::TRIM_WHITESPACE,
+                                          base::SPLIT_WANT_ALL);
     *message = pieces[0];
     index = 1;
   } else if (!stack_trace.empty()) {
-    pieces = base::SplitStringUsingSubstr(
-        stack_trace, base::UTF8ToUTF16(kStackFrameDelimiter),
-        base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+    pieces = base::SplitStringUsingSubstr(stack_trace, kStackFrameDelimiter,
+                                          base::TRIM_WHITESPACE,
+                                          base::SPLIT_WANT_ALL);
   }
 
   // If we got a stack trace, parse each frame from the text.
diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn
index 9b8c229..0934432 100644
--- a/gpu/config/BUILD.gn
+++ b/gpu/config/BUILD.gn
@@ -178,6 +178,7 @@
   deps = [
     ":process_json",
     "//base",
+    "//base/util/values:values_util",
     "//build:branding_buildflags",
     "//build:chromecast_buildflags",
     "//build:chromeos_buildflags",
diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc
index 9db98fb..fbb9f58 100644
--- a/gpu/config/gpu_control_list.cc
+++ b/gpu/config/gpu_control_list.cc
@@ -14,6 +14,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/system/sys_info.h"
+#include "base/util/values/values_util.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -601,18 +602,17 @@
 }
 
 void GpuControlList::Entry::GetFeatureNames(
-    base::ListValue* feature_names,
+    base::Value& feature_names,
     const FeatureMap& feature_map) const {
-  DCHECK(feature_names);
   for (size_t ii = 0; ii < feature_size; ++ii) {
     auto iter = feature_map.find(features[ii]);
     DCHECK(iter != feature_map.end());
-    feature_names->AppendString(iter->second);
+    feature_names.Append(iter->second);
   }
   for (size_t ii = 0; ii < disabled_extension_size; ++ii) {
     std::string name =
         base::StringPrintf("disable(%s)", disabled_extensions[ii]);
-    feature_names->AppendString(name);
+    feature_names.Append(name);
   }
 }
 
@@ -738,30 +738,30 @@
                                   disabled_webgl_extensions.end());
 }
 
-void GpuControlList::GetReasons(base::ListValue* problem_list,
+void GpuControlList::GetReasons(base::Value& problem_list,
                                 const std::string& tag,
                                 const std::vector<uint32_t>& entries) const {
-  DCHECK(problem_list);
   for (auto index : entries) {
     DCHECK_LT(index, entry_count_);
     const Entry& entry = entries_[index];
-    auto problem = std::make_unique<base::DictionaryValue>();
+    auto problem = base::Value(base::Value::Type::DICTIONARY);
 
-    problem->SetString("description", entry.description);
+    problem.SetStringKey("description", entry.description);
 
-    auto cr_bugs = std::make_unique<base::ListValue>();
+    auto cr_bugs = base::Value(base::Value::Type::LIST);
     for (size_t jj = 0; jj < entry.cr_bug_size; ++jj)
-      cr_bugs->AppendInteger(entry.cr_bugs[jj]);
-    problem->Set("crBugs", std::move(cr_bugs));
+      cr_bugs.Append(
+          util::Int64ToValue(static_cast<int64_t>(entry.cr_bugs[jj])));
+    problem.SetKey("crBugs", std::move(cr_bugs));
 
-    auto features = std::make_unique<base::ListValue>();
-    entry.GetFeatureNames(features.get(), feature_map_);
-    problem->Set("affectedGpuSettings", std::move(features));
+    auto features = base::Value(base::Value::Type::LIST);
+    entry.GetFeatureNames(features, feature_map_);
+    problem.SetKey("affectedGpuSettings", std::move(features));
 
     DCHECK(tag == "workarounds" || tag == "disabledFeatures");
-    problem->SetString("tag", tag);
+    problem.SetStringKey("tag", tag);
 
-    problem_list->Append(std::move(problem));
+    problem_list.Append(std::move(problem));
   }
 }
 
diff --git a/gpu/config/gpu_control_list.h b/gpu/config/gpu_control_list.h
index 8a27b1c2..ebc5183 100644
--- a/gpu/config/gpu_control_list.h
+++ b/gpu/config/gpu_control_list.h
@@ -240,7 +240,7 @@
     // decision.  It should only be checked if Contains() returns true.
     bool NeedsMoreInfo(const GPUInfo& gpu_info, bool consider_exceptions) const;
 
-    void GetFeatureNames(base::ListValue* feature_names,
+    void GetFeatureNames(base::Value& feature_names,
                          const FeatureMap& feature_map) const;
 
     // Logs a control list match for this rule in the list identified by
@@ -287,7 +287,7 @@
   // }
   // The use case is we compute the entries from GPU process and send them to
   // browser process, and call GetReasons() in browser process.
-  void GetReasons(base::ListValue* problem_list,
+  void GetReasons(base::Value& problem_list,
                   const std::string& tag,
                   const std::vector<uint32_t>& entries) const;
 
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc
index dcd39ed..f0d680d 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -29,10 +29,8 @@
 #include "gpu/command_buffer/common/sync_token.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "gpu/ipc/common/command_buffer_id.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/common/gpu_param_traits.h"
-#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/gfx/buffer_format_util.h"
@@ -78,14 +76,14 @@
   // prevent cleanup on destruction.
   auto channel = std::move(channel_);
 
-  auto params = mojom::CreateCommandBufferParams::New();
-  params->surface_handle = surface_handle;
-  params->share_group_id =
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = surface_handle;
+  init_params.share_group_id =
       share_group ? share_group->route_id_ : MSG_ROUTING_NONE;
-  params->stream_id = stream_id_;
-  params->stream_priority = stream_priority;
-  params->attribs = attribs;
-  params->active_url = active_url;
+  init_params.stream_id = stream_id_;
+  init_params.stream_priority = stream_priority;
+  init_params.attribs = attribs;
+  init_params.active_url = active_url;
 
   TRACE_EVENT0("gpu", "CommandBufferProxyImpl::Initialize");
   std::tie(shared_state_shm_, shared_state_mapping_) =
@@ -118,17 +116,16 @@
   // so it won't cause additional jank.
   // TODO(piman): Make this asynchronous (http://crbug.com/125248).
   ContextResult result = ContextResult::kSuccess;
-  mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync;
-  bool sent = channel->GetGpuChannel().CreateCommandBuffer(
-      std::move(params), route_id_, std::move(region), &result, &capabilities_);
+  bool sent = channel->Send(new GpuChannelMsg_CreateCommandBuffer(
+      init_params, route_id_, std::move(region), &result, &capabilities_));
   if (!sent) {
     channel->RemoveRoute(route_id_);
     LOG(ERROR) << "ContextResult::kTransientFailure: "
-                  "Failed to send GpuControl.CreateCommandBuffer.";
+                  "Failed to send GpuChannelMsg_CreateCommandBuffer.";
     return ContextResult::kTransientFailure;
   }
   if (result != ContextResult::kSuccess) {
-    DLOG(ERROR) << "Failure processing GpuControl.CreateCommandBuffer.";
+    DLOG(ERROR) << "Failure processing GpuChannelMsg_CreateCommandBuffer.";
     channel->RemoveRoute(route_id_);
     return result;
   }
@@ -407,10 +404,7 @@
     return;
 
   last_flush_id_ = channel_->EnqueueDeferredMessage(
-      mojom::DeferredRequestParams::NewCommandBufferRequest(
-          mojom::DeferredCommandBufferRequest::New(
-              route_id_, mojom::DeferredCommandBufferRequestParams::
-                             NewDestroyTransferBuffer(id))));
+      GpuCommandBufferMsg_DestroyTransferBuffer(route_id_, id));
 }
 
 void CommandBufferProxyImpl::SetGpuControlClient(GpuControlClient* client) {
@@ -654,11 +648,7 @@
   // TakeFrontBuffer should be a deferred message so that it's sequenced
   // correctly with respect to preceding ReturnFrontBuffer messages.
   last_flush_id_ = channel_->EnqueueDeferredMessage(
-      mojom::DeferredRequestParams::NewCommandBufferRequest(
-          mojom::DeferredCommandBufferRequest::New(
-              route_id_,
-              mojom::DeferredCommandBufferRequestParams::NewTakeFrontBuffer(
-                  mailbox))));
+      GpuCommandBufferMsg_TakeFrontBuffer(route_id_, mailbox));
 }
 
 void CommandBufferProxyImpl::ReturnFrontBuffer(const gpu::Mailbox& mailbox,
@@ -670,11 +660,7 @@
     return;
 
   last_flush_id_ = channel_->EnqueueDeferredMessage(
-      mojom::DeferredRequestParams::NewCommandBufferRequest(
-          mojom::DeferredCommandBufferRequest::New(
-              route_id_,
-              mojom::DeferredCommandBufferRequestParams::NewReturnFrontBuffer(
-                  mojom::ReturnFrontBufferParams::New(mailbox, is_lost)))),
+      GpuCommandBufferMsg_ReturnFrontBuffer(route_id_, mailbox, is_lost),
       {sync_token});
 }
 
@@ -921,10 +907,7 @@
     return;
   disconnected_ = true;
   channel_->VerifyFlush(UINT32_MAX);
-
-  mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync;
-  channel_->GetGpuChannel().DestroyCommandBuffer(route_id_);
-
+  channel_->Send(new GpuChannelMsg_DestroyCommandBuffer(route_id_));
   channel_->RemoveRoute(route_id_);
   if (gpu_control_client_)
     gpu_control_client_->OnGpuControlLostContext();
diff --git a/gpu/ipc/client/command_buffer_proxy_impl_unittest.cc b/gpu/ipc/client/command_buffer_proxy_impl_unittest.cc
index b89a407b..debadfd 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl_unittest.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl_unittest.cc
@@ -9,20 +9,15 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "gpu/command_buffer/client/gpu_control_client.h"
-#include "gpu/command_buffer/common/context_creation_attribs.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
+#include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/common/mock_gpu_channel.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "ipc/ipc_test_sink.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
 
-using ::testing::_;
-using ::testing::Invoke;
-using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
 
 namespace gpu {
@@ -87,20 +82,6 @@
     auto proxy = std::make_unique<CommandBufferProxyImpl>(
         channel_, nullptr /* gpu_memory_buffer_manager */, 0 /* stream_id */,
         base::ThreadTaskRunnerHandle::Get());
-
-    // The Initialize() call below synchronously requests a new CommandBuffer
-    // using the channel's GpuControl interface.  Simulate success, since we're
-    // not actually talking to the service in these tests.
-    EXPECT_CALL(mock_gpu_channel_, CreateCommandBuffer(_, _, _, _, _))
-        .Times(1)
-        .WillOnce(Invoke(
-            [&](mojom::CreateCommandBufferParamsPtr params, int32_t routing_id,
-                base::UnsafeSharedMemoryRegion shared_state,
-                ContextResult* result, Capabilities* capabilities) -> bool {
-              *result = ContextResult::kSuccess;
-              return true;
-            }));
-
     proxy->Initialize(kNullSurfaceHandle, nullptr, SchedulingPriority::kNormal,
                       ContextCreationAttribs(), GURL());
     // Use an arbitrary valid shm_id. The command buffer doesn't use this
@@ -110,19 +91,14 @@
     return proxy;
   }
 
-  void ExpectOrderingBarrier(const mojom::DeferredRequest& request,
+  void ExpectOrderingBarrier(const GpuDeferredMessage& params,
                              int32_t route_id,
                              int32_t put_offset) {
-    ASSERT_TRUE(request.params->is_command_buffer_request());
-
-    const auto& command_buffer_request =
-        *request.params->get_command_buffer_request();
-    ASSERT_TRUE(command_buffer_request.params->is_async_flush());
-    EXPECT_EQ(command_buffer_request.routing_id, route_id);
-
-    const auto& flush_request =
-        *command_buffer_request.params->get_async_flush();
-    EXPECT_EQ(flush_request.put_offset, put_offset);
+    EXPECT_EQ(params.message.routing_id(), route_id);
+    GpuCommandBufferMsg_AsyncFlush::Param async_flush;
+    ASSERT_TRUE(
+        GpuCommandBufferMsg_AsyncFlush::Read(&params.message, &async_flush));
+    EXPECT_EQ(std::get<0>(async_flush), put_offset);
   }
 
  protected:
@@ -136,80 +112,73 @@
   auto proxy1 = CreateAndInitializeProxy();
   auto proxy2 = CreateAndInitializeProxy();
 
-  EXPECT_CALL(mock_gpu_channel_, FlushDeferredRequests(_))
-      .Times(1)
-      .WillOnce(Invoke([&](std::vector<mojom::DeferredRequestPtr> requests) {
-        EXPECT_EQ(3u, requests.size());
-        ExpectOrderingBarrier(*requests[0], proxy1->route_id(), 10);
-        ExpectOrderingBarrier(*requests[1], proxy2->route_id(), 20);
-        ExpectOrderingBarrier(*requests[2], proxy1->route_id(), 50);
-      }));
-
   proxy1->OrderingBarrier(10);
   proxy2->OrderingBarrier(20);
   proxy1->OrderingBarrier(30);
   proxy1->OrderingBarrier(40);
   proxy1->Flush(50);
 
-  // Once for each proxy.
-  EXPECT_CALL(mock_gpu_channel_, DestroyCommandBuffer(_))
-      .Times(2)
-      .WillRepeatedly(Return(true));
+  EXPECT_EQ(1u, sink_.message_count());
+  const IPC::Message* msg =
+      sink_.GetFirstMessageMatching(GpuChannelMsg_FlushDeferredMessages::ID);
+  ASSERT_TRUE(msg);
+  GpuChannelMsg_FlushDeferredMessages::Param params;
+  ASSERT_TRUE(GpuChannelMsg_FlushDeferredMessages::Read(msg, &params));
+  std::vector<GpuDeferredMessage> deferred_messages =
+      std::get<0>(std::move(params));
+  EXPECT_EQ(3u, deferred_messages.size());
+  ExpectOrderingBarrier(deferred_messages[0], proxy1->route_id(), 10);
+  ExpectOrderingBarrier(deferred_messages[1], proxy2->route_id(), 20);
+  ExpectOrderingBarrier(deferred_messages[2], proxy1->route_id(), 50);
 
-  // Each proxy sends a sync GpuControl flush on disconnect.
+  // Each proxy sends a sync GpuChannel flush on disconnect.
   EXPECT_CALL(mock_gpu_channel_, Flush()).Times(2).WillRepeatedly(Return(true));
-  EXPECT_EQ(0u, sink_.message_count());
 }
 
 TEST_F(CommandBufferProxyImplTest, FlushPendingWorkFlushesOrderingBarriers) {
   auto proxy1 = CreateAndInitializeProxy();
   auto proxy2 = CreateAndInitializeProxy();
 
-  EXPECT_CALL(mock_gpu_channel_, FlushDeferredRequests(_))
-      .Times(1)
-      .WillOnce(Invoke([&](std::vector<mojom::DeferredRequestPtr> requests) {
-        EXPECT_EQ(3u, requests.size());
-        ExpectOrderingBarrier(*requests[0], proxy1->route_id(), 10);
-        ExpectOrderingBarrier(*requests[1], proxy2->route_id(), 20);
-        ExpectOrderingBarrier(*requests[2], proxy1->route_id(), 30);
-      }));
-
   proxy1->OrderingBarrier(10);
   proxy2->OrderingBarrier(20);
   proxy1->OrderingBarrier(30);
   proxy2->FlushPendingWork();
 
-  // Once for each proxy.
-  EXPECT_CALL(mock_gpu_channel_, DestroyCommandBuffer(_))
-      .Times(2)
-      .WillRepeatedly(Return(true));
+  EXPECT_EQ(1u, sink_.message_count());
+  const IPC::Message* msg =
+      sink_.GetFirstMessageMatching(GpuChannelMsg_FlushDeferredMessages::ID);
+  ASSERT_TRUE(msg);
+  GpuChannelMsg_FlushDeferredMessages::Param params;
+  ASSERT_TRUE(GpuChannelMsg_FlushDeferredMessages::Read(msg, &params));
+  std::vector<GpuDeferredMessage> deferred_messages =
+      std::get<0>(std::move(params));
+  EXPECT_EQ(3u, deferred_messages.size());
+  ExpectOrderingBarrier(deferred_messages[0], proxy1->route_id(), 10);
+  ExpectOrderingBarrier(deferred_messages[1], proxy2->route_id(), 20);
+  ExpectOrderingBarrier(deferred_messages[2], proxy1->route_id(), 30);
 
-  // Each proxy sends a sync GpuControl flush on disconnect.
+  // Each proxy sends a sync GpuChannel flush on disconnect.
   EXPECT_CALL(mock_gpu_channel_, Flush()).Times(2).WillRepeatedly(Return(true));
-  EXPECT_EQ(0u, sink_.message_count());
 }
 
 TEST_F(CommandBufferProxyImplTest, EnsureWorkVisibleFlushesOrderingBarriers) {
   auto proxy1 = CreateAndInitializeProxy();
   auto proxy2 = CreateAndInitializeProxy();
 
-  // Ordering of these flush operations must be preserved.
-  {
-    ::testing::InSequence in_sequence;
-
-    // First we expect to see a FlushDeferredRequests call.
-    EXPECT_CALL(mock_gpu_channel_, FlushDeferredRequests(_))
-        .Times(1)
-        .WillOnce(Invoke([&](std::vector<mojom::DeferredRequestPtr> requests) {
-          EXPECT_EQ(3u, requests.size());
-          ExpectOrderingBarrier(*requests[0], proxy1->route_id(), 10);
-          ExpectOrderingBarrier(*requests[1], proxy2->route_id(), 20);
-          ExpectOrderingBarrier(*requests[2], proxy1->route_id(), 30);
-        }));
-
-    // Next we expect a full `Flush()`.
-    EXPECT_CALL(mock_gpu_channel_, Flush()).Times(1).RetiresOnSaturation();
-  }
+  GpuChannelMsg_FlushDeferredMessages::Param params;
+  EXPECT_CALL(mock_gpu_channel_, Flush())
+      .Times(1)
+      .WillOnce(::testing::InvokeWithoutArgs([&]() -> bool {
+        // FlushDeferredMessages should have already been sent first.
+        EXPECT_EQ(1u, sink_.message_count());
+        const IPC::Message* msg = sink_.GetMessageAt(0);
+        CHECK(msg);
+        EXPECT_EQ(
+            static_cast<uint32_t>(GpuChannelMsg_FlushDeferredMessages::ID),
+            msg->type());
+        CHECK(GpuChannelMsg_FlushDeferredMessages::Read(msg, &params));
+        return true;
+      }));
 
   proxy1->OrderingBarrier(10);
   proxy2->OrderingBarrier(20);
@@ -217,14 +186,15 @@
 
   proxy2->EnsureWorkVisible();
 
-  // Once for each proxy.
-  EXPECT_CALL(mock_gpu_channel_, DestroyCommandBuffer(_))
-      .Times(2)
-      .WillRepeatedly(Return(true));
+  std::vector<GpuDeferredMessage> deferred_messages =
+      std::get<0>(std::move(params));
+  EXPECT_EQ(3u, deferred_messages.size());
+  ExpectOrderingBarrier(deferred_messages[0], proxy1->route_id(), 10);
+  ExpectOrderingBarrier(deferred_messages[1], proxy2->route_id(), 20);
+  ExpectOrderingBarrier(deferred_messages[2], proxy1->route_id(), 30);
 
-  // Each proxy sends a sync GpuControl flush on disconnect.
+  // Each proxy sends a sync GpuChannel flush on disconnect.
   EXPECT_CALL(mock_gpu_channel_, Flush()).Times(2).WillRepeatedly(Return(true));
-  EXPECT_EQ(0u, sink_.message_count());
 }
 
 TEST_F(CommandBufferProxyImplTest,
@@ -234,37 +204,25 @@
   proxy1->OrderingBarrier(10);
   proxy1->OrderingBarrier(20);
   channel_->EnqueueDeferredMessage(
-      mojom::DeferredRequestParams::NewCommandBufferRequest(
-          mojom::DeferredCommandBufferRequest::New(
-              proxy1->route_id(), mojom::DeferredCommandBufferRequestParams::
-                                      NewDestroyTransferBuffer(3))));
-
-  // Make sure the above requests don't hit our mock yet.
-  base::RunLoop().RunUntilIdle();
-
-  // Now we can expect a FlushDeferredRequests to be elicited by the
-  // FlushPendingWork call below.
-  EXPECT_CALL(mock_gpu_channel_, FlushDeferredRequests(_))
-      .Times(1)
-      .WillOnce(Invoke([&](std::vector<mojom::DeferredRequestPtr> requests) {
-        EXPECT_EQ(2u, requests.size());
-        ExpectOrderingBarrier(*requests[0], proxy1->route_id(), 20);
-        ASSERT_TRUE(requests[1]->params->is_command_buffer_request());
-
-        auto& request = *requests[1]->params->get_command_buffer_request();
-        ASSERT_TRUE(request.params->is_destroy_transfer_buffer());
-        EXPECT_EQ(3, request.params->get_destroy_transfer_buffer());
-      }));
-
+      GpuCommandBufferMsg_DestroyTransferBuffer(proxy1->route_id(), 3));
+  EXPECT_EQ(0u, sink_.message_count());
   proxy1->FlushPendingWork();
 
-  EXPECT_CALL(mock_gpu_channel_, DestroyCommandBuffer(_))
-      .Times(1)
-      .WillOnce(Return(true));
+  EXPECT_EQ(1u, sink_.message_count());
+  const IPC::Message* msg =
+      sink_.GetFirstMessageMatching(GpuChannelMsg_FlushDeferredMessages::ID);
+  ASSERT_TRUE(msg);
+  GpuChannelMsg_FlushDeferredMessages::Param params;
+  ASSERT_TRUE(GpuChannelMsg_FlushDeferredMessages::Read(msg, &params));
+  std::vector<GpuDeferredMessage> deferred_messages =
+      std::get<0>(std::move(params));
+  EXPECT_EQ(2u, deferred_messages.size());
+  ExpectOrderingBarrier(deferred_messages[0], proxy1->route_id(), 20);
+  EXPECT_EQ(deferred_messages[1].message.type(),
+            GpuCommandBufferMsg_DestroyTransferBuffer::ID);
 
-  // The proxy sends a sync GpuControl flush on disconnect.
+  // The proxy sends a sync GpuChannel flush on disconnect.
   EXPECT_CALL(mock_gpu_channel_, Flush()).Times(1).WillRepeatedly(Return(true));
-  EXPECT_EQ(0u, sink_.message_count());
 }
 
 TEST_F(CommandBufferProxyImplTest, CreateTransferBufferOOM) {
@@ -313,11 +271,7 @@
       std::numeric_limits<uint32_t>::max(), &id,
       TransferBufferAllocationOption::kLoseContextOnOOM);
 
-  EXPECT_CALL(mock_gpu_channel_, DestroyCommandBuffer(_))
-      .Times(1)
-      .WillOnce(Return(true));
-
-  // The proxy sends a sync GpuControl flush on disconnect.
+  // The proxy sends a sync GpuChannel flush on disconnect.
   EXPECT_CALL(mock_gpu_channel_, Flush()).Times(1).WillRepeatedly(Return(true));
 }
 
diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc
index 105c328..0a08b006 100644
--- a/gpu/ipc/client/gpu_channel_host.cc
+++ b/gpu/ipc/client/gpu_channel_host.cc
@@ -151,14 +151,16 @@
 }
 
 uint32_t GpuChannelHost::EnqueueDeferredMessage(
-    mojom::DeferredRequestParamsPtr params,
+    const IPC::Message& message,
     std::vector<SyncToken> sync_token_fences) {
   AutoLock lock(context_lock_);
 
   EnqueuePendingOrderingBarrier();
   enqueued_deferred_message_id_ = next_deferred_message_id_++;
-  deferred_messages_.push_back(mojom::DeferredRequest::New(
-      std::move(params), std::move(sync_token_fences)));
+  GpuDeferredMessage deferred_message;
+  deferred_message.message = message;
+  deferred_message.sync_token_fences = std::move(sync_token_fences);
+  deferred_messages_.push_back(std::move(deferred_message));
   return enqueued_deferred_message_id_;
 }
 
@@ -187,17 +189,15 @@
             pending_ordering_barrier_->deferred_message_id);
   enqueued_deferred_message_id_ =
       pending_ordering_barrier_->deferred_message_id;
-  auto params = mojom::AsyncFlushParams::New(
+  GpuDeferredMessage deferred_message;
+  deferred_message.message = GpuCommandBufferMsg_AsyncFlush(
+      pending_ordering_barrier_->route_id,
       pending_ordering_barrier_->put_offset,
       pending_ordering_barrier_->deferred_message_id,
       pending_ordering_barrier_->sync_token_fences);
-  deferred_messages_.push_back(mojom::DeferredRequest::New(
-      mojom::DeferredRequestParams::NewCommandBufferRequest(
-          mojom::DeferredCommandBufferRequest::New(
-              pending_ordering_barrier_->route_id,
-              mojom::DeferredCommandBufferRequestParams::NewAsyncFlush(
-                  std::move(params)))),
-      std::move(pending_ordering_barrier_->sync_token_fences)));
+  deferred_message.sync_token_fences =
+      std::move(pending_ordering_barrier_->sync_token_fences);
+  deferred_messages_.push_back(std::move(deferred_message));
   pending_ordering_barrier_.reset();
 }
 
@@ -209,7 +209,9 @@
       deferred_message_id > flushed_deferred_message_id_) {
     DCHECK_EQ(enqueued_deferred_message_id_, next_deferred_message_id_ - 1);
 
-    GetGpuChannel().FlushDeferredRequests(std::move(deferred_messages_));
+    Send(
+        new GpuChannelMsg_FlushDeferredMessages(std::move(deferred_messages_)));
+
     deferred_messages_.clear();
     flushed_deferred_message_id_ = next_deferred_message_id_ - 1;
   }
diff --git a/gpu/ipc/client/gpu_channel_host.h b/gpu/ipc/client/gpu_channel_host.h
index 760bfd49d..d11bd2136 100644
--- a/gpu/ipc/client/gpu_channel_host.h
+++ b/gpu/ipc/client/gpu_channel_host.h
@@ -37,6 +37,7 @@
 struct PendingSyncMsg;
 class ChannelMojo;
 }
+struct GpuDeferredMessage;
 
 namespace gpu {
 class ClientSharedImageInterface;
@@ -101,7 +102,7 @@
   // being released, but is handled in-order relative to other such IPCs and/or
   // OrderingBarriers. Returns a deferred message id just like OrderingBarrier.
   uint32_t EnqueueDeferredMessage(
-      mojom::DeferredRequestParamsPtr params,
+      const IPC::Message& message,
       std::vector<SyncToken> sync_token_fences = {});
 
   // Ensure that the all deferred messages prior upto |deferred_message_id| have
@@ -272,7 +273,7 @@
   // Protects |deferred_messages_|, |pending_ordering_barrier_| and
   // |*_deferred_message_id_|.
   mutable base::Lock context_lock_;
-  std::vector<mojom::DeferredRequestPtr> deferred_messages_;
+  std::vector<GpuDeferredMessage> deferred_messages_;
   base::Optional<OrderingBarrierInfo> pending_ordering_barrier_;
   uint32_t next_deferred_message_id_ = 1;
   // Highest deferred message id in |deferred_messages_|.
diff --git a/gpu/ipc/client/shared_image_interface_proxy.cc b/gpu/ipc/client/shared_image_interface_proxy.cc
index b250556..e976fc7 100644
--- a/gpu/ipc/client/shared_image_interface_proxy.cc
+++ b/gpu/ipc/client/shared_image_interface_proxy.cc
@@ -85,28 +85,25 @@
     GrSurfaceOrigin surface_origin,
     SkAlphaType alpha_type,
     uint32_t usage) {
-  auto mailbox = Mailbox::GenerateForSharedImage();
-  auto params = mojom::CreateSharedImageParams::New();
-  params->mailbox = mailbox;
-  params->format = format;
-  params->size = size;
-  params->color_space = color_space;
-  params->usage = usage;
-  params->surface_origin = surface_origin;
-  params->alpha_type = alpha_type;
+  GpuChannelMsg_CreateSharedImage_Params params;
+  params.mailbox = Mailbox::GenerateForSharedImage();
+  params.format = format;
+  params.size = size;
+  params.color_space = color_space;
+  params.usage = usage;
+  params.surface_origin = surface_origin;
+  params.alpha_type = alpha_type;
   {
     base::AutoLock lock(lock_);
-    AddMailbox(mailbox, usage);
-    params->release_id = ++next_release_id_;
+    AddMailbox(params.mailbox, usage);
+    params.release_id = ++next_release_id_;
     // Note: we enqueue the IPC under the lock to guarantee monotonicity of the
     // release ids as seen by the service.
     last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewCreateSharedImage(
-                std::move(params))));
+        GpuChannelMsg_CreateSharedImage(route_id_, params));
   }
 
-  return mailbox;
+  return params.mailbox;
 }
 
 Mailbox SharedImageInterfaceProxy::CreateSharedImage(
@@ -117,8 +114,8 @@
     SkAlphaType alpha_type,
     uint32_t usage,
     base::span<const uint8_t> pixel_data) {
-  // Pixel data's size must fit into a uint32_t to be sent in
-  // CreateSharedImageWithDataParams.
+  // Pixel data's size must fit into a uint32_t to be sent via
+  // GpuChannelMsg_CreateSharedImageWithData_Params.
   if (!base::IsValueInRangeForNumericType<uint32_t>(pixel_data.size())) {
     LOG(ERROR)
         << "CreateSharedImage: SharedImage upload data overflows uint32_t";
@@ -137,25 +134,23 @@
     return Mailbox();
   }
 
-  auto mailbox = Mailbox::GenerateForSharedImage();
-  auto params = mojom::CreateSharedImageWithDataParams::New();
-  params->mailbox = mailbox;
-  params->format = format;
-  params->size = size;
-  params->color_space = color_space;
-  params->usage = usage;
-  params->pixel_data_offset = shm_offset;
-  params->pixel_data_size = pixel_data.size();
-  params->done_with_shm = done_with_shm;
-  params->release_id = ++next_release_id_;
-  params->surface_origin = surface_origin;
-  params->alpha_type = alpha_type;
+  GpuChannelMsg_CreateSharedImageWithData_Params params;
+  params.mailbox = Mailbox::GenerateForSharedImage();
+  params.format = format;
+  params.size = size;
+  params.color_space = color_space;
+  params.usage = usage;
+  params.pixel_data_offset = shm_offset;
+  params.pixel_data_size = pixel_data.size();
+  params.done_with_shm = done_with_shm;
+  params.release_id = ++next_release_id_;
+  params.surface_origin = surface_origin;
+  params.alpha_type = alpha_type;
   last_flush_id_ = host_->EnqueueDeferredMessage(
-      mojom::DeferredRequestParams::NewSharedImageRequest(
-          mojom::DeferredSharedImageRequest::NewCreateSharedImageWithData(
-              std::move(params))));
-  AddMailbox(mailbox, usage);
-  return mailbox;
+      GpuChannelMsg_CreateSharedImageWithData(route_id_, params));
+
+  AddMailbox(params.mailbox, usage);
+  return params.mailbox;
 }
 
 Mailbox SharedImageInterfaceProxy::CreateSharedImage(
@@ -227,10 +222,8 @@
     AddMailbox(out_mailbox, usage);
     gfx::GpuFenceHandle acquire_fence_handle;
     last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewCreateSharedImageWithAhb(
-                mojom::CreateSharedImageWithAHBParams::New(
-                    out_mailbox, mailbox, usage, ++next_release_id_))),
+        GpuChannelMsg_CreateSharedImageWithAHB(route_id_, out_mailbox, mailbox,
+                                               usage, ++next_release_id_),
         std::move(dependencies));
   }
   return out_mailbox;
@@ -254,15 +247,23 @@
   {
     base::AutoLock lock(lock_);
 
+    // IPC accepts handles by const reference. However, on platforms where the
+    // handle is backed by base::ScopedFD, const is casted away and the handle
+    // is forcibly taken from you.
     gfx::GpuFenceHandle acquire_fence_handle;
-    if (acquire_fence)
+    if (acquire_fence) {
       acquire_fence_handle = acquire_fence->GetGpuFenceHandle().Clone();
+      // TODO(dcastagna): This message will be wrapped, handles can't be passed
+      // in inner messages. Use EnqueueDeferredMessage if it will be possible to
+      // have handles in inner messages in the future.
+      host_->EnsureFlush(last_flush_id_);
+      host_->Send(new GpuChannelMsg_UpdateSharedImage(
+          route_id_, mailbox, ++next_release_id_, acquire_fence_handle));
+      return;
+    }
     last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewUpdateSharedImage(
-                mojom::UpdateSharedImageParams::New(
-                    mailbox, ++next_release_id_,
-                    std::move(acquire_fence_handle)))),
+        GpuChannelMsg_UpdateSharedImage(route_id_, mailbox, ++next_release_id_,
+                                        acquire_fence_handle),
         std::move(dependencies));
   }
 }
@@ -278,8 +279,7 @@
     mailbox_to_usage_.erase(mailbox);
 
     last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewDestroySharedImage(mailbox)),
+        GpuChannelMsg_DestroySharedImage(route_id_, mailbox),
         std::move(dependencies));
   }
 }
@@ -308,10 +308,8 @@
       GenerateDependenciesFromSyncToken(std::move(sync_token), host_);
   {
     base::AutoLock lock(lock_);
-    last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewNop(0)),
-        std::move(dependencies));
+    last_flush_id_ = host_->EnqueueDeferredMessage(GpuChannelMsg_Nop(route_id_),
+                                                   std::move(dependencies));
   }
 }
 
@@ -391,30 +389,26 @@
                                            SkAlphaType alpha_type,
                                            uint32_t usage) {
 #if defined(OS_WIN)
-  const SharedImageInterface::SwapChainMailboxes mailboxes = {
-      Mailbox::GenerateForSharedImage(), Mailbox::GenerateForSharedImage()};
-  auto params = mojom::CreateSwapChainParams::New();
-  params->front_buffer_mailbox = mailboxes.front_buffer;
-  params->back_buffer_mailbox = mailboxes.back_buffer;
-  params->format = format;
-  params->size = size;
-  params->color_space = color_space;
-  params->usage = usage;
-  params->surface_origin = surface_origin;
-  params->alpha_type = alpha_type;
+  GpuChannelMsg_CreateSwapChain_Params params;
+  params.front_buffer_mailbox = Mailbox::GenerateForSharedImage();
+  params.back_buffer_mailbox = Mailbox::GenerateForSharedImage();
+  params.format = format;
+  params.size = size;
+  params.color_space = color_space;
+  params.usage = usage;
+  params.surface_origin = surface_origin;
+  params.alpha_type = alpha_type;
   {
     base::AutoLock lock(lock_);
 
-    AddMailbox(mailboxes.front_buffer, usage);
-    AddMailbox(mailboxes.back_buffer, usage);
+    AddMailbox(params.front_buffer_mailbox, usage);
+    AddMailbox(params.back_buffer_mailbox, usage);
 
-    params->release_id = ++next_release_id_;
+    params.release_id = ++next_release_id_;
     last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewCreateSwapChain(
-                std::move(params))));
+        GpuChannelMsg_CreateSwapChain(route_id_, params));
   }
-  return mailboxes;
+  return {params.front_buffer_mailbox, params.back_buffer_mailbox};
 #else
   NOTREACHED();
   return {};
@@ -430,9 +424,7 @@
     base::AutoLock lock(lock_);
     uint32_t release_id = ++next_release_id_;
     last_flush_id_ = host_->EnqueueDeferredMessage(
-        mojom::DeferredRequestParams::NewSharedImageRequest(
-            mojom::DeferredSharedImageRequest::NewPresentSwapChain(
-                mojom::PresentSwapChainParams::New(mailbox, release_id))),
+        GpuChannelMsg_PresentSwapChain(route_id_, mailbox, release_id),
         std::move(dependencies));
     host_->EnsureFlush(last_flush_id_);
   }
diff --git a/gpu/ipc/common/BUILD.gn b/gpu/ipc/common/BUILD.gn
index 87ca0a1..c610f040 100644
--- a/gpu/ipc/common/BUILD.gn
+++ b/gpu/ipc/common/BUILD.gn
@@ -225,52 +225,11 @@
 }
 
 mojom("gpu_channel_mojom") {
-  visibility = [ ":ipc_common_sources" ]
-
   sources = [ "gpu_channel.mojom" ]
 
   public_deps = [
-    "//mojo/public/mojom/base",
-    "//services/viz/public/mojom:resource_format",
-    "//skia/public/mojom",
     "//ui/gfx/geometry/mojom",
     "//ui/gfx/mojom",
-    "//ui/gl/mojom",
-    "//url/mojom:url_mojom_gurl",
-  ]
-
-  mojom_source_deps = [ ":interfaces" ]
-
-  cpp_typemaps = [
-    {
-      types = [
-        {
-          mojom = "gpu.mojom.ContextColorspace"
-          cpp = "::gpu::ColorSpace"
-        },
-        {
-          mojom = "gpu.mojom.ContextCreationAttribs"
-          cpp = "::gpu::ContextCreationAttribs"
-        },
-        {
-          mojom = "gpu.mojom.ContextType"
-          cpp = "::gpu::ContextType"
-        },
-      ]
-      traits_headers = [ "context_creation_attribs_mojom_traits.h" ]
-      traits_sources = [ "context_creation_attribs_mojom_traits.cc" ]
-      traits_public_deps = [ "//gpu/command_buffer/common:common_base_sources" ]
-    },
-    {
-      types = [
-        {
-          mojom = "gpu.mojom.SchedulingPriority"
-          cpp = "::gpu::SchedulingPriority"
-        },
-      ]
-      traits_headers = [ "scheduling_priority_mojom_traits.h" ]
-      traits_public_deps = [ "//gpu/command_buffer/common:common_base_sources" ]
-    },
   ]
 
   # Unlike some definitions in ":interfaces_mojom", these bindings are never
@@ -725,7 +684,7 @@
     "mock_gpu_channel.h",
   ]
   public_deps = [
-    ":common",
+    ":interfaces",
     "//testing/gmock",
   ]
 }
diff --git a/gpu/ipc/common/DEPS b/gpu/ipc/common/DEPS
index cdd62f7..0439348 100644
--- a/gpu/ipc/common/DEPS
+++ b/gpu/ipc/common/DEPS
@@ -14,8 +14,5 @@
   ],
   "dxgi_helpers.cc": [
     "+third_party/libyuv/include/libyuv/planar_functions.h",
-  ],
-  "surface_origin_mojom_traits.h": [
-    "+third_party/skia/include/gpu/GrTypes.h",
-  ],
+  ]
 }
diff --git a/gpu/ipc/common/context_creation_attribs_mojom_traits.cc b/gpu/ipc/common/context_creation_attribs_mojom_traits.cc
deleted file mode 100644
index d650cc6..0000000
--- a/gpu/ipc/common/context_creation_attribs_mojom_traits.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "gpu/ipc/common/context_creation_attribs_mojom_traits.h"
-
-#include "ui/gfx/geometry/mojom/geometry_mojom_traits.h"
-#include "ui/gl/mojom/gpu_preference_mojom_traits.h"
-
-namespace mojo {
-
-bool StructTraits<gpu::mojom::ContextCreationAttribsDataView,
-                  gpu::ContextCreationAttribs>::
-    Read(gpu::mojom::ContextCreationAttribsDataView data,
-         gpu::ContextCreationAttribs* out) {
-  if (!data.ReadOffscreenFramebufferSize(&out->offscreen_framebuffer_size) ||
-      !data.ReadGpuPreference(&out->gpu_preference) ||
-      !data.ReadContextType(&out->context_type) ||
-      !data.ReadColorSpace(&out->color_space)) {
-    return false;
-  }
-  out->alpha_size = data.alpha_size();
-  out->blue_size = data.blue_size();
-  out->green_size = data.green_size();
-  out->red_size = data.red_size();
-  out->depth_size = data.depth_size();
-  out->stencil_size = data.stencil_size();
-  out->samples = data.samples();
-  out->sample_buffers = data.sample_buffers();
-  out->buffer_preserved = data.buffer_preserved();
-  out->bind_generates_resource = data.bind_generates_resource();
-  out->fail_if_major_perf_caveat = data.fail_if_major_perf_caveat();
-  out->lose_context_when_out_of_memory = data.lose_context_when_out_of_memory();
-  out->should_use_native_gmb_for_backbuffer =
-      data.should_use_native_gmb_for_backbuffer();
-  out->own_offscreen_surface = data.own_offscreen_surface();
-  out->single_buffer = data.single_buffer();
-  out->enable_gles2_interface = data.enable_gles2_interface();
-  out->enable_grcontext = data.enable_grcontext();
-  out->enable_raster_interface = data.enable_raster_interface();
-  out->enable_oop_rasterization = data.enable_oop_rasterization();
-  out->enable_swap_timestamps_if_supported =
-      data.enable_swap_timestamps_if_supported();
-  return true;
-}
-
-}  // namespace mojo
diff --git a/gpu/ipc/common/context_creation_attribs_mojom_traits.h b/gpu/ipc/common/context_creation_attribs_mojom_traits.h
deleted file mode 100644
index 3f786ebf..0000000
--- a/gpu/ipc/common/context_creation_attribs_mojom_traits.h
+++ /dev/null
@@ -1,214 +0,0 @@
-// 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 GPU_IPC_COMMON_CONTEXT_CREATION_ATTRIBS_MOJOM_TRAITS_H_
-#define GPU_IPC_COMMON_CONTEXT_CREATION_ATTRIBS_MOJOM_TRAITS_H_
-
-#include "gpu/command_buffer/common/context_creation_attribs.h"
-#include "gpu/gpu_export.h"
-#include "gpu/ipc/common/gpu_channel.mojom-shared.h"
-#include "mojo/public/cpp/bindings/enum_traits.h"
-#include "mojo/public/cpp/bindings/struct_traits.h"
-
-namespace mojo {
-
-template <>
-struct GPU_EXPORT EnumTraits<gpu::mojom::ContextColorSpace, gpu::ColorSpace> {
-  static gpu::mojom::ContextColorSpace ToMojom(gpu::ColorSpace color_space) {
-    switch (color_space) {
-      case gpu::COLOR_SPACE_UNSPECIFIED:
-        return gpu::mojom::ContextColorSpace::kUnspecified;
-      case gpu::COLOR_SPACE_SRGB:
-        return gpu::mojom::ContextColorSpace::kSRGB;
-      case gpu::COLOR_SPACE_DISPLAY_P3:
-        return gpu::mojom::ContextColorSpace::kDisplayP3;
-      default:
-        NOTREACHED();
-    }
-  }
-
-  static bool FromMojom(gpu::mojom::ContextColorSpace color_space,
-                        gpu::ColorSpace* out) {
-    switch (color_space) {
-      case gpu::mojom::ContextColorSpace::kUnspecified:
-        *out = gpu::COLOR_SPACE_UNSPECIFIED;
-        return true;
-      case gpu::mojom::ContextColorSpace::kSRGB:
-        *out = gpu::COLOR_SPACE_SRGB;
-        return true;
-      case gpu::mojom::ContextColorSpace::kDisplayP3:
-        *out = gpu::COLOR_SPACE_DISPLAY_P3;
-        return true;
-      default:
-        return false;
-    }
-  }
-};
-
-template <>
-struct GPU_EXPORT EnumTraits<gpu::mojom::ContextType, gpu::ContextType> {
-  static gpu::mojom::ContextType ToMojom(gpu::ContextType type) {
-    switch (type) {
-      case gpu::CONTEXT_TYPE_WEBGL1:
-        return gpu::mojom::ContextType::kWebGL1;
-      case gpu::CONTEXT_TYPE_WEBGL2:
-        return gpu::mojom::ContextType::kWebGL2;
-      case gpu::CONTEXT_TYPE_OPENGLES2:
-        return gpu::mojom::ContextType::kOpenGLES2;
-      case gpu::CONTEXT_TYPE_OPENGLES3:
-        return gpu::mojom::ContextType::kOpenGLES3;
-      case gpu::CONTEXT_TYPE_OPENGLES31_FOR_TESTING:
-        return gpu::mojom::ContextType::kOpenGLES31ForTesting;
-      case gpu::CONTEXT_TYPE_WEBGPU:
-        return gpu::mojom::ContextType::kWebGPU;
-      default:
-        NOTREACHED();
-    }
-  }
-
-  static bool FromMojom(gpu::mojom::ContextType type, gpu::ContextType* out) {
-    switch (type) {
-      case gpu::mojom::ContextType::kWebGL1:
-        *out = gpu::CONTEXT_TYPE_WEBGL1;
-        return true;
-      case gpu::mojom::ContextType::kWebGL2:
-        *out = gpu::CONTEXT_TYPE_WEBGL2;
-        return true;
-      case gpu::mojom::ContextType::kOpenGLES2:
-        *out = gpu::CONTEXT_TYPE_OPENGLES2;
-        return true;
-      case gpu::mojom::ContextType::kOpenGLES3:
-        *out = gpu::CONTEXT_TYPE_OPENGLES3;
-        return true;
-      case gpu::mojom::ContextType::kOpenGLES31ForTesting:
-        *out = gpu::CONTEXT_TYPE_OPENGLES31_FOR_TESTING;
-        return true;
-      case gpu::mojom::ContextType::kWebGPU:
-        *out = gpu::CONTEXT_TYPE_WEBGPU;
-        return true;
-      default:
-        return false;
-    }
-  }
-};
-
-template <>
-struct GPU_EXPORT StructTraits<gpu::mojom::ContextCreationAttribsDataView,
-                               gpu::ContextCreationAttribs> {
-  static gfx::Size offscreen_framebuffer_size(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.offscreen_framebuffer_size;
-  }
-
-  static gl::GpuPreference gpu_preference(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.gpu_preference;
-  }
-
-  static int32_t alpha_size(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.alpha_size;
-  }
-
-  static int32_t blue_size(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.blue_size;
-  }
-
-  static int32_t green_size(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.green_size;
-  }
-
-  static int32_t red_size(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.red_size;
-  }
-
-  static int32_t depth_size(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.depth_size;
-  }
-
-  static int32_t stencil_size(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.stencil_size;
-  }
-
-  static int32_t samples(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.samples;
-  }
-
-  static int32_t sample_buffers(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.sample_buffers;
-  }
-
-  static bool buffer_preserved(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.buffer_preserved;
-  }
-
-  static bool bind_generates_resource(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.bind_generates_resource;
-  }
-
-  static bool fail_if_major_perf_caveat(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.fail_if_major_perf_caveat;
-  }
-
-  static bool lose_context_when_out_of_memory(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.lose_context_when_out_of_memory;
-  }
-
-  static bool should_use_native_gmb_for_backbuffer(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.should_use_native_gmb_for_backbuffer;
-  }
-
-  static bool own_offscreen_surface(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.own_offscreen_surface;
-  }
-
-  static bool single_buffer(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.single_buffer;
-  }
-
-  static bool enable_gles2_interface(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.enable_gles2_interface;
-  }
-
-  static bool enable_grcontext(const gpu::ContextCreationAttribs& attribs) {
-    return attribs.enable_grcontext;
-  }
-
-  static bool enable_raster_interface(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.enable_raster_interface;
-  }
-
-  static bool enable_oop_rasterization(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.enable_oop_rasterization;
-  }
-
-  static bool enable_swap_timestamps_if_supported(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.enable_swap_timestamps_if_supported;
-  }
-
-  static gpu::ContextType context_type(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.context_type;
-  }
-
-  static gpu::ColorSpace color_space(
-      const gpu::ContextCreationAttribs& attribs) {
-    return attribs.color_space;
-  }
-
-  static bool Read(gpu::mojom::ContextCreationAttribsDataView data,
-                   gpu::ContextCreationAttribs* out);
-};
-
-}  // namespace mojo
-
-#endif  // GPU_IPC_COMMON_CONTEXT_CREATION_ATTRIBS_MOJOM_TRAITS_H_
diff --git a/gpu/ipc/common/gpu_channel.mojom b/gpu/ipc/common/gpu_channel.mojom
index 95ef9ec..60f3cd3 100644
--- a/gpu/ipc/common/gpu_channel.mojom
+++ b/gpu/ipc/common/gpu_channel.mojom
@@ -4,84 +4,8 @@
 
 module gpu.mojom;
 
-import "gpu/ipc/common/capabilities.mojom";
-import "gpu/ipc/common/context_result.mojom";
-import "gpu/ipc/common/mailbox.mojom";
-import "gpu/ipc/common/surface_handle.mojom";
-import "gpu/ipc/common/sync_token.mojom";
-import "mojo/public/mojom/base/shared_memory.mojom";
-import "services/viz/public/mojom/compositing/resource_format.mojom";
-import "skia/public/mojom/image_info.mojom";
-import "skia/public/mojom/surface_origin.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "ui/gfx/mojom/color_space.mojom";
-import "ui/gfx/mojom/gpu_fence_handle.mojom";
-import "ui/gl/mojom/gpu_preference.mojom";
-import "url/mojom/url.mojom";
-
-// Maps to its namesake in gpu/command_buffer/common/context_creation_attribs.h.
-enum ContextType {
-  kWebGL1,
-  kWebGL2,
-  kOpenGLES2,
-  kOpenGLES3,
-  kOpenGLES31ForTesting,
-  kWebGPU,
-};
-
-// Maps to its namesake in gpu/command_buffer/common/context_creation_attribs.h.
-enum ContextColorSpace {
-  kUnspecified,
-  kSRGB,
-  kDisplayP3,
-};
-
-// Maps to its namesake in gpu/command_buffer/common/scheduling_priority.h.
-enum SchedulingPriority {
-  kHigh,
-  kNormal,
-  kLow,
-};
-
-// Maps to its namesake in gpu/command_buffer/common/context_creation_attribs.h.
-struct ContextCreationAttribs {
-  gfx.mojom.Size offscreen_framebuffer_size;
-  gl.mojom.GpuPreference gpu_preference = kLowPower;
-
-  // -1 if invalid or unspecified.
-  int32 alpha_size = -1;
-  int32 blue_size = -1;
-  int32 green_size = -1;
-  int32 red_size = -1;
-  int32 depth_size = -1;
-  int32 stencil_size = -1;
-  int32 samples = -1;
-  int32 sample_buffers = -1;
-  bool buffer_preserved = true;
-  bool bind_generates_resource = true;
-  bool fail_if_major_perf_caveat = false;
-  bool lose_context_when_out_of_memory = false;
-  bool should_use_native_gmb_for_backbuffer = false;
-  bool own_offscreen_surface = false;
-  bool single_buffer = false;
-  bool enable_gles2_interface = true;
-  bool enable_grcontext = false;
-  bool enable_raster_interface = false;
-  bool enable_oop_rasterization = false;
-  bool enable_swap_timestamps_if_supported = false;
-
-  ContextType context_type = kOpenGLES2;
-  ContextColorSpace color_space = kUnspecified;
-};
-
-struct CreateCommandBufferParams {
-  SurfaceHandle surface_handle;
-  int32 share_group_id;
-  int32 stream_id;
-  SchedulingPriority stream_priority;
-  ContextCreationAttribs attribs;
-  url.mojom.Url active_url;
-};
 
 struct ScheduleImageDecodeParams {
   array<uint8> encoded_data;
@@ -122,275 +46,9 @@
   // GPU IO thread, which is never allowed to block.
   [Sync, NoInterrupt] Flush() => ();
 
-  // Tells the GPU process to create a new command buffer. A corresponding
-  // CommandBufferStub is created.  If `params` provides a non-null
-  // SurfaceHandle, |size| is ignored and it will render directly to the native
-  // surface (only the browser process is allowed to create those). Otherwise it
-  // will create an offscreen backbuffer of dimensions `size`.
-  [Sync, NoInterrupt] CreateCommandBuffer(
-      CreateCommandBufferParams params, int32 routing_id,
-      mojo_base.mojom.UnsafeSharedMemoryRegion shared_state)
-      => (ContextResult result, Capabilities capabilties);
-
-  // The CommandBufferProxy sends this to the CommandBufferStub in its
-  // destructor, so that the stub deletes the actual CommandBufferService
-  // object that it's hosting.
-  [Sync, NoInterrupt] DestroyCommandBuffer(int32 routing_id) => ();
-
   // Schedules a hardware-accelerated image decode in the GPU process. Renderers
   // should use gpu::ImageDecodeAcceleratorProxy to schedule decode requests
   // which are processed by gpu::ImageDecodeAcceleratorStub on the service side.
   ScheduleImageDecode(ScheduleImageDecodeParams params,
                       uint64 decode_release_count);
-
-  // Sends a batch of DeferredRequests to be executed by the service.
-  FlushDeferredRequests(array<DeferredRequest> requests);
-
-  // Creates a StreamTexture associated with the given `stream_id`.
-  [Sync] CreateStreamTexture(int32 stream_id) => (bool success);
-};
-
-// DeferredRequests are batched locally by clients and sent to the service only
-// when flushing the channel via GpuChannelHost's EnsureFlush or VerifyFlush.
-struct DeferredRequest {
-  // Parameters for the specific type of request.
-  DeferredRequestParams params;
-
-  // A list of SyncTokens which must be released before the request can be
-  // processed.
-  array<SyncToken> sync_token_fences;
-};
-
-// Details of a batched request.
-union DeferredRequestParams {
-  // This request targets a specific command buffer instance.
-  DeferredCommandBufferRequest command_buffer_request;
-
-  // This request pertains to shared image management.
-  DeferredSharedImageRequest shared_image_request;
-
-  // Sent to indicate the client wants to destroy the StreamTexture identified
-  // by this ID.
-  [EnableIf=is_android]
-  int32 destroy_stream_texture;
-};
-
-// Details of a batched request targeting a specific command buffer.
-struct DeferredCommandBufferRequest {
-  // Identifies the command buffer targeted by this request.
-  int32 routing_id;
-
-  // The specific request parameters.
-  DeferredCommandBufferRequestParams params;
-};
-
-// Details of a batched request targeting a specific command buffer.
-union DeferredCommandBufferRequestParams {
-  // See AsyncFlushParams.
-  AsyncFlushParams async_flush;
-
-  // Destroys a transfer buffer identified by this ID.
-  int32 destroy_transfer_buffer;
-
-  // Takes the front buffer into a mailbox. Allows another context to draw the
-  // output of this context.
-  // NOTE: This exists only to support PNaCl.
-  Mailbox take_front_buffer;
-
-  // See ReturnFrontBufferParams.
-  ReturnFrontBufferParams return_front_buffer;
-};
-
-// Details of a batched request pertaining to shared image management.
-union DeferredSharedImageRequest {
-  // Sent by clients to record a point in the sequence of pending deferred
-  // messages. Actual value is ignored.
-  uint8 nop;
-
-  // See CreateSharedImageParams.
-  CreateSharedImageParams create_shared_image;
-
-  // See CreateSharedImageWithDataParams.
-  CreateSharedImageWithDataParams create_shared_image_with_data;
-
-  // See CreateSharedImageWithAHBParams.
-  [EnableIf=is_android]
-  CreateSharedImageWithAHBParams create_shared_image_with_ahb;
-
-  // See UpdateSharedImageParams.
-  UpdateSharedImageParams update_shared_image;
-
-  // Destroys the shared image identified by this Mailbox.
-  Mailbox destroy_shared_image;
-
-  // See CreateSwapChainParams.
-  [EnableIf=is_win]
-  CreateSwapChainParams create_swap_chain;
-
-  // See PresentSwapChainParams.
-  [EnableIf=is_win]
-  PresentSwapChainParams present_swap_chain;
-};
-
-// Asynchronously synchronize the put and get offsets of both processes.
-// Caller passes its current put offset. Current state (including get offset)
-// is returned in shared memory.
-// TODO(sunnyps): This is an internal implementation detail of the gpu service
-// and is not sent by the client. Remove this once the non-scheduler code path
-// is removed.
-struct AsyncFlushParams {
-  int32 put_offset;
-  uint32 flush_id;
-  array<SyncToken> sync_token_fences;
-};
-
-// Returns a front buffer taken via TakeFrontBufferParams in a previous
-// DeferredRequest. This allows it to be reused.
-// NOTE: This exists only to support PNaCl.
-struct ReturnFrontBufferParams {
-  // The mailbox which last took the front buffer in question.
-  Mailbox mailbox;
-
-  // Indicates that the resource was lost and should be destroyed.
-  bool is_lost;
-};
-
-// Creates a new shared image.
-struct CreateSharedImageParams {
-  // The mailbox used to identify the shared image.
-  Mailbox mailbox;
-
-  // The pixel format of the image.
-  viz.mojom.ResourceFormat format;
-
-  // Size in pixels.
-  gfx.mojom.Size size;
-
-  // Color space.
-  gfx.mojom.ColorSpace color_space;
-
-  // Usage flags corresponding to values defined in
-  // gpu/command_buffer/common/shared_image_usage.h.
-  uint32 usage;
-
-  // The fence to release once the image is created.
-  uint32 release_id;
-
-  // Which corner is considered the origin of the new image.
-  skia.mojom.SurfaceOrigin surface_origin;
-
-  // Indicates how the alpha component of each pixel is interpreted.
-  skia.mojom.AlphaType alpha_type;
-};
-
-// Creates a new shared image and populates it with pixel data from a shared
-// memory region previously configured by the client.
-struct CreateSharedImageWithDataParams {
-  // The mailbox used to identify the shared image.
-  Mailbox mailbox;
-
-  // The pixel format of the image.
-  viz.mojom.ResourceFormat format;
-
-  // Size in pixels.
-  gfx.mojom.Size size;
-
-  // Color space.
-  gfx.mojom.ColorSpace color_space;
-
-  // Usage flags corresponding to values defined in
-  // gpu/command_buffer/common/shared_image_usage.h.
-  uint32 usage;
-
-  // The fence to release once the image is created.
-  uint32 release_id;
-
-  // Offset from which to copy data out of the current image upload shared
-  // memory region.
-  uint32 pixel_data_offset;
-
-  // Size of data to copy out of the current image upload shared memory region.
-  uint32 pixel_data_size;
-
-  // If `true`, this is the last usage of the current image upload shared memory
-  // region, so the region can be discarded after servicing this request.
-  bool done_with_shm;
-
-  // Which corner is considered the origin of the new image.
-  skia.mojom.SurfaceOrigin surface_origin;
-
-  // Indicates how the alpha component of each pixel is interpreted.
-  skia.mojom.AlphaType alpha_type;
-};
-
-// Creates a new shared image from an existing one backed by an AHardwareBuffer.
-[EnableIf=is_android]
-struct CreateSharedImageWithAHBParams {
-  // Mailbox to identify the new image.
-  Mailbox out_mailbox;
-
-  // Mailbox identifying the existing shared image whose AHardwareBuffer will
-  // back the new image.
-  Mailbox in_mailbox;
-
-  // Usage flags corresponding to values defined in
-  // gpu/command_buffer/common/shared_image_usage.h.
-  uint32 usage;
-
-  // The fence to release once the image is created.
-  uint32 release_id;
-};
-
-// Updates the contents of a shared image from whatever is backing it, if
-// applicable.
-struct UpdateSharedImageParams {
-  // The mailbox identifying the shared image to update.
-  Mailbox mailbox;
-
-  // The fence to release once the image has been updated.
-  uint32 release_id;
-
-  // An optional GpuFenceHandle for the server to wait on before updating the
-  // image from whatever backs it.
-  gfx.mojom.GpuFenceHandle? in_fence_handle;
-};
-
-[EnableIf=is_win]
-struct CreateSwapChainParams {
-  // Mailbox identifying the front buffer of the swap chain.
-  Mailbox front_buffer_mailbox;
-
-  // Mailbox identifying the back buffer of the swap chain.
-  Mailbox back_buffer_mailbox;
-
-  // Pixel format of the front and back buffers.
-  viz.mojom.ResourceFormat format;
-
-  // Size of the buffers.
-  gfx.mojom.Size size;
-
-  // Color space of the buffers.
-  gfx.mojom.ColorSpace color_space;
-
-  // Usage flags corresponding to values defined in
-  // gpu/command_buffer/common/shared_image_usage.h.
-  uint32 usage;
-
-  // The fence to release once the swap chain is created.
-  uint32 release_id;
-
-  // Which corner is considered the origin of the swap chain's buffers.
-  skia.mojom.SurfaceOrigin surface_origin;
-
-  // Indicates how the alpha component of each pixel is interpreted.
-  skia.mojom.AlphaType alpha_type;
-};
-
-[EnableIf=is_win]
-struct PresentSwapChainParams {
-  // Mailbox identifying the swap chain to present.
-  Mailbox mailbox;
-
-  // The fence to release once the swap chain is presented.
-  uint32 release_id;
 };
diff --git a/gpu/ipc/common/gpu_messages.h b/gpu/ipc/common/gpu_messages.h
index f03e137..5c4f8dff 100644
--- a/gpu/ipc/common/gpu_messages.h
+++ b/gpu/ipc/common/gpu_messages.h
@@ -63,6 +63,15 @@
   IPC_STRUCT_MEMBER(std::string, message)
 IPC_STRUCT_END()
 
+IPC_STRUCT_BEGIN(GPUCreateCommandBufferConfig)
+  IPC_STRUCT_MEMBER(gpu::SurfaceHandle, surface_handle)
+  IPC_STRUCT_MEMBER(int32_t, share_group_id)
+  IPC_STRUCT_MEMBER(int32_t, stream_id)
+  IPC_STRUCT_MEMBER(gpu::SchedulingPriority, stream_priority)
+  IPC_STRUCT_MEMBER(gpu::ContextCreationAttribs, attribs)
+  IPC_STRUCT_MEMBER(GURL, active_url)
+IPC_STRUCT_END()
+
 IPC_STRUCT_BEGIN(GpuCommandBufferMsg_CreateImage_Params)
   IPC_STRUCT_MEMBER(int32_t, id)
   IPC_STRUCT_MEMBER(gfx::GpuMemoryBufferHandle, gpu_memory_buffer)
@@ -74,6 +83,31 @@
 
 IPC_ENUM_TRAITS_MAX_VALUE(GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin)
 
+IPC_STRUCT_BEGIN(GpuChannelMsg_CreateSharedImage_Params)
+  IPC_STRUCT_MEMBER(gpu::Mailbox, mailbox)
+  IPC_STRUCT_MEMBER(viz::ResourceFormat, format)
+  IPC_STRUCT_MEMBER(gfx::Size, size)
+  IPC_STRUCT_MEMBER(gfx::ColorSpace, color_space)
+  IPC_STRUCT_MEMBER(uint32_t, usage)
+  IPC_STRUCT_MEMBER(uint32_t, release_id)
+  IPC_STRUCT_MEMBER(GrSurfaceOrigin, surface_origin)
+  IPC_STRUCT_MEMBER(SkAlphaType, alpha_type)
+IPC_STRUCT_END()
+
+IPC_STRUCT_BEGIN(GpuChannelMsg_CreateSharedImageWithData_Params)
+  IPC_STRUCT_MEMBER(gpu::Mailbox, mailbox)
+  IPC_STRUCT_MEMBER(viz::ResourceFormat, format)
+  IPC_STRUCT_MEMBER(gfx::Size, size)
+  IPC_STRUCT_MEMBER(gfx::ColorSpace, color_space)
+  IPC_STRUCT_MEMBER(uint32_t, usage)
+  IPC_STRUCT_MEMBER(uint32_t, release_id)
+  IPC_STRUCT_MEMBER(uint32_t, pixel_data_offset)
+  IPC_STRUCT_MEMBER(uint32_t, pixel_data_size)
+  IPC_STRUCT_MEMBER(bool, done_with_shm)
+  IPC_STRUCT_MEMBER(GrSurfaceOrigin, surface_origin)
+  IPC_STRUCT_MEMBER(SkAlphaType, alpha_type)
+IPC_STRUCT_END()
+
 IPC_STRUCT_BEGIN(GpuChannelMsg_CreateGMBSharedImage_Params)
   IPC_STRUCT_MEMBER(gpu::Mailbox, mailbox)
   IPC_STRUCT_MEMBER(gfx::GpuMemoryBufferHandle, handle)
@@ -87,13 +121,84 @@
   IPC_STRUCT_MEMBER(SkAlphaType, alpha_type)
 IPC_STRUCT_END()
 
+#if defined(OS_WIN)
+IPC_STRUCT_BEGIN(GpuChannelMsg_CreateSwapChain_Params)
+  IPC_STRUCT_MEMBER(gpu::Mailbox, front_buffer_mailbox)
+  IPC_STRUCT_MEMBER(gpu::Mailbox, back_buffer_mailbox)
+  IPC_STRUCT_MEMBER(viz::ResourceFormat, format)
+  IPC_STRUCT_MEMBER(gfx::Size, size)
+  IPC_STRUCT_MEMBER(gfx::ColorSpace, color_space)
+  IPC_STRUCT_MEMBER(uint32_t, usage)
+  IPC_STRUCT_MEMBER(uint32_t, release_id)
+  IPC_STRUCT_MEMBER(GrSurfaceOrigin, surface_origin)
+  IPC_STRUCT_MEMBER(SkAlphaType, alpha_type)
+IPC_STRUCT_END()
+#endif  // OS_WIN
+
+IPC_STRUCT_BEGIN(GpuDeferredMessage)
+  IPC_STRUCT_MEMBER(IPC::Message, message)
+  IPC_STRUCT_MEMBER(std::vector<gpu::SyncToken>, sync_token_fences)
+IPC_STRUCT_END()
+
 //------------------------------------------------------------------------------
 // GPU Channel Messages
 // These are messages from a renderer process to the GPU process.
 
+// Tells the GPU process to create a new command buffer. A corresponding
+// CommandBufferStub is created.  If |surface_handle| is non-null, |size|
+// is ignored, and it will render directly to the native surface (only the
+// browser process is allowed to create those). Otherwise it will create an
+// offscreen backbuffer of dimensions |size|.
+IPC_SYNC_MESSAGE_CONTROL3_2(GpuChannelMsg_CreateCommandBuffer,
+                            GPUCreateCommandBufferConfig /* init_params */,
+                            int32_t /* route_id */,
+                            base::UnsafeSharedMemoryRegion /* shared_state */,
+                            gpu::ContextResult,
+                            gpu::Capabilities /* capabilities */)
+
+// The CommandBufferProxy sends this to the CommandBufferStub in its
+// destructor, so that the stub deletes the actual CommandBufferService
+// object that it's hosting.
+IPC_SYNC_MESSAGE_CONTROL1_0(GpuChannelMsg_DestroyCommandBuffer,
+                            int32_t /* instance_id */)
+
+IPC_MESSAGE_CONTROL1(GpuChannelMsg_FlushDeferredMessages,
+                     std::vector<GpuDeferredMessage> /* deferred_messages */)
+
+IPC_MESSAGE_ROUTED1(GpuChannelMsg_CreateSharedImage,
+                    GpuChannelMsg_CreateSharedImage_Params /* params */)
+IPC_MESSAGE_ROUTED1(GpuChannelMsg_CreateSharedImageWithData,
+                    GpuChannelMsg_CreateSharedImageWithData_Params /* params */)
 IPC_MESSAGE_ROUTED1(GpuChannelMsg_CreateGMBSharedImage,
                     GpuChannelMsg_CreateGMBSharedImage_Params /* params */)
 
+// The following IPC message, that can be used by the browser or renderers,
+// updates the SharedImage referenced by |id| after its contents are modified
+// (e.g: its GpuMemoryBuffer is modified via the CPU or through external
+// devices).
+// The sync token in the shared image sequence at position |release_id| will be
+// released once this command has been executed service side.
+// |in_fence_handle|, if not null, represents a fence that will be waited on
+// before reading the contents represented by the shared image.
+IPC_MESSAGE_ROUTED3(GpuChannelMsg_UpdateSharedImage,
+                    gpu::Mailbox /* id */,
+                    uint32_t /* release_id */,
+                    gfx::GpuFenceHandle /* in_fence_handle */)
+#if defined(OS_ANDROID)
+IPC_MESSAGE_ROUTED4(GpuChannelMsg_CreateSharedImageWithAHB,
+                    gpu::Mailbox /* out id */,
+                    gpu::Mailbox /* in id */,
+                    uint32_t /* usage */,
+                    uint32_t /* release_id */)
+#endif
+IPC_MESSAGE_ROUTED1(GpuChannelMsg_DestroySharedImage, gpu::Mailbox /* id */)
+#if defined(OS_WIN)
+IPC_MESSAGE_ROUTED1(GpuChannelMsg_CreateSwapChain,
+                    GpuChannelMsg_CreateSwapChain_Params /* params */)
+IPC_MESSAGE_ROUTED2(GpuChannelMsg_PresentSwapChain,
+                    gpu::Mailbox /* mailbox */,
+                    uint32_t /* release_id */)
+#endif  // OS_WIN
 #if defined(OS_FUCHSIA)
 IPC_MESSAGE_ROUTED5(GpuChannelMsg_RegisterSysmemBufferCollection,
                     gfx::SysmemBufferCollectionId /* id */,
@@ -107,6 +212,15 @@
 IPC_MESSAGE_ROUTED1(GpuChannelMsg_RegisterSharedImageUploadBuffer,
                     base::ReadOnlySharedMemoryRegion /* shm */)
 
+// Simple NOP message which can be used as fence to ensure all previous sent
+// messages have been received.
+IPC_SYNC_MESSAGE_ROUTED0_0(GpuChannelMsg_Nop)
+
+// Creates a StreamTexture attached to the provided |stream_id|.
+IPC_SYNC_MESSAGE_CONTROL1_1(GpuChannelMsg_CreateStreamTexture,
+                            int32_t, /* stream_id */
+                            bool /* succeeded */)
+
 #if defined(OS_ANDROID)
 //------------------------------------------------------------------------------
 // Tells the StreamTexture to send its SurfaceTexture to the browser process,
@@ -132,6 +246,9 @@
 IPC_MESSAGE_ROUTED1(GpuStreamTextureMsg_UpdateRotatedVisibleSize,
                     gfx::Size /* rotated_visible_size */)
 
+// Destroys the StreamTexture attached to the provided |stream_id|.
+IPC_MESSAGE_ROUTED0(GpuStreamTextureMsg_Destroy)
+
 #endif
 
 //------------------------------------------------------------------------------
@@ -142,6 +259,17 @@
 // Sets the shared memory buffer used for commands.
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_SetGetBuffer, int32_t /* shm_id */)
 
+// Takes the front buffer into a mailbox. This allows another context to draw
+// the output of this context.
+IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_TakeFrontBuffer,
+                    gpu::Mailbox /* mailbox */)
+
+// Returns a front buffer taken with GpuCommandBufferMsg_TakeFrontBuffer. This
+// allows it to be reused.
+IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_ReturnFrontBuffer,
+                    gpu::Mailbox /* mailbox */,
+                    bool /* is_lost */)
+
 // Wait until the token is in a specific range, inclusive.
 IPC_SYNC_MESSAGE_ROUTED2_1(GpuCommandBufferMsg_WaitForTokenInRange,
                            int32_t /* start */,
@@ -155,6 +283,17 @@
                            int32_t /* end */,
                            gpu::CommandBuffer::State /* state */)
 
+// Asynchronously synchronize the put and get offsets of both processes.
+// Caller passes its current put offset. Current state (including get offset)
+// is returned in shared memory.
+// TODO(sunnyps): This is an internal implementation detail of the gpu service
+// and is not sent by the client. Remove this once the non-scheduler code path
+// is removed.
+IPC_MESSAGE_ROUTED3(GpuCommandBufferMsg_AsyncFlush,
+                    int32_t /* put_offset */,
+                    uint32_t /* flush_id */,
+                    std::vector<gpu::SyncToken> /* sync_token_fences */)
+
 // Sent by the GPU process to display messages in the console.
 IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_ConsoleMsg,
                     GPUCommandBufferConsoleMessage /* msg */)
@@ -169,6 +308,9 @@
                     int32_t /* id */,
                     base::UnsafeSharedMemoryRegion /* transfer_buffer */)
 
+// Destroy a previously created transfer buffer.
+IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_DestroyTransferBuffer, int32_t /* id */)
+
 // Tells the proxy that there was an error and the command buffer had to be
 // destroyed for some reason.
 IPC_MESSAGE_ROUTED2(GpuCommandBufferMsg_Destroyed,
diff --git a/gpu/ipc/common/gpu_param_traits_macros.h b/gpu/ipc/common/gpu_param_traits_macros.h
index 4514e1a..9fd93a4b 100644
--- a/gpu/ipc/common/gpu_param_traits_macros.h
+++ b/gpu/ipc/common/gpu_param_traits_macros.h
@@ -21,6 +21,8 @@
 
 IPC_ENUM_TRAITS_MAX_VALUE(gpu::SchedulingPriority,
                           gpu::SchedulingPriority::kLast)
+IPC_ENUM_TRAITS_MAX_VALUE(gpu::ContextResult,
+                          gpu::ContextResult::kLastContextResult)
 
 IPC_STRUCT_TRAITS_BEGIN(gpu::SwapBuffersCompleteParams)
   IPC_STRUCT_TRAITS_MEMBER(ca_layer_params)
diff --git a/gpu/ipc/common/mock_gpu_channel.h b/gpu/ipc/common/mock_gpu_channel.h
index aa41104..f83be0a 100644
--- a/gpu/ipc/common/mock_gpu_channel.h
+++ b/gpu/ipc/common/mock_gpu_channel.h
@@ -20,26 +20,8 @@
   MOCK_METHOD0(TerminateForTesting, void());
   MOCK_METHOD0(Flush, bool());
   MOCK_METHOD1(Flush, void(FlushCallback));
-  MOCK_METHOD4(CreateCommandBuffer,
-               void(mojom::CreateCommandBufferParamsPtr,
-                    int32_t,
-                    base::UnsafeSharedMemoryRegion,
-                    CreateCommandBufferCallback));
-  MOCK_METHOD5(CreateCommandBuffer,
-               bool(mojom::CreateCommandBufferParamsPtr,
-                    int32_t,
-                    base::UnsafeSharedMemoryRegion,
-                    ContextResult*,
-                    Capabilities*));
-  MOCK_METHOD1(DestroyCommandBuffer, bool(int32_t));
-  MOCK_METHOD2(DestroyCommandBuffer,
-               void(int32_t, DestroyCommandBufferCallback));
   MOCK_METHOD2(ScheduleImageDecode,
                void(mojom::ScheduleImageDecodeParamsPtr, uint64_t));
-  MOCK_METHOD1(FlushDeferredRequests,
-               void(std::vector<mojom::DeferredRequestPtr>));
-  MOCK_METHOD2(CreateStreamTexture, bool(int32_t, bool*));
-  MOCK_METHOD2(CreateStreamTexture, void(int32_t, CreateStreamTextureCallback));
 };
 
 }  // namespace gpu
diff --git a/gpu/ipc/common/scheduling_priority_mojom_traits.h b/gpu/ipc/common/scheduling_priority_mojom_traits.h
deleted file mode 100644
index 85cfa10..0000000
--- a/gpu/ipc/common/scheduling_priority_mojom_traits.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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 GPU_IPC_COMMON_SCHEDULING_PRIORITY_MOJOM_TRAITS_H_
-#define GPU_IPC_COMMON_SCHEDULING_PRIORITY_MOJOM_TRAITS_H_
-
-#include "gpu/command_buffer/common/scheduling_priority.h"
-#include "gpu/gpu_export.h"
-#include "gpu/ipc/common/gpu_channel.mojom-shared.h"
-#include "mojo/public/cpp/bindings/enum_traits.h"
-
-namespace mojo {
-
-template <>
-struct GPU_EXPORT
-    EnumTraits<gpu::mojom::SchedulingPriority, gpu::SchedulingPriority> {
-  static gpu::mojom::SchedulingPriority ToMojom(
-      gpu::SchedulingPriority priority) {
-    switch (priority) {
-      case gpu::SchedulingPriority::kHigh:
-        return gpu::mojom::SchedulingPriority::kHigh;
-      case gpu::SchedulingPriority::kNormal:
-        return gpu::mojom::SchedulingPriority::kNormal;
-      case gpu::SchedulingPriority::kLow:
-        return gpu::mojom::SchedulingPriority::kNormal;
-      default:
-        NOTREACHED();
-    }
-  }
-
-  static bool FromMojom(gpu::mojom::SchedulingPriority priority,
-                        gpu::SchedulingPriority* out_priority) {
-    switch (priority) {
-      case gpu::mojom::SchedulingPriority::kHigh:
-        *out_priority = gpu::SchedulingPriority::kHigh;
-        return true;
-      case gpu::mojom::SchedulingPriority::kNormal:
-        *out_priority = gpu::SchedulingPriority::kNormal;
-        return true;
-      case gpu::mojom::SchedulingPriority::kLow:
-        *out_priority = gpu::SchedulingPriority::kLow;
-        return true;
-      default:
-        return false;
-    }
-  }
-};
-
-}  // namespace mojo
-
-#endif  // GPU_IPC_COMMON_SCHEDULING_PRIORITY_MOJOM_TRAITS_H_
diff --git a/gpu/ipc/service/command_buffer_stub.cc b/gpu/ipc/service/command_buffer_stub.cc
index 9bc0ab7a..8448fcb1 100644
--- a/gpu/ipc/service/command_buffer_stub.cc
+++ b/gpu/ipc/service/command_buffer_stub.cc
@@ -100,7 +100,7 @@
 
 CommandBufferStub::CommandBufferStub(
     GpuChannel* channel,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     CommandBufferId command_buffer_id,
     SequenceId sequence_id,
     int32_t stream_id,
@@ -123,60 +123,6 @@
   Destroy();
 }
 
-void CommandBufferStub::ExecuteDeferredRequest(
-    mojom::DeferredCommandBufferRequestParams& params) {
-  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GPUTask",
-               "data", DevToolsChannelData::CreateForChannel(channel()));
-  UpdateActiveUrl();
-  // TODO(sunnyps): Should this use ScopedCrashKey instead?
-  crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1"
-                                                                        : "0");
-
-  // Ensure the appropriate GL context is current before handling any IPC
-  // messages directed at the command buffer. This ensures that the message
-  // handler can assume that the context is current (not necessary for
-  // RetireSyncPoint or WaitSyncPoint).
-  bool have_context = false;
-  base::Optional<gles2::ProgramCache::ScopedCacheUse> cache_use;
-  if (!params.is_destroy_transfer_buffer()) {
-    if (!MakeCurrent())
-      return;
-    cache_use.emplace(CreateCacheUse());
-    have_context = true;
-  }
-
-  switch (params.which()) {
-    case mojom::DeferredCommandBufferRequestParams::Tag::kAsyncFlush: {
-      auto& flush = *params.get_async_flush();
-      OnAsyncFlush(flush.put_offset, flush.flush_id, flush.sync_token_fences);
-      break;
-    }
-
-    case mojom::DeferredCommandBufferRequestParams::Tag::kDestroyTransferBuffer:
-      OnDestroyTransferBuffer(params.get_destroy_transfer_buffer());
-      break;
-
-    case mojom::DeferredCommandBufferRequestParams::Tag::kTakeFrontBuffer:
-      OnTakeFrontBuffer(params.get_take_front_buffer());
-      break;
-
-    case mojom::DeferredCommandBufferRequestParams::Tag::kReturnFrontBuffer: {
-      OnReturnFrontBuffer(params.get_return_front_buffer()->mailbox,
-                          params.get_return_front_buffer()->is_lost);
-      break;
-    }
-  }
-
-  CheckCompleteWaits();
-
-  if (have_context) {
-    if (decoder_context_)
-      decoder_context_->ProcessPendingQueries(false);
-    ScheduleDelayedWork(
-        base::TimeDelta::FromMilliseconds(kHandleMoreWorkPeriodMs));
-  }
-}
-
 bool CommandBufferStub::OnMessageReceived(const IPC::Message& message) {
   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GPUTask",
                "data", DevToolsChannelData::CreateForChannel(channel()));
@@ -184,6 +130,26 @@
   // TODO(sunnyps): Should this use ScopedCrashKey instead?
   crash_keys::gpu_gl_context_is_virtual.Set(use_virtualized_gl_context_ ? "1"
                                                                         : "0");
+  bool have_context = false;
+  base::Optional<gles2::ProgramCache::ScopedCacheUse> cache_use;
+  // Ensure the appropriate GL context is current before handling any IPC
+  // messages directed at the command buffer. This ensures that the message
+  // handler can assume that the context is current (not necessary for
+  // RetireSyncPoint or WaitSyncPoint).
+  if (decoder_context_.get() &&
+      message.type() != GpuCommandBufferMsg_SetGetBuffer::ID &&
+      message.type() != GpuCommandBufferMsg_WaitForTokenInRange::ID &&
+      message.type() != GpuCommandBufferMsg_WaitForGetOffsetInRange::ID &&
+      message.type() != GpuCommandBufferMsg_RegisterTransferBuffer::ID &&
+      message.type() != GpuCommandBufferMsg_DestroyTransferBuffer::ID &&
+      message.type() != GpuCommandBufferMsg_SignalSyncToken::ID &&
+      message.type() != GpuCommandBufferMsg_SignalQuery::ID) {
+    if (!MakeCurrent())
+      return false;
+    cache_use.emplace(CreateCacheUse());
+    have_context = true;
+  }
+
   bool handled = HandleMessage(message);
   if (!handled) {
     handled = true;
@@ -197,8 +163,11 @@
       IPC_MESSAGE_HANDLER_DELAY_REPLY(
           GpuCommandBufferMsg_WaitForGetOffsetInRange,
           OnWaitForGetOffsetInRange);
+      IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_AsyncFlush, OnAsyncFlush);
       IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_RegisterTransferBuffer,
                           OnRegisterTransferBuffer);
+      IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyTransferBuffer,
+                          OnDestroyTransferBuffer);
       IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncToken,
                           OnSignalSyncToken)
       IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalQuery, OnSignalQuery)
@@ -208,6 +177,14 @@
 
   CheckCompleteWaits();
 
+  // Ensure that any delayed work that was created will be handled.
+  if (have_context) {
+    if (decoder_context_)
+      decoder_context_->ProcessPendingQueries(false);
+    ScheduleDelayedWork(
+        base::TimeDelta::FromMilliseconds(kHandleMoreWorkPeriodMs));
+  }
+
   return handled;
 }
 
diff --git a/gpu/ipc/service/command_buffer_stub.h b/gpu/ipc/service/command_buffer_stub.h
index 49bfb00..ce899fa 100644
--- a/gpu/ipc/service/command_buffer_stub.h
+++ b/gpu/ipc/service/command_buffer_stub.h
@@ -21,13 +21,11 @@
 #include "gpu/command_buffer/common/command_buffer_id.h"
 #include "gpu/command_buffer/common/constants.h"
 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
-#include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/command_buffer/service/decoder_client.h"
 #include "gpu/command_buffer/service/program_cache.h"
 #include "gpu/command_buffer/service/sequence_id.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "gpu/ipc/service/context_url.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
@@ -41,6 +39,8 @@
 #include "ui/gl/gpu_preference.h"
 #include "url/gurl.h"
 
+struct GPUCreateCommandBufferConfig;
+
 namespace gpu {
 class DecoderContext;
 class MemoryTracker;
@@ -68,7 +68,7 @@
   };
 
   CommandBufferStub(GpuChannel* channel,
-                    const mojom::CreateCommandBufferParams& init_params,
+                    const GPUCreateCommandBufferConfig& init_params,
                     CommandBufferId command_buffer_id,
                     SequenceId sequence_id,
                     int32_t stream_id,
@@ -81,16 +81,12 @@
   // the gpu::Capabilities.
   virtual gpu::ContextResult Initialize(
       CommandBufferStub* share_group,
-      const mojom::CreateCommandBufferParams& params,
+      const GPUCreateCommandBufferConfig& init_params,
       base::UnsafeSharedMemoryRegion shared_state_shm) = 0;
 
   MemoryTracker* GetMemoryTracker() const;
   virtual MemoryTracker* GetContextGroupMemoryTracker() const = 0;
 
-  // Executes a DeferredRequest routed to this command buffer by a GpuChannel.
-  void ExecuteDeferredRequest(
-      mojom::DeferredCommandBufferRequestParams& params);
-
   // IPC::Listener implementation:
   bool OnMessageReceived(const IPC::Message& message) override;
 
@@ -149,8 +145,6 @@
 
  protected:
   virtual bool HandleMessage(const IPC::Message& message) = 0;
-  virtual void OnTakeFrontBuffer(const Mailbox& mailbox) {}
-  virtual void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) {}
 
   std::unique_ptr<MemoryTracker> CreateMemoryTracker() const;
 
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index d01073bf..b10b139 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -62,7 +62,7 @@
 
 GLES2CommandBufferStub::GLES2CommandBufferStub(
     GpuChannel* channel,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     CommandBufferId command_buffer_id,
     SequenceId sequence_id,
     int32_t stream_id,
@@ -79,7 +79,7 @@
 
 gpu::ContextResult GLES2CommandBufferStub::Initialize(
     CommandBufferStub* share_command_buffer_stub,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     base::UnsafeSharedMemoryRegion shared_state_shm) {
   TRACE_EVENT0("gpu", "GLES2CommandBufferStub::Initialize");
   UpdateActiveUrl();
@@ -442,6 +442,9 @@
 bool GLES2CommandBufferStub::HandleMessage(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(GLES2CommandBufferStub, message)
+    IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_TakeFrontBuffer, OnTakeFrontBuffer);
+    IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ReturnFrontBuffer,
+                        OnReturnFrontBuffer);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateImage, OnCreateImage);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_DestroyImage, OnDestroyImage);
     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_CreateGpuFenceFromHandle,
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.h b/gpu/ipc/service/gles2_command_buffer_stub.h
index 38d9d9552..5eca68f 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.h
+++ b/gpu/ipc/service/gles2_command_buffer_stub.h
@@ -22,7 +22,7 @@
       public base::SupportsWeakPtr<GLES2CommandBufferStub> {
  public:
   GLES2CommandBufferStub(GpuChannel* channel,
-                         const mojom::CreateCommandBufferParams& init_params,
+                         const GPUCreateCommandBufferConfig& init_params,
                          CommandBufferId command_buffer_id,
                          SequenceId sequence_id,
                          int32_t stream_id,
@@ -35,7 +35,7 @@
   // the gpu::Capabilities.
   gpu::ContextResult Initialize(
       CommandBufferStub* share_group,
-      const mojom::CreateCommandBufferParams& init_params,
+      const GPUCreateCommandBufferConfig& init_params,
       base::UnsafeSharedMemoryRegion shared_state_shm) override;
   MemoryTracker* GetContextGroupMemoryTracker() const override;
 
@@ -57,8 +57,8 @@
 
  private:
   bool HandleMessage(const IPC::Message& message) override;
-  void OnTakeFrontBuffer(const Mailbox& mailbox) override;
-  void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost) override;
+  void OnTakeFrontBuffer(const Mailbox& mailbox);
+  void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost);
   void OnCreateGpuFenceFromHandle(uint32_t gpu_fence_id,
                                   gfx::GpuFenceHandle handle);
   void OnGetGpuFenceHandle(uint32_t gpu_fence_id);
diff --git a/gpu/ipc/service/gpu_channel.cc b/gpu/ipc/service/gpu_channel.cc
index 7de04ca..7f5a02e 100644
--- a/gpu/ipc/service/gpu_channel.cc
+++ b/gpu/ipc/service/gpu_channel.cc
@@ -17,7 +17,6 @@
 
 #include "base/atomicops.h"
 #include "base/bind.h"
-#include "base/bind_post_task.h"
 #include "base/command_line.h"
 #include "base/containers/circular_deque.h"
 #include "base/location.h"
@@ -81,17 +80,6 @@
   DISALLOW_COPY_AND_ASSIGN(GpuChannelMessage);
 };
 
-namespace {
-
-bool TryCreateStreamTexture(base::WeakPtr<GpuChannel> channel,
-                            int32_t stream_id) {
-  if (!channel)
-    return false;
-  return channel->CreateStreamTexture(stream_id);
-}
-
-}  // namespace
-
 // This filter does the following:
 // - handles the Nop message used for verifying sync tokens on the IO thread
 // - forwards messages to child message filters
@@ -140,24 +128,16 @@
 
   SequenceId GetSequenceId(int32_t route_id) const;
 
+  bool HandleFlushMessage(const IPC::Message& message);
+
   bool MessageErrorHandler(const IPC::Message& message, const char* error_msg);
 
   // mojom::GpuChannel:
   void CrashForTesting() override;
   void TerminateForTesting() override;
   void Flush(FlushCallback callback) override;
-  void CreateCommandBuffer(mojom::CreateCommandBufferParamsPtr config,
-                           int32_t routing_id,
-                           base::UnsafeSharedMemoryRegion shared_state,
-                           CreateCommandBufferCallback callback) override;
-  void DestroyCommandBuffer(int32_t routing_id,
-                            DestroyCommandBufferCallback callback) override;
   void ScheduleImageDecode(mojom::ScheduleImageDecodeParamsPtr params,
                            uint64_t decode_release_count) override;
-  void FlushDeferredRequests(
-      std::vector<mojom::DeferredRequestPtr> requests) override;
-  void CreateStreamTexture(int32_t stream_id,
-                           CreateStreamTextureCallback callback) override;
 
   IPC::Channel* ipc_channel_ = nullptr;
   base::ProcessId peer_pid_ = base::kNullProcessId;
@@ -287,6 +267,24 @@
   if (message.should_unblock() || message.is_reply())
     return MessageErrorHandler(message, "Unexpected message type");
 
+  switch (message.type()) {
+    case GpuCommandBufferMsg_AsyncFlush::ID:
+    case GpuCommandBufferMsg_DestroyTransferBuffer::ID:
+    case GpuCommandBufferMsg_ReturnFrontBuffer::ID:
+    case GpuCommandBufferMsg_TakeFrontBuffer::ID:
+    case GpuChannelMsg_CreateSharedImage::ID:
+    case GpuChannelMsg_DestroySharedImage::ID:
+      return MessageErrorHandler(message, "Invalid message");
+    default:
+      break;
+  }
+
+  if (message.type() == GpuChannelMsg_Nop::ID) {
+    IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
+    ipc_channel_->Send(reply);
+    return true;
+  }
+
   for (scoped_refptr<IPC::MessageFilter>& filter : channel_filters_) {
     if (filter->OnMessageReceived(message))
       return true;
@@ -296,6 +294,10 @@
   if (!gpu_channel_)
     return MessageErrorHandler(message, "Channel destroyed");
 
+  // Handle flush first so that it doesn't get handled out of order.
+  if (message.type() == GpuChannelMsg_FlushDeferredMessages::ID)
+    return HandleFlushMessage(message);
+
   bool handle_out_of_order =
       message.routing_id() == MSG_ROUTING_CONTROL ||
       message.type() == GpuCommandBufferMsg_WaitForTokenInRange::ID ||
@@ -331,43 +333,33 @@
   return it->second;
 }
 
-void GpuChannelMessageFilter::FlushDeferredRequests(
-    std::vector<mojom::DeferredRequestPtr> requests) {
-  base::AutoLock auto_lock(gpu_channel_lock_);
+bool GpuChannelMessageFilter::HandleFlushMessage(const IPC::Message& message) {
+  DCHECK_EQ(message.type(), GpuChannelMsg_FlushDeferredMessages::ID);
+  gpu_channel_lock_.AssertAcquired();
+
+  GpuChannelMsg_FlushDeferredMessages::Param params;
+  if (!GpuChannelMsg_FlushDeferredMessages::Read(&message, &params))
+    return MessageErrorHandler(message, "Invalid flush message");
+
+  std::vector<GpuDeferredMessage> deferred_messages =
+      std::get<0>(std::move(params));
 
   std::vector<Scheduler::Task> tasks;
-  tasks.reserve(requests.size());
-  for (auto& request : requests) {
-    int32_t routing_id;
-    switch (request->params->which()) {
-#if defined(OS_ANDROID)
-      case mojom::DeferredRequestParams::Tag::kDestroyStreamTexture:
-        routing_id = request->params->get_destroy_stream_texture();
-        break;
-#endif  // defined(OS_ANDROID)
-
-      case mojom::DeferredRequestParams::Tag::kCommandBufferRequest:
-        routing_id = request->params->get_command_buffer_request()->routing_id;
-        break;
-
-      case mojom::DeferredRequestParams::Tag::kSharedImageRequest:
-        routing_id = static_cast<int32_t>(
-            GpuChannelReservedRoutes::kSharedImageInterface);
-        break;
-    }
-
-    auto it = route_sequences_.find(routing_id);
+  tasks.reserve(deferred_messages.size());
+  for (auto& deferred_message : deferred_messages) {
+    auto it = route_sequences_.find(deferred_message.message.routing_id());
     if (it == route_sequences_.end()) {
       DLOG(ERROR) << "Invalid route id in flush list";
       continue;
     }
-    tasks.emplace_back(
-        it->second /* sequence_id */,
-        base::BindOnce(&gpu::GpuChannel::ExecuteDeferredRequest,
-                       gpu_channel_->AsWeakPtr(), std::move(request->params)),
-        std::move(request->sync_token_fences));
+    tasks.emplace_back(it->second /* sequence_id */,
+                       base::BindOnce(&gpu::GpuChannel::HandleMessage,
+                                      gpu_channel_->AsWeakPtr(),
+                                      std::move(deferred_message.message)),
+                       std::move(deferred_message.sync_token_fences));
   }
   scheduler_->ScheduleTasks(std::move(tasks));
+  return true;
 }
 
 bool GpuChannelMessageFilter::MessageErrorHandler(const IPC::Message& message,
@@ -403,42 +395,6 @@
   std::move(callback).Run();
 }
 
-void GpuChannelMessageFilter::CreateCommandBuffer(
-    mojom::CreateCommandBufferParamsPtr params,
-    int32_t routing_id,
-    base::UnsafeSharedMemoryRegion shared_state,
-    CreateCommandBufferCallback callback) {
-  base::AutoLock auto_lock(gpu_channel_lock_);
-  if (!gpu_channel_) {
-    std::move(callback).Run(ContextResult::kFatalFailure, Capabilities());
-    return;
-  }
-
-  main_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&gpu::GpuChannel::CreateCommandBuffer,
-                     gpu_channel_->AsWeakPtr(), std::move(params), routing_id,
-                     std::move(shared_state),
-                     base::BindPostTask(base::ThreadTaskRunnerHandle::Get(),
-                                        std::move(callback))));
-}
-
-void GpuChannelMessageFilter::DestroyCommandBuffer(
-    int32_t routing_id,
-    DestroyCommandBufferCallback callback) {
-  base::AutoLock auto_lock(gpu_channel_lock_);
-  if (!gpu_channel_) {
-    std::move(callback).Run();
-    return;
-  }
-
-  main_task_runner_->PostTaskAndReply(
-      FROM_HERE,
-      base::BindOnce(&gpu::GpuChannel::DestroyCommandBuffer,
-                     gpu_channel_->AsWeakPtr(), routing_id),
-      std::move(callback));
-}
-
 void GpuChannelMessageFilter::ScheduleImageDecode(
     mojom::ScheduleImageDecodeParamsPtr params,
     uint64_t decode_release_count) {
@@ -446,16 +402,6 @@
                                                       decode_release_count);
 }
 
-void GpuChannelMessageFilter::CreateStreamTexture(
-    int32_t stream_id,
-    CreateStreamTextureCallback callback) {
-  main_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&TryCreateStreamTexture, gpu_channel_->AsWeakPtr(),
-                     stream_id),
-      std::move(callback));
-}
-
 GpuChannel::GpuChannel(
     GpuChannelManager* gpu_channel_manager,
     Scheduler* scheduler,
@@ -639,6 +585,20 @@
   router_.RemoveRoute(route_id);
 }
 
+bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) {
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateCommandBuffer,
+                        OnCreateCommandBuffer)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
+                        OnDestroyCommandBuffer)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateStreamTexture,
+                        OnCreateStreamTexture)
+    IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+  return handled;
+}
+
 void GpuChannel::HandleMessage(const IPC::Message& msg) {
   int32_t routing_id = msg.routing_id();
   CommandBufferStub* stub = LookupCommandBuffer(routing_id);
@@ -649,44 +609,13 @@
            << " with type " << msg.type();
 
   HandleMessageHelper(msg);
-}
 
-void GpuChannel::ExecuteDeferredRequest(
-    mojom::DeferredRequestParamsPtr params) {
-  switch (params->which()) {
-#if defined(OS_ANDROID)
-    case mojom::DeferredRequestParams::Tag::kDestroyStreamTexture:
-      DestroyStreamTexture(params->get_destroy_stream_texture());
-      break;
-#endif  // defined(OS_ANDROID)
-
-    case mojom::DeferredRequestParams::Tag::kCommandBufferRequest: {
-      mojom::DeferredCommandBufferRequest& request =
-          *params->get_command_buffer_request();
-      CommandBufferStub* stub = LookupCommandBuffer(request.routing_id);
-      if (!stub || !stub->IsScheduled()) {
-        DLOG(ERROR) << "Invalid routing ID in deferred request";
-        return;
-      }
-
-      stub->ExecuteDeferredRequest(*request.params);
-
-      // If we get descheduled or yield while processing a message.
-      if (stub->HasUnprocessedCommands() || !stub->IsScheduled()) {
-        DCHECK_EQ(mojom::DeferredCommandBufferRequestParams::Tag::kAsyncFlush,
-                  request.params->which());
-        scheduler_->ContinueTask(
-            stub->sequence_id(),
-            base::BindOnce(&GpuChannel::ExecuteDeferredRequest, AsWeakPtr(),
-                           std::move(params)));
-      }
-      break;
-    }
-
-    case mojom::DeferredRequestParams::Tag::kSharedImageRequest:
-      shared_image_stub_->ExecuteDeferredRequest(
-          std::move(params->get_shared_image_request()));
-      break;
+  // If we get descheduled or yield while processing a message.
+  if (stub && (stub->HasUnprocessedCommands() || !stub->IsScheduled())) {
+    DCHECK_EQ(GpuCommandBufferMsg_AsyncFlush::ID, msg.type());
+    scheduler_->ContinueTask(
+        stub->sequence_id(),
+        base::BindOnce(&GpuChannel::HandleMessage, AsWeakPtr(), msg));
   }
 }
 
@@ -722,8 +651,11 @@
   int32_t routing_id = msg.routing_id();
 
   bool handled = false;
-  if (routing_id != MSG_ROUTING_CONTROL)
+  if (routing_id == MSG_ROUTING_CONTROL) {
+    handled = OnControlMessageReceived(msg);
+  } else {
     handled = router_.RouteMessage(msg);
+  }
 
   if (!handled && unhandled_message_listener_)
     handled = unhandled_message_listener_->OnMessageReceived(msg);
@@ -761,39 +693,20 @@
 }
 #endif
 
-// Helper to ensure CreateCommandBuffer below always invokes its response
-// callback.
-class ScopedCreateCommandBufferResponder {
- public:
-  explicit ScopedCreateCommandBufferResponder(
-      mojom::GpuChannel::CreateCommandBufferCallback callback)
-      : callback_(std::move(callback)) {}
-  ~ScopedCreateCommandBufferResponder() {
-    std::move(callback_).Run(result_, capabilities_);
-  }
-
-  void set_result(ContextResult result) { result_ = result; }
-  void set_capabilities(const Capabilities& capabilities) {
-    capabilities_ = capabilities;
-  }
-
- private:
-  mojom::GpuChannel::CreateCommandBufferCallback callback_;
-  ContextResult result_ = ContextResult::kFatalFailure;
-  Capabilities capabilities_;
-};
-
-void GpuChannel::CreateCommandBuffer(
-    mojom::CreateCommandBufferParamsPtr init_params,
+void GpuChannel::OnCreateCommandBuffer(
+    const GPUCreateCommandBufferConfig& init_params,
     int32_t route_id,
     base::UnsafeSharedMemoryRegion shared_state_shm,
-    mojom::GpuChannel::CreateCommandBufferCallback callback) {
-  ScopedCreateCommandBufferResponder responder(std::move(callback));
-  TRACE_EVENT2("gpu", "GpuChannel::CreateCommandBuffer", "route_id", route_id,
-               "offscreen",
-               (init_params->surface_handle == kNullSurfaceHandle));
+    ContextResult* result,
+    gpu::Capabilities* capabilities) {
+  TRACE_EVENT2("gpu", "GpuChannel::OnCreateCommandBuffer", "route_id", route_id,
+               "offscreen", (init_params.surface_handle == kNullSurfaceHandle));
+  // Default result on failure. Override with a more accurate failure if needed,
+  // or with success.
+  *result = ContextResult::kFatalFailure;
+  *capabilities = gpu::Capabilities();
 
-  if (init_params->surface_handle != kNullSurfaceHandle && !is_gpu_host_) {
+  if (init_params.surface_handle != kNullSurfaceHandle && !is_gpu_host_) {
     LOG(ERROR)
         << "ContextResult::kFatalFailure: "
            "attempt to create a view context on a non-privileged channel";
@@ -803,12 +716,12 @@
   if (gpu_channel_manager_->delegate()->IsExiting()) {
     LOG(ERROR) << "ContextResult::kTransientFailure: trying to create command "
                   "buffer during process shutdown.";
-    responder.set_result(ContextResult::kTransientFailure);
+    *result = gpu::ContextResult::kTransientFailure;
     return;
   }
 
-  int32_t stream_id = init_params->stream_id;
-  int32_t share_group_id = init_params->share_group_id;
+  int32_t stream_id = init_params.stream_id;
+  int32_t share_group_id = init_params.share_group_id;
   CommandBufferStub* share_group = LookupCommandBuffer(share_group_id);
 
   if (!share_group && share_group_id != MSG_ROUTING_NONE) {
@@ -834,7 +747,7 @@
     // The caller should retry to get a context.
     LOG(ERROR) << "ContextResult::kTransientFailure: "
                   "shared context was already lost";
-    responder.set_result(ContextResult::kTransientFailure);
+    *result = gpu::ContextResult::kTransientFailure;
     return;
   }
 
@@ -843,38 +756,35 @@
 
   SequenceId sequence_id = stream_sequences_[stream_id];
   if (sequence_id.is_null()) {
-    sequence_id = scheduler_->CreateSequence(init_params->stream_priority);
+    sequence_id = scheduler_->CreateSequence(init_params.stream_priority);
     stream_sequences_[stream_id] = sequence_id;
   }
 
   std::unique_ptr<CommandBufferStub> stub;
-  if (init_params->attribs.context_type == CONTEXT_TYPE_WEBGPU) {
+  if (init_params.attribs.context_type == CONTEXT_TYPE_WEBGPU) {
     if (!gpu_channel_manager_->gpu_preferences().enable_webgpu) {
       DLOG(ERROR) << "ContextResult::kFatalFailure: WebGPU not enabled";
       return;
     }
 
     stub = std::make_unique<WebGPUCommandBufferStub>(
-        this, *init_params, command_buffer_id, sequence_id, stream_id,
-        route_id);
-  } else if (init_params->attribs.enable_raster_interface &&
-             !init_params->attribs.enable_gles2_interface &&
-             !init_params->attribs.enable_grcontext) {
+        this, init_params, command_buffer_id, sequence_id, stream_id, route_id);
+  } else if (init_params.attribs.enable_raster_interface &&
+             !init_params.attribs.enable_gles2_interface &&
+             !init_params.attribs.enable_grcontext) {
     stub = std::make_unique<RasterCommandBufferStub>(
-        this, *init_params, command_buffer_id, sequence_id, stream_id,
-        route_id);
+        this, init_params, command_buffer_id, sequence_id, stream_id, route_id);
   } else {
     stub = std::make_unique<GLES2CommandBufferStub>(
-        this, *init_params, command_buffer_id, sequence_id, stream_id,
-        route_id);
+        this, init_params, command_buffer_id, sequence_id, stream_id, route_id);
   }
 
   auto stub_result =
-      stub->Initialize(share_group, *init_params, std::move(shared_state_shm));
+      stub->Initialize(share_group, init_params, std::move(shared_state_shm));
   if (stub_result != gpu::ContextResult::kSuccess) {
     DLOG(ERROR) << "GpuChannel::CreateCommandBuffer(): failed to initialize "
                    "CommandBufferStub";
-    responder.set_result(stub_result);
+    *result = stub_result;
     return;
   }
 
@@ -883,12 +793,12 @@
     return;
   }
 
-  responder.set_result(ContextResult::kSuccess);
-  responder.set_capabilities(stub->decoder_context()->GetCapabilities());
+  *result = ContextResult::kSuccess;
+  *capabilities = stub->decoder_context()->GetCapabilities();
   stubs_[route_id] = std::move(stub);
 }
 
-void GpuChannel::DestroyCommandBuffer(int32_t route_id) {
+void GpuChannel::OnDestroyCommandBuffer(int32_t route_id) {
   TRACE_EVENT1("gpu", "GpuChannel::OnDestroyCommandBuffer", "route_id",
                route_id);
 
@@ -908,23 +818,25 @@
   RemoveRoute(route_id);
 }
 
-bool GpuChannel::CreateStreamTexture(int32_t stream_id) {
+void GpuChannel::OnCreateStreamTexture(int32_t stream_id, bool* succeeded) {
 #if defined(OS_ANDROID)
   auto found = stream_textures_.find(stream_id);
   if (found != stream_textures_.end()) {
     LOG(ERROR)
         << "Trying to create a StreamTexture with an existing stream_id.";
-    return false;
+    *succeeded = false;
+    return;
   }
   scoped_refptr<StreamTexture> stream_texture =
       StreamTexture::Create(this, stream_id);
   if (!stream_texture) {
-    return false;
+    *succeeded = false;
+    return;
   }
   stream_textures_.emplace(stream_id, std::move(stream_texture));
-  return true;
+  *succeeded = true;
 #else
-  return false;
+  *succeeded = false;
 #endif
 }
 
diff --git a/gpu/ipc/service/gpu_channel.h b/gpu/ipc/service/gpu_channel.h
index 38c9517c..757f77e8 100644
--- a/gpu/ipc/service/gpu_channel.h
+++ b/gpu/ipc/service/gpu_channel.h
@@ -21,10 +21,8 @@
 #include "base/single_thread_task_runner.h"
 #include "base/trace_event/memory_dump_provider.h"
 #include "build/build_config.h"
-#include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/context_result.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "gpu/ipc/service/command_buffer_stub.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 #include "gpu/ipc/service/shared_image_stub.h"
@@ -36,6 +34,8 @@
 #include "ui/gl/gl_share_group.h"
 #include "ui/gl/gpu_preference.h"
 
+struct GPUCreateCommandBufferConfig;
+
 namespace base {
 class WaitableEvent;
 }
@@ -155,10 +155,6 @@
 
   void HandleMessage(const IPC::Message& msg);
 
-  // Executes a DeferredRequest that was previously received and has now been
-  // scheduled by the scheduler.
-  void ExecuteDeferredRequest(mojom::DeferredRequestParamsPtr params);
-
   // Some messages such as WaitForGetOffsetInRange and WaitForTokenInRange are
   // processed as soon as possible because the client is blocked until they
   // are completed.
@@ -181,14 +177,6 @@
     return shared_image_stub_.get();
   }
 
-  void CreateCommandBuffer(
-      mojom::CreateCommandBufferParamsPtr init_params,
-      int32_t routing_id,
-      base::UnsafeSharedMemoryRegion shared_state_shm,
-      mojom::GpuChannel::CreateCommandBufferCallback callback);
-  void DestroyCommandBuffer(int32_t routing_id);
-  bool CreateStreamTexture(int32_t stream_id);
-
  private:
   // Takes ownership of the renderer process handle.
   GpuChannel(GpuChannelManager* gpu_channel_manager,
@@ -202,11 +190,18 @@
              bool is_gpu_host,
              ImageDecodeAcceleratorWorker* image_decode_accelerator_worker);
 
-  void OnDestroyCommandBuffer(int32_t route_id);
+  bool OnControlMessageReceived(const IPC::Message& msg);
 
   void HandleMessageHelper(const IPC::Message& msg);
 
   // Message handlers for control messages.
+  void OnCreateCommandBuffer(const GPUCreateCommandBufferConfig& init_params,
+                             int32_t route_id,
+                             base::UnsafeSharedMemoryRegion shared_state_shm,
+                             gpu::ContextResult* result,
+                             gpu::Capabilities* capabilities);
+  void OnDestroyCommandBuffer(int32_t route_id);
+  void OnCreateStreamTexture(int32_t stream_id, bool* succeeded);
   bool CreateSharedImageStub();
 
   std::unique_ptr<IPC::SyncChannel> sync_channel_;  // nullptr in tests.
diff --git a/gpu/ipc/service/gpu_channel_manager_unittest.cc b/gpu/ipc/service/gpu_channel_manager_unittest.cc
index 8b59b1bd..1baf932 100644
--- a/gpu/ipc/service/gpu_channel_manager_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_manager_unittest.cc
@@ -12,11 +12,9 @@
 #include "base/trace_event/trace_event_filter.h"
 #include "base/trace_event/trace_event_impl.h"
 #include "base/trace_event/trace_log.h"
-#include "gpu/command_buffer/common/capabilities.h"
-#include "gpu/command_buffer/common/context_result.h"
 #include "gpu/command_buffer/service/memory_tracking.h"
 #include "gpu/ipc/common/command_buffer_id.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
+#include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_channel_test_common.h"
@@ -134,20 +132,20 @@
         static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
     const SurfaceHandle kFakeSurfaceHandle = 1;
     SurfaceHandle surface_handle = kFakeSurfaceHandle;
-    auto init_params = mojom::CreateCommandBufferParams::New();
-    init_params->surface_handle = surface_handle;
-    init_params->share_group_id = MSG_ROUTING_NONE;
-    init_params->stream_id = 0;
-    init_params->stream_priority = SchedulingPriority::kNormal;
-    init_params->attribs = ContextCreationAttribs();
-    init_params->attribs.context_type = type;
-    init_params->active_url = GURL();
-
-    ContextResult result = ContextResult::kFatalFailure;
-    Capabilities capabilities;
-    CreateCommandBuffer(*channel, std::move(init_params), kRouteId,
-                        GetSharedMemoryRegion(), &result, &capabilities);
-    EXPECT_EQ(result, ContextResult::kSuccess);
+    GPUCreateCommandBufferConfig init_params;
+    init_params.surface_handle = surface_handle;
+    init_params.share_group_id = MSG_ROUTING_NONE;
+    init_params.stream_id = 0;
+    init_params.stream_priority = SchedulingPriority::kNormal;
+    init_params.attribs = ContextCreationAttribs();
+    init_params.attribs.context_type = type;
+    init_params.active_url = GURL();
+    gpu::ContextResult result = gpu::ContextResult::kFatalFailure;
+    gpu::Capabilities capabilities;
+    HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                               init_params, kRouteId, GetSharedMemoryRegion(),
+                               &result, &capabilities));
+    EXPECT_EQ(result, gpu::ContextResult::kSuccess);
 
     auto raster_decoder_state =
         channel_manager()->GetSharedContextState(&result);
diff --git a/gpu/ipc/service/gpu_channel_test_common.cc b/gpu/ipc/service/gpu_channel_test_common.cc
index 4de3cfc..7364038 100644
--- a/gpu/ipc/service/gpu_channel_test_common.cc
+++ b/gpu/ipc/service/gpu_channel_test_common.cc
@@ -7,8 +7,6 @@
 #include <memory>
 
 #include "base/memory/unsafe_shared_memory_region.h"
-#include "base/run_loop.h"
-#include "base/test/bind.h"
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/memory_dump_manager.h"
@@ -72,9 +70,11 @@
     bool use_stub_bindings)
     : memory_dump_manager_(
           base::trace_event::MemoryDumpManager::CreateInstanceForTesting()),
+      task_runner_(new base::TestSimpleTaskRunner),
+      io_task_runner_(new base::TestSimpleTaskRunner),
       sync_point_manager_(new SyncPointManager()),
       shared_image_manager_(new SharedImageManager(false /* thread_safe */)),
-      scheduler_(new Scheduler(task_environment_.GetMainThreadTaskRunner(),
+      scheduler_(new Scheduler(task_runner_,
                                sync_point_manager_.get(),
                                GpuPreferences())),
       channel_manager_delegate_(
@@ -92,8 +92,7 @@
 
   channel_manager_ = std::make_unique<GpuChannelManager>(
       GpuPreferences(), channel_manager_delegate_.get(), nullptr, /* watchdog */
-      task_environment_.GetMainThreadTaskRunner(),
-      task_environment_.GetMainThreadTaskRunner(), scheduler_.get(),
+      task_runner_.get(), io_task_runner_.get(), scheduler_.get(),
       sync_point_manager_.get(), shared_image_manager_.get(),
       nullptr, /* gpu_memory_buffer_factory */
       std::move(feature_info), GpuProcessActivityFlags(),
@@ -104,7 +103,11 @@
 GpuChannelTestCommon::~GpuChannelTestCommon() {
   // Command buffers can post tasks and run GL in destruction so do this first.
   channel_manager_ = nullptr;
-  task_environment_.RunUntilIdle();
+
+  // Clear pending tasks to avoid refptr cycles that get flagged by ASAN.
+  task_runner_->ClearPendingTasks();
+  io_task_runner_->ClearPendingTasks();
+
   gl::init::ShutdownGL(false);
 }
 
@@ -119,26 +122,6 @@
   return channel;
 }
 
-void GpuChannelTestCommon::CreateCommandBuffer(
-    GpuChannel& channel,
-    mojom::CreateCommandBufferParamsPtr init_params,
-    int32_t routing_id,
-    base::UnsafeSharedMemoryRegion shared_state,
-    ContextResult* out_result,
-    Capabilities* out_capabilities) {
-  base::RunLoop loop;
-  auto quit = loop.QuitClosure();
-  channel.CreateCommandBuffer(
-      std::move(init_params), routing_id, std::move(shared_state),
-      base::BindLambdaForTesting(
-          [&](ContextResult result, const Capabilities& capabilities) {
-            *out_result = result;
-            *out_capabilities = capabilities;
-            quit.Run();
-          }));
-  loop.Run();
-}
-
 void GpuChannelTestCommon::HandleMessage(GpuChannel* channel,
                                          IPC::Message* msg) {
   // Some IPCs (such as GpuCommandBufferMsg_Initialize) will generate more
@@ -152,7 +135,7 @@
   channel->HandleMessageForTesting(*msg);
 
   // Run the HandleMessage task posted to the main thread.
-  task_environment_.RunUntilIdle();
+  task_runner()->RunPendingTasks();
 
   // Replies are sent to the sink.
   if (msg->is_sync()) {
diff --git a/gpu/ipc/service/gpu_channel_test_common.h b/gpu/ipc/service/gpu_channel_test_common.h
index 3efbfcc..be33807 100644
--- a/gpu/ipc/service/gpu_channel_test_common.h
+++ b/gpu/ipc/service/gpu_channel_test_common.h
@@ -10,18 +10,16 @@
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/unsafe_shared_memory_region.h"
-#include "base/sequenced_task_runner.h"
-#include "base/test/task_environment.h"
-#include "gpu/command_buffer/common/capabilities.h"
-#include "gpu/command_buffer/common/context_result.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "ipc/ipc_test_sink.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
+class TestSimpleTaskRunner;
+
 namespace trace_event {
 class MemoryDumpManager;
 }  // namespace trace_event
+
 }  // namespace base
 
 namespace IPC {
@@ -47,25 +45,22 @@
  protected:
   Scheduler* scheduler() const { return scheduler_.get(); }
   GpuChannelManager* channel_manager() const { return channel_manager_.get(); }
-  base::test::TaskEnvironment& task_environment() { return task_environment_; }
+  base::TestSimpleTaskRunner* task_runner() const { return task_runner_.get(); }
+  base::TestSimpleTaskRunner* io_task_runner() const {
+    return io_task_runner_.get();
+  }
 
   GpuChannel* CreateChannel(int32_t client_id, bool is_gpu_host);
 
-  void CreateCommandBuffer(GpuChannel& channel,
-                           mojom::CreateCommandBufferParamsPtr init_params,
-                           int32_t routing_id,
-                           base::UnsafeSharedMemoryRegion shared_state,
-                           ContextResult* out_result,
-                           Capabilities* out_capabilities);
-
   void HandleMessage(GpuChannel* channel, IPC::Message* msg);
 
   base::UnsafeSharedMemoryRegion GetSharedMemoryRegion();
 
  private:
-  base::test::TaskEnvironment task_environment_;
   std::unique_ptr<base::trace_event::MemoryDumpManager> memory_dump_manager_;
   IPC::TestSink sink_;
+  scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
+  scoped_refptr<base::TestSimpleTaskRunner> io_task_runner_;
   std::unique_ptr<SyncPointManager> sync_point_manager_;
   std::unique_ptr<SharedImageManager> shared_image_manager_;
   std::unique_ptr<Scheduler> scheduler_;
diff --git a/gpu/ipc/service/gpu_channel_unittest.cc b/gpu/ipc/service/gpu_channel_unittest.cc
index 578bb68..d9eb13b 100644
--- a/gpu/ipc/service/gpu_channel_unittest.cc
+++ b/gpu/ipc/service/gpu_channel_unittest.cc
@@ -4,10 +4,9 @@
 
 #include <stdint.h>
 
-#include "base/run_loop.h"
 #include "base/test/test_simple_task_runner.h"
 #include "gpu/ipc/common/command_buffer_id.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
+#include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/service/gpu_channel.h"
 #include "gpu/ipc/service/gpu_channel_manager.h"
 #include "gpu/ipc/service/gpu_channel_test_common.h"
@@ -37,17 +36,18 @@
 
   int32_t kRouteId =
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
-  auto init_params = mojom::CreateCommandBufferParams::New();
-  init_params->surface_handle = surface_handle;
-  init_params->share_group_id = MSG_ROUTING_NONE;
-  init_params->stream_id = 0;
-  init_params->stream_priority = SchedulingPriority::kNormal;
-  init_params->attribs = ContextCreationAttribs();
-  init_params->active_url = GURL();
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = surface_handle;
+  init_params.share_group_id = MSG_ROUTING_NONE;
+  init_params.stream_id = 0;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
   gpu::ContextResult result = gpu::ContextResult::kSuccess;
   gpu::Capabilities capabilities;
-  CreateCommandBuffer(*channel, std::move(init_params), kRouteId,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kSuccess);
 
   CommandBufferStub* stub = channel->LookupCommandBuffer(kRouteId);
@@ -65,17 +65,18 @@
 
   int32_t kRouteId =
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
-  auto init_params = mojom::CreateCommandBufferParams::New();
-  init_params->surface_handle = surface_handle;
-  init_params->share_group_id = MSG_ROUTING_NONE;
-  init_params->stream_id = 0;
-  init_params->stream_priority = SchedulingPriority::kNormal;
-  init_params->attribs = ContextCreationAttribs();
-  init_params->active_url = GURL();
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = surface_handle;
+  init_params.share_group_id = MSG_ROUTING_NONE;
+  init_params.stream_id = 0;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
   gpu::ContextResult result = gpu::ContextResult::kSuccess;
   gpu::Capabilities capabilities;
-  CreateCommandBuffer(*channel, std::move(init_params), kRouteId,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kFatalFailure);
 
   CommandBufferStub* stub = channel->LookupCommandBuffer(kRouteId);
@@ -89,17 +90,18 @@
 
   int32_t kRouteId =
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
-  auto init_params = mojom::CreateCommandBufferParams::New();
-  init_params->surface_handle = kNullSurfaceHandle;
-  init_params->share_group_id = MSG_ROUTING_NONE;
-  init_params->stream_id = 0;
-  init_params->stream_priority = SchedulingPriority::kNormal;
-  init_params->attribs = ContextCreationAttribs();
-  init_params->active_url = GURL();
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = kNullSurfaceHandle;
+  init_params.share_group_id = MSG_ROUTING_NONE;
+  init_params.stream_id = 0;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
   gpu::ContextResult result = gpu::ContextResult::kSuccess;
   gpu::Capabilities capabilities;
-  CreateCommandBuffer(*channel, std::move(init_params), kRouteId,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kSuccess);
 
   CommandBufferStub* stub = channel->LookupCommandBuffer(kRouteId);
@@ -115,17 +117,18 @@
   int32_t kRouteId1 =
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
   int32_t kStreamId1 = 1;
-  auto init_params = mojom::CreateCommandBufferParams::New();
-  init_params->surface_handle = kNullSurfaceHandle;
-  init_params->share_group_id = MSG_ROUTING_NONE;
-  init_params->stream_id = kStreamId1;
-  init_params->stream_priority = SchedulingPriority::kNormal;
-  auto init_params2 = init_params.Clone();
-
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = kNullSurfaceHandle;
+  init_params.share_group_id = MSG_ROUTING_NONE;
+  init_params.stream_id = kStreamId1;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
   gpu::ContextResult result = gpu::ContextResult::kSuccess;
   gpu::Capabilities capabilities;
-  CreateCommandBuffer(*channel, std::move(init_params), kRouteId1,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId1, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kSuccess);
 
   CommandBufferStub* stub = channel->LookupCommandBuffer(kRouteId1);
@@ -135,11 +138,14 @@
   int32_t kRouteId2 = kRouteId1 + 1;
   int32_t kStreamId2 = 2;
 
-  init_params2->share_group_id = kRouteId1;
-  init_params2->stream_id = kStreamId2;
-  init_params2->stream_priority = SchedulingPriority::kNormal;
-  CreateCommandBuffer(*channel, std::move(init_params2), kRouteId2,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  init_params.share_group_id = kRouteId1;
+  init_params.stream_id = kStreamId2;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId2, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kFatalFailure);
 
   stub = channel->LookupCommandBuffer(kRouteId2);
@@ -156,17 +162,19 @@
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
   {
     SCOPED_TRACE("kSharedRouteId");
-    auto init_params = mojom::CreateCommandBufferParams::New();
-    init_params->surface_handle = kNullSurfaceHandle;
-    init_params->share_group_id = MSG_ROUTING_NONE;
-    init_params->stream_id = 0;
-    init_params->stream_priority = SchedulingPriority::kNormal;
-    init_params->attribs = ContextCreationAttribs();
-    init_params->active_url = GURL();
+    GPUCreateCommandBufferConfig init_params;
+    init_params.surface_handle = kNullSurfaceHandle;
+    init_params.share_group_id = MSG_ROUTING_NONE;
+    init_params.stream_id = 0;
+    init_params.stream_priority = SchedulingPriority::kNormal;
+    init_params.attribs = ContextCreationAttribs();
+    init_params.active_url = GURL();
     gpu::ContextResult result = gpu::ContextResult::kSuccess;
     gpu::Capabilities capabilities;
-    CreateCommandBuffer(*channel, std::move(init_params), kSharedRouteId,
-                        GetSharedMemoryRegion(), &result, &capabilities);
+    HandleMessage(channel,
+                  new GpuChannelMsg_CreateCommandBuffer(
+                      init_params, kSharedRouteId, GetSharedMemoryRegion(),
+                      &result, &capabilities));
     EXPECT_EQ(result, gpu::ContextResult::kSuccess);
   }
   EXPECT_TRUE(channel->LookupCommandBuffer(kSharedRouteId));
@@ -175,17 +183,19 @@
   int32_t kFriendlyRouteId = kSharedRouteId + 1;
   {
     SCOPED_TRACE("kFriendlyRouteId");
-    auto init_params = mojom::CreateCommandBufferParams::New();
-    init_params->surface_handle = kNullSurfaceHandle;
-    init_params->share_group_id = kSharedRouteId;
-    init_params->stream_id = 0;
-    init_params->stream_priority = SchedulingPriority::kNormal;
-    init_params->attribs = ContextCreationAttribs();
-    init_params->active_url = GURL();
+    GPUCreateCommandBufferConfig init_params;
+    init_params.surface_handle = kNullSurfaceHandle;
+    init_params.share_group_id = kSharedRouteId;
+    init_params.stream_id = 0;
+    init_params.stream_priority = SchedulingPriority::kNormal;
+    init_params.attribs = ContextCreationAttribs();
+    init_params.active_url = GURL();
     gpu::ContextResult result = gpu::ContextResult::kSuccess;
     gpu::Capabilities capabilities;
-    CreateCommandBuffer(*channel, std::move(init_params), kFriendlyRouteId,
-                        GetSharedMemoryRegion(), &result, &capabilities);
+    HandleMessage(channel,
+                  new GpuChannelMsg_CreateCommandBuffer(
+                      init_params, kFriendlyRouteId, GetSharedMemoryRegion(),
+                      &result, &capabilities));
     EXPECT_EQ(result, gpu::ContextResult::kSuccess);
   }
   EXPECT_TRUE(channel->LookupCommandBuffer(kFriendlyRouteId));
@@ -198,17 +208,19 @@
   int32_t kAnotherRouteId = kFriendlyRouteId + 1;
   {
     SCOPED_TRACE("kAnotherRouteId");
-    auto init_params = mojom::CreateCommandBufferParams::New();
-    init_params->surface_handle = kNullSurfaceHandle;
-    init_params->share_group_id = kSharedRouteId;
-    init_params->stream_id = 0;
-    init_params->stream_priority = SchedulingPriority::kNormal;
-    init_params->attribs = ContextCreationAttribs();
-    init_params->active_url = GURL();
+    GPUCreateCommandBufferConfig init_params;
+    init_params.surface_handle = kNullSurfaceHandle;
+    init_params.share_group_id = kSharedRouteId;
+    init_params.stream_id = 0;
+    init_params.stream_priority = SchedulingPriority::kNormal;
+    init_params.attribs = ContextCreationAttribs();
+    init_params.active_url = GURL();
     gpu::ContextResult result = gpu::ContextResult::kSuccess;
     gpu::Capabilities capabilities;
-    CreateCommandBuffer(*channel, std::move(init_params), kAnotherRouteId,
-                        GetSharedMemoryRegion(), &result, &capabilities);
+    HandleMessage(channel,
+                  new GpuChannelMsg_CreateCommandBuffer(
+                      init_params, kAnotherRouteId, GetSharedMemoryRegion(),
+                      &result, &capabilities));
     EXPECT_EQ(result, gpu::ContextResult::kTransientFailure);
   }
   EXPECT_FALSE(channel->LookupCommandBuffer(kAnotherRouteId));
@@ -218,8 +230,10 @@
   EXPECT_TRUE(channel->LookupCommandBuffer(kSharedRouteId));
 
   // Destroy the command buffers we initialized before destoying GL.
-  channel->DestroyCommandBuffer(kFriendlyRouteId);
-  channel->DestroyCommandBuffer(kSharedRouteId);
+  HandleMessage(channel,
+                new GpuChannelMsg_DestroyCommandBuffer(kFriendlyRouteId));
+  HandleMessage(channel,
+                new GpuChannelMsg_DestroyCommandBuffer(kSharedRouteId));
 }
 
 class GpuChannelExitForContextLostTest : public GpuChannelTestCommon {
@@ -240,7 +254,7 @@
 
   // Calling OnContextLost() above may destroy the gpu channel via post task.
   // Ensure that post task has happened.
-  base::RunLoop().RunUntilIdle();
+  task_runner()->RunPendingTasks();
 
   // If the channel is destroyed, then skip the test.
   if (!channel_manager()->LookupChannel(kClientId))
@@ -249,17 +263,18 @@
   // Try to create a context.
   int32_t kRouteId =
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
-  auto init_params = mojom::CreateCommandBufferParams::New();
-  init_params->surface_handle = kNullSurfaceHandle;
-  init_params->share_group_id = MSG_ROUTING_NONE;
-  init_params->stream_id = 0;
-  init_params->stream_priority = SchedulingPriority::kNormal;
-  init_params->attribs = ContextCreationAttribs();
-  init_params->active_url = GURL();
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = kNullSurfaceHandle;
+  init_params.share_group_id = MSG_ROUTING_NONE;
+  init_params.stream_id = 0;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
   gpu::ContextResult result = gpu::ContextResult::kSuccess;
   gpu::Capabilities capabilities;
-  CreateCommandBuffer(*channel, std::move(init_params), kRouteId,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kTransientFailure);
   EXPECT_FALSE(channel->LookupCommandBuffer(kRouteId));
 }
@@ -277,17 +292,18 @@
   // Try to create a context.
   int32_t kRouteId =
       static_cast<int32_t>(GpuChannelReservedRoutes::kMaxValue) + 1;
-  auto init_params = mojom::CreateCommandBufferParams::New();
-  init_params->surface_handle = kNullSurfaceHandle;
-  init_params->share_group_id = MSG_ROUTING_NONE;
-  init_params->stream_id = 0;
-  init_params->stream_priority = SchedulingPriority::kNormal;
-  init_params->attribs = ContextCreationAttribs();
-  init_params->active_url = GURL();
+  GPUCreateCommandBufferConfig init_params;
+  init_params.surface_handle = kNullSurfaceHandle;
+  init_params.share_group_id = MSG_ROUTING_NONE;
+  init_params.stream_id = 0;
+  init_params.stream_priority = SchedulingPriority::kNormal;
+  init_params.attribs = ContextCreationAttribs();
+  init_params.active_url = GURL();
   gpu::ContextResult result = gpu::ContextResult::kSuccess;
   gpu::Capabilities capabilities;
-  CreateCommandBuffer(*channel, std::move(init_params), kRouteId,
-                      GetSharedMemoryRegion(), &result, &capabilities);
+  HandleMessage(channel, new GpuChannelMsg_CreateCommandBuffer(
+                             init_params, kRouteId, GetSharedMemoryRegion(),
+                             &result, &capabilities));
   EXPECT_EQ(result, gpu::ContextResult::kTransientFailure);
   EXPECT_FALSE(channel->LookupCommandBuffer(kRouteId));
 }
diff --git a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
index ae11064..8e9bdbe 100644
--- a/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
+++ b/gpu/ipc/service/image_decode_accelerator_stub_unittest.cc
@@ -306,27 +306,30 @@
     // would make RunTasksUntilIdle() run forever.
     CommandBufferStub::SetMemoryTrackerFactoryForTesting(
         base::BindRepeating(&CreateMockMemoryTracker));
-    auto init_params = mojom::CreateCommandBufferParams::New();
-    init_params->surface_handle = kNullSurfaceHandle;
-    init_params->share_group_id = MSG_ROUTING_NONE;
-    init_params->stream_id = 0;
-    init_params->stream_priority = SchedulingPriority::kNormal;
-    init_params->attribs = ContextCreationAttribs();
-    init_params->attribs.enable_gles2_interface = false;
-    init_params->attribs.enable_raster_interface = true;
-    init_params->attribs.bind_generates_resource = false;
-    init_params->active_url = GURL();
+    GPUCreateCommandBufferConfig init_params;
+    init_params.surface_handle = kNullSurfaceHandle;
+    init_params.share_group_id = MSG_ROUTING_NONE;
+    init_params.stream_id = 0;
+    init_params.stream_priority = SchedulingPriority::kNormal;
+    init_params.attribs = ContextCreationAttribs();
+    init_params.attribs.enable_gles2_interface = false;
+    init_params.attribs.enable_raster_interface = true;
+    init_params.attribs.bind_generates_resource = false;
+    init_params.active_url = GURL();
     ContextResult result = ContextResult::kTransientFailure;
     Capabilities capabilities;
-    CreateCommandBuffer(*channel, std::move(init_params), kCommandBufferRouteId,
-                        GetSharedMemoryRegion(), &result, &capabilities);
+    HandleMessage(channel,
+                  new GpuChannelMsg_CreateCommandBuffer(
+                      init_params, kCommandBufferRouteId,
+                      GetSharedMemoryRegion(), &result, &capabilities));
     ASSERT_EQ(ContextResult::kSuccess, result);
     CommandBufferStub* command_buffer =
         channel->LookupCommandBuffer(kCommandBufferRouteId);
     ASSERT_TRUE(command_buffer);
 
     // Make sure there are no pending tasks before starting the test.
-    ASSERT_TRUE(task_environment().MainThreadIsIdle());
+    ASSERT_EQ(0u, task_runner()->NumPendingTasks());
+    ASSERT_EQ(0u, io_task_runner()->NumPendingTasks());
   }
 
   void TearDown() override {
@@ -421,7 +424,13 @@
     return decode_sync_token;
   }
 
-  void RunTasksUntilIdle() { task_environment().RunUntilIdle(); }
+  void RunTasksUntilIdle() {
+    while (task_runner()->HasPendingTask() ||
+           io_task_runner()->HasPendingTask()) {
+      task_runner()->RunUntilIdle();
+      io_task_runner()->RunUntilIdle();
+    }
+  }
 
   void CheckTransferCacheEntries(
       const std::vector<ExpectedCacheEntry>& expected_entries) {
@@ -645,6 +654,7 @@
   }
 
  protected:
+  base::test::SingleThreadTaskEnvironment task_environment_;
   StrictMock<MockImageDecodeAcceleratorWorker> image_decode_accelerator_worker_;
 
  private:
diff --git a/gpu/ipc/service/raster_command_buffer_stub.cc b/gpu/ipc/service/raster_command_buffer_stub.cc
index 538516e..120a429 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.cc
+++ b/gpu/ipc/service/raster_command_buffer_stub.cc
@@ -50,7 +50,7 @@
 
 RasterCommandBufferStub::RasterCommandBufferStub(
     GpuChannel* channel,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     CommandBufferId command_buffer_id,
     SequenceId sequence_id,
     int32_t stream_id,
@@ -66,7 +66,7 @@
 
 gpu::ContextResult RasterCommandBufferStub::Initialize(
     CommandBufferStub* share_command_buffer_stub,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     base::UnsafeSharedMemoryRegion shared_state_shm) {
   TRACE_EVENT0("gpu", "RasterBufferStub::Initialize");
   UpdateActiveUrl();
diff --git a/gpu/ipc/service/raster_command_buffer_stub.h b/gpu/ipc/service/raster_command_buffer_stub.h
index 40ca9da..0c9162d 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.h
+++ b/gpu/ipc/service/raster_command_buffer_stub.h
@@ -13,7 +13,7 @@
     : public CommandBufferStub {
  public:
   RasterCommandBufferStub(GpuChannel* channel,
-                          const mojom::CreateCommandBufferParams& init_params,
+                          const GPUCreateCommandBufferConfig& init_params,
                           CommandBufferId command_buffer_id,
                           SequenceId sequence_id,
                           int32_t stream_id,
@@ -25,7 +25,7 @@
   // the gpu::Capabilities.
   gpu::ContextResult Initialize(
       CommandBufferStub* share_group,
-      const mojom::CreateCommandBufferParams& init_params,
+      const GPUCreateCommandBufferConfig& init_params,
       base::UnsafeSharedMemoryRegion shared_state_shm) override;
   MemoryTracker* GetContextGroupMemoryTracker() const override;
 
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc
index d52d07d..42d1481 100644
--- a/gpu/ipc/service/shared_image_stub.cc
+++ b/gpu/ipc/service/shared_image_stub.cc
@@ -12,7 +12,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
 #include "components/viz/common/features.h"
 #include "gpu/command_buffer/service/mailbox_manager.h"
 #include "gpu/command_buffer/service/scheduler.h"
@@ -70,62 +69,26 @@
   return stub;
 }
 
-void SharedImageStub::ExecuteDeferredRequest(
-    mojom::DeferredSharedImageRequestPtr request) {
-  switch (request->which()) {
-    case mojom::DeferredSharedImageRequest::Tag::kNop:
-      break;
-
-    case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImage:
-      OnCreateSharedImage(std::move(request->get_create_shared_image()));
-      break;
-
-    case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImageWithData:
-      OnCreateSharedImageWithData(
-          std::move(request->get_create_shared_image_with_data()));
-      break;
-
-#if defined(OS_ANDROID)
-    case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImageWithAhb: {
-      auto& create_request = *request->get_create_shared_image_with_ahb();
-      OnCreateSharedImageWithAHB(
-          create_request.out_mailbox, create_request.in_mailbox,
-          create_request.usage, create_request.release_id);
-      break;
-    }
-#endif  // defined(OS_ANDROID)
-
-    case mojom::DeferredSharedImageRequest::Tag::kUpdateSharedImage: {
-      auto& update = *request->get_update_shared_image();
-      OnUpdateSharedImage(update.mailbox, update.release_id,
-                          std::move(update.in_fence_handle));
-      break;
-    }
-
-    case mojom::DeferredSharedImageRequest::Tag::kDestroySharedImage:
-      OnDestroySharedImage(request->get_destroy_shared_image());
-      break;
-
-#if defined(OS_WIN)
-    case mojom::DeferredSharedImageRequest::Tag::kCreateSwapChain:
-      OnCreateSwapChain(std::move(request->get_create_swap_chain()));
-      break;
-
-    case mojom::DeferredSharedImageRequest::Tag::kPresentSwapChain:
-      OnPresentSwapChain(request->get_present_swap_chain()->mailbox,
-                         request->get_present_swap_chain()->release_id);
-      break;
-#endif  // defined(OS_WIN)
-  }
-}
-
 bool SharedImageStub::OnMessageReceived(const IPC::Message& msg) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(SharedImageStub, msg)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateSharedImage, OnCreateSharedImage)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateSharedImageWithData,
+                        OnCreateSharedImageWithData)
     IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateGMBSharedImage,
                         OnCreateGMBSharedImage)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_UpdateSharedImage, OnUpdateSharedImage)
+#if defined(OS_ANDROID)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateSharedImageWithAHB,
+                        OnCreateSharedImageWithAHB)
+#endif
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroySharedImage, OnDestroySharedImage)
     IPC_MESSAGE_HANDLER(GpuChannelMsg_RegisterSharedImageUploadBuffer,
                         OnRegisterSharedImageUploadBuffer)
+#if defined(OS_WIN)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateSwapChain, OnCreateSwapChain)
+    IPC_MESSAGE_HANDLER(GpuChannelMsg_PresentSwapChain, OnPresentSwapChain)
+#endif  // OS_WIN
 #if defined(OS_FUCHSIA)
     IPC_MESSAGE_HANDLER(GpuChannelMsg_RegisterSysmemBufferCollection,
                         OnRegisterSysmemBufferCollection)
@@ -218,10 +181,10 @@
 #endif
 
 void SharedImageStub::OnCreateSharedImage(
-    mojom::CreateSharedImageParamsPtr params) {
+    const GpuChannelMsg_CreateSharedImage_Params& params) {
   TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImage", "width",
-               params->size.width(), "height", params->size.height());
-  if (!params->mailbox.IsSharedImage()) {
+               params.size.width(), "height", params.size.height());
+  if (!params.mailbox.IsSharedImage()) {
     LOG(ERROR) << "SharedImageStub: Trying to create a SharedImage with a "
                   "non-SharedImage mailbox.";
     OnError();
@@ -233,10 +196,10 @@
     return;
   }
 
-  if (!factory_->CreateSharedImage(params->mailbox, params->format,
-                                   params->size, params->color_space,
-                                   params->surface_origin, params->alpha_type,
-                                   gpu::kNullSurfaceHandle, params->usage)) {
+  if (!factory_->CreateSharedImage(params.mailbox, params.format, params.size,
+                                   params.color_space, params.surface_origin,
+                                   params.alpha_type, gpu::kNullSurfaceHandle,
+                                   params.usage)) {
     LOG(ERROR) << "SharedImageStub: Unable to create shared image";
     OnError();
     return;
@@ -244,15 +207,15 @@
 
   SyncToken sync_token(sync_point_client_state_->namespace_id(),
                        sync_point_client_state_->command_buffer_id(),
-                       params->release_id);
-  sync_point_client_state_->ReleaseFenceSync(params->release_id);
+                       params.release_id);
+  sync_point_client_state_->ReleaseFenceSync(params.release_id);
 }
 
 void SharedImageStub::OnCreateSharedImageWithData(
-    mojom::CreateSharedImageWithDataParamsPtr params) {
+    const GpuChannelMsg_CreateSharedImageWithData_Params& params) {
   TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImageWithData", "width",
-               params->size.width(), "height", params->size.height());
-  if (!params->mailbox.IsSharedImage()) {
+               params.size.width(), "height", params.size.height());
+  if (!params.mailbox.IsSharedImage()) {
     LOG(ERROR) << "SharedImageStub: Trying to create a SharedImage with a "
                   "non-SharedImage mailbox.";
     OnError();
@@ -265,8 +228,8 @@
   }
 
   base::CheckedNumeric<size_t> safe_required_span_size =
-      params->pixel_data_offset;
-  safe_required_span_size += params->pixel_data_size;
+      params.pixel_data_offset;
+  safe_required_span_size += params.pixel_data_size;
   size_t required_span_size;
   if (!safe_required_span_size.AssignIfValid(&required_span_size)) {
     LOG(ERROR) << "SharedImageStub: upload data size and offset is invalid";
@@ -283,26 +246,26 @@
   }
 
   auto subspan =
-      memory.subspan(params->pixel_data_offset, params->pixel_data_size);
+      memory.subspan(params.pixel_data_offset, params.pixel_data_size);
 
-  if (!factory_->CreateSharedImage(
-          params->mailbox, params->format, params->size, params->color_space,
-          params->surface_origin, params->alpha_type, params->usage, subspan)) {
+  if (!factory_->CreateSharedImage(params.mailbox, params.format, params.size,
+                                   params.color_space, params.surface_origin,
+                                   params.alpha_type, params.usage, subspan)) {
     LOG(ERROR) << "SharedImageStub: Unable to create shared image";
     OnError();
     return;
   }
 
   // If this is the last upload using a given buffer, release it.
-  if (params->done_with_shm) {
+  if (params.done_with_shm) {
     upload_memory_mapping_ = base::ReadOnlySharedMemoryMapping();
     upload_memory_ = base::ReadOnlySharedMemoryRegion();
   }
 
   SyncToken sync_token(sync_point_client_state_->namespace_id(),
                        sync_point_client_state_->command_buffer_id(),
-                       params->release_id);
-  sync_point_client_state_->ReleaseFenceSync(params->release_id);
+                       params.release_id);
+  sync_point_client_state_->ReleaseFenceSync(params.release_id);
 }
 
 void SharedImageStub::OnCreateGMBSharedImage(
@@ -379,11 +342,11 @@
 
 #if defined(OS_WIN)
 void SharedImageStub::OnCreateSwapChain(
-    mojom::CreateSwapChainParamsPtr params) {
+    const GpuChannelMsg_CreateSwapChain_Params& params) {
   TRACE_EVENT0("gpu", "SharedImageStub::OnCreateSwapChain");
 
-  if (!params->front_buffer_mailbox.IsSharedImage() ||
-      !params->back_buffer_mailbox.IsSharedImage()) {
+  if (!params.front_buffer_mailbox.IsSharedImage() ||
+      !params.back_buffer_mailbox.IsSharedImage()) {
     DLOG(ERROR) << "SharedImageStub: Trying to access SharedImage with a "
                    "non-SharedImage mailbox.";
     OnError();
@@ -396,15 +359,15 @@
   }
 
   if (!factory_->CreateSwapChain(
-          params->front_buffer_mailbox, params->back_buffer_mailbox,
-          params->format, params->size, params->color_space,
-          params->surface_origin, params->alpha_type, params->usage)) {
+          params.front_buffer_mailbox, params.back_buffer_mailbox,
+          params.format, params.size, params.color_space, params.surface_origin,
+          params.alpha_type, params.usage)) {
     DLOG(ERROR) << "SharedImageStub: Unable to create swap chain";
     OnError();
     return;
   }
 
-  sync_point_client_state_->ReleaseFenceSync(params->release_id);
+  sync_point_client_state_->ReleaseFenceSync(params.release_id);
 }
 
 void SharedImageStub::OnPresentSwapChain(const Mailbox& mailbox,
diff --git a/gpu/ipc/service/shared_image_stub.h b/gpu/ipc/service/shared_image_stub.h
index f307e2e..69e953a6 100644
--- a/gpu/ipc/service/shared_image_stub.h
+++ b/gpu/ipc/service/shared_image_stub.h
@@ -13,7 +13,6 @@
 #include "gpu/command_buffer/service/sequence_id.h"
 #include "gpu/command_buffer/service/sync_point_manager.h"
 #include "gpu/ipc/common/command_buffer_id.h"
-#include "gpu/ipc/common/gpu_channel.mojom.h"
 #include "gpu/ipc/common/gpu_messages.h"
 #include "gpu/ipc/service/gpu_ipc_service_export.h"
 #include "ipc/ipc_listener.h"
@@ -37,9 +36,6 @@
   static std::unique_ptr<SharedImageStub> Create(GpuChannel* channel,
                                                  int32_t route_id);
 
-  // Executes a DeferredRequest routed to this stub by a GpuChannel.
-  void ExecuteDeferredRequest(mojom::DeferredSharedImageRequestPtr request);
-
   // IPC::Listener implementation:
   bool OnMessageReceived(const IPC::Message& msg) override;
 
@@ -85,9 +81,10 @@
  private:
   SharedImageStub(GpuChannel* channel, int32_t route_id);
 
-  void OnCreateSharedImage(mojom::CreateSharedImageParamsPtr params);
+  void OnCreateSharedImage(
+      const GpuChannelMsg_CreateSharedImage_Params& params);
   void OnCreateSharedImageWithData(
-      mojom::CreateSharedImageWithDataParamsPtr params);
+      const GpuChannelMsg_CreateSharedImageWithData_Params& params);
   void OnCreateGMBSharedImage(GpuChannelMsg_CreateGMBSharedImage_Params params);
   void OnUpdateSharedImage(const Mailbox& mailbox,
                            uint32_t release_id,
@@ -101,7 +98,7 @@
   void OnDestroySharedImage(const Mailbox& mailbox);
   void OnRegisterSharedImageUploadBuffer(base::ReadOnlySharedMemoryRegion shm);
 #if defined(OS_WIN)
-  void OnCreateSwapChain(mojom::CreateSwapChainParamsPtr params);
+  void OnCreateSwapChain(const GpuChannelMsg_CreateSwapChain_Params& params);
   void OnPresentSwapChain(const Mailbox& mailbox, uint32_t release_id);
 #endif  // OS_WIN
 #if defined(OS_FUCHSIA)
diff --git a/gpu/ipc/service/stream_texture_android.cc b/gpu/ipc/service/stream_texture_android.cc
index eb8af40..72a2cb2 100644
--- a/gpu/ipc/service/stream_texture_android.cc
+++ b/gpu/ipc/service/stream_texture_android.cc
@@ -277,6 +277,7 @@
                         OnForwardForSurfaceRequest)
     IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_UpdateRotatedVisibleSize,
                         OnUpdateRotatedVisibleSize)
+    IPC_MESSAGE_HANDLER(GpuStreamTextureMsg_Destroy, OnDestroy)
     IPC_MESSAGE_UNHANDLED(handled = false)
   IPC_END_MESSAGE_MAP()
 
@@ -352,6 +353,14 @@
     OnFrameAvailable();
 }
 
+void StreamTexture::OnDestroy() {
+  DCHECK(channel_);
+
+  // The following call may delete the StreamTexture, so we must ensure that no
+  // access to |this| occurs after the call.
+  channel_->DestroyStreamTexture(route_id_);
+}
+
 StreamTexture::BindOrCopy StreamTexture::ShouldBindOrCopy() {
   return COPY;
 }
diff --git a/gpu/ipc/service/stream_texture_android.h b/gpu/ipc/service/stream_texture_android.h
index f1a4e69..4d29d5f 100644
--- a/gpu/ipc/service/stream_texture_android.h
+++ b/gpu/ipc/service/stream_texture_android.h
@@ -105,6 +105,7 @@
   void OnStartListening();
   void OnForwardForSurfaceRequest(const base::UnguessableToken& request_token);
   void OnUpdateRotatedVisibleSize(const gfx::Size& natural_size);
+  void OnDestroy();
 
   // The TextureOwner which receives frames.
   scoped_refptr<TextureOwner> texture_owner_;
diff --git a/gpu/ipc/service/webgpu_command_buffer_stub.cc b/gpu/ipc/service/webgpu_command_buffer_stub.cc
index f798872..c4dd246 100644
--- a/gpu/ipc/service/webgpu_command_buffer_stub.cc
+++ b/gpu/ipc/service/webgpu_command_buffer_stub.cc
@@ -51,7 +51,7 @@
 
 WebGPUCommandBufferStub::WebGPUCommandBufferStub(
     GpuChannel* channel,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     CommandBufferId command_buffer_id,
     SequenceId sequence_id,
     int32_t stream_id,
@@ -72,7 +72,7 @@
 
 gpu::ContextResult WebGPUCommandBufferStub::Initialize(
     CommandBufferStub* share_command_buffer_stub,
-    const mojom::CreateCommandBufferParams& init_params,
+    const GPUCreateCommandBufferConfig& init_params,
     base::UnsafeSharedMemoryRegion shared_state_shm) {
 #if defined(OS_FUCHSIA)
   // TODO(crbug.com/707031): Implement this.
diff --git a/gpu/ipc/service/webgpu_command_buffer_stub.h b/gpu/ipc/service/webgpu_command_buffer_stub.h
index 9c94a41..913eae7 100644
--- a/gpu/ipc/service/webgpu_command_buffer_stub.h
+++ b/gpu/ipc/service/webgpu_command_buffer_stub.h
@@ -13,7 +13,7 @@
     : public CommandBufferStub {
  public:
   WebGPUCommandBufferStub(GpuChannel* channel,
-                          const mojom::CreateCommandBufferParams& init_params,
+                          const GPUCreateCommandBufferConfig& init_params,
                           CommandBufferId command_buffer_id,
                           SequenceId sequence_id,
                           int32_t stream_id,
@@ -25,7 +25,7 @@
   // the gpu::Capabilities.
   gpu::ContextResult Initialize(
       CommandBufferStub* share_group,
-      const mojom::CreateCommandBufferParams& init_params,
+      const GPUCreateCommandBufferConfig& init_params,
       base::UnsafeSharedMemoryRegion shared_state_shm) override;
   MemoryTracker* GetContextGroupMemoryTracker() const override;
 
diff --git a/gpu/vulkan/vulkan_swap_chain.cc b/gpu/vulkan/vulkan_swap_chain.cc
index 6574e18..d82aab0 100644
--- a/gpu/vulkan/vulkan_swap_chain.cc
+++ b/gpu/vulkan/vulkan_swap_chain.cc
@@ -501,14 +501,11 @@
   FenceAndSemaphores fence_and_semaphores;
   do {
 #if !defined(OS_FUCHSIA)
-    // On Linux, some drivers may never release an image acquire fence,
-    // It should be a driver bug. When it happens, return {}, and then
-    // it will cause context lost instead of creating new fences and
-    // semaphores and causing OOM.
-    // https://crbug.com/1189069
-    constexpr size_t kQueueSizeLimit = 64;
-    if (UNLIKELY(fence_and_semaphores_queue_.size() >= kQueueSizeLimit))
-      break;
+    // This crash key is for diagnosing OOM crash.
+    // TODO(penghuang): remove it when OOM crash is fixed, or find out it is not
+    // related.
+    SCOPED_CRASH_KEY_NUMBER("VulkanSwapChian", "queue_.size()",
+                            fence_and_semaphores_queue_.size());
 
     if (LIKELY(!fence_and_semaphores_queue_.empty())) {
       fence_and_semaphores = fence_and_semaphores_queue_.front();
diff --git a/ios/build/bots/scripts/run.py b/ios/build/bots/scripts/run.py
index fa4d088..f9c46f7 100755
--- a/ios/build/bots/scripts/run.py
+++ b/ios/build/bots/scripts/run.py
@@ -49,41 +49,53 @@
     if args:
       self.parse_args(args)
 
-  def install_xcode(self, xcode_build_version, mac_toolchain_cmd,
-                    xcode_app_path):
+  def install_xcode(self):
     """Installs the requested Xcode build version.
 
-    Args:
-      xcode_build_version: (string) Xcode build version to install.
-      mac_toolchain_cmd: (string) Path to mac_toolchain command to install Xcode
-      See https://chromium.googlesource.com/infra/infra/+/master/go/src/infra/cmd/mac_toolchain/
-      xcode_app_path: (string) Path to install the contents of Xcode.app.
-
     Returns:
-      True if installation was successful. False otherwise.
+      (bool, bool)
+        First bool: True if installation was successful. False otherwise.
+        Second bool: True if Xcode is legacy package. False if it's new.
     """
     try:
-      if not mac_toolchain_cmd:
-        raise test_runner.MacToolchainNotFoundError(mac_toolchain_cmd)
+      if not self.args.mac_toolchain_cmd:
+        raise test_runner.MacToolchainNotFoundError(self.args.mac_toolchain_cmd)
       # Guard against incorrect install paths. On swarming, this path
       # should be a requested named cache, and it must exist.
-      if not os.path.exists(xcode_app_path):
-        raise test_runner.XcodePathNotFoundError(xcode_app_path)
+      if not os.path.exists(self.args.xcode_path):
+        raise test_runner.XcodePathNotFoundError(self.args.xcode_path)
 
-      # TODO(crbug.com/1191260): Pass in runtime args and handle moving runtime
-      # back to cache after runtime cache is set up in swarming, if Xcode
-      # installed is a new version (without runtime bundled)
-      xcode.install(mac_toolchain_cmd, xcode_build_version, xcode_app_path)
-      xcode.select(xcode_app_path)
+      runtime_cache_folder = None
+      # Runner script only utilizes runtime cache when it's a simulator task.
+      if self.args.version:
+        runtime_cache_folder = xcode.construct_runtime_cache_folder(
+            self.args.runtime_cache_prefix, self.args.version)
+        if not os.path.exists(runtime_cache_folder):
+          # Depending on infra project, runtime named cache might not be
+          # deployed. Create the dir if it doesn't exist since xcode_util
+          # assumes it exists.
+          # TODO(crbug.com/1191260): Raise error instead of creating dirs after
+          # runtime named cache is deployed everywhere.
+          os.makedirs(runtime_cache_folder)
+      # xcode.install() installs the Xcode & iOS runtime, and returns a bool
+      # indicating if the Xcode version in CIPD is a legacy Xcode package (which
+      # includes iOS runtimes).
+      is_legacy_xcode = xcode.install(
+          self.args.mac_toolchain_cmd,
+          self.args.xcode_build_version,
+          self.args.xcode_path,
+          runtime_cache_folder=runtime_cache_folder,
+          ios_version=self.args.version)
+      xcode.select(self.args.xcode_path)
     except subprocess.CalledProcessError as e:
       # Flush buffers to ensure correct output ordering.
       sys.stdout.flush()
       sys.stderr.write('Xcode build version %s failed to install: %s\n' %
-                       (xcode_build_version, e))
+                       (self.args.xcode_build_version, e))
       sys.stderr.flush()
-      return False
-
-    return True
+      return (False, False)
+    else:
+      return (True, is_legacy_xcode)
 
   def run(self, args):
     """
@@ -93,9 +105,8 @@
 
     # This logic is run by default before the otool command is invoked such that
     # otool has the correct Xcode selected for command line dev tools.
-    if not self.install_xcode(self.args.xcode_build_version,
-                              self.args.mac_toolchain_cmd,
-                              self.args.xcode_path):
+    install_success, is_legacy_xcode = self.install_xcode()
+    if not install_success:
       raise test_runner.XcodeVersionNotFoundError(self.args.xcode_build_version)
 
     # GTEST_SHARD_INDEX and GTEST_TOTAL_SHARDS are additional test environment
@@ -232,6 +243,14 @@
         with open(output_json_path, 'w') as f:
           json.dump(tr.test_results, f)
 
+      # Move the iOS runtime back to cache dir if the Xcode package is not
+      # legacy (i.e. Xcode program & runtimes are in different CIPD packages.)
+      # and it's a simulator task.
+      if not is_legacy_xcode and self.args.version:
+        runtime_cache_folder = xcode.construct_runtime_cache_folder(
+            self.args.runtime_cache_prefix, self.args.version)
+        xcode.move_runtime(runtime_cache_folder, self.args.xcode_path, False)
+
       test_runner.defaults_delete('com.apple.CoreSimulator',
                                   'FramebufferServerRendererPolicy')
 
@@ -334,6 +353,17 @@
         type=int,
     )
     parser.add_argument(
+        '--runtime-cache-prefix',
+        metavar='PATH',
+        help=(
+            'Path prefix for runtime cache folder. The prefix will be appended '
+            'with iOS version to construct the path. iOS simulator will be '
+            'installed to the path and further copied into Xcode. Default: '
+            '%(default)s. WARNING: this folder will be overwritten! This '
+            'folder is intended to be a cached CIPD installation.'),
+        default='Runtime-ios-',
+    )
+    parser.add_argument(
         '-s',
         '--shards',
         help='Number of shards to split test cases.',
diff --git a/ios/build/bots/scripts/run_test.py b/ios/build/bots/scripts/run_test.py
index 9d82d27..27dabb2 100755
--- a/ios/build/bots/scripts/run_test.py
+++ b/ios/build/bots/scripts/run_test.py
@@ -5,10 +5,12 @@
 """Unittests for run.py."""
 
 import json
+import mock
 import re
 import unittest
 
 import run
+import test_runner_test
 
 
 class UnitTest(unittest.TestCase):
@@ -19,17 +21,23 @@
         './foo-Runner.app',
         '--host-app',
         './bar.app',
+        '--runtime-cache-prefix',
+        'some/dir',
+        '--xcode-path',
+        'some/Xcode.app',
 
         # Required
         '--xcode-build-version',
         '123abc',
         '--out-dir',
-        'some/dir'
+        'some/dir',
     ]
 
     runner = run.Runner()
     runner.parse_args(cmd)
     self.assertTrue(runner.args.app == './foo-Runner.app')
+    self.assertTrue(runner.args.runtime_cache_prefix == 'some/dir')
+    self.assertTrue(runner.args.xcode_path == 'some/Xcode.app')
 
   def test_parse_args_iossim_platform_version(self):
     """
@@ -175,5 +183,116 @@
     self.assertEquals(runner.args.shards, 2)
 
 
+class RunnerInstallXcodeTest(test_runner_test.TestCase):
+  """Tests Xcode and runtime installing logic in Runner.run()"""
+
+  def setUp(self):
+    super(RunnerInstallXcodeTest, self).setUp()
+    self.runner = run.Runner()
+
+    self.mock(self.runner, 'parse_args', lambda _: None)
+    self.runner.args = mock.MagicMock()
+    # Make run() choose xcodebuild_runner.SimulatorParallelTestRunner as tr.
+    self.runner.args.xcode_parallelization = True
+    # Used in run.Runner.install_xcode().
+    self.runner.args.mac_toolchain_cmd = 'mac_toolchain'
+    self.runner.args.xcode_path = 'test/xcode/path'
+    self.runner.args.xcode_build_version = 'testXcodeVersion'
+    self.runner.args.runtime_cache_prefix = 'test/runtime-ios-'
+    self.runner.args.version = '14.4'
+
+  @mock.patch('test_runner.defaults_delete')
+  @mock.patch('json.dump')
+  @mock.patch('xcode_util.select', autospec=True)
+  @mock.patch('os.path.exists', autospec=True, return_value=True)
+  @mock.patch('xcodebuild_runner.SimulatorParallelTestRunner')
+  @mock.patch('xcode_util.construct_runtime_cache_folder', autospec=True)
+  @mock.patch('xcode_util.install', autospec=True, return_value=True)
+  @mock.patch('xcode_util.move_runtime', autospec=True)
+  def test_legacy_xcode(self, mock_move_runtime, mock_install,
+                        mock_construct_runtime_cache_folder, mock_tr, _1, _2,
+                        _3, _4):
+    mock_construct_runtime_cache_folder.side_effect = lambda a, b: a + b
+    test_runner = mock_tr.return_value
+    test_runner.launch.return_value = True
+    test_runner.logs = {}
+
+    with mock.patch('run.open', mock.mock_open()):
+      self.runner.run(None)
+
+    mock_install.assert_called_with(
+        'mac_toolchain',
+        'testXcodeVersion',
+        'test/xcode/path',
+        runtime_cache_folder='test/runtime-ios-14.4',
+        ios_version='14.4')
+    mock_construct_runtime_cache_folder.assert_called_once_with(
+        'test/runtime-ios-', '14.4')
+    self.assertFalse(mock_move_runtime.called)
+
+  @mock.patch('test_runner.defaults_delete')
+  @mock.patch('json.dump')
+  @mock.patch('xcode_util.select', autospec=True)
+  @mock.patch('os.path.exists', autospec=True, return_value=True)
+  @mock.patch('xcodebuild_runner.SimulatorParallelTestRunner')
+  @mock.patch('xcode_util.construct_runtime_cache_folder', autospec=True)
+  @mock.patch('xcode_util.install', autospec=True, return_value=False)
+  @mock.patch('xcode_util.move_runtime', autospec=True)
+  def test_not_legacy_xcode(self, mock_move_runtime, mock_install,
+                            mock_construct_runtime_cache_folder, mock_tr, _1,
+                            _2, _3, _4):
+    mock_construct_runtime_cache_folder.side_effect = lambda a, b: a + b
+    test_runner = mock_tr.return_value
+    test_runner.launch.return_value = True
+    test_runner.logs = {}
+
+    with mock.patch('run.open', mock.mock_open()):
+      self.runner.run(None)
+
+    mock_install.assert_called_with(
+        'mac_toolchain',
+        'testXcodeVersion',
+        'test/xcode/path',
+        runtime_cache_folder='test/runtime-ios-14.4',
+        ios_version='14.4')
+    self.assertEqual(2, mock_construct_runtime_cache_folder.call_count)
+    mock_construct_runtime_cache_folder.assert_has_calls(calls=[
+        mock.call('test/runtime-ios-', '14.4'),
+        mock.call('test/runtime-ios-', '14.4'),
+    ])
+    mock_move_runtime.assert_called_with('test/runtime-ios-14.4',
+                                         'test/xcode/path', False)
+
+  @mock.patch('test_runner.defaults_delete')
+  @mock.patch('json.dump')
+  @mock.patch('xcode_util.select', autospec=True)
+  @mock.patch('os.path.exists', autospec=True, return_value=True)
+  @mock.patch('xcodebuild_runner.SimulatorParallelTestRunner')
+  @mock.patch('xcode_util.construct_runtime_cache_folder', autospec=True)
+  @mock.patch('xcode_util.install', autospec=True, return_value=False)
+  @mock.patch('xcode_util.move_runtime', autospec=True)
+  def test_device_task(self, mock_move_runtime, mock_install,
+                       mock_construct_runtime_cache_folder, mock_tr, _1, _2, _3,
+                       _4):
+    """Check if Xcode is correctly installed for device tasks."""
+    self.runner.args.version = None
+    test_runner = mock_tr.return_value
+    test_runner.launch.return_value = True
+    test_runner.logs = {}
+
+    with mock.patch('run.open', mock.mock_open()):
+      self.runner.run(None)
+
+    mock_install.assert_called_with(
+        'mac_toolchain',
+        'testXcodeVersion',
+        'test/xcode/path',
+        ios_version=None,
+        runtime_cache_folder=None)
+
+    self.assertFalse(mock_construct_runtime_cache_folder.called)
+    self.assertFalse(mock_move_runtime.called)
+
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/ios/build/bots/scripts/xcode_util.py b/ios/build/bots/scripts/xcode_util.py
index c566dfd..dfe45696 100644
--- a/ios/build/bots/scripts/xcode_util.py
+++ b/ios/build/bots/scripts/xcode_util.py
@@ -91,6 +91,16 @@
   return output
 
 
+def construct_runtime_cache_folder(runtime_cache_prefix, ios_version):
+  """Composes runtime cache folder from it's prefix and ios_version.
+
+  Note: Please keep the pattern consistent between what's being passed into
+  runner script in gn(build/config/ios/ios_test_runner_wrapper.gni), and what's
+  being configured for swarming cache in test configs (testing/buildbot/*).
+  """
+  return runtime_cache_prefix + ios_version
+
+
 def move_runtime(runtime_cache_folder, xcode_app_path, into_xcode):
   """Moves runtime from runtime cache into xcode or vice versa.
 
@@ -220,7 +230,8 @@
     mac_toolchain: (string) Path to mac_toolchain command to install Xcode
     See https://chromium.googlesource.com/infra/infra/+/main/go/src/infra/cmd/mac_toolchain/
     xcode_app_path: (string) Path to install the contents of Xcode.app.
-    runtime_args: Keyword arguments related with runtime installation, namely:
+    runtime_args: Keyword arguments related with runtime installation. Can be
+    empty when installing an Xcode w/o runtime (for real device tasks). Namely:
       runtime_cache_folder: (string) Path to the folder where runtime package
           file (e.g. iOS.simruntime) is stored.
       ios_version: (string) iOS version requested to be in Xcode package.
@@ -246,7 +257,8 @@
     raise test_runner_errors.XcodeMacToolchainMismatchError(xcode_build_version)
 
   # Install & move the runtime to Xcode. Can only work with new mac_toolchain.
-  if not is_legacy_xcode_package:
+  # Only install runtime when it's working for a simulator task.
+  if not is_legacy_xcode_package and runtime_args.get('ios_version'):
     runtime_cache_folder = runtime_args.get('runtime_cache_folder')
     ios_version = runtime_args.get('ios_version')
     if not runtime_cache_folder or not ios_version:
diff --git a/ios/build/bots/scripts/xcode_util_test.py b/ios/build/bots/scripts/xcode_util_test.py
index f146db4..202450a 100644
--- a/ios/build/bots/scripts/xcode_util_test.py
+++ b/ios/build/bots/scripts/xcode_util_test.py
@@ -118,6 +118,28 @@
     mock_move_runtime.assert_called_with('test/path/Runtime',
                                          'test/path/Xcode.app', True)
 
+  @mock.patch('xcode_util.move_runtime', autospec=True)
+  @mock.patch('xcode_util._install_runtime')
+  @mock.patch('xcode_util._install_xcode')
+  def test_new_mactoolchain_new_xcode_no_runtime(self, mock_install_xcode,
+                                                 mock_install_runtime,
+                                                 mock_move_runtime):
+    self.mock(xcode_util, '_using_new_mac_toolchain', lambda cmd: True)
+    self.mock(xcode_util, '_is_legacy_xcode_package', lambda path: False)
+
+    is_legacy_xcode = xcode_util.install(
+        self.mac_toolchain,
+        self.xcode_build_version,
+        self.xcode_app_path,
+        runtime_cache_folder=None,
+        ios_version=None)
+
+    self.assertFalse(is_legacy_xcode, 'install should return False')
+    mock_install_xcode.assert_called_with('mac_toolchain', 'TestXcodeVersion',
+                                          'test/path/Xcode.app', True)
+    self.assertFalse(mock_install_runtime.called)
+    self.assertFalse(mock_move_runtime.called)
+
 
 class HelperFunctionTests(XcodeUtilTest):
   """Test class for xcode_util misc util functions."""
diff --git a/ios/chrome/browser/ui/first_run/welcome/BUILD.gn b/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
index a3be426..0503241 100644
--- a/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
+++ b/ios/chrome/browser/ui/first_run/welcome/BUILD.gn
@@ -12,6 +12,7 @@
     ":static_file_ui",
     ":welcome_ui",
     "//base",
+    "//components/metrics",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
@@ -27,11 +28,16 @@
   sources = [
     "checkbox_button.h",
     "checkbox_button.mm",
+    "welcome_screen_mediator.h",
+    "welcome_screen_mediator.mm",
     "welcome_screen_view_controller.h",
     "welcome_screen_view_controller.mm",
   ]
   deps = [
     "//base",
+    "//components/metrics",
+    "//components/prefs",
+    "//ios/chrome/browser",
     "//ios/chrome/browser/ui/first_run:first_run_ui",
     "//ios/chrome/browser/ui/first_run/resources:welcome_metrics_checkmark",
     "//ios/chrome/browser/ui/first_run/resources:welcome_screen_banner",
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm
index 6f1ca93..ebf3d52 100644
--- a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.mm
@@ -5,9 +5,12 @@
 #import "ios/chrome/browser/ui/first_run/welcome/welcome_screen_coordinator.h"
 
 #include "base/mac/bundle_locations.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/strings/sys_string_conversions.h"
 #include "ios/chrome/browser/main/browser.h"
 #include "ios/chrome/browser/ui/first_run/welcome/static_file_view_controller.h"
+#include "ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.h"
 #include "ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.h"
 #include "ios/chrome/browser/ui/util/terms_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -19,10 +22,16 @@
 
 @interface WelcomeScreenCoordinator () <WelcomeScreenViewControllerDelegate>
 
+@property(nonatomic, weak) id<FirstRunScreenDelegate> delegate;
+
 // Welcome screen view controller.
 @property(nonatomic, strong) WelcomeScreenViewController* viewController;
 
-@property(nonatomic, weak) id<FirstRunScreenDelegate> delegate;
+// Welcome screen mediator.
+@property(nonatomic, strong) WelcomeScreenMediator* mediator;
+
+// Whether the user tapped on the TOS link.
+@property(nonatomic, assign) BOOL TOSLinkWasTapped;
 
 @end
 
@@ -51,6 +60,8 @@
   // if yes:
   self.viewController = [[WelcomeScreenViewController alloc] init];
   self.viewController.delegate = self;
+  self.mediator = [[WelcomeScreenMediator alloc] init];
+
   BOOL animated = self.baseNavigationController.topViewController != nil;
   [self.baseNavigationController setViewControllers:@[ self.viewController ]
                                            animated:animated];
@@ -59,11 +70,14 @@
 - (void)stop {
   self.delegate = nil;
   self.viewController = nil;
+  self.mediator = nil;
 }
 
 #pragma mark - WelcomeScreenViewControllerDelegate
 
 - (void)didTapTOSLink {
+  self.TOSLinkWasTapped = YES;
+
   // Create a StaticFileViewController to show the terms of service page.
   NSString* title = l10n_util::GetNSString(IDS_IOS_FIRSTRUN_TERMS_TITLE);
 
@@ -90,11 +104,18 @@
                                            animated:YES];
 }
 
+- (BOOL)isCheckboxSelectedByDefault {
+  return [self.mediator isCheckboxSelectedByDefault];
+}
+
 - (void)didTapPrimaryActionButton {
-  // TODO(crbug.com/1189815):
-  // 1. Update the pref seervice if the checkbox is selected.
-  // 2. Store a status that the welcome screen has been shown to an
-  // NSUserDefault object.
+  // TODO(crbug.com/1189815): Remember that the welcome screen has been shown in
+  // NSUserDefaults.
+  [self.mediator
+      setMetricsReportingEnabled:self.viewController.checkBoxSelected];
+  if (self.TOSLinkWasTapped) {
+    base::RecordAction(base::UserMetricsAction("MobileFreTOSLinkTapped"));
+  }
 
   [self.delegate willFinishPresenting];
 }
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.h b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.h
new file mode 100644
index 0000000..58b8c9d
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.h
@@ -0,0 +1,24 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_WELCOME_SCREEN_MEDIATOR_H_
+#define IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_WELCOME_SCREEN_MEDIATOR_H_
+
+#import <Foundation/Foundation.h>
+
+// Mediator that handles writing to prefs for the welcome screen.
+@interface WelcomeScreenMediator : NSObject
+
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+// Returns whether the metrics reporting consent checkbox should be selected or
+// not by default.
+- (BOOL)isCheckboxSelectedByDefault;
+
+// Persists the opt-in state for metrics reporting to the prefs.
+- (void)setMetricsReportingEnabled:(BOOL)enabled;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_WELCOME_SCREEN_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.mm b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.mm
new file mode 100644
index 0000000..c0db9df
--- /dev/null
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.mm
@@ -0,0 +1,72 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/first_run/welcome/welcome_screen_mediator.h"
+
+#include "components/metrics/metrics_pref_names.h"
+#include "components/metrics/metrics_reporting_default_state.h"
+#include "components/prefs/pref_service.h"
+#import "ios/chrome/browser/application_context.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Default value for metrics reporting state. "YES" corresponding to "opt-out"
+// state.
+const BOOL kDefaultStatsCheckboxValue = YES;
+
+}  // namespace
+
+@interface WelcomeScreenMediator ()
+
+@end
+
+@implementation WelcomeScreenMediator
+
+#pragma mark - Public
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    [self recordMetricsReportingDefaultState];
+  }
+  return self;
+}
+
+- (BOOL)isCheckboxSelectedByDefault {
+  return kDefaultStatsCheckboxValue;
+}
+
+- (void)setMetricsReportingEnabled:(BOOL)enabled {
+  GetApplicationContext()->GetLocalState()->SetBoolean(
+      metrics::prefs::kMetricsReportingEnabled, enabled);
+}
+
+#pragma mark - Private
+
+// Records what the default opt-in state for metrics reporting is in the local
+// prefs, based on whether the consent checkbox should be selected by default.
+- (void)recordMetricsReportingDefaultState {
+  // Record metrics reporting as opt-in/opt-out only once.
+  static dispatch_once_t once;
+  dispatch_once(&once, ^{
+    // Don't call RecordMetricsReportingDefaultState twice. This can happen if
+    // the app is quit before accepting the TOS, or via experiment settings.
+    if (metrics::GetMetricsReportingDefaultState(
+            GetApplicationContext()->GetLocalState()) !=
+        metrics::EnableMetricsDefault::DEFAULT_UNKNOWN) {
+      return;
+    }
+
+    metrics::RecordMetricsReportingDefaultState(
+        GetApplicationContext()->GetLocalState(),
+        kDefaultStatsCheckboxValue ? metrics::EnableMetricsDefault::OPT_OUT
+                                   : metrics::EnableMetricsDefault::OPT_IN);
+  });
+}
+
+@end
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.h b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.h
index 0f80c20..62800b8 100644
--- a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.h
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.h
@@ -14,6 +14,10 @@
 // Called when the user taps to see the terms and services page.
 - (void)didTapTOSLink;
 
+// Returns whether the metrics reporting consent checkbox should be selected or
+// not by default.
+- (BOOL)isCheckboxSelectedByDefault;
+
 @end
 
 // View controller of welcome screen.
@@ -21,6 +25,9 @@
 
 @property(nonatomic, weak) id<WelcomeScreenViewControllerDelegate> delegate;
 
+// Whether the metrics reporting check box is selected.
+@property(nonatomic, readonly, assign) BOOL checkBoxSelected;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_FIRST_RUN_WELCOME_WELCOME_SCREEN_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
index 5d440d4..fb4b372 100644
--- a/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/welcome/welcome_screen_view_controller.mm
@@ -74,6 +74,12 @@
   [super viewDidLoad];
 }
 
+#pragma mark - Accessors
+
+- (BOOL)checkBoxSelected {
+  return self.metricsConsentButton.selected;
+}
+
 #pragma mark - Private
 
 // Creates and configures the UMA consent checkbox button.
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 052035a..641875c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-62c8b9846fc3d994d4d76ce07390365b30fe127f
\ No newline at end of file
+24f83876706d62d40a8653f5686db19afd609d37
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index 177de99b..c5282bd 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-629dc7e3a44a995f81aa98331b285d97893f613d
\ No newline at end of file
+e6d0f5ab02f467525d48a40b68b73ddb770c786c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index 0df1dd7..ecf23dd1 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-01812579879424e11b5219caab72537bc4bc372e
\ No newline at end of file
+b4d7d4b969e9e98cb30ad98db40112d32d1a264d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index 780547c..5b5e80b 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-ae136fa4fba85714d3e3726dc8bb5cb7f51e987a
\ No newline at end of file
+4106fc4c2563e1289bff15e6fc725683b2fd249a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 12e51e2..5828efe6 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-456f38937b2986fd6cef11e08e4c771f677ee360
\ No newline at end of file
+217768c844e19a6672f4931a1f65c59d91f6520a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 2e93344..2b35363 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-7fd824febf1a64d57436e6c1a61cc7f7864efcfe
\ No newline at end of file
+a8e67b0b28245ad04e0e1e4706fa689086551cd9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 86a5b0b..5640664 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-a224b478669138d21993ef5fde96b22da66896d4
\ No newline at end of file
+3918b8941cff50883ffee71fc2a5e5b865415fbb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 93a1496d..c59e79d 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-189d694320cbd3412d221a34bb52be437d46a62f
\ No newline at end of file
+768d75cce848a1d775c1b924815a7719564162fa
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index e6ca423..505b854 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-43e257e1739aba49f123b36eff40b696cbf1b144
\ No newline at end of file
+04fa41e0f36cc83cfa56dc68772e429d24f34f4b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 98aaa7f..0b0d6f5 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-bf00f574b416035d3ddd49d2ca5083ba24a083b3
\ No newline at end of file
+af614afd68aee82eec99ee86ef5194a7e6e10459
\ No newline at end of file
diff --git a/media/base/audio_buffer.cc b/media/base/audio_buffer.cc
index 4a1a0940..56761fb 100644
--- a/media/base/audio_buffer.cc
+++ b/media/base/audio_buffer.cc
@@ -6,6 +6,7 @@
 
 #include <cmath>
 
+#include "base/callback_helpers.h"
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "media/base/audio_bus.h"
@@ -275,6 +276,44 @@
                       nullptr, 0, kNoTimestamp, nullptr));
 }
 
+// static
+std::unique_ptr<AudioBus> AudioBuffer::WrapOrCopyToAudioBus(
+    scoped_refptr<AudioBuffer> buffer) {
+  DCHECK(buffer);
+
+  const int channels = buffer->channel_count();
+  const int frames = buffer->frame_count();
+
+  DCHECK(channels);
+  DCHECK(frames);
+
+  // |buffer| might already have the right memory layout. Prevent a data copy
+  // by wrapping it instead.
+  if (buffer->sample_format() == SampleFormat::kSampleFormatPlanarF32) {
+    auto audio_bus = AudioBus::CreateWrapper(channels);
+
+    for (int ch = 0; ch < channels; ++ch) {
+      audio_bus->SetChannelData(
+          ch, reinterpret_cast<float*>(buffer->channel_data()[ch]));
+    }
+
+    audio_bus->set_frames(frames);
+
+    // Keep |buffer| alive as long as |audio_bus|.
+    audio_bus->SetWrappedDataDeleter(
+        base::BindOnce(base::DoNothing::Once<scoped_refptr<AudioBuffer>>(),
+                       std::move(buffer)));
+
+    return audio_bus;
+  }
+
+  // |buffer|'s memory can't be wrapped directly. Convert and copy it instead.
+  auto audio_bus = AudioBus::Create(channels, frames);
+  buffer->ReadFrames(frames, 0, 0, audio_bus.get());
+
+  return audio_bus;
+}
+
 void AudioBuffer::AdjustSampleRate(int sample_rate) {
   DCHECK(!end_of_stream_);
   sample_rate_ = sample_rate;
diff --git a/media/base/audio_buffer.h b/media/base/audio_buffer.h
index af0a4f6..7042fab 100644
--- a/media/base/audio_buffer.h
+++ b/media/base/audio_buffer.h
@@ -119,6 +119,13 @@
       int frame_count,
       const base::TimeDelta timestamp);
 
+  // Helper function that creates a new AudioBus which wraps |audio_buffer| and
+  // takes a reference on it, if the memory layout (e.g. |sample_format_|) is
+  // compatible with wrapping. Otherwise, this copies |audio_buffer| to a new
+  // AudioBus, using ReadFrames().
+  static std::unique_ptr<AudioBus> WrapOrCopyToAudioBus(
+      scoped_refptr<AudioBuffer> audio_buffer);
+
   // Create a AudioBuffer indicating we've reached end of stream.
   // Calling any method other than end_of_stream() on the resulting buffer
   // is disallowed.
diff --git a/media/base/audio_buffer_unittest.cc b/media/base/audio_buffer_unittest.cc
index 5c000a8..88ea630 100644
--- a/media/base/audio_buffer_unittest.cc
+++ b/media/base/audio_buffer_unittest.cc
@@ -494,6 +494,38 @@
   VerifyBus(bus.get(), frames, 1, 1, ValueType::kFloat);
 }
 
+TEST(AudioBufferTest, WrapOrCopyToAudioBus) {
+  const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0;
+  const int channels = ChannelLayoutToChannelCount(channel_layout);
+  const int frames = 100;
+  const base::TimeDelta start_time;
+  scoped_refptr<AudioBuffer> buffer =
+      MakeAudioBuffer<float>(kSampleFormatPlanarF32, channel_layout, channels,
+                             kSampleRate, 1.0f, 1.0f, frames, start_time);
+
+  // With kSampleFormatPlanarF32, the memory layout should allow |bus| to
+  // directly wrap |buffer|'s data.
+  std::unique_ptr<AudioBus> bus = AudioBuffer::WrapOrCopyToAudioBus(buffer);
+  for (int ch = 0; ch < channels; ++ch) {
+    EXPECT_EQ(bus->channel(ch),
+              reinterpret_cast<float*>(buffer->channel_data()[ch]));
+  }
+
+  // |bus| should have its own reference on |buffer|, so clearing it here should
+  // not free the underlying data.
+  buffer.reset();
+  VerifyBus(bus.get(), frames, 1, 1, ValueType::kFloat);
+
+  // Interleaved samples cannot be wrapped, and samples will be copied out.
+  buffer = MakeAudioBuffer<float>(kSampleFormatF32, channel_layout, channels,
+                                  kSampleRate, 1.0f, 1.0f, frames, start_time);
+
+  bus = AudioBuffer::WrapOrCopyToAudioBus(buffer);
+  buffer.reset();
+
+  VerifyBus(bus.get(), frames, 1, 1, ValueType::kFloat);
+}
+
 TEST(AudioBufferTest, EmptyBuffer) {
   const ChannelLayout channel_layout = CHANNEL_LAYOUT_4_0;
   const int channels = ChannelLayoutToChannelCount(channel_layout);
diff --git a/media/base/audio_bus.cc b/media/base/audio_bus.cc
index 76ee5c2..3115558f 100644
--- a/media/base/audio_bus.cc
+++ b/media/base/audio_bus.cc
@@ -64,8 +64,7 @@
 }
 
 AudioBus::AudioBus(int channels, int frames)
-    : frames_(frames),
-      can_set_channel_data_(false) {
+    : frames_(frames), is_wrapper_(false) {
   ValidateConfig(channels, frames_);
 
   int aligned_frames = 0;
@@ -78,8 +77,7 @@
 }
 
 AudioBus::AudioBus(int channels, int frames, float* data)
-    : frames_(frames),
-      can_set_channel_data_(false) {
+    : frames_(frames), is_wrapper_(false) {
   // Since |data| may have come from an external source, ensure it's valid.
   CHECK(data);
   ValidateConfig(channels, frames_);
@@ -91,9 +89,7 @@
 }
 
 AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data)
-    : channel_data_(channel_data),
-      frames_(frames),
-      can_set_channel_data_(false) {
+    : channel_data_(channel_data), frames_(frames), is_wrapper_(false) {
   ValidateConfig(
       base::checked_cast<int>(channel_data_.size()), frames_);
 
@@ -103,15 +99,16 @@
 }
 
 AudioBus::AudioBus(int channels)
-    : channel_data_(channels),
-      frames_(0),
-      can_set_channel_data_(true) {
+    : channel_data_(channels), frames_(0), is_wrapper_(true) {
   CHECK_GT(channels, 0);
   for (size_t i = 0; i < channel_data_.size(); ++i)
     channel_data_[i] = NULL;
 }
 
-AudioBus::~AudioBus() = default;
+AudioBus::~AudioBus() {
+  if (wrapped_data_deleter_cb_)
+    std::move(wrapped_data_deleter_cb_).Run();
+}
 
 std::unique_ptr<AudioBus> AudioBus::Create(int channels, int frames) {
   return base::WrapUnique(new AudioBus(channels, frames));
@@ -171,7 +168,7 @@
 }
 
 void AudioBus::SetChannelData(int channel, float* data) {
-  CHECK(can_set_channel_data_);
+  CHECK(is_wrapper_);
   CHECK(data);
   CHECK_GE(channel, 0);
   CHECK_LT(static_cast<size_t>(channel), channel_data_.size());
@@ -180,11 +177,17 @@
 }
 
 void AudioBus::set_frames(int frames) {
-  CHECK(can_set_channel_data_);
+  CHECK(is_wrapper_);
   ValidateConfig(static_cast<int>(channel_data_.size()), frames);
   frames_ = frames;
 }
 
+void AudioBus::SetWrappedDataDeleter(base::OnceClosure deleter) {
+  CHECK(is_wrapper_);
+  DCHECK(!wrapped_data_deleter_cb_);
+  wrapped_data_deleter_cb_ = std::move(deleter);
+}
+
 size_t AudioBus::GetBitstreamDataSize() const {
   DCHECK(is_bitstream_format_);
   return bitstream_data_size_;
diff --git a/media/base/audio_bus.h b/media/base/audio_bus.h
index 1c520279..75792ba 100644
--- a/media/base/audio_bus.h
+++ b/media/base/audio_bus.h
@@ -10,6 +10,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/macros.h"
 #include "base/memory/aligned_memory.h"
 #include "media/base/audio_sample_types.h"
@@ -76,6 +77,13 @@
   void SetChannelData(int channel, float* data);
   void set_frames(int frames);
 
+  // Method optionally called after AudioBus::CreateWrapper().
+  // Runs |deleter| when on |this|' destruction, freeing external data
+  // referenced by SetChannelData().
+  // Note: It is illegal to call this method when using a factory method other
+  // than CreateWrapper().
+  void SetWrappedDataDeleter(base::OnceClosure deleter);
+
   // Methods for compressed bitstream formats. The data size may not be equal to
   // the capacity of the AudioBus. Also, the frame count may not be equal to the
   // capacity of the AudioBus. Thus, we need extra methods to access the real
@@ -220,8 +228,13 @@
   std::vector<float*> channel_data_;
   int frames_;
 
-  // Protect SetChannelData() and set_frames() for use by CreateWrapper().
-  bool can_set_channel_data_;
+  // Protect SetChannelData(), set_frames() and SetWrappedDataDeleter() for use
+  // by CreateWrapper().
+  bool is_wrapper_;
+
+  // Run on destruction. Frees memory to the data set via SetChannelData().
+  // Only used with CreateWrapper().
+  base::OnceClosure wrapped_data_deleter_cb_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioBus);
 };
diff --git a/media/base/audio_bus_unittest.cc b/media/base/audio_bus_unittest.cc
index 38d5cbd09..e2cac3c8 100644
--- a/media/base/audio_bus_unittest.cc
+++ b/media/base/audio_bus_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/memory/aligned_memory.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "media/base/audio_bus.h"
@@ -157,8 +158,16 @@
   for (int i = 0; i < bus->channels(); ++i)
     bus->SetChannelData(i, data_[i]);
 
+  bool deleted = false;
+  bus->SetWrappedDataDeleter(
+      base::BindLambdaForTesting([&]() { deleted = true; }));
+
   VerifyChannelAndFrameCount(bus.get());
   VerifyReadWriteAndAlignment(bus.get());
+
+  EXPECT_FALSE(deleted);
+  bus.reset();
+  EXPECT_TRUE(deleted);
 }
 
 // Verify an AudioBus created via wrapping a vector works as advertised.
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 9eeeb52..72f109e 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -580,16 +580,16 @@
   DCHECK(cdm_context_ || !HasEncryptedStream())
       << "CDM should be available now if has encrypted stream";
 
-  base::Optional<RendererFactoryType> factory_type;
+  base::Optional<RendererType> renderer_type;
 
 #if defined(OS_WIN)
   if (cdm_context_ && cdm_context_->RequiresMediaFoundationRenderer())
-    factory_type = RendererFactoryType::kMediaFoundation;
+    renderer_type = RendererType::kMediaFoundation;
 #endif  // defined(OS_WIN)
 
   // TODO(xhwang): During Resume(), the |default_renderer_| might already match
-  // the |factory_type|, in which case we shouldn't need to create a new one.
-  if (!default_renderer_ || factory_type) {
+  // the |renderer_type|, in which case we shouldn't need to create a new one.
+  if (!default_renderer_ || renderer_type) {
     // Create the Renderer asynchronously on the main task runner. Use
     // BindToCurrentLoop to call OnRendererCreated() on the media task runner.
     auto renderer_created_cb = BindToCurrentLoop(
@@ -598,7 +598,7 @@
     main_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(&PipelineImpl::AsyncCreateRenderer, weak_pipeline_,
-                       factory_type, std::move(renderer_created_cb)));
+                       renderer_type, std::move(renderer_created_cb)));
     return;
   }
 
@@ -1507,12 +1507,12 @@
 #undef RETURN_STRING
 
 void PipelineImpl::AsyncCreateRenderer(
-    base::Optional<RendererFactoryType> factory_type,
+    base::Optional<RendererType> renderer_type,
     RendererCreatedCB renderer_created_cb) {
   DVLOG(2) << __func__;
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  std::move(renderer_created_cb).Run(create_renderer_cb_.Run(factory_type));
+  std::move(renderer_created_cb).Run(create_renderer_cb_.Run(renderer_type));
 }
 
 void PipelineImpl::OnError(PipelineStatus error) {
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index beb88ad..74f9fd5 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -25,13 +25,13 @@
 
 class MediaLog;
 
-// Callbacks used for Renderer creation. When the FactoryType is nullopt, the
+// Callbacks used for Renderer creation. When the RendererType is nullopt, the
 // current base one will be created.
 using CreateRendererCB = base::RepeatingCallback<std::unique_ptr<Renderer>(
-    base::Optional<RendererFactoryType>)>;
+    base::Optional<RendererType>)>;
 using RendererCreatedCB = base::OnceCallback<void(std::unique_ptr<Renderer>)>;
 using AsyncCreateRendererCB =
-    base::RepeatingCallback<void(base::Optional<RendererFactoryType>,
+    base::RepeatingCallback<void(base::Optional<RendererType>,
                                  RendererCreatedCB)>;
 
 // Pipeline runs the media pipeline.  Filters are created and called on the
@@ -146,7 +146,7 @@
 
   // Create a Renderer asynchronously. Must be called on the main task runner
   // and the callback will be called on the main task runner as well.
-  void AsyncCreateRenderer(base::Optional<RendererFactoryType> factory_type,
+  void AsyncCreateRenderer(base::Optional<RendererType> renderer_type,
                            RendererCreatedCB renderer_created_cb);
 
   // Notifications from RendererWrapper.
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc
index 8ff37cf..dc0a9b8 100644
--- a/media/base/pipeline_impl_unittest.cc
+++ b/media/base/pipeline_impl_unittest.cc
@@ -295,7 +295,7 @@
   }
 
   std::unique_ptr<Renderer> CreateRenderer(
-      base::Optional<RendererFactoryType> /* factory_type */) {
+      base::Optional<RendererType> /* renderer_type */) {
     return std::move(scoped_renderer_);
   }
 
diff --git a/media/base/renderer_factory_selector.cc b/media/base/renderer_factory_selector.cc
index 0e912d8..c3d8fa5 100644
--- a/media/base/renderer_factory_selector.cc
+++ b/media/base/renderer_factory_selector.cc
@@ -13,30 +13,30 @@
 RendererFactorySelector::~RendererFactorySelector() = default;
 
 void RendererFactorySelector::AddBaseFactory(
-    RendererFactoryType type,
+    RendererType type,
     std::unique_ptr<RendererFactory> factory) {
   DVLOG(1) << __func__ << ": type=" << static_cast<int>(type);
-  DCHECK(!base_factory_type_) << "At most one base factory!";
+  DCHECK(!base_renderer_type_) << "At most one base factory!";
 
   AddFactory(type, std::move(factory));
-  SetBaseFactoryType(type);
+  SetBaseRendererType(type);
 }
 
 void RendererFactorySelector::AddConditionalFactory(
-    RendererFactoryType type,
+    RendererType type,
     std::unique_ptr<RendererFactory> factory,
     ConditionalFactoryCB callback) {
   DCHECK(factory);
   DCHECK(callback);
-  DCHECK(!conditional_factory_types_.count(type))
+  DCHECK(!conditional_factories_.count(type))
       << "At most one conditional factory for a given type!";
 
-  conditional_factory_types_.emplace(type, callback);
+  conditional_factories_.emplace(type, callback);
   AddFactory(type, std::move(factory));
 }
 
 void RendererFactorySelector::AddFactory(
-    RendererFactoryType type,
+    RendererType type,
     std::unique_ptr<RendererFactory> factory) {
   DCHECK(factory);
   DCHECK(!factories_.count(type));
@@ -44,26 +44,26 @@
   factories_[type] = std::move(factory);
 }
 
-void RendererFactorySelector::SetBaseFactoryType(RendererFactoryType type) {
+void RendererFactorySelector::SetBaseRendererType(RendererType type) {
   DCHECK(factories_.count(type));
-  base_factory_type_ = type;
+  base_renderer_type_ = type;
 }
 
-RendererFactoryType RendererFactorySelector::GetCurrentFactoryType() {
-  for (const auto& entry : conditional_factory_types_) {
+RendererType RendererFactorySelector::GetCurrentRendererType() {
+  for (const auto& entry : conditional_factories_) {
     if (entry.second.Run())
       return entry.first;
   }
 
-  return base_factory_type_.value();
+  return base_renderer_type_.value();
 }
 
 RendererFactory* RendererFactorySelector::GetCurrentFactory() {
-  RendererFactoryType current_factory_type = GetCurrentFactoryType();
+  RendererType current_renderer_type = GetCurrentRendererType();
 
   DVLOG(1) << __func__ << " Selecting factory type: "
-           << static_cast<int>(current_factory_type);
-  auto* current_factory = factories_[current_factory_type].get();
+           << static_cast<int>(current_renderer_type);
+  auto* current_factory = factories_[current_renderer_type].get();
   DCHECK(current_factory);
 
   return current_factory;
diff --git a/media/base/renderer_factory_selector.h b/media/base/renderer_factory_selector.h
index f8db4a9..0522e32 100644
--- a/media/base/renderer_factory_selector.h
+++ b/media/base/renderer_factory_selector.h
@@ -30,14 +30,14 @@
 //
 // Notes:
 // - One and at most one base factory must be set.
-// - The base factory can be changed by calling SetBaseFactoryType().
+// - The base factory can be changed by calling SetBaseRendererType().
 // - Multiple conditional factories are supported but there should be at most
 //   one conditional factory for any factory type. If multiple conditions are
 //   met, it's up to the implementation detail which factory will be returned.
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
-enum class RendererFactoryType {
+enum class RendererType {
   kDefault = 0,          // DefaultRendererFactory
   kMojo = 1,             // MojoRendererFactory
   kMediaPlayer = 2,      // MediaPlayerRendererClientFactory
@@ -46,7 +46,7 @@
   kCast = 5,             // CastRendererClientFactory
   kMediaFoundation = 6,  // MediaFoundationRendererClientFactory
   kFuchsia = 7,          // FuchsiaRendererFactory
-  kRemoting = 8,         // RemotingRendererFactory
+  kRemoting = 8,         // RemotingRendererFactory for remoting::Receiver
   kMaxValue = kRemoting,
 };
 
@@ -58,27 +58,26 @@
   ~RendererFactorySelector();
 
   // See file level comments above.
-  void AddBaseFactory(RendererFactoryType type,
+  void AddBaseFactory(RendererType type,
                       std::unique_ptr<RendererFactory> factory);
-  void AddConditionalFactory(RendererFactoryType type,
+  void AddConditionalFactory(RendererType type,
                              std::unique_ptr<RendererFactory> factory,
                              ConditionalFactoryCB callback);
-  void AddFactory(RendererFactoryType type,
-                  std::unique_ptr<RendererFactory> factory);
+  void AddFactory(RendererType type, std::unique_ptr<RendererFactory> factory);
 
   // Sets the base factory to be returned, when there are no signals telling us
   // to select any specific factory.
-  // NOTE: |type| can be different than FactoryType::kDefault. kDefault is used
+  // NOTE: |type| can be different than RendererType::kDefault. kDefault is used
   // to identify the DefaultRendererFactory, not to indicate that a factory
   // should be used by default.
-  void SetBaseFactoryType(RendererFactoryType type);
+  void SetBaseRendererType(RendererType type);
 
-  // Returns the type of the factory that GetCurrentFactory() would return.
-  // NOTE: SetBaseFactoryType() must be called before calling this method.
-  RendererFactoryType GetCurrentFactoryType();
+  // Returns the type of the Renderer for what GetCurrentFactory() would return.
+  // NOTE: SetBaseRendererType() must be called before calling this method.
+  RendererType GetCurrentRendererType();
 
   // Updates |current_factory_| if necessary, and returns its value.
-  // NOTE: SetBaseFactoryType() must be called before calling this method.
+  // NOTE: SetBaseRendererType() must be called before calling this method.
   RendererFactory* GetCurrentFactory();
 
 #if defined(OS_ANDROID)
@@ -94,15 +93,14 @@
 #endif
 
  private:
-  base::Optional<RendererFactoryType> base_factory_type_;
+  base::Optional<RendererType> base_renderer_type_;
 
-  // Use a map to avoid duplicate entires for the same FactoryType.
-  std::map<RendererFactoryType, ConditionalFactoryCB>
-      conditional_factory_types_;
+  // Use a map to avoid duplicate entries for the same RendererType.
+  std::map<RendererType, ConditionalFactoryCB> conditional_factories_;
 
   RequestRemotePlayStateChangeCB remote_play_state_change_cb_request_;
 
-  std::map<RendererFactoryType, std::unique_ptr<RendererFactory>> factories_;
+  std::map<RendererType, std::unique_ptr<RendererFactory>> factories_;
 
   DISALLOW_COPY_AND_ASSIGN(RendererFactorySelector);
 };
diff --git a/media/base/renderer_factory_selector_unittest.cc b/media/base/renderer_factory_selector_unittest.cc
index c4900ab..468d49c 100644
--- a/media/base/renderer_factory_selector_unittest.cc
+++ b/media/base/renderer_factory_selector_unittest.cc
@@ -14,11 +14,9 @@
 
 class RendererFactorySelectorTest : public testing::Test {
  public:
-  using FactoryType = RendererFactoryType;
-
   class FakeFactory : public RendererFactory {
    public:
-    explicit FakeFactory(FactoryType type) : type_(type) {}
+    explicit FakeFactory(RendererType type) : type_(type) {}
 
     std::unique_ptr<Renderer> CreateRenderer(
         const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
@@ -30,23 +28,23 @@
       return nullptr;
     }
 
-    FactoryType factory_type() { return type_; }
+    RendererType factory_type() { return type_; }
 
    private:
-    FactoryType type_;
+    RendererType type_;
   };
 
   RendererFactorySelectorTest() = default;
 
-  void AddBaseFactory(FactoryType type) {
+  void AddBaseFactory(RendererType type) {
     selector_.AddBaseFactory(type, std::make_unique<FakeFactory>(type));
   }
 
-  void AddFactory(FactoryType type) {
+  void AddFactory(RendererType type) {
     selector_.AddFactory(type, std::make_unique<FakeFactory>(type));
   }
 
-  void AddConditionalFactory(FactoryType type) {
+  void AddConditionalFactory(RendererType type) {
     condition_met_map_[type] = false;
     selector_.AddConditionalFactory(
         type, std::make_unique<FakeFactory>(type),
@@ -54,75 +52,75 @@
                             base::Unretained(this), type));
   }
 
-  FactoryType GetCurrentlySelectedFactoryType() {
+  RendererType GetCurrentlySelectedRendererType() {
     return reinterpret_cast<FakeFactory*>(selector_.GetCurrentFactory())
         ->factory_type();
   }
 
-  bool IsConditionMet(FactoryType type) {
+  bool IsConditionMet(RendererType type) {
     DCHECK(condition_met_map_.count(type));
     return condition_met_map_[type];
   }
 
  protected:
   RendererFactorySelector selector_;
-  std::map<FactoryType, bool> condition_met_map_;
+  std::map<RendererType, bool> condition_met_map_;
 
   DISALLOW_COPY_AND_ASSIGN(RendererFactorySelectorTest);
 };
 
 TEST_F(RendererFactorySelectorTest, SingleFactory) {
-  AddBaseFactory(FactoryType::kDefault);
-  EXPECT_EQ(FactoryType::kDefault, GetCurrentlySelectedFactoryType());
+  AddBaseFactory(RendererType::kDefault);
+  EXPECT_EQ(RendererType::kDefault, GetCurrentlySelectedRendererType());
 }
 
 TEST_F(RendererFactorySelectorTest, MultipleFactory) {
-  AddBaseFactory(FactoryType::kDefault);
-  AddFactory(FactoryType::kMojo);
+  AddBaseFactory(RendererType::kDefault);
+  AddFactory(RendererType::kMojo);
 
-  EXPECT_EQ(FactoryType::kDefault, GetCurrentlySelectedFactoryType());
+  EXPECT_EQ(RendererType::kDefault, GetCurrentlySelectedRendererType());
 
-  selector_.SetBaseFactoryType(FactoryType::kMojo);
-  EXPECT_EQ(FactoryType::kMojo, GetCurrentlySelectedFactoryType());
+  selector_.SetBaseRendererType(RendererType::kMojo);
+  EXPECT_EQ(RendererType::kMojo, GetCurrentlySelectedRendererType());
 }
 
 TEST_F(RendererFactorySelectorTest, ConditionalFactory) {
-  AddBaseFactory(FactoryType::kDefault);
-  AddFactory(FactoryType::kMojo);
-  AddConditionalFactory(FactoryType::kCourier);
+  AddBaseFactory(RendererType::kDefault);
+  AddFactory(RendererType::kMojo);
+  AddConditionalFactory(RendererType::kCourier);
 
-  EXPECT_EQ(FactoryType::kDefault, GetCurrentlySelectedFactoryType());
+  EXPECT_EQ(RendererType::kDefault, GetCurrentlySelectedRendererType());
 
-  condition_met_map_[FactoryType::kCourier] = true;
-  EXPECT_EQ(FactoryType::kCourier, GetCurrentlySelectedFactoryType());
+  condition_met_map_[RendererType::kCourier] = true;
+  EXPECT_EQ(RendererType::kCourier, GetCurrentlySelectedRendererType());
 
-  selector_.SetBaseFactoryType(FactoryType::kMojo);
-  EXPECT_EQ(FactoryType::kCourier, GetCurrentlySelectedFactoryType());
+  selector_.SetBaseRendererType(RendererType::kMojo);
+  EXPECT_EQ(RendererType::kCourier, GetCurrentlySelectedRendererType());
 
-  condition_met_map_[FactoryType::kCourier] = false;
-  EXPECT_EQ(FactoryType::kMojo, GetCurrentlySelectedFactoryType());
+  condition_met_map_[RendererType::kCourier] = false;
+  EXPECT_EQ(RendererType::kMojo, GetCurrentlySelectedRendererType());
 }
 
 TEST_F(RendererFactorySelectorTest, MultipleConditionalFactories) {
-  AddBaseFactory(FactoryType::kDefault);
-  AddConditionalFactory(FactoryType::kFlinging);
-  AddConditionalFactory(FactoryType::kCourier);
+  AddBaseFactory(RendererType::kDefault);
+  AddConditionalFactory(RendererType::kFlinging);
+  AddConditionalFactory(RendererType::kCourier);
 
-  EXPECT_EQ(FactoryType::kDefault, GetCurrentlySelectedFactoryType());
+  EXPECT_EQ(RendererType::kDefault, GetCurrentlySelectedRendererType());
 
-  condition_met_map_[FactoryType::kFlinging] = false;
-  condition_met_map_[FactoryType::kCourier] = true;
-  EXPECT_EQ(FactoryType::kCourier, GetCurrentlySelectedFactoryType());
+  condition_met_map_[RendererType::kFlinging] = false;
+  condition_met_map_[RendererType::kCourier] = true;
+  EXPECT_EQ(RendererType::kCourier, GetCurrentlySelectedRendererType());
 
-  condition_met_map_[FactoryType::kFlinging] = true;
-  condition_met_map_[FactoryType::kCourier] = false;
-  EXPECT_EQ(FactoryType::kFlinging, GetCurrentlySelectedFactoryType());
+  condition_met_map_[RendererType::kFlinging] = true;
+  condition_met_map_[RendererType::kCourier] = false;
+  EXPECT_EQ(RendererType::kFlinging, GetCurrentlySelectedRendererType());
 
   // It's up to the implementation detail to decide which one to use.
-  condition_met_map_[FactoryType::kFlinging] = true;
-  condition_met_map_[FactoryType::kCourier] = true;
-  EXPECT_TRUE(GetCurrentlySelectedFactoryType() == FactoryType::kFlinging ||
-              GetCurrentlySelectedFactoryType() == FactoryType::kCourier);
+  condition_met_map_[RendererType::kFlinging] = true;
+  condition_met_map_[RendererType::kCourier] = true;
+  EXPECT_TRUE(GetCurrentlySelectedRendererType() == RendererType::kFlinging ||
+              GetCurrentlySelectedRendererType() == RendererType::kCourier);
 }
 
 }  // namespace media
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index 551bdba..6bcd9bd 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -105,7 +105,7 @@
 
 const char kWatchTimeHistogram[] = "Media.WebMediaPlayerImpl.WatchTime";
 
-void RecordSimpleWatchTimeUMA(RendererFactoryType type) {
+void RecordSimpleWatchTimeUMA(RendererType type) {
   UMA_HISTOGRAM_ENUMERATION(kWatchTimeHistogram, type);
 }
 
@@ -1799,8 +1799,7 @@
         "Media.WebMediaPlayerImpl.HLS.IsMixedContent",
         frame_url_is_cryptographic && !manifest_url_is_cryptographic);
 
-    renderer_factory_selector_->SetBaseFactoryType(
-        RendererFactoryType::kMediaPlayer);
+    renderer_factory_selector_->SetBaseRendererType(RendererType::kMediaPlayer);
 
     loaded_url_ = mb_data_source_->GetUrlAfterRedirects();
     DCHECK(data_source_);
@@ -2725,7 +2724,7 @@
 }
 
 std::unique_ptr<Renderer> WebMediaPlayerImpl::CreateRenderer(
-    base::Optional<RendererFactoryType> factory_type) {
+    base::Optional<RendererType> renderer_type) {
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
   // Make sure that overlays are enabled if they're always allowed.
@@ -2738,13 +2737,14 @@
       &WebMediaPlayerImpl::OnOverlayInfoRequested, weak_this_));
 #endif
 
-  if (factory_type) {
+  if (renderer_type) {
     DVLOG(1) << __func__
-             << ": factory_type=" << static_cast<int>(factory_type.value());
-    renderer_factory_selector_->SetBaseFactoryType(factory_type.value());
+             << ": renderer_type=" << static_cast<int>(renderer_type.value());
+    renderer_factory_selector_->SetBaseRendererType(renderer_type.value());
   }
 
-  reported_renderer_type_ = renderer_factory_selector_->GetCurrentFactoryType();
+  reported_renderer_type_ =
+      renderer_factory_selector_->GetCurrentRendererType();
 
   return renderer_factory_selector_->GetCurrentFactory()->CreateRenderer(
       media_task_runner_, worker_task_runner_, audio_source_provider_.get(),
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index 6bcb8362..1fb68b7 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -372,10 +372,10 @@
                               ProvideOverlayInfoCB provide_overlay_info_cb);
 
   // Creates a Renderer via the |renderer_factory_selector_|. If the
-  // |factory_type| is base::nullopt, create the base Renderer. Otherwise, set
-  // the base type to be |factory_type| and create a Renderer of that type.
+  // |renderer_type| is base::nullopt, create the base Renderer. Otherwise, set
+  // the base type to be |renderer_type| and create a Renderer of that type.
   std::unique_ptr<Renderer> CreateRenderer(
-      base::Optional<RendererFactoryType> factory_type);
+      base::Optional<RendererType> renderer_type);
 
   // Finishes starting the pipeline due to a call to load().
   void StartPipeline();
@@ -1015,7 +1015,7 @@
   base::CancelableOnceClosure have_enough_after_lazy_load_cb_;
 
   // State for simplified watch time reporting.
-  RendererFactoryType reported_renderer_type_ = RendererFactoryType::kDefault;
+  RendererType reported_renderer_type_ = RendererType::kDefault;
   SimpleWatchTimer simple_watch_timer_;
 
   LearningExperimentHelper will_play_helper_;
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc
index 3aa9942..cab3ca1 100644
--- a/media/blink/webmediaplayer_impl_unittest.cc
+++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -389,14 +389,14 @@
     decoder_factory_ = std::make_unique<media::DefaultDecoderFactory>(nullptr);
 #if defined(OS_ANDROID)
     factory_selector->AddBaseFactory(
-        RendererFactoryType::kDefault,
+        RendererType::kDefault,
         std::make_unique<DefaultRendererFactory>(
             media_log.get(), decoder_factory_.get(),
             DefaultRendererFactory::GetGpuFactoriesCB()));
     factory_selector->StartRequestRemotePlayStateCB(base::DoNothing());
 #else
     factory_selector->AddBaseFactory(
-        RendererFactoryType::kDefault,
+        RendererType::kDefault,
         std::make_unique<DefaultRendererFactory>(
             media_log.get(), decoder_factory_.get(),
             DefaultRendererFactory::GetGpuFactoriesCB(), nullptr));
@@ -1755,7 +1755,7 @@
         return mock_renderer;
       })));
 
-  renderer_factory_selector_->AddFactory(RendererFactoryType::kMediaFoundation,
+  renderer_factory_selector_->AddFactory(RendererType::kMediaFoundation,
                                          std::move(mock_renderer_factory));
 
   // Create and set CDM. The CDM doesn't support a Decryptor and requires Media
diff --git a/media/cast/receiver/video_decoder.cc b/media/cast/receiver/video_decoder.cc
index ecf20f4..27f6aed 100644
--- a/media/cast/receiver/video_decoder.cc
+++ b/media/cast/receiver/video_decoder.cc
@@ -199,17 +199,10 @@
       return nullptr;
     std::unique_ptr<base::Value> values(base::JSONReader::ReadDeprecated(
         base::StringPiece(reinterpret_cast<char*>(data), len)));
-    if (!values)
+    if (!values || !values->is_dict())
       return nullptr;
-    base::DictionaryValue* dict = nullptr;
-    values->GetAsDictionary(&dict);
 
-    bool key = false;
-    int id = 0;
-    int ref = 0;
-    dict->GetBoolean("key", &key);
-    dict->GetInteger("id", &id);
-    dict->GetInteger("ref", &ref);
+    int id = values->FindIntKey("id").value_or(0);
     DCHECK(id == last_decoded_id_ + 1);
     last_decoded_id_ = id;
     return media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
diff --git a/media/remoting/integration_test.cc b/media/remoting/integration_test.cc
index bbac6f6..0f81ff1 100644
--- a/media/remoting/integration_test.cc
+++ b/media/remoting/integration_test.cc
@@ -25,9 +25,9 @@
 
  private:
   std::unique_ptr<Renderer> CreateEnd2EndTestRenderer(
-      base::Optional<RendererFactoryType> factory_type) {
+      base::Optional<RendererType> renderer_type) {
     return std::make_unique<End2EndTestRenderer>(
-        this->CreateDefaultRenderer(factory_type));
+        this->CreateDefaultRenderer(renderer_type));
   }
 
   DISALLOW_COPY_AND_ASSIGN(MediaRemotingIntegrationTest);
diff --git a/media/renderers/paint_canvas_video_renderer.cc b/media/renderers/paint_canvas_video_renderer.cc
index 6036e66..fd23d7d3 100644
--- a/media/renderers/paint_canvas_video_renderer.cc
+++ b/media/renderers/paint_canvas_video_renderer.cc
@@ -698,6 +698,13 @@
     return raster_context_provider_;
   }
 
+  // Used only for recycling this TextureBacking - where we need to keep the
+  // texture/mailbox alive, but replace the SkImage.
+  void ReplaceAcceleratedSkImage(sk_sp<SkImage> sk_image) {
+    sk_image_ = sk_image;
+    sk_image_info_ = sk_image->imageInfo();
+  }
+
   sk_sp<SkImage> GetSkImageViaReadback() override {
     if (sk_image_)
       return sk_image_->makeNonTextureImage();
@@ -1746,36 +1753,42 @@
 
       cache_->coded_size = video_frame->coded_size();
       cache_->visible_rect = video_frame->visible_rect();
-      if (!cache_->texture_backing) {
-        if (supports_oop_raster) {
-          SkImageInfo sk_image_info =
-              SkImageInfo::Make(gfx::SizeToSkISize(cache_->coded_size),
-                                kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-          cache_->texture_backing = sk_make_sp<VideoTextureBacking>(
-              mailbox, sk_image_info, wraps_video_frame_texture,
-              raster_context_provider);
-        } else {
-          cache_->source_texture = ri->CreateAndConsumeForGpuRaster(mailbox);
 
-          // TODO(nazabris): Handle scoped access correctly. This follows the
-          // current pattern but is most likely bugged. Access should last for
-          // the lifetime of the SkImage.
-          ScopedSharedImageAccess(ri, cache_->source_texture, mailbox);
-          auto source_image =
-              WrapGLTexture(wraps_video_frame_texture
-                                ? video_frame->mailbox_holder(0).texture_target
-                                : GL_TEXTURE_2D,
-                            cache_->source_texture, video_frame->coded_size(),
-                            raster_context_provider);
-          if (!source_image) {
-            // Couldn't create the SkImage.
-            cache_.reset();
-            return false;
-          }
+      // In OOPR mode, we can keep the entire TextureBacking. In non-OOPR,
+      // we can recycle the mailbox/texture, but have to replace the SkImage.
+      if (!supports_oop_raster) {
+        cache_->source_texture = ri->CreateAndConsumeForGpuRaster(mailbox);
+
+        // TODO(nazabris): Handle scoped access correctly. This follows the
+        // current pattern but is most likely bugged. Access should last for
+        // the lifetime of the SkImage.
+        ScopedSharedImageAccess(ri, cache_->source_texture, mailbox);
+        auto source_image =
+            WrapGLTexture(wraps_video_frame_texture
+                              ? video_frame->mailbox_holder(0).texture_target
+                              : GL_TEXTURE_2D,
+                          cache_->source_texture, video_frame->coded_size(),
+                          raster_context_provider);
+        if (!source_image) {
+          // Couldn't create the SkImage.
+          cache_.reset();
+          return false;
+        }
+        if (!cache_->texture_backing) {
           cache_->texture_backing = sk_make_sp<VideoTextureBacking>(
               std::move(source_image), mailbox, wraps_video_frame_texture,
               raster_context_provider);
+        } else {
+          cache_->texture_backing->ReplaceAcceleratedSkImage(
+              std::move(source_image));
         }
+      } else if (!cache_->texture_backing) {
+        SkImageInfo sk_image_info =
+            SkImageInfo::Make(gfx::SizeToSkISize(cache_->coded_size),
+                              kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+        cache_->texture_backing = sk_make_sp<VideoTextureBacking>(
+            mailbox, sk_image_info, wraps_video_frame_texture,
+            raster_context_provider);
       }
       paint_image_builder.set_texture_backing(
           cache_->texture_backing, cc::PaintImage::GetNextContentId());
diff --git a/media/renderers/win/media_foundation_renderer_integration_test.cc b/media/renderers/win/media_foundation_renderer_integration_test.cc
index 163708f6..0e3e8b4 100644
--- a/media/renderers/win/media_foundation_renderer_integration_test.cc
+++ b/media/renderers/win/media_foundation_renderer_integration_test.cc
@@ -61,7 +61,7 @@
 
  private:
   std::unique_ptr<Renderer> CreateMediaFoundationRenderer(
-      base::Optional<RendererFactoryType> factory_type) {
+      base::Optional<RendererType> /*renderer_type*/) {
     auto renderer = std::make_unique<MediaFoundationRenderer>(
         /*muted=*/false, task_environment_.GetMainThreadTaskRunner(),
         /*force_dcomp_mode_for_testing=*/true);
diff --git a/media/test/pipeline_integration_test_base.cc b/media/test/pipeline_integration_test_base.cc
index 29ce88c..d9c770b 100644
--- a/media/test/pipeline_integration_test_base.cc
+++ b/media/test/pipeline_integration_test_base.cc
@@ -463,17 +463,17 @@
 }
 
 std::unique_ptr<Renderer> PipelineIntegrationTestBase::CreateRenderer(
-    base::Optional<RendererFactoryType> factory_type) {
+    base::Optional<RendererType> renderer_type) {
   if (create_renderer_cb_)
-    return create_renderer_cb_.Run(factory_type);
+    return create_renderer_cb_.Run(renderer_type);
 
-  return CreateDefaultRenderer(factory_type);
+  return CreateDefaultRenderer(renderer_type);
 }
 
 std::unique_ptr<Renderer> PipelineIntegrationTestBase::CreateDefaultRenderer(
-    base::Optional<RendererFactoryType> factory_type) {
-  if (factory_type && *factory_type != RendererFactoryType::kDefault) {
-    DVLOG(1) << __func__ << ": factory_type not supported";
+    base::Optional<RendererType> renderer_type) {
+  if (renderer_type && *renderer_type != RendererType::kDefault) {
+    DVLOG(1) << __func__ << ": renderer_type not supported";
     return nullptr;
   }
 
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h
index 6d4d957..ad716b1 100644
--- a/media/test/pipeline_integration_test_base.h
+++ b/media/test/pipeline_integration_test_base.h
@@ -138,7 +138,7 @@
   }
 
   std::unique_ptr<Renderer> CreateRenderer(
-      base::Optional<RendererFactoryType> factory_type);
+      base::Optional<RendererType> renderer_type);
 
  protected:
   NiceMock<MockMediaLog> media_log_;
@@ -173,11 +173,11 @@
   // if |create_renderer_cb_| is set, it'll be used to create the Renderer
   // instead.
   using CreateRendererCB = base::RepeatingCallback<std::unique_ptr<Renderer>(
-      base::Optional<RendererFactoryType> factory_type)>;
+      base::Optional<RendererType> renderer_type)>;
   CreateRendererCB create_renderer_cb_;
 
   std::unique_ptr<Renderer> CreateDefaultRenderer(
-      base::Optional<RendererFactoryType> factory_type);
+      base::Optional<RendererType> renderer_type);
 
   // Sets |create_renderer_cb_| which will be used to wrap the Renderer created
   // by CreateDefaultRenderer().
diff --git a/mojo/public/cpp/bindings/sync_call_restrictions.h b/mojo/public/cpp/bindings/sync_call_restrictions.h
index fd96264..73c3cf30 100644
--- a/mojo/public/cpp/bindings/sync_call_restrictions.h
+++ b/mojo/public/cpp/bindings/sync_call_restrictions.h
@@ -20,20 +20,14 @@
 }  // namespace chromecast
 
 namespace content {
-class AndroidOverlaySyncHelper;
 class DesktopCapturerLacros;
-class StreamTextureFactory;
+class AndroidOverlaySyncHelper;
 }  // namespace content
 
 namespace crosapi {
 class ScopedAllowSyncCall;
 }  // namespace crosapi
 
-namespace gpu {
-class CommandBufferProxyImpl;
-class GpuChannelHost;
-}  // namespace gpu
-
 namespace ui {
 class Compositor;
 }  // namespace ui
@@ -105,10 +99,8 @@
   // Android requires synchronous processing when overlay surfaces are
   // destroyed, else behavior is undefined.
   friend class content::AndroidOverlaySyncHelper;
-  // GPU client code uses a few sync IPCs, grandfathered in from legacy IPC.
+  // GpuChannelHost uses a few sync IPCs, grandfathered in from legacy IPC.
   friend class gpu::GpuChannelHost;
-  friend class gpu::CommandBufferProxyImpl;
-  friend class content::StreamTextureFactory;
   // END ALLOWED USAGE.
 
 #if ENABLE_SYNC_CALL_RESTRICTIONS
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index 12de9e7..cb8a7fd 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -226,18 +226,6 @@
 #       A list of extra configs to apply to the Blink variant of generated C++
 #       bindings.
 #
-#   mojom_source_deps (optional)
-#       A list of mojoms this target depends upon. This is equivalent to
-#       public_deps except that the C++ bindings depend on each of the named
-#       "foo" targets' "foo_cpp_sources" rather than on foo's
-#       `cpp_proxy_target`. It only makes sense to use this for dependencies
-#       that set `cpp_proxy_target`, and only when the dependent mojom() would
-#       otherwise have circular dependencies with that proxy target.
-#
-#   mojom_blink_source_deps (optional)
-#       Same as above but depends on "foo_blink_cpp_sources" and is used for
-#       dependencies that specify a `blink_cpp_proxy_target`.
-#
 #   generate_java (optional)
 #       If set to true, Java bindings are generated for Android builds. If
 #       |cpp_only| is set to true, it overrides this to prevent generation of
@@ -515,22 +503,12 @@
       !invoker.disallow_interfaces
 
   all_deps = []
-  mojom_cpp_deps = []
   if (defined(invoker.deps)) {
     all_deps += invoker.deps
-    mojom_cpp_deps += invoker.deps
   }
   if (defined(invoker.public_deps)) {
     all_deps += invoker.public_deps
-    mojom_cpp_deps += invoker.public_deps
   }
-  if (defined(invoker.mojom_source_deps)) {
-    all_deps += invoker.mojom_source_deps
-  }
-  if (defined(invoker.mojom_blink_source_deps)) {
-    all_deps += invoker.mojom_blink_source_deps
-  }
-  not_needed([ "mojom_deps" ])
 
   if (defined(invoker.component_macro_prefix)) {
     assert(defined(invoker.component_output_prefix))
@@ -1088,7 +1066,6 @@
     extra_configs = []
     output_visibility = []
     output_visibility = [ "*" ]
-    cpp_source_deps = []
     if (defined(bindings_configuration.for_blink) &&
         bindings_configuration.for_blink) {
       if (defined(invoker.blink_cpp_typemaps)) {
@@ -1109,9 +1086,6 @@
         output_visibility = []
         output_visibility = invoker.visibility_blink
       }
-      if (defined(invoker.mojom_blink_source_deps)) {
-        cpp_source_deps = invoker.mojom_blink_source_deps
-      }
     } else {
       if (defined(invoker.cpp_typemaps)) {
         cpp_typemap_configs = invoker.cpp_typemaps
@@ -1131,15 +1105,8 @@
         output_visibility = []
         output_visibility = invoker.visibility
       }
-      if (defined(invoker.mojom_source_deps)) {
-        cpp_source_deps = invoker.mojom_source_deps
-      }
     }
     not_needed([ "cpp_typemap_configs" ])
-    if (proxy_target != "") {
-      group("${target_name}${variant_suffix}__has_cpp_proxy") {
-      }
-    }
 
     if (!export_defines_overridden && defined(invoker.component_macro_prefix)) {
       output_name_override =
@@ -1516,20 +1483,13 @@
       if (sources_list != []) {
         public_deps += [ ":$generator_target_name" ]
       }
-      foreach(d, mojom_cpp_deps) {
+      foreach(d, all_deps) {
         # Resolve the name, so that a target //mojo/something becomes
         # //mojo/something:something and we can append variant_suffix to
         # get the cpp dependency name.
-        full_name = get_label_info(d, "label_no_toolchain")
+        full_name = get_label_info("$d", "label_no_toolchain")
         public_deps += [ "${full_name}${variant_suffix}" ]
       }
-      foreach(d, cpp_source_deps) {
-        full_name = get_label_info(d, "label_no_toolchain")
-        public_deps += [
-          "${full_name}${variant_suffix}__has_cpp_proxy",
-          "${full_name}${variant_suffix}_cpp_sources",
-        ]
-      }
       if (defined(bindings_configuration.for_blink) &&
           bindings_configuration.for_blink) {
         if (defined(invoker.overridden_deps_blink)) {
diff --git a/remoting/host/installer/linux/Makefile b/remoting/host/installer/linux/Makefile
index e0468d4..e345401 100644
--- a/remoting/host/installer/linux/Makefile
+++ b/remoting/host/installer/linux/Makefile
@@ -89,6 +89,10 @@
 	  "$(INSTALL_DIR)/remote-open-url"
 
 	install -m 0644 \
+	  "$(SRC_DIR)/remoting/host/linux/crd_url_forwarder.desktop" \
+	  "$(INSTALL_DIR)/crd-url-forwarder.desktop"
+
+	install -m 0644 \
 	  "$(BUILD_DIR)/icudtl.dat" "$(INSTALL_DIR)/icudtl.dat"
 
 	for locale in $$(ls $(BUILD_DIR)/remoting_locales); do \
diff --git a/remoting/host/linux/BUILD.gn b/remoting/host/linux/BUILD.gn
index 10ae674..68532fa 100644
--- a/remoting/host/linux/BUILD.gn
+++ b/remoting/host/linux/BUILD.gn
@@ -51,10 +51,16 @@
     }
   }
 
+  copy("remoting_me2me_host_copy_url_forwarder_desktop_entry") {
+    sources = [ "crd_url_forwarder.desktop" ]
+    outputs = [ "$root_build_dir/remoting/crd-url-forwarder.desktop" ]
+  }
+
   group("remoting_dev_me2me_host") {
     deps = [
       ":remoting_me2me_host_copy_host_wrapper",
       ":remoting_me2me_host_copy_script",
+      ":remoting_me2me_host_copy_url_forwarder_desktop_entry",
       ":remoting_me2me_host_copy_user_session",
       ":remoting_me2me_host_copy_user_session_wrapper",
       ":remoting_native_messaging_host",
diff --git a/remoting/host/linux/crd_url_forwarder.desktop b/remoting/host/linux/crd_url_forwarder.desktop
new file mode 100644
index 0000000..1e7275d8
--- /dev/null
+++ b/remoting/host/linux/crd_url_forwarder.desktop
@@ -0,0 +1,11 @@
+# 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.
+
+[Desktop Entry]
+Name=Chrome Remote Desktop URL Forwarder
+Exec=/opt/google/chrome-remote-desktop/remote-open-url %U
+Type=Application
+Terminal=false
+Comment=Helper application to open a URL on the Chrome Remote Desktop client
+MimeType=x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/mailto;
diff --git a/remoting/host/pairing_registry_delegate_linux.cc b/remoting/host/pairing_registry_delegate_linux.cc
index cbe034c..ca9cd35 100644
--- a/remoting/host/pairing_registry_delegate_linux.cc
+++ b/remoting/host/pairing_registry_delegate_linux.cc
@@ -98,13 +98,13 @@
     return PairingRegistry::Pairing();
   }
 
-  base::DictionaryValue* pairing_dictionary;
-  if (!pairing->GetAsDictionary(&pairing_dictionary)) {
+  if (!pairing->is_dict()) {
     LOG(WARNING) << "Failed to parse pairing information: not a dictionary.";
     return PairingRegistry::Pairing();
   }
 
-  return PairingRegistry::Pairing::CreateFromValue(*pairing_dictionary);
+  return PairingRegistry::Pairing::CreateFromValue(
+      base::Value::AsDictionaryValue(*pairing));
 }
 
 bool PairingRegistryDelegateLinux::Save(
diff --git a/remoting/host/security_key/security_key_extension_session.cc b/remoting/host/security_key/security_key_extension_session.cc
index 643f466..e9d66f0 100644
--- a/remoting/host/security_key/security_key_extension_session.cc
+++ b/remoting/host/security_key/security_key_extension_session.cc
@@ -4,6 +4,7 @@
 
 #include "remoting/host/security_key/security_key_extension_session.h"
 
+#include <string>
 #include <utility>
 
 #include "base/bind.h"
@@ -42,18 +43,18 @@
 
 // Creates a string of byte data from a ListValue of numbers. Returns true if
 // all of the list elements are numbers.
-bool ConvertListValueToString(base::ListValue* bytes, std::string* out) {
+bool ConvertListToString(const base::Value& bytes, std::string* out) {
   out->clear();
 
-  unsigned int byte_count = bytes->GetSize();
+  unsigned int byte_count = bytes.GetList().size();
   if (byte_count != 0) {
     out->reserve(byte_count);
     for (unsigned int i = 0; i < byte_count; i++) {
-      int value;
-      if (!bytes->GetInteger(i, &value)) {
+      auto value = bytes.GetList()[i].GetIfInt();
+      if (!value.has_value()) {
         return false;
       }
-      out->push_back(static_cast<char>(value));
+      out->push_back(static_cast<char>(value.value()));
     }
   }
   return true;
@@ -97,18 +98,19 @@
 
   std::unique_ptr<base::Value> value =
       base::JSONReader::ReadDeprecated(message.data());
-  base::DictionaryValue* client_message;
-  if (!value || !value->GetAsDictionary(&client_message)) {
+  if (!value || !value->is_dict()) {
     LOG(WARNING) << "Failed to retrieve data from gnubby-auth message.";
     return true;
   }
 
-  std::string type;
-  if (!client_message->GetString(kMessageType, &type)) {
+  std::string* maybe_type = value->FindStringKey(kMessageType);
+  if (!maybe_type) {
     LOG(WARNING) << "Invalid gnubby-auth message format.";
     return true;
   }
+  std::string type = *maybe_type;
 
+  base::Value::DictStorage client_message = value->TakeDict();
   if (type == kControlMessage) {
     ProcessControlMessage(client_message);
   } else if (type == kDataMessage) {
@@ -123,12 +125,13 @@
 }
 
 void SecurityKeyExtensionSession::ProcessControlMessage(
-    base::DictionaryValue* message_data) const {
-  std::string option;
-  if (!message_data->GetString(kControlOption, &option)) {
+    const base::Value::DictStorage& message_data) const {
+  auto option_iter = message_data.find(kControlOption);
+  if (option_iter == message_data.end() || !option_iter->second.is_string()) {
     LOG(WARNING) << "Could not extract control option from message.";
     return;
   }
+  auto option = option_iter->second.GetString();
 
   if (option == kSecurityKeyAuthV1) {
     security_key_auth_handler_->CreateSecurityKeyConnection();
@@ -138,12 +141,14 @@
 }
 
 void SecurityKeyExtensionSession::ProcessDataMessage(
-    base::DictionaryValue* message_data) const {
-  int connection_id;
-  if (!message_data->GetInteger(kConnectionId, &connection_id)) {
+    const base::Value::DictStorage& message_data) const {
+  auto connection_id_iter = message_data.find(kConnectionId);
+  if (connection_id_iter == message_data.end() ||
+      !connection_id_iter->second.is_int()) {
     LOG(WARNING) << "Could not extract connection id from message.";
     return;
   }
+  auto connection_id = connection_id_iter->second.GetInt();
 
   if (!security_key_auth_handler_->IsValidConnectionId(connection_id)) {
     LOG(WARNING) << "Unknown gnubby-auth data connection: '" << connection_id
@@ -151,10 +156,10 @@
     return;
   }
 
-  base::ListValue* bytes;
   std::string response;
-  if (message_data->GetList(kDataPayload, &bytes) &&
-      ConvertListValueToString(bytes, &response)) {
+  auto bytes_iter = message_data.find(kDataPayload);
+  if (bytes_iter != message_data.end() &&
+      ConvertListToString(bytes_iter->second, &response)) {
     HOST_LOG << "Sending security key response: " << GetCommandCode(response);
     security_key_auth_handler_->SendClientResponse(connection_id, response);
   } else {
@@ -165,12 +170,14 @@
 }
 
 void SecurityKeyExtensionSession::ProcessErrorMessage(
-    base::DictionaryValue* message_data) const {
-  int connection_id;
-  if (!message_data->GetInteger(kConnectionId, &connection_id)) {
+    const base::Value::DictStorage& message_data) const {
+  auto connection_id_iter = message_data.find(kConnectionId);
+  if (connection_id_iter == message_data.end() ||
+      !connection_id_iter->second.is_int()) {
     LOG(WARNING) << "Could not extract connection id from message.";
     return;
   }
+  auto connection_id = connection_id_iter->second.GetInt();
 
   if (security_key_auth_handler_->IsValidConnectionId(connection_id)) {
     HOST_LOG << "Sending security key error";
diff --git a/remoting/host/security_key/security_key_extension_session.h b/remoting/host/security_key/security_key_extension_session.h
index f0365a5..1858eec 100644
--- a/remoting/host/security_key/security_key_extension_session.h
+++ b/remoting/host/security_key/security_key_extension_session.h
@@ -12,10 +12,10 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/threading/thread_checker.h"
+#include "base/values.h"
 #include "remoting/host/host_extension_session.h"
 
 namespace base {
-class DictionaryValue;
 class SingleThreadTaskRunner;
 }  // namespace base
 
@@ -48,9 +48,10 @@
 
  private:
   // These methods process specific security key extension message types.
-  void ProcessControlMessage(base::DictionaryValue* message_data) const;
-  void ProcessDataMessage(base::DictionaryValue* message_data) const;
-  void ProcessErrorMessage(base::DictionaryValue* message_data) const;
+  void ProcessControlMessage(
+      const base::Value::DictStorage& message_data) const;
+  void ProcessDataMessage(const base::Value::DictStorage& message_data) const;
+  void ProcessErrorMessage(const base::Value::DictStorage& message_data) const;
 
   void SendMessageToClient(int connection_id, const std::string& data) const;
 
diff --git a/remoting/host/token_validator_base.cc b/remoting/host/token_validator_base.cc
index c0229700..52e0873 100644
--- a/remoting/host/token_validator_base.cc
+++ b/remoting/host/token_validator_base.cc
@@ -301,24 +301,21 @@
           : data_;
 
   base::Optional<base::Value> value = base::JSONReader::Read(responseData);
-  base::DictionaryValue* dict;
-  if (!value || !value->GetAsDictionary(&dict)) {
+  if (!value || !value->is_dict()) {
     LOG(ERROR) << "Invalid token validation response: '" << data_ << "'";
     return std::string();
   }
 
-  std::string token_scope;
-  dict->GetStringWithoutPathExpansion("scope", &token_scope);
-  if (!IsValidScope(token_scope)) {
-    LOG(ERROR) << "Invalid scope: '" << token_scope << "', expected: '"
+  std::string* token_scope = value->FindStringKey("scope");
+  if (!token_scope || !IsValidScope(*token_scope)) {
+    LOG(ERROR) << "Invalid scope: '" << *token_scope << "', expected: '"
                << token_scope_ << "'.";
     return std::string();
   }
 
-  std::string shared_secret;
   // Everything is valid, so return the shared secret to the caller.
-  dict->GetStringWithoutPathExpansion("access_token", &shared_secret);
-  return shared_secret;
+  std::string* shared_secret = value->FindStringKey("access_token");
+  return shared_secret ? *shared_secret : std::string();
 }
 
 }  // namespace remoting
diff --git a/remoting/protocol/ice_config.cc b/remoting/protocol/ice_config.cc
index c7bf3e3..ba80a52 100644
--- a/remoting/protocol/ice_config.cc
+++ b/remoting/protocol/ice_config.cc
@@ -138,33 +138,37 @@
   // Parse iceServers list and store them in |ice_config|.
   bool errors_found = false;
   ice_config.max_bitrate_kbps = 0;
-  for (const auto& server : ice_servers_list->GetList()) {
-    const base::DictionaryValue* server_dict;
-    if (!server.GetAsDictionary(&server_dict)) {
+  for (const auto& server : *ice_servers_list) {
+    if (!server.is_dict()) {
       errors_found = true;
       continue;
     }
 
-    const base::ListValue* urls_list = nullptr;
-    if (!server_dict->GetList("urls", &urls_list)) {
+    const base::Value* urls_list = server.FindListKey("urls");
+    if (!urls_list) {
       errors_found = true;
       continue;
     }
 
     std::string username;
-    server_dict->GetString("username", &username);
+    const std::string* maybe_username = server.FindStringKey("username");
+    if (maybe_username)
+      username = *maybe_username;
 
     std::string password;
-    server_dict->GetString("credential", &password);
+    const std::string* maybe_password = server.FindStringKey("credential");
+    if (maybe_password)
+      password = *maybe_password;
 
     // Compute the lowest specified bitrate of all the ICE servers.
     // Ideally the bitrate would be stored per ICE server, but it is not
     // possible (at the application level) to look up which particular
     // ICE server was used for the P2P connection.
-    double new_bitrate_double;
-    if (server_dict->GetDouble("maxRateKbps", &new_bitrate_double)) {
-      ice_config.max_bitrate_kbps = MinimumSpecified(
-          ice_config.max_bitrate_kbps, static_cast<int>(new_bitrate_double));
+    auto new_bitrate_double = server.FindDoubleKey("maxRateKbps");
+    if (new_bitrate_double.has_value()) {
+      ice_config.max_bitrate_kbps =
+          MinimumSpecified(ice_config.max_bitrate_kbps,
+                           static_cast<int>(new_bitrate_double.value()));
     }
 
     for (const auto& url : urls_list->GetList()) {
@@ -206,20 +210,19 @@
     return IceConfig();
   }
 
-  base::DictionaryValue* dictionary = nullptr;
-  if (!json->GetAsDictionary(&dictionary)) {
+  if (!json->is_dict()) {
     return IceConfig();
   }
 
   // Handle the case when the config is wrapped in 'data', i.e. as {'data': {
   // 'iceServers': {...} }}.
-  base::DictionaryValue* data_dictionary = nullptr;
-  if (!dictionary->HasKey("iceServers") &&
-      dictionary->GetDictionary("data", &data_dictionary)) {
-    return Parse(*data_dictionary);
+  if (!json->FindKey("iceServers")) {
+    base::Value* data_dictionary = json->FindDictKey("data");
+    if (data_dictionary)
+      return Parse(base::Value::AsDictionaryValue(*data_dictionary));
   }
 
-  return Parse(*dictionary);
+  return Parse(base::Value::AsDictionaryValue(*json));
 }
 
 // static
diff --git a/services/device/public/cpp/bluetooth/bluetooth_utils_unittest.cc b/services/device/public/cpp/bluetooth/bluetooth_utils_unittest.cc
index 21755d2..303645f 100644
--- a/services/device/public/cpp/bluetooth/bluetooth_utils_unittest.cc
+++ b/services/device/public/cpp/bluetooth/bluetooth_utils_unittest.cc
@@ -21,7 +21,9 @@
 constexpr std::array<uint8_t, 6> kAddress = {0x00, 0x00, 0x00,
                                              0x00, 0x00, 0x00};
 constexpr char kName[] = "Foo Bar";
+constexpr char16_t kName16[] = u"Foo Bar";
 constexpr char kUnicodeName[] = "❤❤❤❤";
+constexpr char16_t kUnicodeName16[] = u"❤❤❤❤";
 constexpr char kEmptyName[] = "";
 constexpr char kWhitespaceName[] = "    ";
 constexpr char kUnicodeWhitespaceName[] = "    ";
@@ -62,7 +64,7 @@
   info->address = kAddress;
   info->name = kName;
   info->device_type = BluetoothDeviceInfo::DeviceType::kUnknown;
-  EXPECT_EQ(base::UTF8ToUTF16(kName), GetBluetoothDeviceNameForDisplay(info));
+  EXPECT_EQ(kName16, GetBluetoothDeviceNameForDisplay(info));
 }
 
 TEST(BluetoothUtilsTest,
@@ -71,7 +73,7 @@
   info->address = kAddress;
   info->name = kName;
   info->device_type = BluetoothDeviceInfo::DeviceType::kComputer;
-  EXPECT_EQ(base::UTF8ToUTF16(kName), GetBluetoothDeviceNameForDisplay(info));
+  EXPECT_EQ(kName16, GetBluetoothDeviceNameForDisplay(info));
 }
 
 TEST(BluetoothUtilsTest, GetBluetoothDeviceNameForDisplay_UnicodeName) {
@@ -79,8 +81,7 @@
   info->address = kAddress;
   info->name = kUnicodeName;
   info->device_type = BluetoothDeviceInfo::DeviceType::kComputer;
-  EXPECT_EQ(base::UTF8ToUTF16(kUnicodeName),
-            GetBluetoothDeviceNameForDisplay(info));
+  EXPECT_EQ(kUnicodeName16, GetBluetoothDeviceNameForDisplay(info));
 }
 
 TEST(BluetoothUtilsTest, GetBluetoothDeviceNameForDisplay_EmptyName) {
@@ -129,15 +130,14 @@
 TEST(BluetoothUtilsTest, GetBluetoothDeviceLabelForAccessibility) {
   EXPECT_EQ(
       l10n_util::GetStringFUTF16(
-          IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_COMPUTER,
-          base::UTF8ToUTF16(kName)),
+          IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_COMPUTER, kName16),
       LabelFromTypeWithName(BluetoothDeviceInfo::DeviceType::kComputer, kName));
 
-  EXPECT_EQ(l10n_util::GetStringFUTF16(
-                IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_CAR_AUDIO,
-                base::UTF8ToUTF16(kUnicodeName)),
-            LabelFromTypeWithName(BluetoothDeviceInfo::DeviceType::kCarAudio,
-                                  kUnicodeName));
+  EXPECT_EQ(
+      l10n_util::GetStringFUTF16(
+          IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_CAR_AUDIO, kUnicodeName16),
+      LabelFromTypeWithName(BluetoothDeviceInfo::DeviceType::kCarAudio,
+                            kUnicodeName));
   EXPECT_EQ(l10n_util::GetStringFUTF16(
                 IDS_BLUETOOTH_ACCESSIBILITY_DEVICE_TYPE_KEYBOARD,
                 u"00:00:00:00:00:00"),
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 28cf047..4c3e4eb1 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -476,15 +476,14 @@
     bool insecure_dns_client_enabled,
     net::SecureDnsMode secure_dns_mode,
     base::Optional<std::vector<mojom::DnsOverHttpsServerPtr>>
-        dns_over_https_servers) {
+        dns_over_https_servers,
+    bool additional_dns_types_enabled) {
   DCHECK(!dns_over_https_servers || !dns_over_https_servers->empty());
 
   // 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, true);
+      insecure_dns_client_enabled, additional_dns_types_enabled);
 
   // Configure DNS over HTTPS.
   net::DnsConfigOverrides overrides;
diff --git a/services/network/network_service.h b/services/network/network_service.h
index 26ec6fe..5fa6898 100644
--- a/services/network/network_service.h
+++ b/services/network/network_service.h
@@ -132,7 +132,8 @@
       bool insecure_dns_client_enabled,
       net::SecureDnsMode secure_dns_mode,
       base::Optional<std::vector<mojom::DnsOverHttpsServerPtr>>
-          dns_over_https_servers) override;
+          dns_over_https_servers,
+      bool additional_dns_types_enabled) override;
   void DisableQuic() override;
   void SetUpHttpAuth(
       mojom::HttpAuthStaticParamsPtr http_auth_static_params) override;
diff --git a/services/network/network_service_unittest.cc b/services/network/network_service_unittest.cc
index 14934d4..e8754bd25 100644
--- a/services/network/network_service_unittest.cc
+++ b/services/network/network_service_unittest.cc
@@ -472,22 +472,25 @@
       std::move(dns_client));
 
   service()->ConfigureStubHostResolver(
-      true /* insecure_dns_client_enabled */, net::SecureDnsMode::kOff,
-      base::nullopt /* dns_over_https_servers */);
+      /*insecure_dns_client_enabled=*/true, net::SecureDnsMode::kOff,
+      /*dns_over_https_servers=*/base::nullopt,
+      /*additional_dns_types_enabled=*/true);
   EXPECT_TRUE(dns_client_ptr->CanUseInsecureDnsTransactions());
   EXPECT_EQ(net::SecureDnsMode::kOff,
             dns_client_ptr->GetEffectiveConfig()->secure_dns_mode);
 
   service()->ConfigureStubHostResolver(
-      false /* insecure_dns_client_enabled */, net::SecureDnsMode::kOff,
-      base::nullopt /* dns_over_https_servers */);
+      /*insecure_dns_client_enabled=*/false, net::SecureDnsMode::kOff,
+      /*dns_over_https_servers=*/base::nullopt,
+      /*additional_dns_types_enabled=*/true);
   EXPECT_FALSE(dns_client_ptr->CanUseInsecureDnsTransactions());
   EXPECT_EQ(net::SecureDnsMode::kOff,
             dns_client_ptr->GetEffectiveConfig()->secure_dns_mode);
 
   service()->ConfigureStubHostResolver(
-      false /* insecure_dns_client_enabled */, net::SecureDnsMode::kAutomatic,
-      base::nullopt /* dns_over_https_servers */);
+      /*insecure_dns_client_enabled=*/false, net::SecureDnsMode::kAutomatic,
+      /*dns_over_https_servers=*/base::nullopt,
+      /*additional_dns_types_enabled=*/true);
   EXPECT_FALSE(dns_client_ptr->CanUseInsecureDnsTransactions());
   EXPECT_EQ(net::SecureDnsMode::kAutomatic,
             dns_client_ptr->GetEffectiveConfig()->secure_dns_mode);
@@ -498,14 +501,39 @@
   dns_over_https_server->server_template = "https://foo/";
   dns_over_https_server->use_post = true;
   dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
-  service()->ConfigureStubHostResolver(false /* insecure_dns_client_enabled */,
-                                       net::SecureDnsMode::kAutomatic,
-                                       std::move(dns_over_https_servers_ptr));
+  service()->ConfigureStubHostResolver(
+      /*insecure_dns_client_enabled=*/false, net::SecureDnsMode::kAutomatic,
+      std::move(dns_over_https_servers_ptr),
+      /*additional_dns_types_enabled=*/true);
   EXPECT_FALSE(dns_client_ptr->CanUseInsecureDnsTransactions());
   EXPECT_EQ(net::SecureDnsMode::kAutomatic,
             dns_client_ptr->GetEffectiveConfig()->secure_dns_mode);
 }
 
+TEST_F(NetworkServiceTest, HandlesAdditionalDnsQueryTypesEnableDisable) {
+  // Create valid DnsConfig.
+  net::DnsConfig config;
+  config.nameservers.emplace_back();
+  auto dns_client = std::make_unique<net::MockDnsClient>(
+      std::move(config), net::MockDnsClientRuleList());
+  dns_client->set_ignore_system_config_changes(true);
+  const net::DnsClient* dns_client_ptr = dns_client.get();
+  service()->host_resolver_manager()->SetDnsClientForTesting(
+      std::move(dns_client));
+
+  service()->ConfigureStubHostResolver(
+      /*insecure_dns_client_enabled=*/true, net::SecureDnsMode::kOff,
+      /*dns_over_https_servers=*/base::nullopt,
+      /*additional_dns_types_enabled=*/true);
+  EXPECT_TRUE(dns_client_ptr->CanQueryAdditionalTypesViaInsecureDns());
+
+  service()->ConfigureStubHostResolver(
+      /*insecure_dns_client_enabled=*/true, net::SecureDnsMode::kOff,
+      /*dns_over_https_servers=*/base::nullopt,
+      /*additional_dns_types_enabled=*/false);
+  EXPECT_FALSE(dns_client_ptr->CanQueryAdditionalTypesViaInsecureDns());
+}
+
 TEST_F(NetworkServiceTest, DnsOverHttpsEnableDisable) {
   const std::string kServer1 = "https://foo/";
   const bool kServer1UsePost = false;
@@ -534,9 +562,10 @@
   dns_over_https_server->use_post = kServer1UsePost;
   dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
 
-  service()->ConfigureStubHostResolver(false /* insecure_dns_client_enabled */,
-                                       net::SecureDnsMode::kAutomatic,
-                                       std::move(dns_over_https_servers_ptr));
+  service()->ConfigureStubHostResolver(
+      /*insecure_dns_client_enabled=*/false, net::SecureDnsMode::kAutomatic,
+      std::move(dns_over_https_servers_ptr),
+      /*additional_dns_types_enabled=*/true);
   EXPECT_TRUE(
       service()->host_resolver_manager()->GetDnsConfigAsValue().is_dict());
   std::vector<net::DnsOverHttpsServerConfig> dns_over_https_servers =
@@ -558,9 +587,10 @@
   dns_over_https_server->use_post = kServer3UsePost;
   dns_over_https_servers_ptr.emplace_back(std::move(dns_over_https_server));
 
-  service()->ConfigureStubHostResolver(true /* insecure_dns_client_enabled */,
-                                       net::SecureDnsMode::kSecure,
-                                       std::move(dns_over_https_servers_ptr));
+  service()->ConfigureStubHostResolver(
+      /*insecure_dns_client_enabled=*/true, net::SecureDnsMode::kSecure,
+      std::move(dns_over_https_servers_ptr),
+      /*additional_dns_types_enabled=*/true);
   EXPECT_TRUE(
       service()->host_resolver_manager()->GetDnsConfigAsValue().is_dict());
   dns_over_https_servers =
@@ -578,8 +608,9 @@
       features::kDnsOverHttpsUpgrade,
       {{"DisabledProviders", "CleanBrowsingSecure, , Cloudflare,Unexpected"}});
   service()->ConfigureStubHostResolver(
-      true /* insecure_dns_client_enabled */, net::SecureDnsMode::kAutomatic,
-      base::nullopt /* dns_over_https_servers */);
+      /*insecure_dns_client_enabled=*/true, net::SecureDnsMode::kAutomatic,
+      /*dns_over_https_servers=*/base::nullopt,
+      /*additional_dns_types_enabled=*/true);
 
   // Set valid DnsConfig.
   net::DnsConfig config;
@@ -1214,13 +1245,13 @@
 
   auto expectation = mojom::TrustTokenKeyCommitmentResult::New();
   expectation->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectation->id = 1;
   expectation->batch_size = 5;
 
   base::RunLoop run_loop;
   network_service_->SetTrustTokenKeyCommitments(
-      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5 } } )",
+      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5 } } )",
       run_loop.QuitClosure());
   run_loop.Run();
 
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index 6141022b..c3c07ef 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -180,13 +180,13 @@
   // https://tools.ietf.org/html/rfc1034#section-5.3.1 for definition of a stub
   // resolver.
   //
-  // |dns_over_https_servers| is an optional list of DNS over HTTPS servers.
-  // DnsTransactions will by default follow the behavior of |secure_dns_mode|.
+  // `dns_over_https_servers` is an optional list of DNS over HTTPS servers.
+  // DnsTransactions will by default follow the behavior of `secure_dns_mode`.
   // In SECURE mode, only DoH lookups will be performed. In AUTOMATIC mode,
   // DoH lookups to available servers will be performed first, and insecure
   // lookups will be used as a fallback. In OFF mode, only insecure lookups will
   // be performed. When insecure lookups are performed, they will be sent by
-  // the async resolver first if |insecure_dns_client_enabled| is true and
+  // the async resolver first if `insecure_dns_client_enabled` is true and
   // then by the system resolver as a fallback.
   //
   // DNS over HTTPS will use the primary NetworkContext, so can only be enabled
@@ -194,9 +194,15 @@
   // limitation, this method can be called at any time to change DNS
   // configuration, though calling it will fail any DNS lookups that have
   // already been started.
+  //
+  // `additional_dns_types_enabled` controls whether additional DNS query types,
+  // e.g. HTTPS (DNS type 65) will be allowed besides the traditional A and AAAA
+  // queries when a request is being made via insecure DNS. Has no effect on
+  // Secure DNS which always allows additional types.
   ConfigureStubHostResolver(bool insecure_dns_client_enabled,
                             SecureDnsMode secure_dns_mode,
-                            array<DnsOverHttpsServer>? dns_over_https_servers);
+                            array<DnsOverHttpsServer>? dns_over_https_servers,
+                            bool additional_dns_types_enabled);
 
   // Disables QUIC for the NetworkService. Affects all existing NetworkContexts,
   // and all new ones that are created. Once called, QUIC cannot be re-enabled.
diff --git a/services/network/public/mojom/trust_tokens.mojom b/services/network/public/mojom/trust_tokens.mojom
index da9f7b9..c0ec622 100644
--- a/services/network/public/mojom/trust_tokens.mojom
+++ b/services/network/public/mojom/trust_tokens.mojom
@@ -10,14 +10,14 @@
 // TrustTokenProtocolVersion enumerates the versions of Trust Token that the
 // client knows about. Different versions represent different configuration
 // flows, data structure meanings, etc and may require clearing the database
-// due to incompatibilities. kTrustTokenV2Pmb and kTrustTokenV2Voprf can
+// due to incompatibilities. kTrustTokenV3Pmb and kTrustTokenV3Voprf can
 // safely be supported at the same time, as they use the same underlying data
 // model; they just use different cryptosystems to generate tokens' signatures.
 // TODO(crbug/1133969): Schema versioning needs to be implemented for future
 // versions that need to clear the database on schema changes.
 enum TrustTokenProtocolVersion {
-  kTrustTokenV2Pmb,
-  kTrustTokenV2Voprf,
+  kTrustTokenV3Pmb,
+  kTrustTokenV3Voprf,
 };
 
 
diff --git a/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer.cc b/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer.cc
index a73b323..ba03e97 100644
--- a/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer.cc
+++ b/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer.cc
@@ -36,10 +36,10 @@
 
   const TRUST_TOKEN_METHOD* method = nullptr;
   switch (issuer_configured_version) {
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb:
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb:
       method = TRUST_TOKEN_experiment_v2_pmb();
       break;
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf:
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf:
       method = TRUST_TOKEN_experiment_v2_voprf();
       break;
   }
diff --git a/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer_unittest.cc b/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer_unittest.cc
index 6276aa9..047d23d 100644
--- a/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer_unittest.cc
+++ b/services/network/trust_tokens/boringssl_trust_token_issuance_cryptographer_unittest.cc
@@ -47,11 +47,11 @@
   // Test that adding more than the number of support keys fails.
   BoringsslTrustTokenIssuanceCryptographer cryptographer;
   ASSERT_TRUE(cryptographer.Initialize(
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb,
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb,
       /*issuer_configured_batch_size=*/10));
 
   size_t max_keys = TrustTokenMaxKeysForVersion(
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb);
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb);
   for (size_t i = 0; i < max_keys; ++i) {
     ASSERT_TRUE(
         cryptographer.AddKey(GenerateValidVerificationKey(KeyType::kPmb)))
@@ -65,11 +65,11 @@
   // Test that adding more than the number of support keys fails.
   BoringsslTrustTokenIssuanceCryptographer cryptographer;
   ASSERT_TRUE(cryptographer.Initialize(
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf,
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf,
       /*issuer_configured_batch_size=*/10));
 
   size_t max_keys = TrustTokenMaxKeysForVersion(
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf);
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf);
   for (size_t i = 0; i < max_keys; ++i) {
     ASSERT_TRUE(
         cryptographer.AddKey(GenerateValidVerificationKey(KeyType::kVoprf)))
diff --git a/services/network/trust_tokens/boringssl_trust_token_redemption_cryptographer.cc b/services/network/trust_tokens/boringssl_trust_token_redemption_cryptographer.cc
index 07403051..7b11e25 100644
--- a/services/network/trust_tokens/boringssl_trust_token_redemption_cryptographer.cc
+++ b/services/network/trust_tokens/boringssl_trust_token_redemption_cryptographer.cc
@@ -31,10 +31,10 @@
 
   const TRUST_TOKEN_METHOD* method = nullptr;
   switch (issuer_configured_version) {
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb:
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb:
       method = TRUST_TOKEN_experiment_v2_pmb();
       break;
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf:
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf:
       method = TRUST_TOKEN_experiment_v2_voprf();
       break;
   }
@@ -95,7 +95,7 @@
   if (!base::Base64Decode(response_header, &decoded_response))
     return base::nullopt;
 
-  // In TrustTokenV2, the entire RR is stored in the body field, and the
+  // In TrustTokenV3, the entire RR is stored in the body field, and the
   // signature field is unused in finish_redemption.
   ScopedBoringsslBytes rr;
   ScopedBoringsslBytes unused;
diff --git a/services/network/trust_tokens/test/trust_token_request_handler.h b/services/network/trust_tokens/test/trust_token_request_handler.h
index 2a4bb7d8..887d0b67 100644
--- a/services/network/trust_tokens/test/trust_token_request_handler.h
+++ b/services/network/trust_tokens/test/trust_token_request_handler.h
@@ -72,7 +72,7 @@
 
     // The protocol version with which to parameterize the server-side
     // cryptographic logic. We return this value in key commitment results.
-    std::string protocol_version = "TrustTokenV2PMB";
+    std::string protocol_version = "TrustTokenV3PMB";
 
     // The key commitment ID.
     int id = 1;
diff --git a/services/network/trust_tokens/trust_token_cryptographers_test.cc b/services/network/trust_tokens/trust_token_cryptographers_test.cc
index 9277366..13b1ad5 100644
--- a/services/network/trust_tokens/trust_token_cryptographers_test.cc
+++ b/services/network/trust_tokens/trust_token_cryptographers_test.cc
@@ -45,7 +45,7 @@
 };
 
 const mojom::TrustTokenProtocolVersion kProtocolVersion =
-    mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+    mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
 
 // Choose this number to be > 1 but fairly small: setting it to 10
 // led to the test running for 2.5 sec on a debug build.
diff --git a/services/network/trust_tokens/trust_token_key_commitment_parser.cc b/services/network/trust_tokens/trust_token_key_commitment_parser.cc
index 63cbf82..ac552c5 100644
--- a/services/network/trust_tokens/trust_token_key_commitment_parser.cc
+++ b/services/network/trust_tokens/trust_token_key_commitment_parser.cc
@@ -173,12 +173,12 @@
       value.FindStringKey(kTrustTokenKeyCommitmentProtocolVersionField);
   if (!maybe_version)
     return nullptr;
-  if (*maybe_version == "TrustTokenV2PMB") {
+  if (*maybe_version == "TrustTokenV3PMB") {
     result->protocol_version =
-        mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
-  } else if (*maybe_version == "TrustTokenV2VOPRF") {
+        mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
+  } else if (*maybe_version == "TrustTokenV3VOPRF") {
     result->protocol_version =
-        mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf;
+        mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf;
   } else {
     return nullptr;
   }
diff --git a/services/network/trust_tokens/trust_token_key_commitment_parser_unittest.cc b/services/network/trust_tokens/trust_token_key_commitment_parser_unittest.cc
index 9951d56..740563e 100644
--- a/services/network/trust_tokens/trust_token_key_commitment_parser_unittest.cc
+++ b/services/network/trust_tokens/trust_token_key_commitment_parser_unittest.cc
@@ -69,7 +69,7 @@
 
 TEST(TrustTokenKeyCommitmentParser, AcceptsMinimal) {
   std::string input =
-      R"( { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+      R"( { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
     } )";
 
   // Sanity check that the input is actually valid JSON.
@@ -77,7 +77,7 @@
 
   auto expectation = mojom::TrustTokenKeyCommitmentResult::New();
   expectation->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectation->id = 1;
   expectation->batch_size = 5;
 
@@ -98,7 +98,7 @@
   // it's encoded as a string.)
   const std::string input = base::StringPrintf(
       R"({
-            "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+            "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "this label is not an integer": {
               "Y": "akey",
               "expiry": "%s"
@@ -125,7 +125,7 @@
 
   const std::string input = base::StringPrintf(
       R"({
-            "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+            "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "-1": {
               "Y": "akey",
               "expiry": "%s"
@@ -152,7 +152,7 @@
 
   const std::string input = base::StringPrintf(
       R"({
-            "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+            "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1000000000000": {
               "Y": "akey",
               "expiry": "%s"
@@ -179,7 +179,7 @@
 
   const std::string input = base::StringPrintf(
       R"({
-            "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+            "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1": {
               "Y": "this key isn't valid base64, so it should be rejected",
               "expiry": "%s"
@@ -205,7 +205,7 @@
 
   const std::string input = base::StringPrintf(
       R"({
-            "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+            "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1": { "Y": "akey", "expiry": "%s" }
          })",
       base::NumberToString(one_minute_from_now_in_micros).c_str());
@@ -239,7 +239,7 @@
 
   const std::string input = base::StringPrintf(
       R"({
-            "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+            "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1": { "Y": "akey", "expiry": "%s" },
             "2": { "Y": "aaaa", "expiry": "%s" }
          })",
@@ -268,7 +268,7 @@
   // If a key has a missing "expiry" field, we should reject the entire
   // record.
   const std::string input =
-      R"( { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+      R"( { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
     "1": { "Y": "akey" } })";
 
   // Sanity check that the input is actually valid JSON.
@@ -284,7 +284,7 @@
   const std::string input =
       R"(
    {
-     "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+     "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
      "1": {
        "Y": "akey",
        "expiry": "absolutely not a valid timestamp"
@@ -317,7 +317,7 @@
   // If the time has passed a key's "expiry" field, we should reject the entire
   // record.
   const std::string input = base::StringPrintf(
-      R"( { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+      R"( { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1": { "Y": "akey", "expiry": "%s" } })",
       base::NumberToString(one_minute_before_now_in_micros).c_str());
 
@@ -326,7 +326,7 @@
 
   auto expectation = mojom::TrustTokenKeyCommitmentResult::New();
   expectation->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectation->id = 1;
   expectation->batch_size = 5;
 
@@ -346,7 +346,7 @@
   // If a key has an expiry but is missing its body,
   // we should reject the entire result.
   const std::string input = base::StringPrintf(
-      R"( { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+      R"( { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1": { "expiry": "%s" } } )",
       base::NumberToString(one_minute_from_now_in_micros).c_str());
 
@@ -363,7 +363,7 @@
   // we should reject the entire result.
 
   const std::string input =
-      R"( { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+      R"( { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
             "1": { } })";
 
   // Sanity check that the input is actually valid JSON,
@@ -377,7 +377,7 @@
 TEST(TrustTokenKeyCommitmentParser, ParsesBatchSize) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+     "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
    })";
   // Double-check that the input is actually valid JSON.
   ASSERT_TRUE(base::JSONReader::Read(input));
@@ -392,7 +392,7 @@
 TEST(TrustTokenKeyCommitmentParser, RejectsMissingBatchSize) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": 1
+     "protocol_version": "TrustTokenV3PMB", "id": 1
    })";
   // Double-check that the input is actually valid JSON.
   ASSERT_TRUE(base::JSONReader::Read(input));
@@ -405,7 +405,7 @@
 TEST(TrustTokenKeyCommitmentParser, RejectsNonpositiveBatchSize) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": 1,
+     "protocol_version": "TrustTokenV3PMB", "id": 1,
      "batchsize": 0
    })";
   // Double-check that the input is actually valid JSON.
@@ -419,7 +419,7 @@
 TEST(TrustTokenKeyCommitmentParser, RejectsTypeUnsafeBatchSize) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": 1,
+     "protocol_version": "TrustTokenV3PMB", "id": 1,
      "batchsize": "not a number"
    })";
   // Double-check that the input is actually valid JSON.
@@ -435,7 +435,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android"],
      "unavailable_local_operation_fallback": "web_issuance"
@@ -455,7 +455,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android", "android", "android"],
      "unavailable_local_operation_fallback": "web_issuance"
@@ -475,7 +475,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1
    })";
   // Double-check that the input is actually valid JSON.
@@ -492,7 +492,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": "not an array",
      "unavailable_local_operation_fallback": "web_issuance"
@@ -511,7 +511,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android", 47],
      "unavailable_local_operation_fallback": "web_issuance"
@@ -530,7 +530,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android", "imaginaryOS"],
      "unavailable_local_operation_fallback": "web_issuance"
@@ -549,7 +549,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android"]
    })";
@@ -567,7 +567,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android"],
      "unavailable_local_operation_fallback": 57
@@ -586,7 +586,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android"],
      "unavailable_local_operation_fallback": "not a valid enum value"
@@ -604,7 +604,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android"],
      "unavailable_local_operation_fallback": "web_issuance"
@@ -626,7 +626,7 @@
       R"({
      "srrkey": "aaaa",
      "batchsize": 1,
-     "protocol_version": "TrustTokenV2PMB",
+     "protocol_version": "TrustTokenV3PMB",
      "id": 1,
      "request_issuance_locally_on": ["android"],
      "unavailable_local_operation_fallback": "return_with_error"
@@ -645,7 +645,7 @@
 TEST(TrustTokenKeyCommitmentParser, ParsesProtocolVersion) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+     "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
      "srrkey": "aaaa"
    })";
   // Make sure the input is actually valid JSON.
@@ -655,7 +655,7 @@
       TrustTokenKeyCommitmentParser().Parse(input);
   ASSERT_TRUE(result);
   EXPECT_EQ(result->protocol_version,
-            mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb);
+            mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb);
 }
 
 TEST(TrustTokenKeyCommitmentParser, RejectsMissingProtocolVersion) {
@@ -702,7 +702,7 @@
 TEST(TrustTokenKeyCommitmentParser, ParsesID) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5,
+     "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5,
      "srrkey": "aaaa"
    })";
   // Make sure the input is actually valid JSON.
@@ -718,7 +718,7 @@
 TEST(TrustTokenKeyCommitmentParser, RejectsMissingID) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "batchsize": 5, "srrkey": "aaaa"
+     "protocol_version": "TrustTokenV3PMB", "batchsize": 5, "srrkey": "aaaa"
    })";
   // Make sure the input is actually valid JSON.
   ASSERT_TRUE(base::JSONReader::Read(input));
@@ -731,7 +731,7 @@
 TEST(TrustTokenKeyCommitmentParser, RejectsTypeUnsafeID) {
   std::string input =
       R"({
-     "protocol_version": "TrustTokenV2PMB", "id": "foo", "srrkey": "aaaa",
+     "protocol_version": "TrustTokenV3PMB", "id": "foo", "srrkey": "aaaa",
      "batchsize": 5
    })";
   // Make sure the input is actually valid JSON.
@@ -775,7 +775,7 @@
   // Test that a key with an unsuitable Trust Tokens origin gets skipped.
   std::string input =
       R"( { "http://insecure.example/":
-             { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+             { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
                  } } )";
 
   // Make sure the input is actually valid JSON.
@@ -803,7 +803,7 @@
 TEST(TrustTokenKeyCommitmentParserMultipleIssuers, SingleIssuer) {
   std::string input =
       R"( { "https://issuer.example/": {
-              "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+              "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
               } } )";
 
   // Make sure the input is actually valid JSON.
@@ -819,18 +819,18 @@
   EXPECT_TRUE(mojo::Equals(
       result->at(issuer),
       parser.Parse(
-          R"({ "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+          R"({ "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
              })")));
 }
 
 TEST(TrustTokenKeyCommitmentParserMultipleIssuers, DuplicateIssuer) {
   std::string input =
-      R"( { "https://issuer.example/": { "protocol_version": "TrustTokenV2PMB",
+      R"( { "https://issuer.example/": { "protocol_version": "TrustTokenV3PMB",
             "id": 1, "batchsize": 5 },
-    "https://other.example/": { "protocol_version": "TrustTokenV2PMB",
+    "https://other.example/": { "protocol_version": "TrustTokenV3PMB",
              "id": 1, "batchsize": 5 },
     "https://issuer.example/this-is-really-the-same-issuer-as-the-first-entry":
-      { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 3 }
+      { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 3 }
     } )";
 
   // Make sure the input is actually valid JSON.
@@ -851,7 +851,7 @@
   EXPECT_TRUE(mojo::Equals(
       result->at(issuer),
       parser.Parse(
-          R"({ "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 3
+          R"({ "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 3
         })")));
 }
 
@@ -862,12 +862,12 @@
 
   std::string input =
       R"( {
-    "https://issuer.example/longer": { "protocol_version": "TrustTokenV2PMB",
+    "https://issuer.example/longer": { "protocol_version": "TrustTokenV3PMB",
       "id": 1, "batchsize": 5 },
-    "https://other.example/": { "protocol_version": "TrustTokenV2PMB", "id": 1,
+    "https://other.example/": { "protocol_version": "TrustTokenV3PMB", "id": 1,
       "batchsize": 5 },
     "https://issuer.example/":
-      { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 3 }
+      { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 3 }
     } )";
 
   // Make sure the input is actually valid JSON.
@@ -888,16 +888,16 @@
   EXPECT_TRUE(mojo::Equals(
       result->at(issuer),
       parser.Parse(
-          R"({ "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+          R"({ "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
         })")));
 }
 
 TEST(TrustTokenKeyCommitmentParserMultipleIssuers,
      MixOfSuitableAndUnsuitableIssuers) {
   std::string input = R"( {
-    "https://issuer.example/": { "protocol_version": "TrustTokenV2PMB", "id": 1,
+    "https://issuer.example/": { "protocol_version": "TrustTokenV3PMB", "id": 1,
       "batchsize": 5 },
-    "http://insecure.example": { "protocol_version": "TrustTokenV2PMB", "id": 1,
+    "http://insecure.example": { "protocol_version": "TrustTokenV3PMB", "id": 1,
       "batchsize": 5 } } )";
 
   // Make sure the input is actually valid JSON.
@@ -915,7 +915,7 @@
   EXPECT_TRUE(mojo::Equals(
       result->at(issuer),
       parser.Parse(
-          R"({ "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5
+          R"({ "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5
         })")));
 }
 
diff --git a/services/network/trust_tokens/trust_token_key_commitments_unittest.cc b/services/network/trust_tokens/trust_token_key_commitments_unittest.cc
index c01a6c9..bf1d858 100644
--- a/services/network/trust_tokens/trust_token_key_commitments_unittest.cc
+++ b/services/network/trust_tokens/trust_token_key_commitments_unittest.cc
@@ -65,7 +65,7 @@
 
   auto expectation = mojom::TrustTokenKeyCommitmentResult::New();
   expectation->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectation->id = 1;
   expectation->batch_size = 5;
 
@@ -92,7 +92,7 @@
       *SuitableTrustTokenOrigin::Create(GURL("https://an-origin.example"));
   auto an_expectation = mojom::TrustTokenKeyCommitmentResult::New();
   an_expectation->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   an_expectation->id = 1;
   an_expectation->batch_size = 5;
 
@@ -121,11 +121,11 @@
   };
 
   expectations[0]->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectations[0]->id = 1;
   expectations[0]->batch_size = 0;
   expectations[1]->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectations[1]->id = 1;
   expectations[1]->batch_size = 1;
 
@@ -144,7 +144,7 @@
 TEST(TrustTokenKeyCommitments, ParseAndSet) {
   TrustTokenKeyCommitments commitments;
   commitments.ParseAndSet(
-      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5 } } )");
+      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5 } } )");
 
   EXPECT_TRUE(GetCommitmentForOrigin(
       commitments,
@@ -155,7 +155,7 @@
   base::test::ScopedCommandLine command_line;
   command_line.GetProcessCommandLine()->AppendSwitchASCII(
       switches::kAdditionalTrustTokenKeyCommitments,
-      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 5 } } )");
+      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 5 } } )");
 
   TrustTokenKeyCommitments commitments;
 
@@ -164,14 +164,14 @@
       *SuitableTrustTokenOrigin::Create(GURL("https://issuer.example"))));
 
   commitments.ParseAndSet(
-      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV2PMB", "id": 1, "batchsize": 10 } } )");
+      R"( { "https://issuer.example": { "protocol_version": "TrustTokenV3PMB", "id": 1, "batchsize": 10 } } )");
 
   auto result = GetCommitmentForOrigin(
       commitments,
       *SuitableTrustTokenOrigin::Create(GURL("https://issuer.example")));
   ASSERT_TRUE(result);
   EXPECT_EQ(result->protocol_version,
-            mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb);
+            mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb);
   EXPECT_EQ(result->id, 1);
   EXPECT_EQ(result->batch_size, 5);
 }
@@ -190,7 +190,7 @@
   commitment_result->keys.push_back(std::move(expired_key));
 
   size_t max_keys = TrustTokenMaxKeysForVersion(
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb);
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb);
   for (size_t i = 0; i < max_keys; ++i) {
     auto not_expired_key = mojom::TrustTokenVerificationKey::New();
     not_expired_key->expiry =
@@ -223,7 +223,7 @@
 
   auto expectation = mojom::TrustTokenKeyCommitmentResult::New();
   expectation->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   expectation->id = 1;
   expectation->batch_size = 5;
 
diff --git a/services/network/trust_tokens/trust_token_parameterization.cc b/services/network/trust_tokens/trust_token_parameterization.cc
index 164fc0d..87c58d7 100644
--- a/services/network/trust_tokens/trust_token_parameterization.cc
+++ b/services/network/trust_tokens/trust_token_parameterization.cc
@@ -10,9 +10,9 @@
 
 size_t TrustTokenMaxKeysForVersion(mojom::TrustTokenProtocolVersion version) {
   switch (version) {
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb:
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb:
       return 3;
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf:
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf:
       return 6;
   }
 }
diff --git a/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc b/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc
index 8275816c..fbabbfe 100644
--- a/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc
+++ b/services/network/trust_tokens/trust_token_request_issuance_helper_unittest.cc
@@ -127,7 +127,7 @@
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   return key_commitment_result;
 }
@@ -339,7 +339,7 @@
   std::string attached_version_header;
   EXPECT_TRUE(request->extra_request_headers().GetHeader(
       kTrustTokensSecTrustTokenVersionHeader, &attached_version_header));
-  EXPECT_EQ(attached_version_header, "TrustTokenV2PMB");
+  EXPECT_EQ(attached_version_header, "TrustTokenV3PMB");
 }
 
 // Check that the issuance helper sets the LOAD_BYPASS_CACHE flag on the
diff --git a/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc b/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc
index ebdcad78..672018c 100644
--- a/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc
+++ b/services/network/trust_tokens/trust_token_request_redemption_helper_unittest.cc
@@ -221,7 +221,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -269,7 +269,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -318,7 +318,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -370,7 +370,7 @@
     key_commitment_result->keys.push_back(
         mojom::TrustTokenVerificationKey::New());
     key_commitment_result->protocol_version =
-        mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+        mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
     key_commitment_result->id = 1;
     key_commitment_result->batch_size =
         static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -454,7 +454,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -505,7 +505,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -566,7 +566,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -635,7 +635,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -704,7 +704,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -763,7 +763,7 @@
   key_commitment_result->keys.push_back(mojom::TrustTokenVerificationKey::New(
       "token verification key", /*expiry=*/base::Time::Max()));
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
@@ -867,7 +867,7 @@
   key_commitment_result->keys.push_back(
       mojom::TrustTokenVerificationKey::New());
   key_commitment_result->protocol_version =
-      mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb;
+      mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb;
   key_commitment_result->id = 1;
   key_commitment_result->batch_size =
       static_cast<int>(kMaximumTrustTokenIssuanceBatchSize);
diff --git a/services/network/trust_tokens/trust_token_request_signing_helper.h b/services/network/trust_tokens/trust_token_request_signing_helper.h
index afb632d8..65e88b9f 100644
--- a/services/network/trust_tokens/trust_token_request_signing_helper.h
+++ b/services/network/trust_tokens/trust_token_request_signing_helper.h
@@ -73,7 +73,7 @@
   // the normative source of the domain separator's value (currently the design
   // doc).
   static constexpr uint8_t kRequestSigningDomainSeparator[] = {
-      'T', 'r', 'u', 's', 't', 'T', 'o', 'k', 'e', 'n', 'V', '2'};
+      'T', 'r', 'u', 's', 't', 'T', 'o', 'k', 'e', 'n', 'V', '3'};
 
   struct Params {
     // Refer to fields' comments for their semantics.
diff --git a/services/network/trust_tokens/types.cc b/services/network/trust_tokens/types.cc
index a3e865ca..75596349 100644
--- a/services/network/trust_tokens/types.cc
+++ b/services/network/trust_tokens/types.cc
@@ -36,10 +36,10 @@
 std::string ProtocolVersionToString(
     mojom::TrustTokenProtocolVersion my_version) {
   switch (my_version) {
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Pmb:
-      return "TrustTokenV2PMB";
-    case mojom::TrustTokenProtocolVersion::kTrustTokenV2Voprf:
-      return "TrustTokenV2VOPRF";
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Pmb:
+      return "TrustTokenV3PMB";
+    case mojom::TrustTokenProtocolVersion::kTrustTokenV3Voprf:
+      return "TrustTokenV3VOPRF";
   }
 }
 
diff --git a/services/viz/public/cpp/compositing/resource_format_mojom_traits.cc b/services/viz/public/cpp/compositing/resource_format_mojom_traits.cc
deleted file mode 100644
index 0be6bde..0000000
--- a/services/viz/public/cpp/compositing/resource_format_mojom_traits.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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 "services/viz/public/cpp/compositing/resource_format_mojom_traits.h"
-
-namespace mojo {
-
-// static
-viz::mojom::ResourceFormat
-EnumTraits<viz::mojom::ResourceFormat, viz::ResourceFormat>::ToMojom(
-    viz::ResourceFormat format) {
-  switch (format) {
-    case viz::ResourceFormat::RGBA_8888:
-      return viz::mojom::ResourceFormat::RGBA_8888;
-    case viz::ResourceFormat::RGBA_4444:
-      return viz::mojom::ResourceFormat::RGBA_4444;
-    case viz::ResourceFormat::BGRA_8888:
-      return viz::mojom::ResourceFormat::BGRA_8888;
-    case viz::ResourceFormat::ALPHA_8:
-      return viz::mojom::ResourceFormat::ALPHA_8;
-    case viz::ResourceFormat::LUMINANCE_8:
-      return viz::mojom::ResourceFormat::LUMINANCE_8;
-    case viz::ResourceFormat::RGB_565:
-      return viz::mojom::ResourceFormat::RGB_565;
-    case viz::ResourceFormat::BGR_565:
-      return viz::mojom::ResourceFormat::BGR_565;
-    case viz::ResourceFormat::ETC1:
-      return viz::mojom::ResourceFormat::ETC1;
-    case viz::ResourceFormat::RED_8:
-      return viz::mojom::ResourceFormat::RED_8;
-    case viz::ResourceFormat::RG_88:
-      return viz::mojom::ResourceFormat::RG_88;
-    case viz::ResourceFormat::LUMINANCE_F16:
-      return viz::mojom::ResourceFormat::LUMINANCE_F16;
-    case viz::ResourceFormat::RGBA_F16:
-      return viz::mojom::ResourceFormat::RGBA_F16;
-    case viz::ResourceFormat::R16_EXT:
-      return viz::mojom::ResourceFormat::R16_EXT;
-    case viz::ResourceFormat::RG16_EXT:
-      return viz::mojom::ResourceFormat::RG16_EXT;
-    case viz::ResourceFormat::RGBX_8888:
-      return viz::mojom::ResourceFormat::RGBX_8888;
-    case viz::ResourceFormat::BGRX_8888:
-      return viz::mojom::ResourceFormat::BGRX_8888;
-    case viz::ResourceFormat::RGBA_1010102:
-      return viz::mojom::ResourceFormat::RGBX_1010102;
-    case viz::ResourceFormat::BGRA_1010102:
-      return viz::mojom::ResourceFormat::BGRX_1010102;
-    case viz::ResourceFormat::YVU_420:
-      return viz::mojom::ResourceFormat::YVU_420;
-    case viz::ResourceFormat::YUV_420_BIPLANAR:
-      return viz::mojom::ResourceFormat::YUV_420_BIPLANAR;
-    case viz::ResourceFormat::P010:
-      return viz::mojom::ResourceFormat::P010;
-  }
-  NOTREACHED();
-  return viz::mojom::ResourceFormat::RGBA_8888;
-}
-
-// static
-bool EnumTraits<viz::mojom::ResourceFormat, viz::ResourceFormat>::FromMojom(
-    viz::mojom::ResourceFormat format,
-    viz::ResourceFormat* out) {
-  switch (format) {
-    case viz::mojom::ResourceFormat::RGBA_8888:
-      *out = viz::ResourceFormat::RGBA_8888;
-      return true;
-    case viz::mojom::ResourceFormat::RGBA_4444:
-      *out = viz::ResourceFormat::RGBA_4444;
-      return true;
-    case viz::mojom::ResourceFormat::BGRA_8888:
-      *out = viz::ResourceFormat::BGRA_8888;
-      return true;
-    case viz::mojom::ResourceFormat::ALPHA_8:
-      *out = viz::ResourceFormat::ALPHA_8;
-      return true;
-    case viz::mojom::ResourceFormat::LUMINANCE_8:
-      *out = viz::ResourceFormat::LUMINANCE_8;
-      return true;
-    case viz::mojom::ResourceFormat::RGB_565:
-      *out = viz::ResourceFormat::RGB_565;
-      return true;
-    case viz::mojom::ResourceFormat::BGR_565:
-      *out = viz::ResourceFormat::BGR_565;
-      return true;
-    case viz::mojom::ResourceFormat::ETC1:
-      *out = viz::ResourceFormat::ETC1;
-      return true;
-    case viz::mojom::ResourceFormat::RED_8:
-      *out = viz::ResourceFormat::RED_8;
-      return true;
-    case viz::mojom::ResourceFormat::RG_88:
-      *out = viz::ResourceFormat::RG_88;
-      return true;
-    case viz::mojom::ResourceFormat::LUMINANCE_F16:
-      *out = viz::ResourceFormat::LUMINANCE_F16;
-      return true;
-    case viz::mojom::ResourceFormat::RGBA_F16:
-      *out = viz::ResourceFormat::RGBA_F16;
-      return true;
-    case viz::mojom::ResourceFormat::R16_EXT:
-      *out = viz::ResourceFormat::R16_EXT;
-      return true;
-    case viz::mojom::ResourceFormat::RG16_EXT:
-      *out = viz::ResourceFormat::RG16_EXT;
-      return true;
-    case viz::mojom::ResourceFormat::RGBX_8888:
-      *out = viz::ResourceFormat::RGBX_8888;
-      return true;
-    case viz::mojom::ResourceFormat::BGRX_8888:
-      *out = viz::ResourceFormat::BGRX_8888;
-      return true;
-    case viz::mojom::ResourceFormat::RGBX_1010102:
-      *out = viz::ResourceFormat::RGBA_1010102;
-      return true;
-    case viz::mojom::ResourceFormat::BGRX_1010102:
-      *out = viz::ResourceFormat::BGRA_1010102;
-      return true;
-    case viz::mojom::ResourceFormat::YVU_420:
-      *out = viz::ResourceFormat::YVU_420;
-      return true;
-    case viz::mojom::ResourceFormat::YUV_420_BIPLANAR:
-      *out = viz::ResourceFormat::YUV_420_BIPLANAR;
-      return true;
-    case viz::mojom::ResourceFormat::P010:
-      *out = viz::ResourceFormat::P010;
-      return true;
-  }
-
-  return false;
-}
-
-}  // namespace mojo
diff --git a/services/viz/public/cpp/compositing/resource_format_mojom_traits.h b/services/viz/public/cpp/compositing/resource_format_mojom_traits.h
deleted file mode 100644
index a571963..0000000
--- a/services/viz/public/cpp/compositing/resource_format_mojom_traits.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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 SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_RESOURCE_FORMAT_MOJOM_TRAITS_H_
-#define SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_RESOURCE_FORMAT_MOJOM_TRAITS_H_
-
-#include "components/viz/common/resources/resource_format.h"
-#include "services/viz/public/mojom/compositing/resource_format.mojom-shared.h"
-
-namespace mojo {
-
-template <>
-struct EnumTraits<viz::mojom::ResourceFormat, viz::ResourceFormat> {
-  static viz::mojom::ResourceFormat ToMojom(viz::ResourceFormat type);
-
-  static bool FromMojom(viz::mojom::ResourceFormat input,
-                        viz::ResourceFormat* out);
-};
-
-}  // namespace mojo
-
-#endif  // SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_RESOURCE_FORMAT_MOJOM_TRAITS_H_
diff --git a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc
index b2be544..271e96a 100644
--- a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc
@@ -14,6 +14,131 @@
 namespace mojo {
 
 // static
+viz::mojom::ResourceFormat
+EnumTraits<viz::mojom::ResourceFormat, viz::ResourceFormat>::ToMojom(
+    viz::ResourceFormat format) {
+  switch (format) {
+    case viz::ResourceFormat::RGBA_8888:
+      return viz::mojom::ResourceFormat::RGBA_8888;
+    case viz::ResourceFormat::RGBA_4444:
+      return viz::mojom::ResourceFormat::RGBA_4444;
+    case viz::ResourceFormat::BGRA_8888:
+      return viz::mojom::ResourceFormat::BGRA_8888;
+    case viz::ResourceFormat::ALPHA_8:
+      return viz::mojom::ResourceFormat::ALPHA_8;
+    case viz::ResourceFormat::LUMINANCE_8:
+      return viz::mojom::ResourceFormat::LUMINANCE_8;
+    case viz::ResourceFormat::RGB_565:
+      return viz::mojom::ResourceFormat::RGB_565;
+    case viz::ResourceFormat::BGR_565:
+      return viz::mojom::ResourceFormat::BGR_565;
+    case viz::ResourceFormat::ETC1:
+      return viz::mojom::ResourceFormat::ETC1;
+    case viz::ResourceFormat::RED_8:
+      return viz::mojom::ResourceFormat::RED_8;
+    case viz::ResourceFormat::RG_88:
+      return viz::mojom::ResourceFormat::RG_88;
+    case viz::ResourceFormat::LUMINANCE_F16:
+      return viz::mojom::ResourceFormat::LUMINANCE_F16;
+    case viz::ResourceFormat::RGBA_F16:
+      return viz::mojom::ResourceFormat::RGBA_F16;
+    case viz::ResourceFormat::R16_EXT:
+      return viz::mojom::ResourceFormat::R16_EXT;
+    case viz::ResourceFormat::RG16_EXT:
+      return viz::mojom::ResourceFormat::RG16_EXT;
+    case viz::ResourceFormat::RGBX_8888:
+      return viz::mojom::ResourceFormat::RGBX_8888;
+    case viz::ResourceFormat::BGRX_8888:
+      return viz::mojom::ResourceFormat::BGRX_8888;
+    case viz::ResourceFormat::RGBA_1010102:
+      return viz::mojom::ResourceFormat::RGBX_1010102;
+    case viz::ResourceFormat::BGRA_1010102:
+      return viz::mojom::ResourceFormat::BGRX_1010102;
+    case viz::ResourceFormat::YVU_420:
+      return viz::mojom::ResourceFormat::YVU_420;
+    case viz::ResourceFormat::YUV_420_BIPLANAR:
+      return viz::mojom::ResourceFormat::YUV_420_BIPLANAR;
+    case viz::ResourceFormat::P010:
+      return viz::mojom::ResourceFormat::P010;
+  }
+  NOTREACHED();
+  return viz::mojom::ResourceFormat::RGBA_8888;
+}
+
+// static
+bool EnumTraits<viz::mojom::ResourceFormat, viz::ResourceFormat>::FromMojom(
+    viz::mojom::ResourceFormat format,
+    viz::ResourceFormat* out) {
+  switch (format) {
+    case viz::mojom::ResourceFormat::RGBA_8888:
+      *out = viz::ResourceFormat::RGBA_8888;
+      return true;
+    case viz::mojom::ResourceFormat::RGBA_4444:
+      *out = viz::ResourceFormat::RGBA_4444;
+      return true;
+    case viz::mojom::ResourceFormat::BGRA_8888:
+      *out = viz::ResourceFormat::BGRA_8888;
+      return true;
+    case viz::mojom::ResourceFormat::ALPHA_8:
+      *out = viz::ResourceFormat::ALPHA_8;
+      return true;
+    case viz::mojom::ResourceFormat::LUMINANCE_8:
+      *out = viz::ResourceFormat::LUMINANCE_8;
+      return true;
+    case viz::mojom::ResourceFormat::RGB_565:
+      *out = viz::ResourceFormat::RGB_565;
+      return true;
+    case viz::mojom::ResourceFormat::BGR_565:
+      *out = viz::ResourceFormat::BGR_565;
+      return true;
+    case viz::mojom::ResourceFormat::ETC1:
+      *out = viz::ResourceFormat::ETC1;
+      return true;
+    case viz::mojom::ResourceFormat::RED_8:
+      *out = viz::ResourceFormat::RED_8;
+      return true;
+    case viz::mojom::ResourceFormat::RG_88:
+      *out = viz::ResourceFormat::RG_88;
+      return true;
+    case viz::mojom::ResourceFormat::LUMINANCE_F16:
+      *out = viz::ResourceFormat::LUMINANCE_F16;
+      return true;
+    case viz::mojom::ResourceFormat::RGBA_F16:
+      *out = viz::ResourceFormat::RGBA_F16;
+      return true;
+    case viz::mojom::ResourceFormat::R16_EXT:
+      *out = viz::ResourceFormat::R16_EXT;
+      return true;
+    case viz::mojom::ResourceFormat::RG16_EXT:
+      *out = viz::ResourceFormat::RG16_EXT;
+      return true;
+    case viz::mojom::ResourceFormat::RGBX_8888:
+      *out = viz::ResourceFormat::RGBX_8888;
+      return true;
+    case viz::mojom::ResourceFormat::BGRX_8888:
+      *out = viz::ResourceFormat::BGRX_8888;
+      return true;
+    case viz::mojom::ResourceFormat::RGBX_1010102:
+      *out = viz::ResourceFormat::RGBA_1010102;
+      return true;
+    case viz::mojom::ResourceFormat::BGRX_1010102:
+      *out = viz::ResourceFormat::BGRA_1010102;
+      return true;
+    case viz::mojom::ResourceFormat::YVU_420:
+      *out = viz::ResourceFormat::YVU_420;
+      return true;
+    case viz::mojom::ResourceFormat::YUV_420_BIPLANAR:
+      *out = viz::ResourceFormat::YUV_420_BIPLANAR;
+      return true;
+    case viz::mojom::ResourceFormat::P010:
+      *out = viz::ResourceFormat::P010;
+      return true;
+  }
+
+  return false;
+}
+
+// static
 bool StructTraits<viz::mojom::TransferableResourceDataView,
                   viz::TransferableResource>::
     Read(viz::mojom::TransferableResourceDataView data,
diff --git a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h
index d6567cf..6c3fb28 100644
--- a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h
@@ -10,14 +10,20 @@
 #include "components/viz/common/resources/transferable_resource.h"
 #include "gpu/ipc/common/vulkan_ycbcr_info.h"
 #include "gpu/ipc/common/vulkan_ycbcr_info_mojom_traits.h"
-#include "services/viz/public/cpp/compositing/resource_format_mojom_traits.h"
-#include "services/viz/public/mojom/compositing/resource_format.mojom-shared.h"
 #include "services/viz/public/mojom/compositing/transferable_resource.mojom-shared.h"
 #include "ui/gfx/ipc/color/gfx_param_traits.h"
 
 namespace mojo {
 
 template <>
+struct EnumTraits<viz::mojom::ResourceFormat, viz::ResourceFormat> {
+  static viz::mojom::ResourceFormat ToMojom(viz::ResourceFormat type);
+
+  static bool FromMojom(viz::mojom::ResourceFormat input,
+                        viz::ResourceFormat* out);
+};
+
+template <>
 struct StructTraits<viz::mojom::TransferableResourceDataView,
                     viz::TransferableResource> {
   static const viz::ResourceId& id(const viz::TransferableResource& resource) {
diff --git a/services/viz/public/mojom/BUILD.gn b/services/viz/public/mojom/BUILD.gn
index c16788a..10755d7b 100644
--- a/services/viz/public/mojom/BUILD.gn
+++ b/services/viz/public/mojom/BUILD.gn
@@ -47,7 +47,6 @@
   ]
 
   public_deps = [
-    ":resource_format",
     "//gpu/ipc/common:interfaces",
     "//media/mojo/mojom",
     "//mojo/public/mojom/base",
@@ -558,25 +557,3 @@
     },
   ]
 }
-
-mojom("resource_format") {
-  generate_java = true
-  sources = [ "compositing/resource_format.mojom" ]
-  cpp_typemaps = [
-    {
-      types = [
-        {
-          mojom = "viz.mojom.ResourceFormat"
-          cpp = "::viz::ResourceFormat"
-        },
-      ]
-      traits_headers = [
-        "//services/viz/public/cpp/compositing/resource_format_mojom_traits.h",
-      ]
-      traits_sources = [
-        "//services/viz/public/cpp/compositing/resource_format_mojom_traits.cc",
-      ]
-      traits_public_deps = [ "//components/viz/common:resource_format" ]
-    },
-  ]
-}
diff --git a/services/viz/public/mojom/compositing/resource_format.mojom b/services/viz/public/mojom/compositing/resource_format.mojom
deleted file mode 100644
index 2fa211f..0000000
--- a/services/viz/public/mojom/compositing/resource_format.mojom
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module viz.mojom;
-
-// Corresponds to viz::ResourceFormat.
-enum ResourceFormat {
-  RGBA_8888,
-  RGBA_4444,
-  BGRA_8888,
-  ALPHA_8,
-  LUMINANCE_8,
-  RGB_565,
-  BGR_565,
-  ETC1,
-  RED_8,
-  RG_88,
-  LUMINANCE_F16,
-  RGBA_F16,
-  R16_EXT,
-  RG16_EXT,
-  RGBX_8888,
-  BGRX_8888,
-  RGBX_1010102,
-  BGRX_1010102,
-  YVU_420,
-  YUV_420_BIPLANAR,
-  P010,
-};
diff --git a/services/viz/public/mojom/compositing/transferable_resource.mojom b/services/viz/public/mojom/compositing/transferable_resource.mojom
index 2cf759b..e62d7cb 100644
--- a/services/viz/public/mojom/compositing/transferable_resource.mojom
+++ b/services/viz/public/mojom/compositing/transferable_resource.mojom
@@ -6,12 +6,36 @@
 
 import "gpu/ipc/common/mailbox_holder.mojom";
 import "gpu/ipc/common/vulkan_ycbcr_info.mojom";
-import "services/viz/public/mojom/compositing/resource_format.mojom";
 import "services/viz/public/mojom/compositing/resource_id.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 import "ui/gfx/mojom/buffer_types.mojom";
 import "ui/gfx/mojom/color_space.mojom";
 
+// Corresponds to viz::ResourceFormat.
+enum ResourceFormat {
+  RGBA_8888,
+  RGBA_4444,
+  BGRA_8888,
+  ALPHA_8,
+  LUMINANCE_8,
+  RGB_565,
+  BGR_565,
+  ETC1,
+  RED_8,
+  RG_88,
+  LUMINANCE_F16,
+  RGBA_F16,
+  R16_EXT,
+  RG16_EXT,
+  RGBX_8888,
+  BGRX_8888,
+  RGBX_1010102,
+  BGRX_1010102,
+  YVU_420,
+  YUV_420_BIPLANAR,
+  P010,
+};
+
 // See components/viz/common/resources/transferable_resource.h.
 struct TransferableResource {
   ResourceId id;
diff --git a/skia/public/mojom/BUILD.gn b/skia/public/mojom/BUILD.gn
index ec98affc..006ed9f 100644
--- a/skia/public/mojom/BUILD.gn
+++ b/skia/public/mojom/BUILD.gn
@@ -15,7 +15,6 @@
     "bitmap_skbitmap_mojom_traits.h",
     "image_info_mojom_traits.cc",
     "image_info_mojom_traits.h",
-    "surface_origin_mojom_traits.h",
   ]
 
   defines = [ "IS_SKIA_SHARED_TRAITS_IMPL" ]
@@ -35,7 +34,6 @@
     "bitmap.mojom",
     "image_info.mojom",
     "skcolor.mojom",
-    "surface_origin.mojom",
     "tile_mode.mojom",
   ]
 
@@ -122,16 +120,6 @@
         "//skia",
       ]
     },
-    {
-      types = [
-        {
-          mojom = "skia.mojom.SurfaceOrigin"
-          cpp = "::GrSurfaceOrigin"
-        },
-      ]
-      traits_headers = [ "surface_origin_mojom_traits.h" ]
-      traits_public_deps = [ ":shared_typemap_traits" ]
-    },
   ]
 
   cpp_typemaps = shared_skia_cpp_typemaps
diff --git a/skia/public/mojom/surface_origin.mojom b/skia/public/mojom/surface_origin.mojom
deleted file mode 100644
index cd097d9..0000000
--- a/skia/public/mojom/surface_origin.mojom
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module skia.mojom;
-
-// Maps to Skia's GrSurfaceOrigin in third_party/skia/include/gpu/GrTypes.h
-enum SurfaceOrigin {
-  kTopLeft,
-  kBottomLeft,
-};
diff --git a/skia/public/mojom/surface_origin_mojom_traits.h b/skia/public/mojom/surface_origin_mojom_traits.h
deleted file mode 100644
index 9d1fdc87..0000000
--- a/skia/public/mojom/surface_origin_mojom_traits.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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 SKIA_PUBLIC_MOJOM_SURFACE_ORIGIN_MOJOM_TRAITS_H_
-#define SKIA_PUBLIC_MOJOM_SURFACE_ORIGIN_MOJOM_TRAITS_H_
-
-#include "mojo/public/cpp/bindings/enum_traits.h"
-#include "skia/public/mojom/surface_origin.mojom-shared.h"
-#include "third_party/skia/include/gpu/GrTypes.h"
-
-namespace mojo {
-
-template <>
-struct EnumTraits<skia::mojom::SurfaceOrigin, GrSurfaceOrigin> {
-  static skia::mojom::SurfaceOrigin ToMojom(GrSurfaceOrigin origin) {
-    switch (origin) {
-      case kTopLeft_GrSurfaceOrigin:
-        return skia::mojom::SurfaceOrigin::kTopLeft;
-      case kBottomLeft_GrSurfaceOrigin:
-        return skia::mojom::SurfaceOrigin::kBottomLeft;
-    }
-    NOTREACHED();
-  }
-
-  static bool FromMojom(skia::mojom::SurfaceOrigin origin,
-                        GrSurfaceOrigin* out_origin) {
-    switch (origin) {
-      case skia::mojom::SurfaceOrigin::kTopLeft:
-        *out_origin = kTopLeft_GrSurfaceOrigin;
-        return true;
-      case skia::mojom::SurfaceOrigin::kBottomLeft:
-        *out_origin = kBottomLeft_GrSurfaceOrigin;
-        return true;
-    }
-
-    // Mojo has already validated that `origin` is a valid value, so it must be
-    // covered by one of the cases above.
-    NOTREACHED();
-    return false;
-  }
-};
-
-}  // namespace mojo
-
-#endif  // SKIA_PUBLIC_MOJOM_SURFACE_ORIGIN_MOJOM_TRAITS_H_
diff --git a/sql/BUILD.gn b/sql/BUILD.gn
index c7fd00f..e589f9a 100644
--- a/sql/BUILD.gn
+++ b/sql/BUILD.gn
@@ -121,8 +121,6 @@
     "test/paths.cc",
     "test/paths.h",
     "test/run_all_unittests.cc",
-    "test/sql_test_base.cc",
-    "test/sql_test_base.h",
     "test/sql_test_suite.cc",
     "test/sql_test_suite.h",
     "transaction_unittest.cc",
diff --git a/sql/database_unittest.cc b/sql/database_unittest.cc
index 9ae6a17..c2d6516 100644
--- a/sql/database_unittest.cc
+++ b/sql/database_unittest.cc
@@ -2,21 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "sql/database.h"
+
 #include <stddef.h>
 #include <stdint.h>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
-#include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/test/gtest_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "build/build_config.h"
-#include "sql/database.h"
 #include "sql/database_memory_dump_provider.h"
 #include "sql/meta_table.h"
 #include "sql/sql_features.h"
@@ -24,7 +23,6 @@
 #include "sql/test/database_test_peer.h"
 #include "sql/test/error_callback_support.h"
 #include "sql/test/scoped_error_expecter.h"
-#include "sql/test/sql_test_base.h"
 #include "sql/test/test_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/sqlite/sqlite3.h"
@@ -37,9 +35,9 @@
 
 // Helper to return the count of items in sqlite_master.  Return -1 in
 // case of error.
-int SqliteMasterCount(sql::Database* db) {
+int SqliteMasterCount(Database* db) {
   const char* kMasterCount = "SELECT COUNT(*) FROM sqlite_master";
-  sql::Statement s(db->GetUniqueStatement(kMasterCount));
+  Statement s(db->GetUniqueStatement(kMasterCount));
   return s.Step() ? s.ColumnInt(0) : -1;
 }
 
@@ -48,7 +46,7 @@
 // explicitly having the ref count live longer than the object.
 class RefCounter {
  public:
-  RefCounter(size_t* counter) : counter_(counter) { (*counter_)++; }
+  explicit RefCounter(size_t* counter) : counter_(counter) { (*counter_)++; }
   RefCounter(const RefCounter& other) : counter_(other.counter_) {
     (*counter_)++;
   }
@@ -61,24 +59,24 @@
 };
 
 // Empty callback for implementation of ErrorCallbackSetHelper().
-void IgnoreErrorCallback(int error, sql::Statement* stmt) {}
+void IgnoreErrorCallback(int error, Statement* stmt) {}
 
-void ErrorCallbackSetHelper(sql::Database* db,
+void ErrorCallbackSetHelper(Database* db,
                             size_t* counter,
                             const RefCounter& r,
                             int error,
-                            sql::Statement* stmt) {
+                            Statement* stmt) {
   // The ref count should not go to zero when changing the callback.
   EXPECT_GT(*counter, 0u);
   db->set_error_callback(base::BindRepeating(&IgnoreErrorCallback));
   EXPECT_GT(*counter, 0u);
 }
 
-void ErrorCallbackResetHelper(sql::Database* db,
+void ErrorCallbackResetHelper(Database* db,
                               size_t* counter,
                               const RefCounter& r,
                               int error,
-                              sql::Statement* stmt) {
+                              Statement* stmt) {
   // The ref count should not go to zero when clearing the callback.
   EXPECT_GT(*counter, 0u);
   db->reset_error_callback();
@@ -86,10 +84,10 @@
 }
 
 // Handle errors by blowing away the database.
-void RazeErrorCallback(sql::Database* db,
+void RazeErrorCallback(Database* db,
                        int expected_error,
                        int error,
-                       sql::Statement* stmt) {
+                       Statement* stmt) {
   // Nothing here needs extended errors at this time.
   EXPECT_EQ(expected_error, expected_error & 0xff);
   EXPECT_EQ(expected_error, error & 0xff);
@@ -106,23 +104,36 @@
   }
   ~ScopedUmaskSetter() { umask(old_umask_); }
 
+  ScopedUmaskSetter(const ScopedUmaskSetter&) = delete;
+  ScopedUmaskSetter& operator=(const ScopedUmaskSetter&) = delete;
+
  private:
   mode_t old_umask_;
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedUmaskSetter);
 };
 #endif  // defined(OS_POSIX)
 
 }  // namespace
 
 // We use the parameter to run all tests with WAL mode on and off.
-class SQLDatabaseTest : public SQLTestBase,
+class SQLDatabaseTest : public testing::Test,
                         public testing::WithParamInterface<bool> {
  public:
-  SQLDatabaseTest() : SQLTestBase(GetDBOptions()) {}
-  explicit SQLDatabaseTest(DatabaseOptions options) : SQLTestBase(options) {}
+  enum class OverwriteType {
+    kTruncate,
+    kOverwrite,
+  };
 
-  sql::DatabaseOptions GetDBOptions() {
-    sql::DatabaseOptions options;
+  ~SQLDatabaseTest() override = default;
+
+  void SetUp() override {
+    db_ = std::make_unique<Database>(GetDBOptions());
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    db_path_ = temp_dir_.GetPath().AppendASCII("database_test.sqlite");
+    ASSERT_TRUE(db_->Open(db_path_));
+  }
+
+  DatabaseOptions GetDBOptions() {
+    DatabaseOptions options;
     options.wal_mode = IsWALEnabled();
     // TODO(crbug.com/1120969): Remove after switching to exclusive mode on by
     // default.
@@ -135,186 +146,211 @@
 #endif  // defined(OS_FUCHSIA)
     return options;
   }
+
   bool IsWALEnabled() { return GetParam(); }
+
+  bool TruncateDatabase() {
+    base::File file(db_path_,
+                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+    return file.SetLength(0);
+  }
+
+  bool OverwriteDatabaseHeader(OverwriteType type) {
+    base::File file(db_path_,
+                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+    if (type == OverwriteType::kTruncate) {
+      if (!file.SetLength(0))
+        return false;
+    }
+
+    static constexpr char kText[] = "Now is the winter of our discontent.";
+    constexpr int kTextBytes = sizeof(kText) - 1;
+    return file.Write(0, kText, kTextBytes) == kTextBytes;
+  }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  base::FilePath db_path_;
+  std::unique_ptr<Database> db_;
 };
 
 TEST_P(SQLDatabaseTest, Execute) {
   // Valid statement should return true.
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  EXPECT_EQ(SQLITE_OK, db().GetErrorCode());
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
+  EXPECT_EQ(SQLITE_OK, db_->GetErrorCode());
 
   // Invalid statement should fail.
   ASSERT_EQ(SQLITE_ERROR,
-            db().ExecuteAndReturnErrorCode("CREATE TAB foo (a, b"));
-  EXPECT_EQ(SQLITE_ERROR, db().GetErrorCode());
+            db_->ExecuteAndReturnErrorCode("CREATE TAB foo (a, b"));
+  EXPECT_EQ(SQLITE_ERROR, db_->GetErrorCode());
 }
 
 TEST_P(SQLDatabaseTest, ExecuteWithErrorCode) {
   ASSERT_EQ(SQLITE_OK,
-            db().ExecuteAndReturnErrorCode("CREATE TABLE foo (a, b)"));
-  ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode("CREATE TABLE TABLE"));
-  ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode(
+            db_->ExecuteAndReturnErrorCode("CREATE TABLE foo (a, b)"));
+  ASSERT_EQ(SQLITE_ERROR, db_->ExecuteAndReturnErrorCode("CREATE TABLE TABLE"));
+  ASSERT_EQ(SQLITE_ERROR, db_->ExecuteAndReturnErrorCode(
                               "INSERT INTO foo(a, b) VALUES (1, 2, 3, 4)"));
 }
 
 TEST_P(SQLDatabaseTest, CachedStatement) {
-  sql::StatementID id1 = SQL_FROM_HERE;
-  sql::StatementID id2 = SQL_FROM_HERE;
+  StatementID id1 = SQL_FROM_HERE;
+  StatementID id2 = SQL_FROM_HERE;
   static const char kId1Sql[] = "SELECT a FROM foo";
   static const char kId2Sql[] = "SELECT b FROM foo";
 
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo(a, b) VALUES (12, 13)"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo(a, b) VALUES (12, 13)"));
 
   sqlite3_stmt* raw_id1_statement;
   sqlite3_stmt* raw_id2_statement;
   {
-    scoped_refptr<sql::Database::StatementRef> ref_from_id1 =
-        db().GetCachedStatement(id1, kId1Sql);
+    scoped_refptr<Database::StatementRef> ref_from_id1 =
+        db_->GetCachedStatement(id1, kId1Sql);
     raw_id1_statement = ref_from_id1->stmt();
 
-    sql::Statement from_id1(std::move(ref_from_id1));
+    Statement from_id1(std::move(ref_from_id1));
     ASSERT_TRUE(from_id1.is_valid());
     ASSERT_TRUE(from_id1.Step());
     EXPECT_EQ(12, from_id1.ColumnInt(0));
 
-    scoped_refptr<sql::Database::StatementRef> ref_from_id2 =
-        db().GetCachedStatement(id2, kId2Sql);
+    scoped_refptr<Database::StatementRef> ref_from_id2 =
+        db_->GetCachedStatement(id2, kId2Sql);
     raw_id2_statement = ref_from_id2->stmt();
     EXPECT_NE(raw_id1_statement, raw_id2_statement);
 
-    sql::Statement from_id2(std::move(ref_from_id2));
+    Statement from_id2(std::move(ref_from_id2));
     ASSERT_TRUE(from_id2.is_valid());
     ASSERT_TRUE(from_id2.Step());
     EXPECT_EQ(13, from_id2.ColumnInt(0));
   }
 
   {
-    scoped_refptr<sql::Database::StatementRef> ref_from_id1 =
-        db().GetCachedStatement(id1, kId1Sql);
+    scoped_refptr<Database::StatementRef> ref_from_id1 =
+        db_->GetCachedStatement(id1, kId1Sql);
     EXPECT_EQ(raw_id1_statement, ref_from_id1->stmt())
         << "statement was not cached";
 
-    sql::Statement from_id1(std::move(ref_from_id1));
+    Statement from_id1(std::move(ref_from_id1));
     ASSERT_TRUE(from_id1.is_valid());
     ASSERT_TRUE(from_id1.Step()) << "cached statement was not reset";
     EXPECT_EQ(12, from_id1.ColumnInt(0));
 
-    scoped_refptr<sql::Database::StatementRef> ref_from_id2 =
-        db().GetCachedStatement(id2, kId2Sql);
+    scoped_refptr<Database::StatementRef> ref_from_id2 =
+        db_->GetCachedStatement(id2, kId2Sql);
     EXPECT_EQ(raw_id2_statement, ref_from_id2->stmt())
         << "statement was not cached";
 
-    sql::Statement from_id2(std::move(ref_from_id2));
+    Statement from_id2(std::move(ref_from_id2));
     ASSERT_TRUE(from_id2.is_valid());
     ASSERT_TRUE(from_id2.Step()) << "cached statement was not reset";
     EXPECT_EQ(13, from_id2.ColumnInt(0));
   }
 
-  EXPECT_DCHECK_DEATH(db().GetCachedStatement(id1, kId2Sql))
+  EXPECT_DCHECK_DEATH(db_->GetCachedStatement(id1, kId2Sql))
       << "Using a different SQL with the same statement ID should DCHECK";
-  EXPECT_DCHECK_DEATH(db().GetCachedStatement(id2, kId1Sql))
+  EXPECT_DCHECK_DEATH(db_->GetCachedStatement(id2, kId1Sql))
       << "Using a different SQL with the same statement ID should DCHECK";
 }
 
 TEST_P(SQLDatabaseTest, IsSQLValidTest) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  ASSERT_TRUE(db().IsSQLValid("SELECT a FROM foo"));
-  ASSERT_FALSE(db().IsSQLValid("SELECT no_exist FROM foo"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_->IsSQLValid("SELECT a FROM foo"));
+  ASSERT_FALSE(db_->IsSQLValid("SELECT no_exist FROM foo"));
 }
 
 TEST_P(SQLDatabaseTest, DoesTableExist) {
-  EXPECT_FALSE(db().DoesTableExist("foo"));
-  EXPECT_FALSE(db().DoesTableExist("foo_index"));
+  EXPECT_FALSE(db_->DoesTableExist("foo"));
+  EXPECT_FALSE(db_->DoesTableExist("foo_index"));
 
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  ASSERT_TRUE(db().Execute("CREATE INDEX foo_index ON foo (a)"));
-  EXPECT_TRUE(db().DoesTableExist("foo"));
-  EXPECT_FALSE(db().DoesTableExist("foo_index"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_->Execute("CREATE INDEX foo_index ON foo (a)"));
+  EXPECT_TRUE(db_->DoesTableExist("foo"));
+  EXPECT_FALSE(db_->DoesTableExist("foo_index"));
 
   // DoesTableExist() is case-sensitive.
-  EXPECT_FALSE(db().DoesTableExist("Foo"));
-  EXPECT_FALSE(db().DoesTableExist("FOO"));
+  EXPECT_FALSE(db_->DoesTableExist("Foo"));
+  EXPECT_FALSE(db_->DoesTableExist("FOO"));
 }
 
 TEST_P(SQLDatabaseTest, DoesIndexExist) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  EXPECT_FALSE(db().DoesIndexExist("foo"));
-  EXPECT_FALSE(db().DoesIndexExist("foo_ubdex"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
+  EXPECT_FALSE(db_->DoesIndexExist("foo"));
+  EXPECT_FALSE(db_->DoesIndexExist("foo_ubdex"));
 
-  ASSERT_TRUE(db().Execute("CREATE INDEX foo_index ON foo (a)"));
-  EXPECT_TRUE(db().DoesIndexExist("foo_index"));
-  EXPECT_FALSE(db().DoesIndexExist("foo"));
+  ASSERT_TRUE(db_->Execute("CREATE INDEX foo_index ON foo (a)"));
+  EXPECT_TRUE(db_->DoesIndexExist("foo_index"));
+  EXPECT_FALSE(db_->DoesIndexExist("foo"));
 
   // DoesIndexExist() is case-sensitive.
-  EXPECT_FALSE(db().DoesIndexExist("Foo_index"));
-  EXPECT_FALSE(db().DoesIndexExist("Foo_Index"));
-  EXPECT_FALSE(db().DoesIndexExist("FOO_INDEX"));
+  EXPECT_FALSE(db_->DoesIndexExist("Foo_index"));
+  EXPECT_FALSE(db_->DoesIndexExist("Foo_Index"));
+  EXPECT_FALSE(db_->DoesIndexExist("FOO_INDEX"));
 }
 
 TEST_P(SQLDatabaseTest, DoesViewExist) {
-  EXPECT_FALSE(db().DoesViewExist("voo"));
-  ASSERT_TRUE(db().Execute("CREATE VIEW voo (a) AS SELECT 1"));
-  EXPECT_FALSE(db().DoesIndexExist("voo"));
-  EXPECT_FALSE(db().DoesTableExist("voo"));
-  EXPECT_TRUE(db().DoesViewExist("voo"));
+  EXPECT_FALSE(db_->DoesViewExist("voo"));
+  ASSERT_TRUE(db_->Execute("CREATE VIEW voo (a) AS SELECT 1"));
+  EXPECT_FALSE(db_->DoesIndexExist("voo"));
+  EXPECT_FALSE(db_->DoesTableExist("voo"));
+  EXPECT_TRUE(db_->DoesViewExist("voo"));
 
   // DoesTableExist() is case-sensitive.
-  EXPECT_FALSE(db().DoesViewExist("Voo"));
-  EXPECT_FALSE(db().DoesViewExist("VOO"));
+  EXPECT_FALSE(db_->DoesViewExist("Voo"));
+  EXPECT_FALSE(db_->DoesViewExist("VOO"));
 }
 
 TEST_P(SQLDatabaseTest, DoesColumnExist) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
 
-  EXPECT_FALSE(db().DoesColumnExist("foo", "bar"));
-  EXPECT_TRUE(db().DoesColumnExist("foo", "a"));
+  EXPECT_FALSE(db_->DoesColumnExist("foo", "bar"));
+  EXPECT_TRUE(db_->DoesColumnExist("foo", "a"));
 
-  ASSERT_FALSE(db().DoesTableExist("bar"));
-  EXPECT_FALSE(db().DoesColumnExist("bar", "b"));
+  ASSERT_FALSE(db_->DoesTableExist("bar"));
+  EXPECT_FALSE(db_->DoesColumnExist("bar", "b"));
 
   // SQLite resolves table/column names without case sensitivity.
-  EXPECT_TRUE(db().DoesColumnExist("FOO", "A"));
-  EXPECT_TRUE(db().DoesColumnExist("FOO", "a"));
-  EXPECT_TRUE(db().DoesColumnExist("foo", "A"));
+  EXPECT_TRUE(db_->DoesColumnExist("FOO", "A"));
+  EXPECT_TRUE(db_->DoesColumnExist("FOO", "a"));
+  EXPECT_TRUE(db_->DoesColumnExist("foo", "A"));
 }
 
 TEST_P(SQLDatabaseTest, GetLastInsertRowId) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (id INTEGER PRIMARY KEY, value)"));
 
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)"));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo (value) VALUES (12)"));
 
   // Last insert row ID should be valid.
-  int64_t row = db().GetLastInsertRowId();
+  int64_t row = db_->GetLastInsertRowId();
   EXPECT_LT(0, row);
 
   // It should be the primary key of the row we just inserted.
-  sql::Statement s(db().GetUniqueStatement("SELECT value FROM foo WHERE id=?"));
+  Statement s(db_->GetUniqueStatement("SELECT value FROM foo WHERE id=?"));
   s.BindInt64(0, row);
   ASSERT_TRUE(s.Step());
   EXPECT_EQ(12, s.ColumnInt(0));
 }
 
 TEST_P(SQLDatabaseTest, Rollback) {
-  ASSERT_TRUE(db().BeginTransaction());
-  ASSERT_TRUE(db().BeginTransaction());
-  EXPECT_EQ(2, db().transaction_nesting());
-  db().RollbackTransaction();
-  EXPECT_FALSE(db().CommitTransaction());
-  EXPECT_TRUE(db().BeginTransaction());
+  ASSERT_TRUE(db_->BeginTransaction());
+  ASSERT_TRUE(db_->BeginTransaction());
+  EXPECT_EQ(2, db_->transaction_nesting());
+  db_->RollbackTransaction();
+  EXPECT_FALSE(db_->CommitTransaction());
+  EXPECT_TRUE(db_->BeginTransaction());
 }
 
 // Test the scoped error expecter by attempting to insert a duplicate
 // value into an index.
 TEST_P(SQLDatabaseTest, ScopedErrorExpecter) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
 
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CONSTRAINT);
-    ASSERT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+    ASSERT_FALSE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
 }
@@ -323,36 +359,36 @@
 // with ScopedErrorExpecter.
 TEST_P(SQLDatabaseTest, ScopedIgnoreUntracked) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_FALSE(db().DoesTableExist("bar"));
-  ASSERT_TRUE(db().DoesTableExist("foo"));
-  ASSERT_TRUE(db().DoesColumnExist("foo", "id"));
-  db().Close();
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_FALSE(db_->DoesTableExist("bar"));
+  ASSERT_TRUE(db_->DoesTableExist("foo"));
+  ASSERT_TRUE(db_->DoesColumnExist("foo", "id"));
+  db_->Close();
 
   // Corrupt the database so that nothing works, including PRAGMAs.
-  ASSERT_TRUE(CorruptSizeInHeaderOfDB());
+  ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path_));
 
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ASSERT_TRUE(db().Open(db_path()));
-    ASSERT_FALSE(db().DoesTableExist("bar"));
-    ASSERT_FALSE(db().DoesTableExist("foo"));
-    ASSERT_FALSE(db().DoesColumnExist("foo", "id"));
+    ASSERT_TRUE(db_->Open(db_path_));
+    ASSERT_FALSE(db_->DoesTableExist("bar"));
+    ASSERT_FALSE(db_->DoesTableExist("foo"));
+    ASSERT_FALSE(db_->DoesColumnExist("foo", "id"));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
 }
 
 TEST_P(SQLDatabaseTest, ErrorCallback) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
 
   int error = SQLITE_OK;
   {
-    sql::ScopedErrorCallback sec(
-        &db(), base::BindRepeating(&sql::CaptureErrorCallback, &error));
-    EXPECT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+    ScopedErrorCallback sec(db_.get(),
+                            base::BindRepeating(&CaptureErrorCallback, &error));
+    EXPECT_FALSE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
 
     // Later versions of SQLite throw SQLITE_CONSTRAINT_UNIQUE.  The specific
     // sub-error isn't really important.
@@ -364,7 +400,7 @@
     error = SQLITE_OK;
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CONSTRAINT);
-    ASSERT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+    ASSERT_FALSE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
     ASSERT_TRUE(expecter.SawExpectedErrors());
     EXPECT_EQ(SQLITE_OK, error);
   }
@@ -380,34 +416,34 @@
   // live.
   {
     size_t count = 0;
-    sql::ScopedErrorCallback sec(
-        &db(), base::BindRepeating(&ErrorCallbackSetHelper, &db(), &count,
-                                   RefCounter(&count)));
+    ScopedErrorCallback sec(
+        db_.get(), base::BindRepeating(&ErrorCallbackSetHelper, db_.get(),
+                                       &count, RefCounter(&count)));
 
-    EXPECT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+    EXPECT_FALSE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
   }
 
   // Same test, but reset_error_callback() case.
   {
     size_t count = 0;
-    sql::ScopedErrorCallback sec(
-        &db(), base::BindRepeating(&ErrorCallbackResetHelper, &db(), &count,
-                                   RefCounter(&count)));
+    ScopedErrorCallback sec(
+        db_.get(), base::BindRepeating(&ErrorCallbackResetHelper, db_.get(),
+                                       &count, RefCounter(&count)));
 
-    EXPECT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
+    EXPECT_FALSE(db_->Execute("INSERT INTO foo (id) VALUES (12)"));
   }
 }
 
-// Test that sql::Database::Raze() results in a database without the
+// Test that Database::Raze() results in a database without the
 // tables from the original database.
 TEST_P(SQLDatabaseTest, Raze) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (value) VALUES (12)"));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo (value) VALUES (12)"));
 
   int pragma_auto_vacuum = 0;
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA auto_vacuum"));
+    Statement s(db_->GetUniqueStatement("PRAGMA auto_vacuum"));
     ASSERT_TRUE(s.Step());
     pragma_auto_vacuum = s.ColumnInt(0);
     ASSERT_TRUE(pragma_auto_vacuum == 0 || pragma_auto_vacuum == 1);
@@ -417,13 +453,13 @@
   const int kExpectedPageCount = 2 + pragma_auto_vacuum;
 
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA page_count"));
+    Statement s(db_->GetUniqueStatement("PRAGMA page_count"));
     ASSERT_TRUE(s.Step());
     EXPECT_EQ(kExpectedPageCount, s.ColumnInt(0));
   }
 
   {
-    sql::Statement s(db().GetUniqueStatement("SELECT * FROM sqlite_master"));
+    Statement s(db_->GetUniqueStatement("SELECT * FROM sqlite_master"));
     ASSERT_TRUE(s.Step());
     EXPECT_EQ("table", s.ColumnString(0));
     EXPECT_EQ("foo", s.ColumnString(1));
@@ -433,18 +469,18 @@
     EXPECT_EQ(kCreateSql, s.ColumnString(4));
   }
 
-  ASSERT_TRUE(db().Raze());
+  ASSERT_TRUE(db_->Raze());
 
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA page_count"));
+    Statement s(db_->GetUniqueStatement("PRAGMA page_count"));
     ASSERT_TRUE(s.Step());
     EXPECT_EQ(1, s.ColumnInt(0));
   }
 
-  ASSERT_EQ(0, SqliteMasterCount(&db()));
+  ASSERT_EQ(0, SqliteMasterCount(db_.get()));
 
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA auto_vacuum"));
+    Statement s(db_->GetUniqueStatement("PRAGMA auto_vacuum"));
     ASSERT_TRUE(s.Step());
     // The new database has the same auto_vacuum as a fresh database.
     EXPECT_EQ(pragma_auto_vacuum, s.ColumnInt(0));
@@ -466,8 +502,8 @@
 
   const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
       base::NumberToString(initial_page_size));
-  sql::Database::Delete(db_path);
-  sql::Database db({.page_size = initial_page_size});
+  Database::Delete(db_path);
+  Database db({.page_size = initial_page_size});
   ASSERT_TRUE(db.Open(db_path));
   ASSERT_TRUE(db.Execute(kCreateSql));
   ASSERT_TRUE(db.Execute(kInsertSql1));
@@ -477,7 +513,7 @@
   db.Close();
 
   // Re-open the database while setting a new |options.page_size| in the object.
-  sql::Database razed_db({.page_size = final_page_size});
+  Database razed_db({.page_size = final_page_size});
   ASSERT_TRUE(razed_db.Open(db_path));
   // Raze will use the page size set in the connection object, which may not
   // match the file's page size.
@@ -495,29 +531,29 @@
   EXPECT_EQ("1", ExecuteWithResult(&razed_db, "PRAGMA page_count"));
 }
 
-// Verify that sql::Recovery maintains the page size, and the virtual table
+// Verify that Recovery maintains the page size, and the virtual table
 // works with page sizes other than SQLite's default.  Also verify the case
 // where the default page size has changed.
 TEST_P(SQLDatabaseTest, RazePageSize) {
   const std::string default_page_size =
-      ExecuteWithResult(&db(), "PRAGMA page_size");
+      ExecuteWithResult(db_.get(), "PRAGMA page_size");
 
   // Sync uses 32k pages.
   EXPECT_NO_FATAL_FAILURE(
-      TestPageSize(db_path(), 32768, "32768", 32768, "32768"));
+      TestPageSize(db_path_, 32768, "32768", 32768, "32768"));
 
   // Many clients use 4k pages.  This is the SQLite default after 3.12.0.
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 4096, "4096", 4096, "4096"));
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 4096, "4096", 4096, "4096"));
 
   // 1k is the default page size before 3.12.0.
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 1024, "1024", 1024, "1024"));
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 1024, "1024", 1024, "1024"));
 
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 2048, "2048", 4096, "4096"));
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 2048, "2048", 4096, "4096"));
 
   // Databases with no page size specified should result in the default
   // page size.  2k has never been the default page size.
   ASSERT_NE("2048", default_page_size);
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 2048, "2048",
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 2048, "2048",
                                        DatabaseOptions::kDefaultPageSize,
                                        default_page_size));
 }
@@ -525,15 +561,15 @@
 // Test that Raze() results are seen in other connections.
 TEST_P(SQLDatabaseTest, RazeMultiple) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
 
-  sql::Database other_db(GetDBOptions());
-  ASSERT_TRUE(other_db.Open(db_path()));
+  Database other_db(GetDBOptions());
+  ASSERT_TRUE(other_db.Open(db_path_));
 
   // Check that the second connection sees the table.
   ASSERT_EQ(1, SqliteMasterCount(&other_db));
 
-  ASSERT_TRUE(db().Raze());
+  ASSERT_TRUE(db_->Raze());
 
   // The second connection sees the updated database.
   ASSERT_EQ(0, SqliteMasterCount(&other_db));
@@ -541,26 +577,26 @@
 
 TEST_P(SQLDatabaseTest, RazeLocked) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
 
   // Open a transaction and write some data in a second connection.
   // This will acquire a PENDING or EXCLUSIVE transaction, which will
   // cause the raze to fail.
-  sql::Database other_db(GetDBOptions());
-  ASSERT_TRUE(other_db.Open(db_path()));
+  Database other_db(GetDBOptions());
+  ASSERT_TRUE(other_db.Open(db_path_));
   ASSERT_TRUE(other_db.BeginTransaction());
   const char* kInsertSql = "INSERT INTO foo VALUES (1, 'data')";
   ASSERT_TRUE(other_db.Execute(kInsertSql));
 
-  ASSERT_FALSE(db().Raze());
+  ASSERT_FALSE(db_->Raze());
 
   // Works after COMMIT.
   ASSERT_TRUE(other_db.CommitTransaction());
-  ASSERT_TRUE(db().Raze());
+  ASSERT_TRUE(db_->Raze());
 
   // Re-create the database.
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kInsertSql));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kInsertSql));
 
   // An unfinished read transaction in the other connection also
   // blocks raze.
@@ -568,13 +604,13 @@
   // write operations when using a WAL.
   if (!IsWALEnabled()) {
     const char* kQuery = "SELECT COUNT(*) FROM foo";
-    sql::Statement s(other_db.GetUniqueStatement(kQuery));
+    Statement s(other_db.GetUniqueStatement(kQuery));
     ASSERT_TRUE(s.Step());
-    ASSERT_FALSE(db().Raze());
+    ASSERT_FALSE(db_->Raze());
 
     // Completing the statement unlocks the database.
     ASSERT_FALSE(s.Step());
-    ASSERT_TRUE(db().Raze());
+    ASSERT_TRUE(db_->Raze());
   }
 }
 
@@ -582,26 +618,26 @@
 // this as an empty database.
 TEST_P(SQLDatabaseTest, RazeEmptyDB) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  db().Close();
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  db_->Close();
 
-  TruncateDatabase();
+  ASSERT_TRUE(TruncateDatabase());
 
-  ASSERT_TRUE(db().Open(db_path()));
-  ASSERT_TRUE(db().Raze());
-  EXPECT_EQ(0, SqliteMasterCount(&db()));
+  ASSERT_TRUE(db_->Open(db_path_));
+  ASSERT_TRUE(db_->Raze());
+  EXPECT_EQ(0, SqliteMasterCount(db_.get()));
 }
 
 // Verify that Raze() can handle a file of junk.
 // Need exclusive mode off here as there are some subtleties (by design) around
 // how the cache is used with it on which causes the test to fail.
 TEST_P(SQLDatabaseTest, RazeNOTADB) {
-  db().Close();
-  sql::Database::Delete(db_path());
-  ASSERT_FALSE(GetPathExists(db_path()));
+  db_->Close();
+  Database::Delete(db_path_);
+  ASSERT_FALSE(base::PathExists(db_path_));
 
-  WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE_AND_TRUNCATE);
-  ASSERT_TRUE(GetPathExists(db_path()));
+  ASSERT_TRUE(OverwriteDatabaseHeader(OverwriteType::kTruncate));
+  ASSERT_TRUE(base::PathExists(db_path_));
 
   // SQLite will successfully open the handle, but fail when running PRAGMA
   // statements that access the database.
@@ -609,25 +645,25 @@
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_NOTADB);
 
-    EXPECT_TRUE(db().Open(db_path()));
+    EXPECT_TRUE(db_->Open(db_path_));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
-  EXPECT_TRUE(db().Raze());
-  db().Close();
+  EXPECT_TRUE(db_->Raze());
+  db_->Close();
 
   // Now empty, the open should open an empty database.
-  EXPECT_TRUE(db().Open(db_path()));
-  EXPECT_EQ(0, SqliteMasterCount(&db()));
+  EXPECT_TRUE(db_->Open(db_path_));
+  EXPECT_EQ(0, SqliteMasterCount(db_.get()));
 }
 
 // Verify that Raze() can handle a database overwritten with garbage.
 TEST_P(SQLDatabaseTest, RazeNOTADB2) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_EQ(1, SqliteMasterCount(&db()));
-  db().Close();
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_EQ(1, SqliteMasterCount(db_.get()));
+  db_->Close();
 
-  WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
+  ASSERT_TRUE(OverwriteDatabaseHeader(OverwriteType::kOverwrite));
 
   // SQLite will successfully open the handle, but will fail with
   // SQLITE_NOTADB on pragma statemenets which attempt to read the
@@ -635,15 +671,15 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_NOTADB);
-    EXPECT_TRUE(db().Open(db_path()));
+    EXPECT_TRUE(db_->Open(db_path_));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
-  EXPECT_TRUE(db().Raze());
-  db().Close();
+  EXPECT_TRUE(db_->Raze());
+  db_->Close();
 
   // Now empty, the open should succeed with an empty database.
-  EXPECT_TRUE(db().Open(db_path()));
-  EXPECT_EQ(0, SqliteMasterCount(&db()));
+  EXPECT_TRUE(db_->Open(db_path_));
+  EXPECT_EQ(0, SqliteMasterCount(db_.get()));
 }
 
 // Test that a callback from Open() can raze the database.  This is
@@ -652,34 +688,34 @@
 // callback does this during Open(), the open is retried and succeeds.
 TEST_P(SQLDatabaseTest, RazeCallbackReopen) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_EQ(1, SqliteMasterCount(&db()));
-  db().Close();
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_EQ(1, SqliteMasterCount(db_.get()));
+  db_->Close();
 
   // Corrupt the database so that nothing works, including PRAGMAs.
-  ASSERT_TRUE(CorruptSizeInHeaderOfDB());
+  ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path_));
 
   // Open() will succeed, even though the PRAGMA calls within will
   // fail with SQLITE_CORRUPT, as will this PRAGMA.
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ASSERT_TRUE(db().Open(db_path()));
-    ASSERT_FALSE(db().Execute("PRAGMA auto_vacuum"));
-    db().Close();
+    ASSERT_TRUE(db_->Open(db_path_));
+    ASSERT_FALSE(db_->Execute("PRAGMA auto_vacuum"));
+    db_->Close();
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
 
-  db().set_error_callback(
-      base::BindRepeating(&RazeErrorCallback, &db(), SQLITE_CORRUPT));
+  db_->set_error_callback(
+      base::BindRepeating(&RazeErrorCallback, db_.get(), SQLITE_CORRUPT));
 
   // When the PRAGMA calls in Open() raise SQLITE_CORRUPT, the error
   // callback will call RazeAndClose().  Open() will then fail and be
   // retried.  The second Open() on the empty database will succeed
   // cleanly.
-  ASSERT_TRUE(db().Open(db_path()));
-  ASSERT_TRUE(db().Execute("PRAGMA auto_vacuum"));
-  EXPECT_EQ(0, SqliteMasterCount(&db()));
+  ASSERT_TRUE(db_->Open(db_path_));
+  ASSERT_TRUE(db_->Execute("PRAGMA auto_vacuum"));
+  EXPECT_EQ(0, SqliteMasterCount(db_.get()));
 }
 
 // Basic test of RazeAndClose() operation.
@@ -689,24 +725,24 @@
 
   // Test that RazeAndClose() closes the database, and that the
   // database is empty when re-opened.
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kPopulateSql));
-  ASSERT_TRUE(db().RazeAndClose());
-  ASSERT_FALSE(db().is_open());
-  db().Close();
-  ASSERT_TRUE(db().Open(db_path()));
-  ASSERT_EQ(0, SqliteMasterCount(&db()));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kPopulateSql));
+  ASSERT_TRUE(db_->RazeAndClose());
+  ASSERT_FALSE(db_->is_open());
+  db_->Close();
+  ASSERT_TRUE(db_->Open(db_path_));
+  ASSERT_EQ(0, SqliteMasterCount(db_.get()));
 
   // Test that RazeAndClose() can break transactions.
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kPopulateSql));
-  ASSERT_TRUE(db().BeginTransaction());
-  ASSERT_TRUE(db().RazeAndClose());
-  ASSERT_FALSE(db().is_open());
-  ASSERT_FALSE(db().CommitTransaction());
-  db().Close();
-  ASSERT_TRUE(db().Open(db_path()));
-  ASSERT_EQ(0, SqliteMasterCount(&db()));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kPopulateSql));
+  ASSERT_TRUE(db_->BeginTransaction());
+  ASSERT_TRUE(db_->RazeAndClose());
+  ASSERT_FALSE(db_->is_open());
+  ASSERT_FALSE(db_->CommitTransaction());
+  db_->Close();
+  ASSERT_TRUE(db_->Open(db_path_));
+  ASSERT_EQ(0, SqliteMasterCount(db_.get()));
 }
 
 // Test that various operations fail without crashing after
@@ -716,53 +752,53 @@
   const char* kPopulateSql = "INSERT INTO foo (value) VALUES (12)";
   const char* kSimpleSql = "SELECT 1";
 
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kPopulateSql));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kPopulateSql));
 
   // Test baseline expectations.
-  db().Preload();
-  ASSERT_TRUE(db().DoesTableExist("foo"));
-  ASSERT_TRUE(db().IsSQLValid(kSimpleSql));
-  ASSERT_EQ(SQLITE_OK, db().ExecuteAndReturnErrorCode(kSimpleSql));
-  ASSERT_TRUE(db().Execute(kSimpleSql));
-  ASSERT_TRUE(db().is_open());
+  db_->Preload();
+  ASSERT_TRUE(db_->DoesTableExist("foo"));
+  ASSERT_TRUE(db_->IsSQLValid(kSimpleSql));
+  ASSERT_EQ(SQLITE_OK, db_->ExecuteAndReturnErrorCode(kSimpleSql));
+  ASSERT_TRUE(db_->Execute(kSimpleSql));
+  ASSERT_TRUE(db_->is_open());
   {
-    sql::Statement s(db().GetUniqueStatement(kSimpleSql));
+    Statement s(db_->GetUniqueStatement(kSimpleSql));
     ASSERT_TRUE(s.Step());
   }
   {
-    sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
+    Statement s(db_->GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
     ASSERT_TRUE(s.Step());
   }
-  ASSERT_TRUE(db().BeginTransaction());
-  ASSERT_TRUE(db().CommitTransaction());
-  ASSERT_TRUE(db().BeginTransaction());
-  db().RollbackTransaction();
+  ASSERT_TRUE(db_->BeginTransaction());
+  ASSERT_TRUE(db_->CommitTransaction());
+  ASSERT_TRUE(db_->BeginTransaction());
+  db_->RollbackTransaction();
 
-  ASSERT_TRUE(db().RazeAndClose());
+  ASSERT_TRUE(db_->RazeAndClose());
 
   // At this point, they should all fail, but not crash.
-  db().Preload();
-  ASSERT_FALSE(db().DoesTableExist("foo"));
-  ASSERT_FALSE(db().IsSQLValid(kSimpleSql));
-  ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode(kSimpleSql));
-  ASSERT_FALSE(db().Execute(kSimpleSql));
-  ASSERT_FALSE(db().is_open());
+  db_->Preload();
+  ASSERT_FALSE(db_->DoesTableExist("foo"));
+  ASSERT_FALSE(db_->IsSQLValid(kSimpleSql));
+  ASSERT_EQ(SQLITE_ERROR, db_->ExecuteAndReturnErrorCode(kSimpleSql));
+  ASSERT_FALSE(db_->Execute(kSimpleSql));
+  ASSERT_FALSE(db_->is_open());
   {
-    sql::Statement s(db().GetUniqueStatement(kSimpleSql));
+    Statement s(db_->GetUniqueStatement(kSimpleSql));
     ASSERT_FALSE(s.Step());
   }
   {
-    sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
+    Statement s(db_->GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
     ASSERT_FALSE(s.Step());
   }
-  ASSERT_FALSE(db().BeginTransaction());
-  ASSERT_FALSE(db().CommitTransaction());
-  ASSERT_FALSE(db().BeginTransaction());
-  db().RollbackTransaction();
+  ASSERT_FALSE(db_->BeginTransaction());
+  ASSERT_FALSE(db_->CommitTransaction());
+  ASSERT_FALSE(db_->BeginTransaction());
+  db_->RollbackTransaction();
 
   // Close normally to reset the poisoned flag.
-  db().Close();
+  db_->Close();
 
 // DEATH tests not supported on Android, iOS, or Fuchsia.
 #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_FUCHSIA)
@@ -770,7 +806,7 @@
   // usage by becoming fatal in debug mode.  Since DEATH tests are
   // expensive, just test one of them.
   if (DLOG_IS_ON(FATAL)) {
-    ASSERT_DEATH({ db().IsSQLValid(kSimpleSql); },
+    ASSERT_DEATH({ db_->IsSQLValid(kSimpleSql); },
                  "Illegal use of Database without a db");
   }
 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_FUCHSIA)
@@ -789,75 +825,75 @@
   // The empty database has 0 or 1 pages.  Raze() should leave it with exactly 1
   // page.  Not checking directly because auto_vacuum on Android adds a freelist
   // page.
-  ASSERT_TRUE(db().Raze());
+  ASSERT_TRUE(db_->Raze());
   int64_t expected_size;
-  ASSERT_TRUE(base::GetFileSize(db_path(), &expected_size));
+  ASSERT_TRUE(base::GetFileSize(db_path_, &expected_size));
   ASSERT_GT(expected_size, 0);
 
   // Cause the database to take a few pages.
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
   for (size_t i = 0; i < 24; ++i) {
     ASSERT_TRUE(
-        db().Execute("INSERT INTO foo (value) VALUES (randomblob(1024))"));
+        db_->Execute("INSERT INTO foo (value) VALUES (randomblob(1024))"));
   }
 
   // In WAL mode, writes don't reach the database file until a checkpoint
   // happens.
-  ASSERT_TRUE(db().CheckpointDatabase());
+  ASSERT_TRUE(db_->CheckpointDatabase());
 
   int64_t db_size;
-  ASSERT_TRUE(base::GetFileSize(db_path(), &db_size));
+  ASSERT_TRUE(base::GetFileSize(db_path_, &db_size));
   ASSERT_GT(db_size, expected_size);
 
   // Make a query covering most of the database file to make sure that the
   // blocks are actually mapped into memory.  Empirically, the truncate problem
   // doesn't seem to happen if no blocks are mapped.
   EXPECT_EQ("24576",
-            ExecuteWithResult(&db(), "SELECT SUM(LENGTH(value)) FROM foo"));
+            ExecuteWithResult(db_.get(), "SELECT SUM(LENGTH(value)) FROM foo"));
 
-  ASSERT_TRUE(db().Raze());
-  ASSERT_TRUE(base::GetFileSize(db_path(), &db_size));
+  ASSERT_TRUE(db_->Raze());
+  ASSERT_TRUE(base::GetFileSize(db_path_, &db_size));
   ASSERT_EQ(expected_size, db_size);
 }
 
 #if defined(OS_ANDROID)
 TEST_P(SQLDatabaseTest, SetTempDirForSQL) {
-  sql::MetaTable meta_table;
+  MetaTable meta_table;
   // Below call needs a temporary directory in sqlite3
   // On Android, it can pass only when the temporary directory is set.
   // Otherwise, sqlite3 doesn't find the correct directory to store
   // temporary files and will report the error 'unable to open
   // database file'.
-  ASSERT_TRUE(meta_table.Init(&db(), 4, 4));
+  ASSERT_TRUE(meta_table.Init(db_.get(), 4, 4));
 }
 #endif  // defined(OS_ANDROID)
 
 TEST_P(SQLDatabaseTest, Delete) {
-  EXPECT_TRUE(db().Execute("CREATE TABLE x (x)"));
-  db().Close();
+  EXPECT_TRUE(db_->Execute("CREATE TABLE x (x)"));
+  db_->Close();
 
-  base::FilePath journal_path = sql::Database::JournalPath(db_path());
-  base::FilePath wal_path = sql::Database::WriteAheadLogPath(db_path());
+  base::FilePath journal_path = Database::JournalPath(db_path_);
+  base::FilePath wal_path = Database::WriteAheadLogPath(db_path_);
 
   // Should have both a main database file and a journal file if
   // journal_mode is TRUNCATE. There is no WAL file as it is deleted on Close.
-  ASSERT_TRUE(GetPathExists(db_path()));
+  ASSERT_TRUE(base::PathExists(db_path_));
   if (!IsWALEnabled()) {  // TRUNCATE mode
-    ASSERT_TRUE(GetPathExists(journal_path));
+    ASSERT_TRUE(base::PathExists(journal_path));
   }
 
-  sql::Database::Delete(db_path());
-  EXPECT_FALSE(GetPathExists(db_path()));
-  EXPECT_FALSE(GetPathExists(journal_path));
-  EXPECT_FALSE(GetPathExists(wal_path));
+  Database::Delete(db_path_);
+  EXPECT_FALSE(base::PathExists(db_path_));
+  EXPECT_FALSE(base::PathExists(journal_path));
+  EXPECT_FALSE(base::PathExists(wal_path));
 }
 
 #if defined(OS_POSIX)  // This test operates on POSIX file permissions.
 TEST_P(SQLDatabaseTest, PosixFilePermissions) {
-  db().Close();
-  sql::Database::Delete(db_path());
-  ASSERT_FALSE(GetPathExists(db_path()));
+  db_->Close();
+  Database::Delete(db_path_);
+  ASSERT_FALSE(base::PathExists(db_path_));
 
   // If the bots all had a restrictive umask setting such that databases are
   // always created with only the owner able to read them, then the code could
@@ -865,37 +901,37 @@
   // umask.
   ScopedUmaskSetter permissive_umask(S_IWGRP | S_IWOTH);
 
-  ASSERT_TRUE(db().Open(db_path()));
+  ASSERT_TRUE(db_->Open(db_path_));
 
   // Cause the journal file to be created. If the default journal_mode is
   // changed back to DELETE, this test will need to be updated.
-  EXPECT_TRUE(db().Execute("CREATE TABLE x (x)"));
+  EXPECT_TRUE(db_->Execute("CREATE TABLE x (x)"));
 
   int mode;
-  ASSERT_TRUE(GetPathExists(db_path()));
-  EXPECT_TRUE(base::GetPosixFilePermissions(db_path(), &mode));
+  ASSERT_TRUE(base::PathExists(db_path_));
+  EXPECT_TRUE(base::GetPosixFilePermissions(db_path_, &mode));
   ASSERT_EQ(mode, 0600);
 
   if (IsWALEnabled()) {  // WAL mode
     // The WAL file is created lazily on first change.
-    ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+    ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
 
-    base::FilePath wal_path = sql::Database::WriteAheadLogPath(db_path());
-    ASSERT_TRUE(GetPathExists(wal_path));
+    base::FilePath wal_path = Database::WriteAheadLogPath(db_path_);
+    ASSERT_TRUE(base::PathExists(wal_path));
     EXPECT_TRUE(base::GetPosixFilePermissions(wal_path, &mode));
     ASSERT_EQ(mode, 0600);
 
     // The shm file doesn't exist in exclusive locking mode.
-    if (ExecuteWithResult(&db(), "PRAGMA locking_mode") == "normal") {
-      base::FilePath shm_path = sql::Database::SharedMemoryFilePath(db_path());
-      ASSERT_TRUE(GetPathExists(shm_path));
+    if (ExecuteWithResult(db_.get(), "PRAGMA locking_mode") == "normal") {
+      base::FilePath shm_path = Database::SharedMemoryFilePath(db_path_);
+      ASSERT_TRUE(base::PathExists(shm_path));
       EXPECT_TRUE(base::GetPosixFilePermissions(shm_path, &mode));
       ASSERT_EQ(mode, 0600);
     }
   } else {  // Truncate mode
-    base::FilePath journal_path = sql::Database::JournalPath(db_path());
+    base::FilePath journal_path = Database::JournalPath(db_path_);
     DLOG(ERROR) << "journal_path: " << journal_path;
-    ASSERT_TRUE(GetPathExists(journal_path));
+    ASSERT_TRUE(base::PathExists(journal_path));
     EXPECT_TRUE(base::GetPosixFilePermissions(journal_path, &mode));
     ASSERT_EQ(mode, 0600);
   }
@@ -904,31 +940,31 @@
 
 // Test that errors start happening once Poison() is called.
 TEST_P(SQLDatabaseTest, Poison) {
-  EXPECT_TRUE(db().Execute("CREATE TABLE x (x)"));
+  EXPECT_TRUE(db_->Execute("CREATE TABLE x (x)"));
 
   // Before the Poison() call, things generally work.
-  EXPECT_TRUE(db().IsSQLValid("INSERT INTO x VALUES ('x')"));
-  EXPECT_TRUE(db().Execute("INSERT INTO x VALUES ('x')"));
+  EXPECT_TRUE(db_->IsSQLValid("INSERT INTO x VALUES ('x')"));
+  EXPECT_TRUE(db_->Execute("INSERT INTO x VALUES ('x')"));
   {
-    sql::Statement s(db().GetUniqueStatement("SELECT COUNT(*) FROM x"));
+    Statement s(db_->GetUniqueStatement("SELECT COUNT(*) FROM x"));
     ASSERT_TRUE(s.is_valid());
     ASSERT_TRUE(s.Step());
   }
 
   // Get a statement which is valid before and will exist across Poison().
-  sql::Statement valid_statement(
-      db().GetUniqueStatement("SELECT COUNT(*) FROM sqlite_master"));
+  Statement valid_statement(
+      db_->GetUniqueStatement("SELECT COUNT(*) FROM sqlite_master"));
   ASSERT_TRUE(valid_statement.is_valid());
   ASSERT_TRUE(valid_statement.Step());
   valid_statement.Reset(true);
 
-  db().Poison();
+  db_->Poison();
 
   // After the Poison() call, things fail.
-  EXPECT_FALSE(db().IsSQLValid("INSERT INTO x VALUES ('x')"));
-  EXPECT_FALSE(db().Execute("INSERT INTO x VALUES ('x')"));
+  EXPECT_FALSE(db_->IsSQLValid("INSERT INTO x VALUES ('x')"));
+  EXPECT_FALSE(db_->Execute("INSERT INTO x VALUES ('x')"));
   {
-    sql::Statement s(db().GetUniqueStatement("SELECT COUNT(*) FROM x"));
+    Statement s(db_->GetUniqueStatement("SELECT COUNT(*) FROM x"));
     ASSERT_FALSE(s.is_valid());
     ASSERT_FALSE(s.Step());
   }
@@ -939,77 +975,77 @@
 
   // Test that poisoning the database during a transaction works (with errors).
   // RazeErrorCallback() poisons the database, the extra COMMIT causes
-  // CommitTransaction() to throw an error while commiting.
-  db().set_error_callback(
-      base::BindRepeating(&RazeErrorCallback, &db(), SQLITE_ERROR));
-  db().Close();
-  ASSERT_TRUE(db().Open(db_path()));
-  EXPECT_TRUE(db().BeginTransaction());
-  EXPECT_TRUE(db().Execute("INSERT INTO x VALUES ('x')"));
-  EXPECT_TRUE(db().Execute("COMMIT"));
-  EXPECT_FALSE(db().CommitTransaction());
+  // CommitTransaction() to throw an error while committing.
+  db_->set_error_callback(
+      base::BindRepeating(&RazeErrorCallback, db_.get(), SQLITE_ERROR));
+  db_->Close();
+  ASSERT_TRUE(db_->Open(db_path_));
+  EXPECT_TRUE(db_->BeginTransaction());
+  EXPECT_TRUE(db_->Execute("INSERT INTO x VALUES ('x')"));
+  EXPECT_TRUE(db_->Execute("COMMIT"));
+  EXPECT_FALSE(db_->CommitTransaction());
 }
 
 TEST_P(SQLDatabaseTest, AttachDatabase) {
-  EXPECT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+  EXPECT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
 
   // Create a database to attach to.
   base::FilePath attach_path =
-      db_path().DirName().AppendASCII("SQLDatabaseAttach.db");
+      db_path_.DirName().AppendASCII("SQLDatabaseAttach.db");
   static const char kAttachmentPoint[] = "other";
   {
-    sql::Database other_db;
+    Database other_db;
     ASSERT_TRUE(other_db.Open(attach_path));
     EXPECT_TRUE(other_db.Execute("CREATE TABLE bar (a, b)"));
     EXPECT_TRUE(other_db.Execute("INSERT INTO bar VALUES ('hello', 'world')"));
   }
 
   // Cannot see the attached database, yet.
-  EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar"));
+  EXPECT_FALSE(db_->IsSQLValid("SELECT count(*) from other.bar"));
 
-  EXPECT_TRUE(
-      DatabaseTestPeer::AttachDatabase(&db(), attach_path, kAttachmentPoint));
-  EXPECT_TRUE(db().IsSQLValid("SELECT count(*) from other.bar"));
+  EXPECT_TRUE(DatabaseTestPeer::AttachDatabase(db_.get(), attach_path,
+                                               kAttachmentPoint));
+  EXPECT_TRUE(db_->IsSQLValid("SELECT count(*) from other.bar"));
 
   // Queries can touch both databases after the ATTACH.
-  EXPECT_TRUE(db().Execute("INSERT INTO foo SELECT a, b FROM other.bar"));
+  EXPECT_TRUE(db_->Execute("INSERT INTO foo SELECT a, b FROM other.bar"));
   {
-    sql::Statement s(db().GetUniqueStatement("SELECT COUNT(*) FROM foo"));
+    Statement s(db_->GetUniqueStatement("SELECT COUNT(*) FROM foo"));
     ASSERT_TRUE(s.Step());
     EXPECT_EQ(1, s.ColumnInt(0));
   }
 
-  EXPECT_TRUE(DatabaseTestPeer::DetachDatabase(&db(), kAttachmentPoint));
-  EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar"));
+  EXPECT_TRUE(DatabaseTestPeer::DetachDatabase(db_.get(), kAttachmentPoint));
+  EXPECT_FALSE(db_->IsSQLValid("SELECT count(*) from other.bar"));
 }
 
 TEST_P(SQLDatabaseTest, AttachDatabaseWithOpenTransaction) {
-  EXPECT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+  EXPECT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
 
   // Create a database to attach to.
   base::FilePath attach_path =
-      db_path().DirName().AppendASCII("SQLDatabaseAttach.db");
+      db_path_.DirName().AppendASCII("SQLDatabaseAttach.db");
   static const char kAttachmentPoint[] = "other";
   {
-    sql::Database other_db;
+    Database other_db;
     ASSERT_TRUE(other_db.Open(attach_path));
     EXPECT_TRUE(other_db.Execute("CREATE TABLE bar (a, b)"));
     EXPECT_TRUE(other_db.Execute("INSERT INTO bar VALUES ('hello', 'world')"));
   }
 
   // Cannot see the attached database, yet.
-  EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar"));
+  EXPECT_FALSE(db_->IsSQLValid("SELECT count(*) from other.bar"));
 
   // Attach succeeds in a transaction.
-  EXPECT_TRUE(db().BeginTransaction());
-  EXPECT_TRUE(
-      DatabaseTestPeer::AttachDatabase(&db(), attach_path, kAttachmentPoint));
-  EXPECT_TRUE(db().IsSQLValid("SELECT count(*) from other.bar"));
+  EXPECT_TRUE(db_->BeginTransaction());
+  EXPECT_TRUE(DatabaseTestPeer::AttachDatabase(db_.get(), attach_path,
+                                               kAttachmentPoint));
+  EXPECT_TRUE(db_->IsSQLValid("SELECT count(*) from other.bar"));
 
   // Queries can touch both databases after the ATTACH.
-  EXPECT_TRUE(db().Execute("INSERT INTO foo SELECT a, b FROM other.bar"));
+  EXPECT_TRUE(db_->Execute("INSERT INTO foo SELECT a, b FROM other.bar"));
   {
-    sql::Statement s(db().GetUniqueStatement("SELECT COUNT(*) FROM foo"));
+    Statement s(db_->GetUniqueStatement("SELECT COUNT(*) FROM foo"));
     ASSERT_TRUE(s.Step());
     EXPECT_EQ(1, s.ColumnInt(0));
   }
@@ -1018,30 +1054,30 @@
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_ERROR);
-    EXPECT_FALSE(DatabaseTestPeer::DetachDatabase(&db(), kAttachmentPoint));
-    EXPECT_TRUE(db().IsSQLValid("SELECT count(*) from other.bar"));
+    EXPECT_FALSE(DatabaseTestPeer::DetachDatabase(db_.get(), kAttachmentPoint));
+    EXPECT_TRUE(db_->IsSQLValid("SELECT count(*) from other.bar"));
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
 
   // Detach succeeds when the transaction is closed.
-  db().RollbackTransaction();
-  EXPECT_TRUE(DatabaseTestPeer::DetachDatabase(&db(), kAttachmentPoint));
-  EXPECT_FALSE(db().IsSQLValid("SELECT count(*) from other.bar"));
+  db_->RollbackTransaction();
+  EXPECT_TRUE(DatabaseTestPeer::DetachDatabase(db_.get(), kAttachmentPoint));
+  EXPECT_FALSE(db_->IsSQLValid("SELECT count(*) from other.bar"));
 }
 
 TEST_P(SQLDatabaseTest, Basic_QuickIntegrityCheck) {
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  EXPECT_TRUE(db().QuickIntegrityCheck());
-  db().Close();
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  EXPECT_TRUE(db_->QuickIntegrityCheck());
+  db_->Close();
 
-  ASSERT_TRUE(CorruptSizeInHeaderOfDB());
+  ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path_));
 
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ASSERT_TRUE(db().Open(db_path()));
-    EXPECT_FALSE(db().QuickIntegrityCheck());
+    ASSERT_TRUE(db_->Open(db_path_));
+    EXPECT_FALSE(db_->QuickIntegrityCheck());
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
 }
@@ -1051,19 +1087,19 @@
   std::vector<std::string> messages;
 
   const char* kCreateSql = "CREATE TABLE foo (id INTEGER PRIMARY KEY, value)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  EXPECT_TRUE(db().FullIntegrityCheck(&messages));
+  ASSERT_TRUE(db_->Execute(kCreateSql));
+  EXPECT_TRUE(db_->FullIntegrityCheck(&messages));
   EXPECT_EQ(1u, messages.size());
   EXPECT_EQ(kOk, messages[0]);
-  db().Close();
+  db_->Close();
 
-  ASSERT_TRUE(CorruptSizeInHeaderOfDB());
+  ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path_));
 
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ASSERT_TRUE(db().Open(db_path()));
-    EXPECT_TRUE(db().FullIntegrityCheck(&messages));
+    ASSERT_TRUE(db_->Open(db_path_));
+    EXPECT_TRUE(db_->FullIntegrityCheck(&messages));
     EXPECT_LT(1u, messages.size());
     EXPECT_NE(kOk, messages[0]);
     ASSERT_TRUE(expecter.SawExpectedErrors());
@@ -1077,38 +1113,38 @@
   base::trace_event::MemoryDumpArgs args = {
       base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
   base::trace_event::ProcessMemoryDump pmd(args);
-  ASSERT_TRUE(db().memory_dump_provider_->OnMemoryDump(args, &pmd));
+  ASSERT_TRUE(db_->memory_dump_provider_->OnMemoryDump(args, &pmd));
   EXPECT_GE(pmd.allocator_dumps().size(), 1u);
 }
 
 // Test that the functions to collect diagnostic data run to completion, without
 // worrying too much about what they generate (since that will change).
 TEST_P(SQLDatabaseTest, CollectDiagnosticInfo) {
-  const std::string corruption_info = db().CollectCorruptionInfo();
+  const std::string corruption_info = db_->CollectCorruptionInfo();
   EXPECT_NE(std::string::npos, corruption_info.find("SQLITE_CORRUPT"));
   EXPECT_NE(std::string::npos, corruption_info.find("integrity_check"));
 
   // A statement to see in the results.
   const char* kSimpleSql = "SELECT 'mountain'";
-  Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
+  Statement s(db_->GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
 
   // Error includes the statement.
-  const std::string readonly_info = db().CollectErrorInfo(SQLITE_READONLY, &s);
+  const std::string readonly_info = db_->CollectErrorInfo(SQLITE_READONLY, &s);
   EXPECT_NE(std::string::npos, readonly_info.find(kSimpleSql));
 
   // Some other error doesn't include the statment.
   // TODO(shess): This is weak.
-  const std::string full_info = db().CollectErrorInfo(SQLITE_FULL, nullptr);
+  const std::string full_info = db_->CollectErrorInfo(SQLITE_FULL, nullptr);
   EXPECT_EQ(std::string::npos, full_info.find(kSimpleSql));
 
   // A table to see in the SQLITE_ERROR results.
-  EXPECT_TRUE(db().Execute("CREATE TABLE volcano (x)"));
+  EXPECT_TRUE(db_->Execute("CREATE TABLE volcano (x)"));
 
   // Version info to see in the SQLITE_ERROR results.
-  sql::MetaTable meta_table;
-  ASSERT_TRUE(meta_table.Init(&db(), 4, 4));
+  MetaTable meta_table;
+  ASSERT_TRUE(meta_table.Init(db_.get(), 4, 4));
 
-  const std::string error_info = db().CollectErrorInfo(SQLITE_ERROR, &s);
+  const std::string error_info = db_->CollectErrorInfo(SQLITE_ERROR, &s);
   EXPECT_NE(std::string::npos, error_info.find(kSimpleSql));
   EXPECT_NE(std::string::npos, error_info.find("volcano"));
   EXPECT_NE(std::string::npos, error_info.find("version: 4"));
@@ -1118,7 +1154,7 @@
 // enabled by SQLite.
 TEST_P(SQLDatabaseTest, MmapInitiallyEnabled) {
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
+    Statement s(db_->GetUniqueStatement("PRAGMA mmap_size"));
     ASSERT_TRUE(s.Step())
         << "All supported SQLite versions should have mmap support";
 
@@ -1127,7 +1163,7 @@
     // returned.  If the VFS does not understand SQLITE_FCNTL_MMAP_SIZE (for
     // instance MojoVFS), -1 is returned.
     if (s.ColumnInt(0) <= 0) {
-      ASSERT_TRUE(db().Execute("PRAGMA mmap_size = 1048576"));
+      ASSERT_TRUE(db_->Execute("PRAGMA mmap_size = 1048576"));
       s.Reset(true);
       ASSERT_TRUE(s.Step());
       EXPECT_LE(s.ColumnInt(0), 0);
@@ -1135,24 +1171,24 @@
   }
 
   // Test that explicit disable prevents mmap'ed I/O.
-  db().Close();
-  sql::Database::Delete(db_path());
-  db().set_mmap_disabled();
-  ASSERT_TRUE(db().Open(db_path()));
-  EXPECT_EQ("0", ExecuteWithResult(&db(), "PRAGMA mmap_size"));
+  db_->Close();
+  Database::Delete(db_path_);
+  db_->set_mmap_disabled();
+  ASSERT_TRUE(db_->Open(db_path_));
+  EXPECT_EQ("0", ExecuteWithResult(db_.get(), "PRAGMA mmap_size"));
 }
 
 // Test whether a fresh database gets mmap enabled when using alternate status
 // storage.
 TEST_P(SQLDatabaseTest, MmapInitiallyEnabledAltStatus) {
   // Re-open fresh database with alt-status flag set.
-  db().Close();
-  sql::Database::Delete(db_path());
-  db().set_mmap_alt_status();
-  ASSERT_TRUE(db().Open(db_path()));
+  db_->Close();
+  Database::Delete(db_path_);
+  db_->set_mmap_alt_status();
+  ASSERT_TRUE(db_->Open(db_path_));
 
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
+    Statement s(db_->GetUniqueStatement("PRAGMA mmap_size"));
     ASSERT_TRUE(s.Step())
         << "All supported SQLite versions should have mmap support";
 
@@ -1161,7 +1197,7 @@
     // returned.  If the VFS does not understand SQLITE_FCNTL_MMAP_SIZE (for
     // instance MojoVFS), -1 is returned.
     if (s.ColumnInt(0) <= 0) {
-      ASSERT_TRUE(db().Execute("PRAGMA mmap_size = 1048576"));
+      ASSERT_TRUE(db_->Execute("PRAGMA mmap_size = 1048576"));
       s.Reset(true);
       ASSERT_TRUE(s.Step());
       EXPECT_LE(s.ColumnInt(0), 0);
@@ -1169,11 +1205,11 @@
   }
 
   // Test that explicit disable overrides set_mmap_alt_status().
-  db().Close();
-  sql::Database::Delete(db_path());
-  db().set_mmap_disabled();
-  ASSERT_TRUE(db().Open(db_path()));
-  EXPECT_EQ("0", ExecuteWithResult(&db(), "PRAGMA mmap_size"));
+  db_->Close();
+  Database::Delete(db_path_);
+  db_->set_mmap_disabled();
+  ASSERT_TRUE(db_->Open(db_path_));
+  EXPECT_EQ("0", ExecuteWithResult(db_.get(), "PRAGMA mmap_size"));
 }
 
 TEST_P(SQLDatabaseTest, GetAppropriateMmapSize) {
@@ -1182,40 +1218,40 @@
 
   // If there is no meta table (as for a fresh database), assume that everything
   // should be mapped, and the status of the meta table is not affected.
-  ASSERT_TRUE(!db().DoesTableExist("meta"));
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
-  ASSERT_TRUE(!db().DoesTableExist("meta"));
+  ASSERT_TRUE(!db_->DoesTableExist("meta"));
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_TRUE(!db_->DoesTableExist("meta"));
 
   // When the meta table is first created, it sets up to map everything.
-  MetaTable().Init(&db(), 1, 1);
-  ASSERT_TRUE(db().DoesTableExist("meta"));
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
-  ASSERT_TRUE(MetaTable::GetMmapStatus(&db(), &mmap_status));
+  MetaTable().Init(db_.get(), 1, 1);
+  ASSERT_TRUE(db_->DoesTableExist("meta"));
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_TRUE(MetaTable::GetMmapStatus(db_.get(), &mmap_status));
   ASSERT_EQ(MetaTable::kMmapSuccess, mmap_status);
 
   // Preload with partial progress of one page.  Should map everything.
-  ASSERT_TRUE(db().Execute("REPLACE INTO meta VALUES ('mmap_status', 1)"));
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
-  ASSERT_TRUE(MetaTable::GetMmapStatus(&db(), &mmap_status));
+  ASSERT_TRUE(db_->Execute("REPLACE INTO meta VALUES ('mmap_status', 1)"));
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_TRUE(MetaTable::GetMmapStatus(db_.get(), &mmap_status));
   ASSERT_EQ(MetaTable::kMmapSuccess, mmap_status);
 
   // Failure status maps nothing.
-  ASSERT_TRUE(db().Execute("REPLACE INTO meta VALUES ('mmap_status', -2)"));
-  ASSERT_EQ(0UL, db().GetAppropriateMmapSize());
+  ASSERT_TRUE(db_->Execute("REPLACE INTO meta VALUES ('mmap_status', -2)"));
+  ASSERT_EQ(0UL, db_->GetAppropriateMmapSize());
 
   // Re-initializing the meta table does not re-create the key if the table
   // already exists.
-  ASSERT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'mmap_status'"));
-  MetaTable().Init(&db(), 1, 1);
+  ASSERT_TRUE(db_->Execute("DELETE FROM meta WHERE key = 'mmap_status'"));
+  MetaTable().Init(db_.get(), 1, 1);
   ASSERT_EQ(MetaTable::kMmapSuccess, mmap_status);
-  ASSERT_TRUE(MetaTable::GetMmapStatus(&db(), &mmap_status));
+  ASSERT_TRUE(MetaTable::GetMmapStatus(db_.get(), &mmap_status));
   ASSERT_EQ(0, mmap_status);
 
   // With no key, map everything and create the key.
   // TODO(shess): This really should be "maps everything after validating it",
   // but that is more complicated to structure.
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
-  ASSERT_TRUE(MetaTable::GetMmapStatus(&db(), &mmap_status));
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_TRUE(MetaTable::GetMmapStatus(db_.get(), &mmap_status));
   ASSERT_EQ(MetaTable::kMmapSuccess, mmap_status);
 }
 
@@ -1223,133 +1259,151 @@
   const size_t kMmapAlot = 25 * 1024 * 1024;
 
   // At this point, Database still expects a future [meta] table.
-  ASSERT_FALSE(db().DoesTableExist("meta"));
-  ASSERT_FALSE(db().DoesViewExist("MmapStatus"));
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
-  ASSERT_FALSE(db().DoesTableExist("meta"));
-  ASSERT_FALSE(db().DoesViewExist("MmapStatus"));
+  ASSERT_FALSE(db_->DoesTableExist("meta"));
+  ASSERT_FALSE(db_->DoesViewExist("MmapStatus"));
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_FALSE(db_->DoesTableExist("meta"));
+  ASSERT_FALSE(db_->DoesViewExist("MmapStatus"));
 
   // Using alt status, everything should be mapped, with state in the view.
-  db().set_mmap_alt_status();
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
-  ASSERT_FALSE(db().DoesTableExist("meta"));
-  ASSERT_TRUE(db().DoesViewExist("MmapStatus"));
+  db_->set_mmap_alt_status();
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_FALSE(db_->DoesTableExist("meta"));
+  ASSERT_TRUE(db_->DoesViewExist("MmapStatus"));
   EXPECT_EQ(base::NumberToString(MetaTable::kMmapSuccess),
-            ExecuteWithResult(&db(), "SELECT * FROM MmapStatus"));
+            ExecuteWithResult(db_.get(), "SELECT * FROM MmapStatus"));
 
   // Also maps everything when kMmapSuccess is already in the view.
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
 
   // Preload with partial progress of one page.  Should map everything.
-  ASSERT_TRUE(db().Execute("DROP VIEW MmapStatus"));
-  ASSERT_TRUE(db().Execute("CREATE VIEW MmapStatus (value) AS SELECT 1"));
-  ASSERT_GT(db().GetAppropriateMmapSize(), kMmapAlot);
+  ASSERT_TRUE(db_->Execute("DROP VIEW MmapStatus"));
+  ASSERT_TRUE(db_->Execute("CREATE VIEW MmapStatus (value) AS SELECT 1"));
+  ASSERT_GT(db_->GetAppropriateMmapSize(), kMmapAlot);
   EXPECT_EQ(base::NumberToString(MetaTable::kMmapSuccess),
-            ExecuteWithResult(&db(), "SELECT * FROM MmapStatus"));
+            ExecuteWithResult(db_.get(), "SELECT * FROM MmapStatus"));
 
   // Failure status leads to nothing being mapped.
-  ASSERT_TRUE(db().Execute("DROP VIEW MmapStatus"));
-  ASSERT_TRUE(db().Execute("CREATE VIEW MmapStatus (value) AS SELECT -2"));
-  ASSERT_EQ(0UL, db().GetAppropriateMmapSize());
+  ASSERT_TRUE(db_->Execute("DROP VIEW MmapStatus"));
+  ASSERT_TRUE(db_->Execute("CREATE VIEW MmapStatus (value) AS SELECT -2"));
+  ASSERT_EQ(0UL, db_->GetAppropriateMmapSize());
   EXPECT_EQ(base::NumberToString(MetaTable::kMmapFailure),
-            ExecuteWithResult(&db(), "SELECT * FROM MmapStatus"));
+            ExecuteWithResult(db_.get(), "SELECT * FROM MmapStatus"));
 }
 
 TEST_P(SQLDatabaseTest, GetMemoryUsage) {
   // Databases with mmap enabled may not follow the assumptions below.
-  db().Close();
-  db().set_mmap_disabled();
-  ASSERT_TRUE(db().Open(db_path()));
+  db_->Close();
+  db_->set_mmap_disabled();
+  ASSERT_TRUE(db_->Open(db_path_));
 
-  int initial_memory = db().GetMemoryUsage();
+  int initial_memory = db_->GetMemoryUsage();
   EXPECT_GT(initial_memory, 0)
       << "SQLite should always use some memory for a database";
 
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo(a, b) VALUES (12, 13)"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo(a, b) VALUES (12, 13)"));
 
-  int post_query_memory = db().GetMemoryUsage();
+  int post_query_memory = db_->GetMemoryUsage();
   EXPECT_GT(post_query_memory, initial_memory)
       << "Page cache usage should go up after executing queries";
 
-  db().TrimMemory();
-  int post_trim_memory = db().GetMemoryUsage();
+  db_->TrimMemory();
+  int post_trim_memory = db_->GetMemoryUsage();
   EXPECT_GT(post_query_memory, post_trim_memory)
       << "Page cache usage should go down after calling TrimMemory()";
 }
 
-class SQLDatabaseTestExclusiveMode : public SQLDatabaseTest {
+class SQLDatabaseTestExclusiveMode : public testing::Test,
+                                     public testing::WithParamInterface<bool> {
  public:
-  SQLDatabaseTestExclusiveMode() : SQLDatabaseTest(GetDBOptions()) {}
+  ~SQLDatabaseTestExclusiveMode() override = default;
+
+  void SetUp() override {
+    db_ = std::make_unique<Database>(GetDBOptions());
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    db_path_ = temp_dir_.GetPath().AppendASCII("recovery_test.sqlite");
+    ASSERT_TRUE(db_->Open(db_path_));
+  }
 
   DatabaseOptions GetDBOptions() {
-    DatabaseOptions options = SQLDatabaseTest::GetDBOptions();
+    DatabaseOptions options;
+    options.wal_mode = IsWALEnabled();
     options.exclusive_locking = true;
     return options;
   }
+
+  bool IsWALEnabled() { return GetParam(); }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  base::FilePath db_path_;
+  std::unique_ptr<Database> db_;
 };
 
 TEST_P(SQLDatabaseTestExclusiveMode, LockingModeExclusive) {
-  EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA locking_mode"), "exclusive");
+  EXPECT_EQ(ExecuteWithResult(db_.get(), "PRAGMA locking_mode"), "exclusive");
 }
 
 TEST_P(SQLDatabaseTest, LockingModeNormal) {
-  EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA locking_mode"), "normal");
+  EXPECT_EQ(ExecuteWithResult(db_.get(), "PRAGMA locking_mode"), "normal");
 }
 
 TEST_P(SQLDatabaseTest, OpenedInCorrectMode) {
   std::string expected_mode = IsWALEnabled() ? "wal" : "truncate";
-  EXPECT_EQ(ExecuteWithResult(&db(), "PRAGMA journal_mode"), expected_mode);
+  EXPECT_EQ(ExecuteWithResult(db_.get(), "PRAGMA journal_mode"), expected_mode);
 }
 
 TEST_P(SQLDatabaseTest, CheckpointDatabase) {
   if (!IsWALEnabled())
     return;
 
-  base::FilePath wal_path = sql::Database::WriteAheadLogPath(db_path());
+  base::FilePath wal_path = Database::WriteAheadLogPath(db_path_);
 
   int64_t wal_size = 0;
   // WAL file initially empty.
-  EXPECT_TRUE(GetPathExists(wal_path));
+  EXPECT_TRUE(base::PathExists(wal_path));
   base::GetFileSize(wal_path, &wal_size);
   EXPECT_EQ(wal_size, 0);
 
   ASSERT_TRUE(
-      db().Execute("CREATE TABLE foo (id INTEGER UNIQUE, value INTEGER)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo VALUES (1, 1)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo VALUES (2, 2)"));
+      db_->Execute("CREATE TABLE foo (id INTEGER UNIQUE, value INTEGER)"));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo VALUES (1, 1)"));
+  ASSERT_TRUE(db_->Execute("INSERT INTO foo VALUES (2, 2)"));
 
   // Writes reach WAL file but not db file.
   base::GetFileSize(wal_path, &wal_size);
   EXPECT_GT(wal_size, 0);
 
   int64_t db_size = 0;
-  base::GetFileSize(db_path(), &db_size);
-  EXPECT_EQ(db_size, db().page_size());
+  base::GetFileSize(db_path_, &db_size);
+  EXPECT_EQ(db_size, db_->page_size());
 
   // Checkpoint database to immediately propagate writes to DB file.
-  EXPECT_TRUE(db().CheckpointDatabase());
+  EXPECT_TRUE(db_->CheckpointDatabase());
 
-  base::GetFileSize(db_path(), &db_size);
-  EXPECT_GT(db_size, db().page_size());
-  EXPECT_EQ(ExecuteWithResult(&db(), "SELECT value FROM foo where id=1"), "1");
-  EXPECT_EQ(ExecuteWithResult(&db(), "SELECT value FROM foo where id=2"), "2");
+  base::GetFileSize(db_path_, &db_size);
+  EXPECT_GT(db_size, db_->page_size());
+  EXPECT_EQ(ExecuteWithResult(db_.get(), "SELECT value FROM foo where id=1"),
+            "1");
+  EXPECT_EQ(ExecuteWithResult(db_.get(), "SELECT value FROM foo where id=2"),
+            "2");
 }
 
 TEST_P(SQLDatabaseTest, CorruptSizeInHeaderTest) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (x)"));
-  ASSERT_TRUE(db().Execute("CREATE TABLE bar (x)"));
-  db().Close();
+  ASSERT_TRUE(db_->Execute("CREATE TABLE foo (x)"));
+  ASSERT_TRUE(db_->Execute("CREATE TABLE bar (x)"));
+  db_->Close();
 
-  ASSERT_TRUE(CorruptSizeInHeaderOfDB());
+  ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path_));
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);
-    ASSERT_TRUE(db().Open(db_path()));
-    EXPECT_FALSE(db().Execute("INSERT INTO foo values (1)"));
-    EXPECT_FALSE(db().DoesTableExist("foo"));
-    EXPECT_FALSE(db().DoesTableExist("bar"));
-    EXPECT_FALSE(db().Execute("SELECT * FROM foo"));
+    ASSERT_TRUE(db_->Open(db_path_));
+    EXPECT_FALSE(db_->Execute("INSERT INTO foo values (1)"));
+    EXPECT_FALSE(db_->DoesTableExist("foo"));
+    EXPECT_FALSE(db_->DoesTableExist("bar"));
+    EXPECT_FALSE(db_->Execute("SELECT * FROM foo"));
     EXPECT_TRUE(expecter.SawExpectedErrors());
   }
 }
@@ -1361,8 +1415,8 @@
 // DEATH tests not supported on Android, iOS, or Fuchsia.
 #if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_FUCHSIA)
   if (DLOG_IS_ON(FATAL)) {
-    db().set_error_callback(base::BindRepeating(&IgnoreErrorCallback));
-    ASSERT_DEATH({ db().GetUniqueStatement("SELECT x"); },
+    db_->set_error_callback(base::BindRepeating(&IgnoreErrorCallback));
+    ASSERT_DEATH({ db_->GetUniqueStatement("SELECT x"); },
                  "SQL compile error no such column: x");
   }
 #endif  // !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_FUCHSIA)
diff --git a/sql/meta_table_unittest.cc b/sql/meta_table_unittest.cc
index 928c0ed..e91380b 100644
--- a/sql/meta_table_unittest.cc
+++ b/sql/meta_table_unittest.cc
@@ -10,22 +10,36 @@
 #include "base/files/scoped_temp_dir.h"
 #include "sql/database.h"
 #include "sql/statement.h"
-#include "sql/test/sql_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace sql {
+
 namespace {
 
-using SQLMetaTableTest = sql::SQLTestBase;
+class SQLMetaTableTest : public testing::Test {
+ public:
+  ~SQLMetaTableTest() override = default;
 
-TEST_F(SQLMetaTableTest, DoesTableExist) {
-  EXPECT_FALSE(sql::MetaTable::DoesTableExist(&db()));
-
-  {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(
+        db_.Open(temp_dir_.GetPath().AppendASCII("meta_table_test.sqlite")));
   }
 
-  EXPECT_TRUE(sql::MetaTable::DoesTableExist(&db()));
+ protected:
+  base::ScopedTempDir temp_dir_;
+  Database db_;
+};
+
+TEST_F(SQLMetaTableTest, DoesTableExist) {
+  EXPECT_FALSE(MetaTable::DoesTableExist(&db_));
+
+  {
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
+  }
+
+  EXPECT_TRUE(MetaTable::DoesTableExist(&db_));
 }
 
 TEST_F(SQLMetaTableTest, RazeIfDeprecated) {
@@ -34,45 +48,45 @@
 
   // Setup a current database.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), kVersion, kVersion));
-    EXPECT_TRUE(db().Execute("CREATE TABLE t(c)"));
-    EXPECT_TRUE(db().DoesTableExist("t"));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, kVersion, kVersion));
+    EXPECT_TRUE(db_.Execute("CREATE TABLE t(c)"));
+    EXPECT_TRUE(db_.DoesTableExist("t"));
   }
 
   // Table should should still exist if the database version is new enough.
-  sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
-  EXPECT_TRUE(db().DoesTableExist("t"));
+  MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersion);
+  EXPECT_TRUE(db_.DoesTableExist("t"));
 
   // TODO(shess): It may make sense to Raze() if meta isn't present or
   // version isn't present.  See meta_table.h TODO on RazeIfDeprecated().
 
   // Table should still exist if the version is not available.
-  EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'"));
+  EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'"));
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), kVersion, kVersion));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, kVersion, kVersion));
     EXPECT_EQ(0, meta_table.GetVersionNumber());
   }
-  sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
-  EXPECT_TRUE(db().DoesTableExist("t"));
+  MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersion);
+  EXPECT_TRUE(db_.DoesTableExist("t"));
 
   // Table should still exist if meta table is missing.
-  EXPECT_TRUE(db().Execute("DROP TABLE meta"));
-  sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
-  EXPECT_TRUE(db().DoesTableExist("t"));
+  EXPECT_TRUE(db_.Execute("DROP TABLE meta"));
+  MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersion);
+  EXPECT_TRUE(db_.DoesTableExist("t"));
 
   // Setup meta with deprecated version.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), kDeprecatedVersion, kDeprecatedVersion));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, kDeprecatedVersion, kDeprecatedVersion));
   }
 
   // Deprecation check should remove the table.
-  EXPECT_TRUE(db().DoesTableExist("t"));
-  sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
-  EXPECT_FALSE(sql::MetaTable::DoesTableExist(&db()));
-  EXPECT_FALSE(db().DoesTableExist("t"));
+  EXPECT_TRUE(db_.DoesTableExist("t"));
+  MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersion);
+  EXPECT_FALSE(MetaTable::DoesTableExist(&db_));
+  EXPECT_FALSE(db_.DoesTableExist("t"));
 }
 
 TEST_F(SQLMetaTableTest, VersionNumber) {
@@ -87,16 +101,16 @@
 
   // First Init() sets the version info as expected.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), kVersionFirst, kCompatVersionFirst));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, kVersionFirst, kCompatVersionFirst));
     EXPECT_EQ(kVersionFirst, meta_table.GetVersionNumber());
     EXPECT_EQ(kCompatVersionFirst, meta_table.GetCompatibleVersionNumber());
   }
 
   // Second Init() does not change the version info.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), kVersionSecond, kCompatVersionSecond));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, kVersionSecond, kCompatVersionSecond));
     EXPECT_EQ(kVersionFirst, meta_table.GetVersionNumber());
     EXPECT_EQ(kCompatVersionFirst, meta_table.GetCompatibleVersionNumber());
 
@@ -106,8 +120,8 @@
 
   // Version info from Set*() calls is seen.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), kVersionThird, kCompatVersionThird));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, kVersionThird, kCompatVersionThird));
     EXPECT_EQ(kVersionSecond, meta_table.GetVersionNumber());
     EXPECT_EQ(kCompatVersionSecond, meta_table.GetCompatibleVersionNumber());
   }
@@ -120,8 +134,8 @@
 
   // Initially, the value isn't there until set.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     std::string value;
     EXPECT_FALSE(meta_table.GetValue(kKey, &value));
@@ -133,8 +147,8 @@
 
   // Value is persistent across different instances.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     std::string value;
     EXPECT_TRUE(meta_table.GetValue(kKey, &value));
@@ -145,8 +159,8 @@
 
   // Existing value was successfully changed.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     std::string value;
     EXPECT_TRUE(meta_table.GetValue(kKey, &value));
@@ -161,8 +175,8 @@
 
   // Initially, the value isn't there until set.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     int value;
     EXPECT_FALSE(meta_table.GetValue(kKey, &value));
@@ -174,8 +188,8 @@
 
   // Value is persistent across different instances.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     int value;
     EXPECT_TRUE(meta_table.GetValue(kKey, &value));
@@ -186,8 +200,8 @@
 
   // Existing value was successfully changed.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     int value;
     EXPECT_TRUE(meta_table.GetValue(kKey, &value));
@@ -202,8 +216,8 @@
 
   // Initially, the value isn't there until set.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     int64_t value;
     EXPECT_FALSE(meta_table.GetValue(kKey, &value));
@@ -215,8 +229,8 @@
 
   // Value is persistent across different instances.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     int64_t value;
     EXPECT_TRUE(meta_table.GetValue(kKey, &value));
@@ -227,8 +241,8 @@
 
   // Existing value was successfully changed.
   {
-    sql::MetaTable meta_table;
-    EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+    MetaTable meta_table;
+    EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
     int64_t value;
     EXPECT_TRUE(meta_table.GetValue(kKey, &value));
@@ -240,8 +254,8 @@
   static const char kKey[] = "String Key";
   const std::string kValue("String Value");
 
-  sql::MetaTable meta_table;
-  EXPECT_TRUE(meta_table.Init(&db(), 1, 1));
+  MetaTable meta_table;
+  EXPECT_TRUE(meta_table.Init(&db_, 1, 1));
 
   // Value isn't present.
   std::string value;
@@ -258,3 +272,5 @@
 }
 
 }  // namespace
+
+}  // namespace sql
diff --git a/sql/recover_module/module_unittest.cc b/sql/recover_module/module_unittest.cc
index a711756..2e0db47 100644
--- a/sql/recover_module/module_unittest.cc
+++ b/sql/recover_module/module_unittest.cc
@@ -7,12 +7,12 @@
 #include <tuple>
 #include <vector>
 
+#include "base/files/scoped_temp_dir.h"
 #include "base/strings/stringprintf.h"
 #include "sql/database.h"
 #include "sql/statement.h"
 #include "sql/test/database_test_peer.h"
 #include "sql/test/scoped_error_expecter.h"
-#include "sql/test/sql_test_base.h"
 #include "sql/test/test_helpers.h"
 #include "sql/transaction.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,123 +21,131 @@
 namespace sql {
 namespace recover {
 
-class RecoverModuleTest : public sql::SQLTestBase {
+class RecoverModuleTest : public testing::Test {
  public:
+  ~RecoverModuleTest() override = default;
+
   void SetUp() override {
-    SQLTestBase::SetUp();
-    ASSERT_TRUE(DatabaseTestPeer::EnableRecoveryExtension(&db()));
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(
+        db_.Open(temp_dir_.GetPath().AppendASCII("recovery_test.sqlite")));
+    ASSERT_TRUE(DatabaseTestPeer::EnableRecoveryExtension(&db_));
   }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  sql::Database db_;
 };
 
 TEST_F(RecoverModuleTest, CreateVtable) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   EXPECT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                   "USING recover(backing, t TEXT)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                  "USING recover(backing, t TEXT)"));
 }
 TEST_F(RecoverModuleTest, CreateVtableWithDatabaseSpecifier) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   EXPECT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                   "USING recover(main.backing, t TEXT)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                  "USING recover(main.backing, t TEXT)"));
 }
 TEST_F(RecoverModuleTest, CreateVtableOnSqliteMaster) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   EXPECT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_backing USING recover("
-                   "sqlite_master, type TEXT, name TEXT, tbl_name TEXT, "
-                   "rootpage INTEGER, sql TEXT)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing USING recover("
+                  "sqlite_master, type TEXT, name TEXT, tbl_name TEXT, "
+                  "rootpage INTEGER, sql TEXT)"));
 }
 
 TEST_F(RecoverModuleTest, CreateVtableFailsOnNonTempTable) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
-    EXPECT_FALSE(db().Execute(
+    EXPECT_FALSE(db_.Execute(
         "CREATE VIRTUAL TABLE recover_backing USING recover(backing, t TEXT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingTable) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_CORRUPT);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_missing "
-                     "USING recover(missing, t TEXT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_missing "
+                    "USING recover(missing, t TEXT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, DISABLED_CreateVtableFailsOnMissingDatabase) {
   // TODO(pwnall): Enable test after removing incorrect DLOG(FATAL) from
   //               sql::Statement::Execute().
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_ERROR);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(db.backing, t TEXT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(db.backing, t TEXT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, CreateVtableFailsOnTableWithInvalidQualifier) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_CORRUPT);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(backing invalid, t TEXT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(backing invalid, t TEXT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, DISABLED_CreateVtableFailsOnMissingTableName) {
   // TODO(pwnall): Enable test after removing incorrect DLOG(FATAL) from
   //               sql::Statement::Execute().
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_ERROR);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(main., t TEXT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(main., t TEXT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingSchemaSpec) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(backing)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(backing)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, CreateVtableFailsOnMissingDbName) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(.backing)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(.backing)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 
 TEST_F(RecoverModuleTest, ColumnTypeMappingAny) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   EXPECT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                   "USING recover(backing, t ANY)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                  "USING recover(backing, t ANY)"));
 
   sql::test::ColumnInfo column_info =
-      sql::test::ColumnInfo::Create(&db(), "temp", "recover_backing", "t");
+      sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "t");
   EXPECT_EQ("(nullptr)", column_info.data_type);
   EXPECT_EQ("BINARY", column_info.collation_sequence);
   EXPECT_FALSE(column_info.has_non_null_constraint);
@@ -145,13 +153,13 @@
   EXPECT_FALSE(column_info.is_auto_incremented);
 }
 TEST_F(RecoverModuleTest, ColumnTypeMappingAnyNotNull) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   EXPECT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                   "USING recover(backing, t ANY NOT NULL)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                  "USING recover(backing, t ANY NOT NULL)"));
 
   sql::test::ColumnInfo column_info =
-      sql::test::ColumnInfo::Create(&db(), "temp", "recover_backing", "t");
+      sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "t");
   EXPECT_EQ("(nullptr)", column_info.data_type);
   EXPECT_EQ("BINARY", column_info.collation_sequence);
   EXPECT_TRUE(column_info.has_non_null_constraint);
@@ -159,58 +167,58 @@
   EXPECT_FALSE(column_info.is_auto_incremented);
 }
 TEST_F(RecoverModuleTest, ColumnTypeMappingAnyStrict) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(backing, t ANY STRICT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(backing, t ANY STRICT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 
 TEST_F(RecoverModuleTest, ColumnTypeExtraKeyword) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(backing, t INTEGER SOMETHING)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(backing, t INTEGER SOMETHING)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, ColumnTypeNotNullExtraKeyword) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(backing, t INTEGER NOT NULL SOMETHING)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(backing, t INTEGER NOT NULL SOMETHING)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, ColumnTypeDoubleTypes) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing "
-                     "USING recover(backing, t INTEGER FLOAT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing "
+                    "USING recover(backing, t INTEGER FLOAT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
 TEST_F(RecoverModuleTest, ColumnTypeNotNullDoubleTypes) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE backing(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE backing(t TEXT)"));
   {
     sql::test::ScopedErrorExpecter error_expecter;
     error_expecter.ExpectError(SQLITE_MISUSE);
     EXPECT_FALSE(
-        db().Execute("CREATE VIRTUAL TABLE temp.recover_backing USING recover("
-                     "backing, t INTEGER NOT NULL TEXT)"));
+        db_.Execute("CREATE VIRTUAL TABLE temp.recover_backing USING recover("
+                    "backing, t INTEGER NOT NULL TEXT)"));
     EXPECT_TRUE(error_expecter.SawExpectedErrors());
   }
 }
@@ -220,30 +228,39 @@
       public ::testing::WithParamInterface<
           std::tuple<const char*, const char*, bool>> {
  public:
+  ~RecoverModuleColumnTypeMappingTest() override = default;
+
   void SetUp() override {
-    RecoverModuleTest::SetUp();
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(
+        db_.Open(temp_dir_.GetPath().AppendASCII("recovery_test.sqlite")));
+    ASSERT_TRUE(DatabaseTestPeer::EnableRecoveryExtension(&db_));
+
     std::string sql =
         base::StringPrintf("CREATE TABLE backing(data %s)", SchemaType());
-    ASSERT_TRUE(db().Execute(sql.c_str()));
+    ASSERT_TRUE(db_.Execute(sql.c_str()));
   }
 
- protected:
   void CreateRecoveryTable(const char* suffix) {
     std::string sql = base::StringPrintf(
         "CREATE VIRTUAL TABLE temp.recover_backing "
         "USING recover(backing, data %s%s)",
         SchemaType(), suffix);
-    ASSERT_TRUE(db().Execute(sql.c_str()));
+    ASSERT_TRUE(db_.Execute(sql.c_str()));
   }
 
   const char* SchemaType() const { return std::get<0>(GetParam()); }
   const char* ExpectedType() const { return std::get<1>(GetParam()); }
   bool IsAlwaysNonNull() const { return std::get<2>(GetParam()); }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  sql::Database db_;
 };
 TEST_P(RecoverModuleColumnTypeMappingTest, Unqualified) {
   CreateRecoveryTable("");
   sql::test::ColumnInfo column_info =
-      sql::test::ColumnInfo::Create(&db(), "temp", "recover_backing", "data");
+      sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data");
   EXPECT_EQ(ExpectedType(), column_info.data_type);
   EXPECT_EQ("BINARY", column_info.collation_sequence);
   EXPECT_EQ(IsAlwaysNonNull(), column_info.has_non_null_constraint);
@@ -253,7 +270,7 @@
 TEST_P(RecoverModuleColumnTypeMappingTest, NotNull) {
   CreateRecoveryTable(" NOT NULL");
   sql::test::ColumnInfo column_info =
-      sql::test::ColumnInfo::Create(&db(), "temp", "recover_backing", "data");
+      sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data");
   EXPECT_EQ(ExpectedType(), column_info.data_type);
   EXPECT_EQ("BINARY", column_info.collation_sequence);
   EXPECT_TRUE(column_info.has_non_null_constraint);
@@ -263,7 +280,7 @@
 TEST_P(RecoverModuleColumnTypeMappingTest, Strict) {
   CreateRecoveryTable(" STRICT");
   sql::test::ColumnInfo column_info =
-      sql::test::ColumnInfo::Create(&db(), "temp", "recover_backing", "data");
+      sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data");
   EXPECT_EQ(ExpectedType(), column_info.data_type);
   EXPECT_EQ("BINARY", column_info.collation_sequence);
   EXPECT_EQ(IsAlwaysNonNull(), column_info.has_non_null_constraint);
@@ -273,7 +290,7 @@
 TEST_P(RecoverModuleColumnTypeMappingTest, StrictNotNull) {
   CreateRecoveryTable(" STRICT NOT NULL");
   sql::test::ColumnInfo column_info =
-      sql::test::ColumnInfo::Create(&db(), "temp", "recover_backing", "data");
+      sql::test::ColumnInfo::Create(&db_, "temp", "recover_backing", "data");
   EXPECT_EQ(ExpectedType(), column_info.data_type);
   EXPECT_EQ("BINARY", column_info.collation_sequence);
   EXPECT_TRUE(column_info.has_non_null_constraint);
@@ -305,12 +322,12 @@
 }  // namespace
 
 TEST_F(RecoverModuleTest, ReadFromAlteredTableNullDefaults) {
-  GenerateAlteredTable(&db());
+  GenerateAlteredTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_altered "
-                   "USING recover(altered, t TEXT, i INTEGER)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_altered "
+                  "USING recover(altered, t TEXT, i INTEGER)"));
 
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT t, i FROM recover_altered ORDER BY rowid"));
   ASSERT_TRUE(statement.Step());
   EXPECT_EQ("a", statement.ColumnString(0));
@@ -329,12 +346,12 @@
 }
 
 TEST_F(RecoverModuleTest, ReadFromAlteredTableSkipsNulls) {
-  GenerateAlteredTable(&db());
+  GenerateAlteredTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_altered "
-                   "USING recover(altered, t TEXT, i INTEGER NOT NULL)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_altered "
+                  "USING recover(altered, t TEXT, i INTEGER NOT NULL)"));
 
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT t, i FROM recover_altered ORDER BY rowid"));
   ASSERT_TRUE(statement.Step());
   EXPECT_EQ("d", statement.ColumnString(0));
@@ -366,13 +383,13 @@
 }  // namespace
 
 TEST_F(RecoverModuleTest, LeafNodes) {
-  GenerateSizedTable(&db(), 10, "Leaf-node-generating line ");
+  GenerateSizedTable(&db_, 10, "Leaf-node-generating line ");
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_sized "
-                   "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized "
+                  "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
 
   sql::Statement statement(
-      db().GetUniqueStatement("SELECT t, i FROM recover_sized ORDER BY rowid"));
+      db_.GetUniqueStatement("SELECT t, i FROM recover_sized ORDER BY rowid"));
   for (int i = 0; i < 10; ++i) {
     ASSERT_TRUE(statement.Step());
     EXPECT_EQ(base::StringPrintf("Leaf-node-generating line %d", i),
@@ -383,22 +400,22 @@
 }
 
 TEST_F(RecoverModuleTest, EmptyTable) {
-  GenerateSizedTable(&db(), 0, "");
+  GenerateSizedTable(&db_, 0, "");
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_sized "
-                   "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized "
+                  "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, t, i FROM recover_sized ORDER BY rowid"));
   EXPECT_FALSE(statement.Step());
 }
 
 TEST_F(RecoverModuleTest, SingleLevelInteriorNodes) {
-  GenerateSizedTable(&db(), 100, "Interior-node-generating line ");
+  GenerateSizedTable(&db_, 100, "Interior-node-generating line ");
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_sized "
-                   "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized "
+                  "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
 
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, t, i FROM recover_sized ORDER BY rowid"));
   for (int i = 0; i < 100; ++i) {
     ASSERT_TRUE(statement.Step());
@@ -411,12 +428,12 @@
 }
 
 TEST_F(RecoverModuleTest, MultiLevelInteriorNodes) {
-  GenerateSizedTable(&db(), 5000, "Interior-node-generating line ");
+  GenerateSizedTable(&db_, 5000, "Interior-node-generating line ");
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_sized "
-                   "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_sized "
+                  "USING recover(sized, t TEXT, i INTEGER NOT NULL)"));
 
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, t, i FROM recover_sized ORDER BY rowid"));
   for (int i = 0; i < 5000; ++i) {
     ASSERT_TRUE(statement.Step());
@@ -444,11 +461,11 @@
 }  // namespace
 
 TEST_F(RecoverModuleTest, Any) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value ANY)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value ANY)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -482,11 +499,11 @@
 }
 
 TEST_F(RecoverModuleTest, Integers) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value INTEGER)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value INTEGER)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -503,11 +520,11 @@
 }
 
 TEST_F(RecoverModuleTest, NonNullIntegers) {
-  GenerateTypesTable(&db());
-  ASSERT_TRUE(db().Execute(
+  GenerateTypesTable(&db_);
+  ASSERT_TRUE(db_.Execute(
       "CREATE VIRTUAL TABLE temp.recover_types "
       "USING recover(types, rowtype TEXT, value INTEGER NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -520,11 +537,11 @@
 }
 
 TEST_F(RecoverModuleTest, Floats) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value FLOAT)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value FLOAT)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -546,11 +563,11 @@
 }
 
 TEST_F(RecoverModuleTest, NonNullFloats) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value FLOAT NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value FLOAT NOT NULL)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -568,11 +585,11 @@
 }
 
 TEST_F(RecoverModuleTest, FloatsStrict) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value FLOAT STRICT)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value FLOAT STRICT)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -589,11 +606,11 @@
 }
 
 TEST_F(RecoverModuleTest, NonNullFloatsStrict) {
-  GenerateTypesTable(&db());
-  ASSERT_TRUE(db().Execute(
+  GenerateTypesTable(&db_);
+  ASSERT_TRUE(db_.Execute(
       "CREATE VIRTUAL TABLE temp.recover_types "
       "USING recover(types, rowtype TEXT, value FLOAT STRICT NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -606,11 +623,11 @@
 }
 
 TEST_F(RecoverModuleTest, Texts) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value TEXT)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value TEXT)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -634,11 +651,11 @@
 }
 
 TEST_F(RecoverModuleTest, NonNullTexts) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value TEXT NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value TEXT NOT NULL)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -658,11 +675,11 @@
 }
 
 TEST_F(RecoverModuleTest, TextsStrict) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value TEXT STRICT)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value TEXT STRICT)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -679,11 +696,11 @@
 }
 
 TEST_F(RecoverModuleTest, NonNullTextsStrict) {
-  GenerateTypesTable(&db());
-  ASSERT_TRUE(db().Execute(
+  GenerateTypesTable(&db_);
+  ASSERT_TRUE(db_.Execute(
       "CREATE VIRTUAL TABLE temp.recover_types "
       "USING recover(types, rowtype TEXT, value TEXT STRICT NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -696,11 +713,11 @@
 }
 
 TEST_F(RecoverModuleTest, Blobs) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value BLOB)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value BLOB)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -719,11 +736,11 @@
 }
 
 TEST_F(RecoverModuleTest, NonNullBlobs) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value BLOB NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value BLOB NOT NULL)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -738,11 +755,11 @@
 }
 
 TEST_F(RecoverModuleTest, AnyNonNull) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_types "
-                   "USING recover(types, rowtype TEXT, value ANY NOT NULL)"));
-  sql::Statement statement(db().GetUniqueStatement(
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_types "
+                  "USING recover(types, rowtype TEXT, value ANY NOT NULL)"));
+  sql::Statement statement(db_.GetUniqueStatement(
       "SELECT rowid, rowtype, value FROM recover_types"));
 
   ASSERT_TRUE(statement.Step());
@@ -772,20 +789,20 @@
 }
 
 TEST_F(RecoverModuleTest, RowidAlias) {
-  GenerateTypesTable(&db());
+  GenerateTypesTable(&db_);
 
   // The id column is an alias for rowid, and its values get serialized as NULL.
-  ASSERT_TRUE(db().Execute(
+  ASSERT_TRUE(db_.Execute(
       "CREATE TABLE types2(id INTEGER PRIMARY KEY, rowtype TEXT, value)"));
   ASSERT_TRUE(
-      db().Execute("INSERT INTO types2(id, rowtype, value) "
-                   "SELECT rowid, rowtype, value FROM types WHERE true"));
-  ASSERT_TRUE(db().Execute(
+      db_.Execute("INSERT INTO types2(id, rowtype, value) "
+                  "SELECT rowid, rowtype, value FROM types WHERE true"));
+  ASSERT_TRUE(db_.Execute(
       "CREATE VIRTUAL TABLE temp.recover_types2 "
       "USING recover(types2, id ROWID NOT NULL, rowtype TEXT, value ANY)"));
 
   sql::Statement statement(
-      db().GetUniqueStatement("SELECT id, rowid, rowtype, value FROM types2"));
+      db_.GetUniqueStatement("SELECT id, rowid, rowtype, value FROM types2"));
 
   ASSERT_TRUE(statement.Step());
   EXPECT_EQ(1, statement.ColumnInt(0));
@@ -819,7 +836,7 @@
 }
 
 TEST_F(RecoverModuleTest, IntegerEncodings) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE integers(value)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE integers(value)"));
 
   const std::vector<int64_t> values = {
       // Encoded directly in type info.
@@ -857,7 +874,7 @@
       -9223372036854775807,
   };
   sql::Statement insert(
-      db().GetUniqueStatement("INSERT INTO integers VALUES(?)"));
+      db_.GetUniqueStatement("INSERT INTO integers VALUES(?)"));
   for (int64_t value : values) {
     insert.BindInt64(0, value);
     ASSERT_TRUE(insert.Run());
@@ -865,10 +882,10 @@
   }
 
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_integers "
-                   "USING recover(integers, value INTEGER)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_integers "
+                  "USING recover(integers, value INTEGER)"));
   sql::Statement select(
-      db().GetUniqueStatement("SELECT rowid, value FROM recover_integers"));
+      db_.GetUniqueStatement("SELECT rowid, value FROM recover_integers"));
   for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_TRUE(select.Step()) << "Was attemping to read " << values[i];
     EXPECT_EQ(static_cast<int>(i + 1), select.ColumnInt(0));
@@ -967,30 +984,30 @@
       -0x8000000000000000,
   };
 
-  ASSERT_TRUE(db().Execute("CREATE TABLE varints(value INTEGER PRIMARY KEY)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE varints(value INTEGER PRIMARY KEY)"));
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_varints "
-                   "USING recover(varints, value ROWID)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_varints "
+                  "USING recover(varints, value ROWID)"));
 
   for (int64_t value : values) {
     sql::Statement insert(
-        db().GetUniqueStatement("INSERT INTO varints VALUES(?)"));
+        db_.GetUniqueStatement("INSERT INTO varints VALUES(?)"));
     insert.BindInt64(0, value);
     ASSERT_TRUE(insert.Run());
 
     sql::Statement select(
-        db().GetUniqueStatement("SELECT rowid, value FROM recover_varints"));
+        db_.GetUniqueStatement("SELECT rowid, value FROM recover_varints"));
     ASSERT_TRUE(select.Step()) << "Was attemping to read " << value;
     EXPECT_EQ(value, select.ColumnInt64(0));
     EXPECT_EQ(value, select.ColumnInt64(1));
     EXPECT_FALSE(select.Step());
 
-    ASSERT_TRUE(db().Execute("DELETE FROM varints"));
+    ASSERT_TRUE(db_.Execute("DELETE FROM varints"));
   }
 }
 
 TEST_F(RecoverModuleTest, TextEncodings) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE encodings(t TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE encodings(t TEXT)"));
 
   const std::vector<std::string> values = {
       "",          "a",         u8"ö",       u8"Mjollnir", u8"Mjölnir",
@@ -998,7 +1015,7 @@
   };
 
   sql::Statement insert(
-      db().GetUniqueStatement("INSERT INTO encodings VALUES(?)"));
+      db_.GetUniqueStatement("INSERT INTO encodings VALUES(?)"));
   for (const std::string& value : values) {
     insert.BindString(0, value);
     ASSERT_TRUE(insert.Run());
@@ -1006,10 +1023,10 @@
   }
 
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_encodings "
-                   "USING recover(encodings, t TEXT)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_encodings "
+                  "USING recover(encodings, t TEXT)"));
   sql::Statement select(
-      db().GetUniqueStatement("SELECT rowid, t FROM recover_encodings"));
+      db_.GetUniqueStatement("SELECT rowid, t FROM recover_encodings"));
   for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_TRUE(select.Step());
     EXPECT_EQ(static_cast<int>(i + 1), select.ColumnInt(0));
@@ -1019,7 +1036,7 @@
 }
 
 TEST_F(RecoverModuleTest, BlobEncodings) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE blob_encodings(t BLOB)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE blob_encodings(t BLOB)"));
 
   const std::vector<std::vector<uint8_t>> values = {
       {},           {0x00},       {0x01},
@@ -1028,7 +1045,7 @@
   };
 
   sql::Statement insert(
-      db().GetUniqueStatement("INSERT INTO blob_encodings VALUES(?)"));
+      db_.GetUniqueStatement("INSERT INTO blob_encodings VALUES(?)"));
   for (const std::vector<uint8_t>& value : values) {
     // std::vector::data() returns nullptr for empty vectors. Unfortunately,
     // sqlite3_bind_blob() always interprets null data as a NULL value. In this
@@ -1043,10 +1060,10 @@
   }
 
   ASSERT_TRUE(
-      db().Execute("CREATE VIRTUAL TABLE temp.recover_blob_encodings "
-                   "USING recover(blob_encodings, t BLOB)"));
+      db_.Execute("CREATE VIRTUAL TABLE temp.recover_blob_encodings "
+                  "USING recover(blob_encodings, t BLOB)"));
   sql::Statement select(
-      db().GetUniqueStatement("SELECT rowid, t FROM recover_blob_encodings"));
+      db_.GetUniqueStatement("SELECT rowid, t FROM recover_blob_encodings"));
   for (size_t i = 0; i < values.size(); ++i) {
     ASSERT_TRUE(select.Step());
     EXPECT_EQ(static_cast<int>(i + 1), select.ColumnInt(0));
@@ -1113,60 +1130,60 @@
 }  // namespace
 
 TEST_F(RecoverModuleTest, ValueWithoutOverflow) {
-  CheckLargeValueRecovery(&db(), db().page_size() - kRecordOverhead);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(2 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+  CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(2 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page and a leaf page";
 }
 
 TEST_F(RecoverModuleTest, ValueWithOneByteOverflow) {
-  CheckLargeValueRecovery(&db(), db().page_size() - kRecordOverhead + 1);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+  CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead + 1);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page, a leaf page, and 1 overflow page";
 }
 
 TEST_F(RecoverModuleTest, ValueWithOneOverflowPage) {
   CheckLargeValueRecovery(
-      &db(), db().page_size() - kRecordOverhead + db().page_size() / 2);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+      &db_, db_.page_size() - kRecordOverhead + db_.page_size() / 2);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page, a leaf page, and 1 overflow page";
 }
 
 TEST_F(RecoverModuleTest, ValueWithOneFullOverflowPage) {
-  CheckLargeValueRecovery(&db(), db().page_size() - kRecordOverhead +
-                                     db().page_size() - kOverflowOverhead);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+  CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead +
+                                    db_.page_size() - kOverflowOverhead);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(3 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page, a leaf page, and 1 overflow page";
 }
 
 TEST_F(RecoverModuleTest, ValueWithOneByteSecondOverflowPage) {
-  CheckLargeValueRecovery(&db(), db().page_size() - kRecordOverhead +
-                                     db().page_size() - kOverflowOverhead + 1);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+  CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead +
+                                    db_.page_size() - kOverflowOverhead + 1);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page, a leaf page, and 2 overflow pages";
 }
 
 TEST_F(RecoverModuleTest, ValueWithTwoOverflowPages) {
-  CheckLargeValueRecovery(&db(), db().page_size() - kRecordOverhead +
-                                     db().page_size() - kOverflowOverhead +
-                                     db().page_size() / 2);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+  CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead +
+                                    db_.page_size() - kOverflowOverhead +
+                                    db_.page_size() / 2);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page, a leaf page, and 2 overflow pages";
 }
 
 TEST_F(RecoverModuleTest, ValueWithTwoFullOverflowPages) {
   // This value is large enough that the varint encoding of its type ID takes up
   // 3 bytes, instead of 2.
-  CheckLargeValueRecovery(&db(),
-                          db().page_size() - kRecordOverhead +
-                              (db().page_size() - kOverflowOverhead) * 2 - 1);
-  int auto_vacuum_pages = HasEnabledAutoVacuum(&db()) ? 1 : 0;
-  ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db()))
+  CheckLargeValueRecovery(&db_, db_.page_size() - kRecordOverhead +
+                                    (db_.page_size() - kOverflowOverhead) * 2 -
+                                    1);
+  int auto_vacuum_pages = HasEnabledAutoVacuum(&db_) ? 1 : 0;
+  ASSERT_EQ(4 + auto_vacuum_pages, sql::test::GetPageCount(&db_))
       << "Database should have a root page, a leaf page, and 2 overflow pages";
 }
 
diff --git a/sql/recovery_unittest.cc b/sql/recovery_unittest.cc
index afd3865..f402907 100644
--- a/sql/recovery_unittest.cc
+++ b/sql/recovery_unittest.cc
@@ -22,11 +22,12 @@
 #include "sql/statement.h"
 #include "sql/test/paths.h"
 #include "sql/test/scoped_error_expecter.h"
-#include "sql/test/sql_test_base.h"
 #include "sql/test/test_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/sqlite/sqlite3.h"
 
+namespace sql {
+
 namespace {
 
 using sql::test::ExecuteWithResult;
@@ -36,81 +37,104 @@
 // schema.  For tables or indices, this will contain the sql command
 // to create the table or index.  For certain automatic SQLite
 // structures with no sql, the name is used.
-std::string GetSchema(sql::Database* db) {
+std::string GetSchema(Database* db) {
   static const char kSql[] =
       "SELECT COALESCE(sql, name) FROM sqlite_master ORDER BY 1";
   return ExecuteWithResults(db, kSql, "|", "\n");
 }
 
-using SQLRecoveryTest = sql::SQLTestBase;
+class SQLRecoveryTest : public testing::Test {
+ public:
+  ~SQLRecoveryTest() override = default;
 
-// Baseline sql::Recovery test covering the different ways to dispose of the
-// scoped pointer received from sql::Recovery::Begin().
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    db_path_ = temp_dir_.GetPath().AppendASCII("recovery_test.sqlite");
+    ASSERT_TRUE(db_.Open(db_path_));
+  }
+
+  bool Reopen() {
+    db_.Close();
+    return db_.Open(db_path_);
+  }
+
+  bool OverwriteDatabaseHeader() {
+    base::File file(db_path_,
+                    base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+    static constexpr char kText[] = "Now is the winter of our discontent.";
+    constexpr int kTextBytes = sizeof(kText) - 1;
+    return file.Write(0, kText, kTextBytes) == kTextBytes;
+  }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  base::FilePath db_path_;
+  Database db_;
+};
+
+// Baseline Recovery test covering the different ways to dispose of the
+// scoped pointer received from Recovery::Begin().
 TEST_F(SQLRecoveryTest, RecoverBasic) {
   static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
   static const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')";
   static const char kAltInsertSql[] =
       "INSERT INTO x VALUES ('That was a test')";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kInsertSql));
-  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute(kInsertSql));
+  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
   // If the Recovery handle goes out of scope without being
   // Recovered(), the database is razed.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery.get());
   }
-  EXPECT_FALSE(db().is_open());
+  EXPECT_FALSE(db_.is_open());
   ASSERT_TRUE(Reopen());
-  EXPECT_TRUE(db().is_open());
-  ASSERT_EQ("", GetSchema(&db()));
+  EXPECT_TRUE(db_.is_open());
+  ASSERT_EQ("", GetSchema(&db_));
 
   // Recreate the database.
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kInsertSql));
-  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute(kInsertSql));
+  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
   // Unrecoverable() also razes.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery.get());
-    sql::Recovery::Unrecoverable(std::move(recovery));
+    Recovery::Unrecoverable(std::move(recovery));
 
-    // TODO(shess): Test that calls to recover.db() start failing.
+    // TODO(shess): Test that calls to recover.db_ start failing.
   }
-  EXPECT_FALSE(db().is_open());
+  EXPECT_FALSE(db_.is_open());
   ASSERT_TRUE(Reopen());
-  EXPECT_TRUE(db().is_open());
-  ASSERT_EQ("", GetSchema(&db()));
+  EXPECT_TRUE(db_.is_open());
+  ASSERT_EQ("", GetSchema(&db_));
 
   // Attempting to recover a previously-recovered handle fails early.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery.get());
     recovery.reset();
 
-    recovery = sql::Recovery::Begin(&db(), db_path());
+    recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_FALSE(recovery.get());
   }
   ASSERT_TRUE(Reopen());
 
   // Recreate the database.
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute(kInsertSql));
-  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute(kInsertSql));
+  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
   // Unrecovered table to distinguish from recovered database.
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)"));
-  ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (c INTEGER)"));
+  ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
   // Recovered() replaces the original with the "recovered" version.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery.get());
 
     // Create the new version of the table.
@@ -120,50 +144,48 @@
     ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
 
     // Successfully recovered.
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
-  EXPECT_FALSE(db().is_open());
+  EXPECT_FALSE(db_.is_open());
   ASSERT_TRUE(Reopen());
-  EXPECT_TRUE(db().is_open());
-  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  EXPECT_TRUE(db_.is_open());
+  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
   const char* kXSql = "SELECT * FROM x ORDER BY 1";
-  ASSERT_EQ("That was a test", ExecuteWithResult(&db(), kXSql));
+  ASSERT_EQ("That was a test", ExecuteWithResult(&db_, kXSql));
 
   // Reset the database contents.
-  ASSERT_TRUE(db().Execute("DELETE FROM x"));
-  ASSERT_TRUE(db().Execute(kInsertSql));
+  ASSERT_TRUE(db_.Execute("DELETE FROM x"));
+  ASSERT_TRUE(db_.Execute(kInsertSql));
 
   // Rollback() discards recovery progress and leaves the database as it was.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery.get());
 
     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
     ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
 
-    sql::Recovery::Rollback(std::move(recovery));
+    Recovery::Rollback(std::move(recovery));
   }
-  EXPECT_FALSE(db().is_open());
+  EXPECT_FALSE(db_.is_open());
   ASSERT_TRUE(Reopen());
-  EXPECT_TRUE(db().is_open());
-  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  EXPECT_TRUE(db_.is_open());
+  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
-  ASSERT_EQ("This is a test", ExecuteWithResult(&db(), kXSql));
+  ASSERT_EQ("This is a test", ExecuteWithResult(&db_, kXSql));
 }
 
-// Test operation of the virtual table used by sql::Recovery.
+// Test operation of the virtual table used by Recovery.
 TEST_F(SQLRecoveryTest, VirtualTable) {
   static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')"));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('That was a test')"));
 
   // Successfully recover the database.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
 
     // Tables to recover original DB, now at [corrupt].
     static const char kRecoveryCreateSql[] =
@@ -182,32 +204,32 @@
     ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql));
 
     // Successfully recovered.
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
+  ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
 
   static const char* kXSql = "SELECT * FROM x ORDER BY 1";
   ASSERT_EQ("That was a test\nThis is a test",
-            ExecuteWithResults(&db(), kXSql, "|", "\n"));
+            ExecuteWithResults(&db_, kXSql, "|", "\n"));
 }
 
-void RecoveryCallback(sql::Database* db,
+void RecoveryCallback(Database* db,
                       const base::FilePath& db_path,
                       const char* create_table,
                       const char* create_index,
                       int* record_error,
                       int error,
-                      sql::Statement* stmt) {
+                      Statement* stmt) {
   *record_error = error;
 
   // Clear the error callback to prevent reentrancy.
   db->reset_error_callback();
 
-  std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
+  std::unique_ptr<Recovery> recovery = Recovery::Begin(db, db_path);
   ASSERT_TRUE(recovery.get());
 
   ASSERT_TRUE(recovery->db()->Execute(create_table));
@@ -216,7 +238,7 @@
   size_t rows = 0;
   ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows));
 
-  ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+  ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
 }
 
 // Build a database, corrupt it by making an index reference to
@@ -224,15 +246,15 @@
 TEST_F(SQLRecoveryTest, RecoverCorruptIndex) {
   static const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
   static const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
-  ASSERT_TRUE(db().Execute(kCreateTable));
-  ASSERT_TRUE(db().Execute(kCreateIndex));
+  ASSERT_TRUE(db_.Execute(kCreateTable));
+  ASSERT_TRUE(db_.Execute(kCreateIndex));
 
   // Insert a bit of data.
   {
-    ASSERT_TRUE(db().BeginTransaction());
+    ASSERT_TRUE(db_.BeginTransaction());
 
     static const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (?, ?)";
-    sql::Statement s(db().GetUniqueStatement(kInsertSql));
+    Statement s(db_.GetUniqueStatement(kInsertSql));
     for (int i = 0; i < 10; ++i) {
       s.Reset(true);
       s.BindInt(0, i);
@@ -241,42 +263,42 @@
       EXPECT_TRUE(s.Succeeded());
     }
 
-    ASSERT_TRUE(db().CommitTransaction());
+    ASSERT_TRUE(db_.CommitTransaction());
   }
-  db().Close();
+  db_.Close();
 
   // Delete a row from the table, while leaving the index entry which
   // references it.
   static const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
-  ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x_id", kDeleteSql));
+  ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path_, "x_id", kDeleteSql));
 
   ASSERT_TRUE(Reopen());
 
   int error = SQLITE_OK;
-  db().set_error_callback(base::BindRepeating(
-      &RecoveryCallback, &db(), db_path(), kCreateTable, kCreateIndex, &error));
+  db_.set_error_callback(base::BindRepeating(
+      &RecoveryCallback, &db_, db_path_, kCreateTable, kCreateIndex, &error));
 
   // This works before the callback is called.
   static const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
-  EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
+  EXPECT_TRUE(db_.IsSQLValid(kTrivialSql));
 
   // TODO(shess): Could this be delete?  Anything which fails should work.
   static const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
-  ASSERT_FALSE(db().Execute(kSelectSql));
+  ASSERT_FALSE(db_.Execute(kSelectSql));
   EXPECT_EQ(SQLITE_CORRUPT, error);
 
   // Database handle has been poisoned.
-  EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
+  EXPECT_FALSE(db_.IsSQLValid(kTrivialSql));
 
   ASSERT_TRUE(Reopen());
 
   // The recovered table should reflect the deletion.
   static const char kSelectAllSql[] = "SELECT v FROM x ORDER BY id";
   EXPECT_EQ("1,2,3,4,5,6,7,8,9",
-            ExecuteWithResults(&db(), kSelectAllSql, "|", ","));
+            ExecuteWithResults(&db_, kSelectAllSql, "|", ","));
 
   // The failing statement should now succeed, with no results.
-  EXPECT_EQ("", ExecuteWithResults(&db(), kSelectSql, "|", ","));
+  EXPECT_EQ("", ExecuteWithResults(&db_, kSelectSql, "|", ","));
 }
 
 // Build a database, corrupt it by making a table contain a row not
@@ -284,15 +306,15 @@
 TEST_F(SQLRecoveryTest, RecoverCorruptTable) {
   static const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
   static const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
-  ASSERT_TRUE(db().Execute(kCreateTable));
-  ASSERT_TRUE(db().Execute(kCreateIndex));
+  ASSERT_TRUE(db_.Execute(kCreateTable));
+  ASSERT_TRUE(db_.Execute(kCreateIndex));
 
   // Insert a bit of data.
   {
-    ASSERT_TRUE(db().BeginTransaction());
+    ASSERT_TRUE(db_.BeginTransaction());
 
     static const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (?, ?)";
-    sql::Statement s(db().GetUniqueStatement(kInsertSql));
+    Statement s(db_.GetUniqueStatement(kInsertSql));
     for (int i = 0; i < 10; ++i) {
       s.Reset(true);
       s.BindInt(0, i);
@@ -301,62 +323,62 @@
       EXPECT_TRUE(s.Succeeded());
     }
 
-    ASSERT_TRUE(db().CommitTransaction());
+    ASSERT_TRUE(db_.CommitTransaction());
   }
-  db().Close();
+  db_.Close();
 
   // Delete a row from the index while leaving a table entry.
   static const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
-  ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql));
+  ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path_, "x", kDeleteSql));
 
   ASSERT_TRUE(Reopen());
 
   int error = SQLITE_OK;
-  db().set_error_callback(base::BindRepeating(
-      &RecoveryCallback, &db(), db_path(), kCreateTable, kCreateIndex, &error));
+  db_.set_error_callback(base::BindRepeating(
+      &RecoveryCallback, &db_, db_path_, kCreateTable, kCreateIndex, &error));
 
   // Index shows one less than originally inserted.
   static const char kCountSql[] = "SELECT COUNT (*) FROM x";
-  EXPECT_EQ("9", ExecuteWithResult(&db(), kCountSql));
+  EXPECT_EQ("9", ExecuteWithResult(&db_, kCountSql));
 
   // A full table scan shows all of the original data.  Using column [v] to
   // force use of the table rather than the index.
   static const char kDistinctSql[] = "SELECT DISTINCT COUNT (v) FROM x";
-  EXPECT_EQ("10", ExecuteWithResult(&db(), kDistinctSql));
+  EXPECT_EQ("10", ExecuteWithResult(&db_, kDistinctSql));
 
   // Insert id 0 again.  Since it is not in the index, the insert
   // succeeds, but results in a duplicate value in the table.
   static const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (0, 100)";
-  ASSERT_TRUE(db().Execute(kInsertSql));
+  ASSERT_TRUE(db_.Execute(kInsertSql));
 
   // Duplication is visible.
-  EXPECT_EQ("10", ExecuteWithResult(&db(), kCountSql));
-  EXPECT_EQ("11", ExecuteWithResult(&db(), kDistinctSql));
+  EXPECT_EQ("10", ExecuteWithResult(&db_, kCountSql));
+  EXPECT_EQ("11", ExecuteWithResult(&db_, kDistinctSql));
 
   // This works before the callback is called.
   static const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
-  EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
+  EXPECT_TRUE(db_.IsSQLValid(kTrivialSql));
 
   // TODO(shess): Figure out a statement which causes SQLite to notice the
   // corruption.  SELECT doesn't see errors because missing index values aren't
   // visible.  UPDATE or DELETE against v=0 don't see errors, even though the
   // index item is missing.  I suspect SQLite only deletes the key in these
   // cases, but doesn't verify that one or more keys were deleted.
-  ASSERT_FALSE(db().Execute("INSERT INTO x (id, v) VALUES (0, 101)"));
+  ASSERT_FALSE(db_.Execute("INSERT INTO x (id, v) VALUES (0, 101)"));
   EXPECT_EQ(SQLITE_CONSTRAINT_UNIQUE, error);
 
   // Database handle has been poisoned.
-  EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
+  EXPECT_FALSE(db_.IsSQLValid(kTrivialSql));
 
   ASSERT_TRUE(Reopen());
 
   // The recovered table has consistency between the index and the table.
-  EXPECT_EQ("10", ExecuteWithResult(&db(), kCountSql));
-  EXPECT_EQ("10", ExecuteWithResult(&db(), kDistinctSql));
+  EXPECT_EQ("10", ExecuteWithResult(&db_, kCountSql));
+  EXPECT_EQ("10", ExecuteWithResult(&db_, kDistinctSql));
 
   // Only one of the values is retained.
   static const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
-  const std::string results = ExecuteWithResult(&db(), kSelectSql);
+  const std::string results = ExecuteWithResult(&db_, kSelectSql);
   EXPECT_TRUE(results=="100" || results=="0") << "Actual results: " << results;
 }
 
@@ -365,45 +387,42 @@
   const int kCompatibleVersion = 2;
 
   {
-    sql::MetaTable meta;
-    EXPECT_TRUE(meta.Init(&db(), kVersion, kCompatibleVersion));
+    MetaTable meta;
+    EXPECT_TRUE(meta.Init(&db_, kVersion, kCompatibleVersion));
     EXPECT_EQ(kVersion, meta.GetVersionNumber());
   }
 
   // Test expected case where everything works.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     EXPECT_TRUE(recovery->SetupMeta());
     int version = 0;
     EXPECT_TRUE(recovery->GetMetaVersionNumber(&version));
     EXPECT_EQ(kVersion, version);
 
-    sql::Recovery::Rollback(std::move(recovery));
+    Recovery::Rollback(std::move(recovery));
   }
   ASSERT_TRUE(Reopen());  // Handle was poisoned.
 
   // Test version row missing.
-  EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'"));
+  EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'"));
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     EXPECT_TRUE(recovery->SetupMeta());
     int version = 0;
     EXPECT_FALSE(recovery->GetMetaVersionNumber(&version));
     EXPECT_EQ(0, version);
 
-    sql::Recovery::Rollback(std::move(recovery));
+    Recovery::Rollback(std::move(recovery));
   }
   ASSERT_TRUE(Reopen());  // Handle was poisoned.
 
   // Test meta table missing.
-  EXPECT_TRUE(db().Execute("DROP TABLE meta"));
+  EXPECT_TRUE(db_.Execute("DROP TABLE meta"));
   {
     sql::test::ScopedErrorExpecter expecter;
     expecter.ExpectError(SQLITE_CORRUPT);  // From virtual table.
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     EXPECT_FALSE(recovery->SetupMeta());
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
@@ -414,23 +433,22 @@
   // BIGINT and VARCHAR to test type affinity.
   static const char kCreateSql[] =
       "CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (11, 'This is', 'a test')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, 'That was', 'a test')"));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (11, 'This is', 'a test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5, 'That was', 'a test')"));
 
   // Save aside a copy of the original schema and data.
-  const std::string orig_schema(GetSchema(&db()));
+  const std::string orig_schema(GetSchema(&db_));
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
-  const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
 
   // Create a lame-duck table which will not be propagated by recovery to
   // detect that the recovery code actually ran.
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
-  ASSERT_NE(orig_schema, GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
+  ASSERT_NE(orig_schema, GetSchema(&db_));
 
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
 
     // Save a copy of the temp db's schema before recovering the table.
@@ -447,26 +465,25 @@
     EXPECT_EQ(temp_schema,
               ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
 
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(orig_schema, GetSchema(&db()));
-  ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_EQ(orig_schema, GetSchema(&db_));
+  ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
 
   // Recovery fails if the target table doesn't exist.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
 
     // TODO(shess): Should this failure implicitly lead to Raze()?
     size_t rows = 0;
     EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows));
 
-    sql::Recovery::Unrecoverable(std::move(recovery));
+    Recovery::Unrecoverable(std::move(recovery));
   }
 }
 
@@ -474,30 +491,30 @@
 // virtual table reads directly from the database, so DEFAULT is not
 // interpretted at that level.
 TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE x (id INTEGER)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE x (id INTEGER)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (15)"));
 
   // ALTER effectively leaves the new columns NULL in the first two
   // rows.  The row with 17 will get the default injected at insert
   // time, while the row with 42 will get the actual value provided.
   // Embedded "'" to make sure default-handling continues to be quoted
   // correctly.
-  ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN t TEXT DEFAULT 'a''a'"));
-  ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN b BLOB DEFAULT x'AA55'"));
-  ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN i INT DEFAULT 93"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x (id) VALUES (17)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (42, 'b', x'1234', 12)"));
+  ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN t TEXT DEFAULT 'a''a'"));
+  ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN b BLOB DEFAULT x'AA55'"));
+  ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN i INT DEFAULT 93"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x (id) VALUES (17)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (42, 'b', x'1234', 12)"));
 
   // Save aside a copy of the original schema and data.
-  const std::string orig_schema(GetSchema(&db()));
+  const std::string orig_schema(GetSchema(&db_));
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
-  const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
 
   // Create a lame-duck table which will not be propagated by recovery to
   // detect that the recovery code actually ran.
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
-  ASSERT_NE(orig_schema, GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
+  ASSERT_NE(orig_schema, GetSchema(&db_));
 
   // Mechanically adjust the stored schema and data to allow detecting
   // where the default value is coming from.  The target table is just
@@ -516,8 +533,7 @@
   }
 
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     // Different default to detect which table provides the default.
     ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str()));
 
@@ -525,14 +541,14 @@
     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
     EXPECT_EQ(4u, rows);
 
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(final_schema, GetSchema(&db()));
-  ASSERT_EQ(final_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_EQ(final_schema, GetSchema(&db_));
+  ASSERT_EQ(final_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
 }
 
 // Test that rows with NULL in a NOT NULL column are filtered
@@ -544,34 +560,33 @@
   static const char kFinalSchema[] =
       "CREATE TABLE x (id INTEGER, t TEXT NOT NULL)";
 
-  ASSERT_TRUE(db().Execute(kOrigSchema));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, NULL)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15, 'this is a test')"));
+  ASSERT_TRUE(db_.Execute(kOrigSchema));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5, NULL)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (15, 'this is a test')"));
 
   // Create a lame-duck table which will not be propagated by recovery to
   // detect that the recovery code actually ran.
-  ASSERT_EQ(kOrigSchema, GetSchema(&db()));
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
-  ASSERT_NE(kOrigSchema, GetSchema(&db()));
+  ASSERT_EQ(kOrigSchema, GetSchema(&db_));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
+  ASSERT_NE(kOrigSchema, GetSchema(&db_));
 
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery->db()->Execute(kFinalSchema));
 
     size_t rows = 0;
     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
     EXPECT_EQ(1u, rows);
 
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // The schema should be the same, but only one row of data should
   // have been recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(kFinalSchema, GetSchema(&db()));
+  ASSERT_EQ(kFinalSchema, GetSchema(&db_));
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
-  ASSERT_EQ("15|this is a test", ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_EQ("15|this is a test", ExecuteWithResults(&db_, kXSql, "|", "\n"));
 }
 
 // Test AutoRecoverTable with a ROWID alias.
@@ -580,37 +595,36 @@
   // put it later.
   static const char kCreateSql[] =
       "CREATE TABLE x (t TEXT, id INTEGER PRIMARY KEY NOT NULL)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test', NULL)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test', NULL)"));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test', NULL)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('That was a test', NULL)"));
 
   // Save aside a copy of the original schema and data.
-  const std::string orig_schema(GetSchema(&db()));
+  const std::string orig_schema(GetSchema(&db_));
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
-  const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
 
   // Create a lame-duck table which will not be propagated by recovery to
   // detect that the recovery code actually ran.
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
-  ASSERT_NE(orig_schema, GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
+  ASSERT_NE(orig_schema, GetSchema(&db_));
 
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
 
     size_t rows = 0;
     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
     EXPECT_EQ(2u, rows);
 
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(orig_schema, GetSchema(&db()));
-  ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_EQ(orig_schema, GetSchema(&db_));
+  ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
 }
 
 // Test that a compound primary key doesn't fire the ROWID code.
@@ -622,41 +636,40 @@
       "t TEXT,"
       "PRIMARY KEY (id, id2)"
       ")";
-  ASSERT_TRUE(db().Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
 
   // NOTE(shess): Do not accidentally use [id] 1, 2, 3, as those will
   // be the ROWID values.
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'a', 'This is a test')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'b', 'That was a test')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'a', 'Another test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'a', 'This is a test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'b', 'That was a test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (2, 'a', 'Another test')"));
 
   // Save aside a copy of the original schema and data.
-  const std::string orig_schema(GetSchema(&db()));
+  const std::string orig_schema(GetSchema(&db_));
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
-  const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
 
   // Create a lame-duck table which will not be propagated by recovery to
   // detect that the recovery code actually ran.
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
-  ASSERT_NE(orig_schema, GetSchema(&db()));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
+  ASSERT_NE(orig_schema, GetSchema(&db_));
 
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
 
     size_t rows = 0;
     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
     EXPECT_EQ(3u, rows);
 
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(orig_schema, GetSchema(&db()));
-  ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_EQ(orig_schema, GetSchema(&db_));
+  ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
 }
 
 // Test recovering from a table with fewer columns than the target.
@@ -665,46 +678,45 @@
       "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
   static const char kAlterSql[] =
       "ALTER TABLE x ADD COLUMN t1 TEXT DEFAULT 't'";
-  ASSERT_TRUE(db().Execute(kCreateSql));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'This is')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'That was')"));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'This is')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (2, 'That was')"));
 
   // Generate the expected info by faking a table to match what recovery will
   // create.
-  const std::string orig_schema(GetSchema(&db()));
+  const std::string orig_schema(GetSchema(&db_));
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
   std::string expected_schema;
   std::string expected_data;
   {
-    ASSERT_TRUE(db().BeginTransaction());
-    ASSERT_TRUE(db().Execute(kAlterSql));
+    ASSERT_TRUE(db_.BeginTransaction());
+    ASSERT_TRUE(db_.Execute(kAlterSql));
 
-    expected_schema = GetSchema(&db());
-    expected_data = ExecuteWithResults(&db(), kXSql, "|", "\n");
+    expected_schema = GetSchema(&db_);
+    expected_data = ExecuteWithResults(&db_, kXSql, "|", "\n");
 
-    db().RollbackTransaction();
+    db_.RollbackTransaction();
   }
 
   // Following tests are pointless if the rollback didn't work.
-  ASSERT_EQ(orig_schema, GetSchema(&db()));
+  ASSERT_EQ(orig_schema, GetSchema(&db_));
 
   // Recover the previous version of the table into the altered version.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
     ASSERT_TRUE(recovery->db()->Execute(kAlterSql));
     size_t rows = 0;
     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
     EXPECT_EQ(2u, rows);
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(expected_schema, GetSchema(&db()));
-  ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_EQ(expected_schema, GetSchema(&db_));
+  ASSERT_EQ(expected_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
 }
 
 // Recover a golden file where an interior page has been manually modified so
@@ -714,13 +726,12 @@
   base::FilePath golden_path;
   ASSERT_TRUE(base::PathService::Get(sql::test::DIR_TEST_DATA, &golden_path));
   golden_path = golden_path.AppendASCII("recovery_387868");
-  db().Close();
-  ASSERT_TRUE(base::CopyFile(golden_path, db_path()));
+  db_.Close();
+  ASSERT_TRUE(base::CopyFile(golden_path, db_path_));
   ASSERT_TRUE(Reopen());
 
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_TRUE(recovery.get());
 
     // Create the new version of the table.
@@ -733,96 +744,95 @@
     EXPECT_EQ(43u, rows);
 
     // Successfully recovered.
-    EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    EXPECT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
 }
 
 // Memory-mapped I/O interacts poorly with I/O errors.  Make sure the recovery
 // database doesn't accidentally enable it.
 TEST_F(SQLRecoveryTest, NoMmap) {
-  std::unique_ptr<sql::Recovery> recovery =
-      sql::Recovery::Begin(&db(), db_path());
+  std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
   ASSERT_TRUE(recovery.get());
 
   // In the current implementation, the PRAGMA successfully runs with no result
   // rows.  Running with a single result of |0| is also acceptable.
-  sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size"));
+  Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size"));
   EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0));
 }
 
 TEST_F(SQLRecoveryTest, RecoverDatabase) {
   // As a side effect, AUTOINCREMENT creates the sqlite_sequence table for
   // RecoverDatabase() to handle.
-  ASSERT_TRUE(db().Execute(
+  ASSERT_TRUE(db_.Execute(
       "CREATE TABLE x (id INTEGER PRIMARY KEY AUTOINCREMENT, v TEXT)"));
-  EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('turtle')"));
-  EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('truck')"));
-  EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('trailer')"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO x (v) VALUES ('turtle')"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO x (v) VALUES ('truck')"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO x (v) VALUES ('trailer')"));
 
   // This table needs index and a unique index to work.
-  ASSERT_TRUE(db().Execute("CREATE TABLE y (name TEXT, v TEXT)"));
-  ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX y_name ON y(name)"));
-  ASSERT_TRUE(db().Execute("CREATE INDEX y_v ON y(v)"));
-  EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('jim', 'telephone')"));
-  EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('bob', 'truck')"));
-  EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('dean', 'trailer')"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE y (name TEXT, v TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX y_name ON y(name)"));
+  ASSERT_TRUE(db_.Execute("CREATE INDEX y_v ON y(v)"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO y VALUES ('jim', 'telephone')"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO y VALUES ('bob', 'truck')"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO y VALUES ('dean', 'trailer')"));
 
   // View which is the intersection of [x.v] and [y.v].
-  ASSERT_TRUE(db().Execute(
-      "CREATE VIEW v AS SELECT x.v FROM x, y WHERE x.v = y.v"));
+  ASSERT_TRUE(
+      db_.Execute("CREATE VIEW v AS SELECT x.v FROM x, y WHERE x.v = y.v"));
 
   // When an element is deleted from [x], trigger a delete on [y].  Between the
   // BEGIN and END, [old] stands for the deleted rows from [x].
-  ASSERT_TRUE(db().Execute("CREATE TRIGGER t AFTER DELETE ON x "
-                           "BEGIN DELETE FROM y WHERE y.v = old.v; END"));
+  ASSERT_TRUE(
+      db_.Execute("CREATE TRIGGER t AFTER DELETE ON x "
+                  "BEGIN DELETE FROM y WHERE y.v = old.v; END"));
 
   // Save aside a copy of the original schema, verifying that it has the created
   // items plus the sqlite_sequence table.
-  const std::string orig_schema(GetSchema(&db()));
+  const std::string orig_schema(GetSchema(&db_));
   ASSERT_EQ(6, std::count(orig_schema.begin(), orig_schema.end(), '\n'));
 
   static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
   static const char kYSql[] = "SELECT * FROM y ORDER BY 1";
   static const char kVSql[] = "SELECT * FROM v ORDER BY 1";
   EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
-            ExecuteWithResults(&db(), kXSql, "|", "\n"));
+            ExecuteWithResults(&db_, kXSql, "|", "\n"));
   EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
-            ExecuteWithResults(&db(), kYSql, "|", "\n"));
-  EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n"));
+            ExecuteWithResults(&db_, kYSql, "|", "\n"));
+  EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db_, kVSql, "|", "\n"));
 
   // Database handle is valid before recovery, poisoned after.
   static const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
-  EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
-  sql::Recovery::RecoverDatabase(&db(), db_path());
-  EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
+  EXPECT_TRUE(db_.IsSQLValid(kTrivialSql));
+  Recovery::RecoverDatabase(&db_, db_path_);
+  EXPECT_FALSE(db_.IsSQLValid(kTrivialSql));
 
   // Since the database was not corrupt, the entire schema and all
   // data should be recovered.
   ASSERT_TRUE(Reopen());
-  ASSERT_EQ(orig_schema, GetSchema(&db()));
+  ASSERT_EQ(orig_schema, GetSchema(&db_));
   EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
-            ExecuteWithResults(&db(), kXSql, "|", "\n"));
+            ExecuteWithResults(&db_, kXSql, "|", "\n"));
   EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
-            ExecuteWithResults(&db(), kYSql, "|", "\n"));
-  EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n"));
+            ExecuteWithResults(&db_, kYSql, "|", "\n"));
+  EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db_, kVSql, "|", "\n"));
 
   // Test that the trigger works.
-  ASSERT_TRUE(db().Execute("DELETE FROM x WHERE v = 'truck'"));
-  EXPECT_EQ("1|turtle\n3|trailer",
-            ExecuteWithResults(&db(), kXSql, "|", "\n"));
+  ASSERT_TRUE(db_.Execute("DELETE FROM x WHERE v = 'truck'"));
+  EXPECT_EQ("1|turtle\n3|trailer", ExecuteWithResults(&db_, kXSql, "|", "\n"));
   EXPECT_EQ("dean|trailer\njim|telephone",
-            ExecuteWithResults(&db(), kYSql, "|", "\n"));
-  EXPECT_EQ("trailer", ExecuteWithResults(&db(), kVSql, "|", "\n"));
+            ExecuteWithResults(&db_, kYSql, "|", "\n"));
+  EXPECT_EQ("trailer", ExecuteWithResults(&db_, kVSql, "|", "\n"));
 }
 
 // When RecoverDatabase() encounters SQLITE_NOTADB, the database is deleted.
 TEST_F(SQLRecoveryTest, RecoverDatabaseDelete) {
   // Create a valid database, then write junk over the header.  This should lead
   // to SQLITE_NOTADB, which will cause ATTACH to fail.
-  ASSERT_TRUE(db().Execute("CREATE TABLE x (t TEXT)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
-  db().Close();
-  WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
+  ASSERT_TRUE(db_.Execute("CREATE TABLE x (t TEXT)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
+  db_.Close();
+  ASSERT_TRUE(OverwriteDatabaseHeader());
 
   {
     sql::test::ScopedErrorExpecter expecter;
@@ -832,74 +842,74 @@
     ASSERT_TRUE(Reopen());
 
     // This should "recover" the database by making it valid, but empty.
-    sql::Recovery::RecoverDatabase(&db(), db_path());
+    Recovery::RecoverDatabase(&db_, db_path_);
 
     ASSERT_TRUE(expecter.SawExpectedErrors());
   }
 
   // Recovery poisoned the handle, must re-open.
-  db().Close();
+  db_.Close();
   ASSERT_TRUE(Reopen());
 
-  EXPECT_EQ("", GetSchema(&db()));
+  EXPECT_EQ("", GetSchema(&db_));
 }
 
 // Allow callers to validate the database between recovery and commit.
 TEST_F(SQLRecoveryTest, BeginRecoverDatabase) {
   // Create a table with a broken index.
-  ASSERT_TRUE(db().Execute("CREATE TABLE t (id INTEGER PRIMARY KEY, c TEXT)"));
-  ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX t_id ON t (id)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (1, 'hello world')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (2, 'testing')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (3, 'nope')"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE t (id INTEGER PRIMARY KEY, c TEXT)"));
+  ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX t_id ON t (id)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO t VALUES (1, 'hello world')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO t VALUES (2, 'testing')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO t VALUES (3, 'nope')"));
 
   // Inject corruption into the index.
-  db().Close();
+  db_.Close();
   static const char kDeleteSql[] = "DELETE FROM t WHERE id = 3";
-  ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "t_id", kDeleteSql));
+  ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path_, "t_id", kDeleteSql));
   ASSERT_TRUE(Reopen());
 
   // id as read from index.
   static const char kSelectIndexIdSql[] = "SELECT id FROM t INDEXED BY t_id";
-  EXPECT_EQ("1,2,3", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
+  EXPECT_EQ("1,2,3", ExecuteWithResults(&db_, kSelectIndexIdSql, "|", ","));
 
   // id as read from table.
   static const char kSelectTableIdSql[] = "SELECT id FROM t NOT INDEXED";
-  EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
+  EXPECT_EQ("1,2", ExecuteWithResults(&db_, kSelectTableIdSql, "|", ","));
 
   // Run recovery code, then rollback.  Database remains the same.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::BeginRecoverDatabase(&db(), db_path());
+    std::unique_ptr<Recovery> recovery =
+        Recovery::BeginRecoverDatabase(&db_, db_path_);
     ASSERT_TRUE(recovery);
-    sql::Recovery::Rollback(std::move(recovery));
+    Recovery::Rollback(std::move(recovery));
   }
-  db().Close();
+  db_.Close();
   ASSERT_TRUE(Reopen());
-  EXPECT_EQ("1,2,3", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
-  EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
+  EXPECT_EQ("1,2,3", ExecuteWithResults(&db_, kSelectIndexIdSql, "|", ","));
+  EXPECT_EQ("1,2", ExecuteWithResults(&db_, kSelectTableIdSql, "|", ","));
 
   // Run recovery code, then commit.  The failing row is dropped.
   {
-    std::unique_ptr<sql::Recovery> recovery =
-        sql::Recovery::BeginRecoverDatabase(&db(), db_path());
+    std::unique_ptr<Recovery> recovery =
+        Recovery::BeginRecoverDatabase(&db_, db_path_);
     ASSERT_TRUE(recovery);
-    ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
+    ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
   }
-  db().Close();
+  db_.Close();
   ASSERT_TRUE(Reopen());
-  EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
-  EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
+  EXPECT_EQ("1,2", ExecuteWithResults(&db_, kSelectIndexIdSql, "|", ","));
+  EXPECT_EQ("1,2", ExecuteWithResults(&db_, kSelectTableIdSql, "|", ","));
 }
 
 // Test histograms recorded when the invalid database cannot be attached.
 TEST_F(SQLRecoveryTest, AttachFailure) {
   // Create a valid database, then write junk over the header.  This should lead
   // to SQLITE_NOTADB, which will cause ATTACH to fail.
-  ASSERT_TRUE(db().Execute("CREATE TABLE x (t TEXT)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
-  db().Close();
-  WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
+  ASSERT_TRUE(db_.Execute("CREATE TABLE x (t TEXT)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
+  db_.Close();
+  ASSERT_TRUE(OverwriteDatabaseHeader());
 
   static const char kEventHistogramName[] = "Sqlite.RecoveryEvents";
   const int kEventEnum = 5;  // RECOVERY_FAILED_ATTACH
@@ -914,8 +924,7 @@
     ASSERT_TRUE(Reopen());
 
     // Begin() should fail.
-    std::unique_ptr<sql::Recovery>
-        recovery = sql::Recovery::Begin(&db(), db_path());
+    std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
     ASSERT_FALSE(recovery.get());
 
     ASSERT_TRUE(expecter.SawExpectedErrors());
@@ -942,8 +951,8 @@
 
   const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
       base::NumberToString(initial_page_size));
-  sql::Database::Delete(db_path);
-  sql::Database db({.page_size = initial_page_size});
+  Database::Delete(db_path);
+  Database db({.page_size = initial_page_size});
   ASSERT_TRUE(db.Open(db_path));
   ASSERT_TRUE(db.Execute(kCreateSql));
   ASSERT_TRUE(db.Execute(kInsertSql1));
@@ -953,18 +962,17 @@
   db.Close();
 
   // Re-open the database while setting a new |options.page_size| in the object.
-  sql::Database recover_db({.page_size = final_page_size});
+  Database recover_db({.page_size = final_page_size});
   ASSERT_TRUE(recover_db.Open(db_path));
   // Recovery will use the page size set in the database object, which may not
   // match the file's page size.
-  sql::Recovery::RecoverDatabase(&recover_db, db_path);
+  Recovery::RecoverDatabase(&recover_db, db_path);
 
   // Recovery poisoned the handle, must re-open.
   recover_db.Close();
 
   // Make sure the page size is read from the file.
-  sql::Database recovered_db(
-      {.page_size = sql::DatabaseOptions::kDefaultPageSize});
+  Database recovered_db({.page_size = DatabaseOptions::kDefaultPageSize});
   ASSERT_TRUE(recovered_db.Open(db_path));
   ASSERT_EQ(expected_final_page_size,
             ExecuteWithResult(&recovered_db, "PRAGMA page_size"));
@@ -972,34 +980,36 @@
             ExecuteWithResults(&recovered_db, kSelectSql, "|", "\n"));
 }
 
-// Verify that sql::Recovery maintains the page size, and the virtual table
+// Verify that Recovery maintains the page size, and the virtual table
 // works with page sizes other than SQLite's default.  Also verify the case
 // where the default page size has changed.
 TEST_F(SQLRecoveryTest, PageSize) {
   const std::string default_page_size =
-      ExecuteWithResult(&db(), "PRAGMA page_size");
+      ExecuteWithResult(&db_, "PRAGMA page_size");
 
   // Check the default page size first.
   EXPECT_NO_FATAL_FAILURE(TestPageSize(
-      db_path(), sql::DatabaseOptions::kDefaultPageSize, default_page_size,
-      sql::DatabaseOptions::kDefaultPageSize, default_page_size));
+      db_path_, DatabaseOptions::kDefaultPageSize, default_page_size,
+      DatabaseOptions::kDefaultPageSize, default_page_size));
 
   // Sync uses 32k pages.
   EXPECT_NO_FATAL_FAILURE(
-      TestPageSize(db_path(), 32768, "32768", 32768, "32768"));
+      TestPageSize(db_path_, 32768, "32768", 32768, "32768"));
 
   // Many clients use 4k pages.  This is the SQLite default after 3.12.0.
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 4096, "4096", 4096, "4096"));
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 4096, "4096", 4096, "4096"));
 
   // 1k is the default page size before 3.12.0.
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 1024, "1024", 1024, "1024"));
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 1024, "1024", 1024, "1024"));
 
   // Databases with no page size specified should recover with the new default
   // page size.  2k has never been the default page size.
   ASSERT_NE("2048", default_page_size);
-  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 2048, "2048",
-                                       sql::DatabaseOptions::kDefaultPageSize,
+  EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 2048, "2048",
+                                       DatabaseOptions::kDefaultPageSize,
                                        default_page_size));
 }
 
 }  // namespace
+
+}  // namespace sql
diff --git a/sql/sql_memory_dump_provider_unittest.cc b/sql/sql_memory_dump_provider_unittest.cc
index c361253..6929639 100644
--- a/sql/sql_memory_dump_provider_unittest.cc
+++ b/sql/sql_memory_dump_provider_unittest.cc
@@ -4,19 +4,41 @@
 
 #include "sql/sql_memory_dump_provider.h"
 
+#include "base/files/scoped_temp_dir.h"
+#include "base/trace_event/memory_dump_request_args.h"
 #include "base/trace_event/process_memory_dump.h"
-#include "sql/test/sql_test_base.h"
+#include "sql/database.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace sql {
+
 namespace {
-using SQLMemoryDumpProviderTest = sql::SQLTestBase;
-}
+
+class SQLMemoryDumpProviderTest : public testing::Test {
+ public:
+  ~SQLMemoryDumpProviderTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(db_.Open(
+        temp_dir_.GetPath().AppendASCII("memory_dump_provider_test.sqlite")));
+
+    ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a, b)"));
+  }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  Database db_;
+};
 
 TEST_F(SQLMemoryDumpProviderTest, OnMemoryDump) {
   base::trace_event::MemoryDumpArgs args = {
       base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
   base::trace_event::ProcessMemoryDump pmd(args);
-  ASSERT_TRUE(
-      sql::SqlMemoryDumpProvider::GetInstance()->OnMemoryDump(args, &pmd));
+  ASSERT_TRUE(SqlMemoryDumpProvider::GetInstance()->OnMemoryDump(args, &pmd));
   ASSERT_TRUE(pmd.GetAllocatorDump("sqlite"));
 }
+
+}  // namespace
+
+}  // namespace sql
diff --git a/sql/sqlite_features_unittest.cc b/sql/sqlite_features_unittest.cc
index 877ec67..a84a51b 100644
--- a/sql/sqlite_features_unittest.cc
+++ b/sql/sqlite_features_unittest.cc
@@ -8,13 +8,13 @@
 #include <string>
 
 #include "base/bind.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/files/scoped_temp_dir.h"
 #include "build/build_config.h"
 #include "sql/database.h"
 #include "sql/statement.h"
-#include "sql/test/sql_test_base.h"
 #include "sql/test/test_helpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/sqlite/sqlite3.h"
@@ -40,16 +40,18 @@
 
 }  // namespace
 
-class SQLiteFeaturesTest : public sql::SQLTestBase {
+class SQLiteFeaturesTest : public testing::Test {
  public:
-  SQLiteFeaturesTest() : error_(SQLITE_OK) {}
+  ~SQLiteFeaturesTest() override = default;
 
   void SetUp() override {
-    SQLTestBase::SetUp();
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    db_path_ = temp_dir_.GetPath().AppendASCII("sqlite_features_test.sqlite");
+    ASSERT_TRUE(db_.Open(db_path_));
 
     // The error delegate will set |error_| and |sql_text_| when any sqlite
     // statement operation returns an error code.
-    db().set_error_callback(
+    db_.set_error_callback(
         base::BindRepeating(&CaptureErrorCallback, &error_, &sql_text_));
   }
 
@@ -57,15 +59,20 @@
     // If any error happened the original sql statement can be found in
     // |sql_text_|.
     EXPECT_EQ(SQLITE_OK, error_) << sql_text_;
-
-    SQLTestBase::TearDown();
   }
 
-  int error() { return error_; }
+  bool Reopen() {
+    db_.Close();
+    return db_.Open(db_path_);
+  }
 
- private:
+ protected:
+  base::ScopedTempDir temp_dir_;
+  base::FilePath db_path_;
+  Database db_;
+
   // The error code of the most recent error.
-  int error_;
+  int error_ = SQLITE_OK;
   // Original statement which has caused the error.
   std::string sql_text_;
 };
@@ -73,21 +80,20 @@
 // Do not include fts1 support, it is not useful, and nobody is
 // looking at it.
 TEST_F(SQLiteFeaturesTest, NoFTS1) {
-  ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode(
-      "CREATE VIRTUAL TABLE foo USING fts1(x)"));
+  ASSERT_EQ(SQLITE_ERROR, db_.ExecuteAndReturnErrorCode(
+                              "CREATE VIRTUAL TABLE foo USING fts1(x)"));
 }
 
 // Do not include fts2 support, it is not useful, and nobody is
 // looking at it.
 TEST_F(SQLiteFeaturesTest, NoFTS2) {
-  ASSERT_EQ(SQLITE_ERROR, db().ExecuteAndReturnErrorCode(
-      "CREATE VIRTUAL TABLE foo USING fts2(x)"));
+  ASSERT_EQ(SQLITE_ERROR, db_.ExecuteAndReturnErrorCode(
+                              "CREATE VIRTUAL TABLE foo USING fts2(x)"));
 }
 
-// fts3 used to be used for history files, and may also be used by WebDatabase
-// clients.
+// fts3 is exposed in WebSQL.
 TEST_F(SQLiteFeaturesTest, FTS3) {
-  ASSERT_TRUE(db().Execute("CREATE VIRTUAL TABLE foo USING fts3(x)"));
+  ASSERT_TRUE(db_.Execute("CREATE VIRTUAL TABLE foo USING fts3(x)"));
 }
 
 // Originally history used fts2, which Chromium patched to treat "foo*" as a
@@ -96,12 +102,12 @@
 TEST_F(SQLiteFeaturesTest, FTS3_Prefix) {
   static const char kCreateSql[] =
       "CREATE VIRTUAL TABLE foo USING fts3(x, tokenize icu)";
-  ASSERT_TRUE(db().Execute(kCreateSql));
+  ASSERT_TRUE(db_.Execute(kCreateSql));
 
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (x) VALUES ('test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO foo (x) VALUES ('test')"));
 
   EXPECT_EQ("test",
-            ExecuteWithResult(&db(), "SELECT x FROM foo WHERE x MATCH 'te*'"));
+            ExecuteWithResult(&db_, "SELECT x FROM foo WHERE x MATCH 'te*'"));
 }
 
 // Verify that Chromium's SQLite is compiled with HAVE_USLEEP defined.  With
@@ -121,9 +127,9 @@
 // Ensure that our SQLite version has working foreign key support with cascade
 // delete support.
 TEST_F(SQLiteFeaturesTest, ForeignKeySupport) {
-  ASSERT_TRUE(db().Execute("PRAGMA foreign_keys=1"));
-  ASSERT_TRUE(db().Execute("CREATE TABLE parents (id INTEGER PRIMARY KEY)"));
-  ASSERT_TRUE(db().Execute(
+  ASSERT_TRUE(db_.Execute("PRAGMA foreign_keys=1"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE parents (id INTEGER PRIMARY KEY)"));
+  ASSERT_TRUE(db_.Execute(
       "CREATE TABLE children ("
       "    id INTEGER PRIMARY KEY,"
       "    pid INTEGER NOT NULL REFERENCES parents(id) ON DELETE CASCADE)"));
@@ -131,40 +137,40 @@
   static const char kSelectChildrenSql[] = "SELECT * FROM children ORDER BY id";
 
   // Inserting without a matching parent should fail with constraint violation.
-  EXPECT_EQ("", ExecuteWithResult(&db(), kSelectParentsSql));
+  EXPECT_EQ("", ExecuteWithResult(&db_, kSelectParentsSql));
   const int insert_error =
-      db().ExecuteAndReturnErrorCode("INSERT INTO children VALUES (10, 1)");
+      db_.ExecuteAndReturnErrorCode("INSERT INTO children VALUES (10, 1)");
   EXPECT_EQ(SQLITE_CONSTRAINT | SQLITE_CONSTRAINT_FOREIGNKEY, insert_error);
-  EXPECT_EQ("", ExecuteWithResult(&db(), kSelectChildrenSql));
+  EXPECT_EQ("", ExecuteWithResult(&db_, kSelectChildrenSql));
 
   // Inserting with a matching parent should work.
-  ASSERT_TRUE(db().Execute("INSERT INTO parents VALUES (1)"));
-  EXPECT_EQ("1", ExecuteWithResults(&db(), kSelectParentsSql, "|", "\n"));
-  EXPECT_TRUE(db().Execute("INSERT INTO children VALUES (11, 1)"));
-  EXPECT_TRUE(db().Execute("INSERT INTO children VALUES (12, 1)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO parents VALUES (1)"));
+  EXPECT_EQ("1", ExecuteWithResults(&db_, kSelectParentsSql, "|", "\n"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO children VALUES (11, 1)"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO children VALUES (12, 1)"));
   EXPECT_EQ("11|1\n12|1",
-            ExecuteWithResults(&db(), kSelectChildrenSql, "|", "\n"));
+            ExecuteWithResults(&db_, kSelectChildrenSql, "|", "\n"));
 
   // Deleting the parent should cascade, deleting the children as well.
-  ASSERT_TRUE(db().Execute("DELETE FROM parents"));
-  EXPECT_EQ("", ExecuteWithResult(&db(), kSelectParentsSql));
-  EXPECT_EQ("", ExecuteWithResult(&db(), kSelectChildrenSql));
+  ASSERT_TRUE(db_.Execute("DELETE FROM parents"));
+  EXPECT_EQ("", ExecuteWithResult(&db_, kSelectParentsSql));
+  EXPECT_EQ("", ExecuteWithResult(&db_, kSelectChildrenSql));
 }
 
 // Ensure that our SQLite version supports booleans.
 TEST_F(SQLiteFeaturesTest, BooleanSupport) {
   ASSERT_TRUE(
-      db().Execute("CREATE TABLE flags ("
-                   "    id INTEGER PRIMARY KEY,"
-                   "    true_flag BOOL NOT NULL DEFAULT TRUE,"
-                   "    false_flag BOOL NOT NULL DEFAULT FALSE)"));
-  ASSERT_TRUE(db().Execute(
+      db_.Execute("CREATE TABLE flags ("
+                  "    id INTEGER PRIMARY KEY,"
+                  "    true_flag BOOL NOT NULL DEFAULT TRUE,"
+                  "    false_flag BOOL NOT NULL DEFAULT FALSE)"));
+  ASSERT_TRUE(db_.Execute(
       "ALTER TABLE flags ADD COLUMN true_flag2 BOOL NOT NULL DEFAULT TRUE"));
-  ASSERT_TRUE(db().Execute(
+  ASSERT_TRUE(db_.Execute(
       "ALTER TABLE flags ADD COLUMN false_flag2 BOOL NOT NULL DEFAULT FALSE"));
-  ASSERT_TRUE(db().Execute("INSERT INTO flags (id) VALUES (1)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO flags (id) VALUES (1)"));
 
-  sql::Statement s(db().GetUniqueStatement(
+  sql::Statement s(db_.GetUniqueStatement(
       "SELECT true_flag, false_flag, true_flag2, false_flag2"
       "    FROM flags WHERE id=1;"));
   ASSERT_TRUE(s.Step());
@@ -177,13 +183,11 @@
 }
 
 TEST_F(SQLiteFeaturesTest, IcuEnabled) {
-  sql::Statement lower_en(
-      db().GetUniqueStatement("SELECT lower('I', 'en_us')"));
+  sql::Statement lower_en(db_.GetUniqueStatement("SELECT lower('I', 'en_us')"));
   ASSERT_TRUE(lower_en.Step());
   EXPECT_EQ("i", lower_en.ColumnString(0));
 
-  sql::Statement lower_tr(
-      db().GetUniqueStatement("SELECT lower('I', 'tr_tr')"));
+  sql::Statement lower_tr(db_.GetUniqueStatement("SELECT lower('I', 'tr_tr')"));
   ASSERT_TRUE(lower_tr.Step());
   EXPECT_EQ("\u0131", lower_tr.ColumnString(0));
 }
@@ -196,14 +200,14 @@
 // be disabled on this platform using SQLITE_MAX_MMAP_SIZE=0.
 TEST_F(SQLiteFeaturesTest, Mmap) {
   // Try to turn on mmap'ed I/O.
-  ignore_result(db().Execute("PRAGMA mmap_size = 1048576"));
+  ignore_result(db_.Execute("PRAGMA mmap_size = 1048576"));
   {
-    sql::Statement s(db().GetUniqueStatement("PRAGMA mmap_size"));
+    sql::Statement s(db_.GetUniqueStatement("PRAGMA mmap_size"));
 
     ASSERT_TRUE(s.Step());
     ASSERT_GT(s.ColumnInt64(0), 0);
   }
-  db().Close();
+  db_.Close();
 
   const uint32_t kFlags =
       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE;
@@ -211,7 +215,7 @@
 
   // Create a file with a block of '0', a block of '1', and a block of '2'.
   {
-    base::File f(db_path(), kFlags);
+    base::File f(db_path_, kFlags);
     ASSERT_TRUE(f.IsValid());
     memset(buf, '0', sizeof(buf));
     ASSERT_EQ(f.Write(0*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
@@ -226,7 +230,7 @@
   // mmap the file and verify that everything looks right.
   {
     base::MemoryMappedFile m;
-    ASSERT_TRUE(m.Initialize(db_path()));
+    ASSERT_TRUE(m.Initialize(db_path_));
 
     memset(buf, '0', sizeof(buf));
     ASSERT_EQ(0, memcmp(buf, m.data() + 0*sizeof(buf), sizeof(buf)));
@@ -240,7 +244,7 @@
     // Scribble some '3' into the first page of the file, and verify that it
     // looks the same in the memory mapping.
     {
-      base::File f(db_path(), kFlags);
+      base::File f(db_path_, kFlags);
       ASSERT_TRUE(f.IsValid());
       memset(buf, '3', sizeof(buf));
       ASSERT_EQ(f.Write(0*sizeof(buf), buf, sizeof(buf)), (int)sizeof(buf));
@@ -251,7 +255,7 @@
     const size_t kOffset = 1*sizeof(buf) + 123;
     ASSERT_NE('4', m.data()[kOffset]);
     {
-      base::File f(db_path(), kFlags);
+      base::File f(db_path_, kFlags);
       ASSERT_TRUE(f.IsValid());
       buf[0] = '4';
       ASSERT_EQ(f.Write(kOffset, buf, 1), 1);
@@ -264,14 +268,14 @@
 // compiled regular expression is effectively cached with the prepared
 // statement, causing errors if the regular expression is rebound.
 TEST_F(SQLiteFeaturesTest, CachedRegexp) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE r (id INTEGER UNIQUE, x TEXT)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO r VALUES (1, 'this is a test')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO r VALUES (2, 'that was a test')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO r VALUES (3, 'this is a stickup')"));
-  ASSERT_TRUE(db().Execute("INSERT INTO r VALUES (4, 'that sucks')"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE r (id INTEGER UNIQUE, x TEXT)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO r VALUES (1, 'this is a test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO r VALUES (2, 'that was a test')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO r VALUES (3, 'this is a stickup')"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO r VALUES (4, 'that sucks')"));
 
   static const char kSimpleSql[] = "SELECT SUM(id) FROM r WHERE x REGEXP ?";
-  sql::Statement s(db().GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
+  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, kSimpleSql));
 
   s.BindString(0, "this.*");
   ASSERT_TRUE(s.Step());
@@ -297,26 +301,26 @@
 // If a database file is marked to be excluded from Time Machine, verify that
 // journal files are also excluded.
 TEST_F(SQLiteFeaturesTest, TimeMachine) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE t (id INTEGER PRIMARY KEY)"));
-  db().Close();
+  ASSERT_TRUE(db_.Execute("CREATE TABLE t (id INTEGER PRIMARY KEY)"));
+  db_.Close();
 
-  base::FilePath journal_path = sql::Database::JournalPath(db_path());
-  ASSERT_TRUE(GetPathExists(db_path()));
-  ASSERT_TRUE(GetPathExists(journal_path));
+  base::FilePath journal_path = sql::Database::JournalPath(db_path_);
+  ASSERT_TRUE(base::PathExists(db_path_));
+  ASSERT_TRUE(base::PathExists(journal_path));
 
   // Not excluded to start.
-  EXPECT_FALSE(base::mac::GetFileBackupExclusion(db_path()));
+  EXPECT_FALSE(base::mac::GetFileBackupExclusion(db_path_));
   EXPECT_FALSE(base::mac::GetFileBackupExclusion(journal_path));
 
   // Exclude the main database file.
-  EXPECT_TRUE(base::mac::SetFileBackupExclusion(db_path()));
+  EXPECT_TRUE(base::mac::SetFileBackupExclusion(db_path_));
 
-  EXPECT_TRUE(base::mac::GetFileBackupExclusion(db_path()));
+  EXPECT_TRUE(base::mac::GetFileBackupExclusion(db_path_));
   EXPECT_FALSE(base::mac::GetFileBackupExclusion(journal_path));
 
-  EXPECT_TRUE(db().Open(db_path()));
-  ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (1)"));
-  EXPECT_TRUE(base::mac::GetFileBackupExclusion(db_path()));
+  EXPECT_TRUE(db_.Open(db_path_));
+  ASSERT_TRUE(db_.Execute("INSERT INTO t VALUES (1)"));
+  EXPECT_TRUE(base::mac::GetFileBackupExclusion(db_path_));
   EXPECT_TRUE(base::mac::GetFileBackupExclusion(journal_path));
 
   // TODO(shess): In WAL mode this will touch -wal and -shm files.  -shm files
@@ -329,30 +333,30 @@
 // additional work into Chromium shutdown.  Verify that SQLite supports a config
 // option to not checkpoint on close.
 TEST_F(SQLiteFeaturesTest, WALNoClose) {
-  base::FilePath wal_path = sql::Database::WriteAheadLogPath(db_path());
+  base::FilePath wal_path = sql::Database::WriteAheadLogPath(db_path_);
 
   // Turn on WAL mode, then verify that the mode changed (WAL is supported).
-  ASSERT_TRUE(db().Execute("PRAGMA journal_mode = WAL"));
-  ASSERT_EQ("wal", ExecuteWithResult(&db(), "PRAGMA journal_mode"));
+  ASSERT_TRUE(db_.Execute("PRAGMA journal_mode = WAL"));
+  ASSERT_EQ("wal", ExecuteWithResult(&db_, "PRAGMA journal_mode"));
 
   // The WAL file is created lazily on first change.
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a, b)"));
 
   // By default, the WAL is checkpointed then deleted on close.
-  ASSERT_TRUE(GetPathExists(wal_path));
-  db().Close();
-  ASSERT_FALSE(GetPathExists(wal_path));
+  ASSERT_TRUE(base::PathExists(wal_path));
+  db_.Close();
+  ASSERT_FALSE(base::PathExists(wal_path));
 
   // Reopen and configure the database to not checkpoint WAL on close.
   ASSERT_TRUE(Reopen());
-  ASSERT_TRUE(db().Execute("PRAGMA journal_mode = WAL"));
-  ASSERT_TRUE(db().Execute("ALTER TABLE foo ADD COLUMN c"));
-  ASSERT_EQ(SQLITE_OK,
-            sqlite3_db_config(db().db_, SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, 1,
-                              nullptr));
-  ASSERT_TRUE(GetPathExists(wal_path));
-  db().Close();
-  ASSERT_TRUE(GetPathExists(wal_path));
+  ASSERT_TRUE(db_.Execute("PRAGMA journal_mode = WAL"));
+  ASSERT_TRUE(db_.Execute("ALTER TABLE foo ADD COLUMN c"));
+  ASSERT_EQ(
+      SQLITE_OK,
+      sqlite3_db_config(db_.db_, SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, 1, nullptr));
+  ASSERT_TRUE(base::PathExists(wal_path));
+  db_.Close();
+  ASSERT_TRUE(base::PathExists(wal_path));
 }
 #endif
 
diff --git a/sql/statement_unittest.cc b/sql/statement_unittest.cc
index 2033ee3..af85326 100644
--- a/sql/statement_unittest.cc
+++ b/sql/statement_unittest.cc
@@ -11,29 +11,40 @@
 #include "sql/statement.h"
 #include "sql/test/error_callback_support.h"
 #include "sql/test/scoped_error_expecter.h"
-#include "sql/test/sql_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/sqlite/sqlite3.h"
 
+namespace sql {
 namespace {
 
-using SQLStatementTest = sql::SQLTestBase;
+class SQLStatementTest : public testing::Test {
+ public:
+  ~SQLStatementTest() override = default;
 
-}  // namespace
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(
+        db_.Open(temp_dir_.GetPath().AppendASCII("statement_test.sqlite")));
+  }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  Database db_;
+};
 
 TEST_F(SQLStatementTest, Assign) {
-  sql::Statement s;
+  Statement s;
   EXPECT_FALSE(s.is_valid());
 
-  s.Assign(db().GetUniqueStatement("CREATE TABLE foo (a, b)"));
+  s.Assign(db_.GetUniqueStatement("CREATE TABLE foo (a, b)"));
   EXPECT_TRUE(s.is_valid());
 }
 
 TEST_F(SQLStatementTest, Run) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (3, 12)"));
 
-  sql::Statement s(db().GetUniqueStatement("SELECT b FROM foo WHERE a=?"));
+  Statement s(db_.GetUniqueStatement("SELECT b FROM foo WHERE a=?"));
   EXPECT_FALSE(s.Succeeded());
 
   // Stepping it won't work since we haven't bound the value.
@@ -44,7 +55,7 @@
   s.Reset(true);
   s.BindInt(0, 3);
   EXPECT_FALSE(s.Run());
-  EXPECT_EQ(SQLITE_ROW, db().GetErrorCode());
+  EXPECT_EQ(SQLITE_ROW, db_.GetErrorCode());
   EXPECT_TRUE(s.Succeeded());
 
   // Resetting it should put it back to the previous state (not runnable).
@@ -62,16 +73,16 @@
 
 // Error callback called for error running a statement.
 TEST_F(SQLStatementTest, ErrorCallback) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)"));
 
   int error = SQLITE_OK;
-  sql::ScopedErrorCallback sec(
-      &db(), base::BindRepeating(&sql::CaptureErrorCallback, &error));
+  ScopedErrorCallback sec(&db_,
+                          base::BindRepeating(&CaptureErrorCallback, &error));
 
   // Insert in the foo table the primary key. It is an error to insert
   // something other than an number. This error causes the error callback
   // handler to be called with SQLITE_MISMATCH as error code.
-  sql::Statement s(db().GetUniqueStatement("INSERT INTO foo (a) VALUES (?)"));
+  Statement s(db_.GetUniqueStatement("INSERT INTO foo (a) VALUES (?)"));
   EXPECT_TRUE(s.is_valid());
   s.BindCString(0, "bad bad");
   EXPECT_FALSE(s.Run());
@@ -80,9 +91,9 @@
 
 // Error expecter works for error running a statement.
 TEST_F(SQLStatementTest, ScopedIgnoreError) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a INTEGER PRIMARY KEY, b)"));
 
-  sql::Statement s(db().GetUniqueStatement("INSERT INTO foo (a) VALUES (?)"));
+  Statement s(db_.GetUniqueStatement("INSERT INTO foo (a) VALUES (?)"));
   EXPECT_TRUE(s.is_valid());
 
   {
@@ -95,12 +106,11 @@
 }
 
 TEST_F(SQLStatementTest, Reset) {
-  ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (3, 12)"));
-  ASSERT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (4, 13)"));
+  ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a, b)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (3, 12)"));
+  ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (4, 13)"));
 
-  sql::Statement s(db().GetUniqueStatement(
-      "SELECT b FROM foo WHERE a = ? "));
+  Statement s(db_.GetUniqueStatement("SELECT b FROM foo WHERE a = ? "));
   s.BindInt(0, 3);
   ASSERT_TRUE(s.Step());
   EXPECT_EQ(12, s.ColumnInt(0));
@@ -115,3 +125,6 @@
   s.Reset(true);
   ASSERT_FALSE(s.Step());
 }
+
+}  // namespace
+}  // namespace sql
diff --git a/sql/test/sql_test_base.cc b/sql/test/sql_test_base.cc
deleted file mode 100644
index ef32741..0000000
--- a/sql/test/sql_test_base.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2015 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 "sql/test/sql_test_base.h"
-
-#include "base/files/file_util.h"
-#include "sql/test/test_helpers.h"
-
-namespace sql {
-
-SQLTestBase::SQLTestBase() = default;
-
-SQLTestBase::SQLTestBase(sql::DatabaseOptions options) : db_(options) {}
-
-SQLTestBase::~SQLTestBase() = default;
-
-base::FilePath SQLTestBase::db_path() {
-  return temp_dir_.GetPath().AppendASCII("SQLTest.db");
-}
-
-sql::Database& SQLTestBase::db() {
-  return db_;
-}
-
-bool SQLTestBase::Reopen() {
-  db_.Close();
-  return db_.Open(db_path());
-}
-
-bool SQLTestBase::GetPathExists(const base::FilePath& path) {
-  return base::PathExists(path);
-}
-
-bool SQLTestBase::CorruptSizeInHeaderOfDB() {
-  return sql::test::CorruptSizeInHeader(db_path());
-}
-
-void SQLTestBase::WriteJunkToDatabase(WriteJunkType type) {
-  base::ScopedFILE file(base::OpenFile(
-      db_path(),
-      type == TYPE_OVERWRITE_AND_TRUNCATE ? "wb" : "rb+"));
-  ASSERT_TRUE(file.get());
-  ASSERT_EQ(0, fseek(file.get(), 0, SEEK_SET));
-
-  const char* kJunk = "Now is the winter of our discontent.";
-  fputs(kJunk, file.get());
-}
-
-void SQLTestBase::TruncateDatabase() {
-  base::ScopedFILE file(base::OpenFile(db_path(), "rb+"));
-  ASSERT_TRUE(file);
-  ASSERT_EQ(0, fseek(file.get(), 0, SEEK_SET));
-  ASSERT_TRUE(base::TruncateFile(file.get()));
-}
-
-void SQLTestBase::SetUp() {
-  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-  ASSERT_TRUE(db_.Open(db_path()));
-}
-
-void SQLTestBase::TearDown() {
-  db_.Close();
-}
-
-}  // namespace sql
diff --git a/sql/test/sql_test_base.h b/sql/test/sql_test_base.h
deleted file mode 100644
index cf35f1e..0000000
--- a/sql/test/sql_test_base.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2015 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 SQL_TEST_SQL_TEST_BASE_H_
-#define SQL_TEST_SQL_TEST_BASE_H_
-
-#include "base/files/file_path.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/macros.h"
-#include "sql/database.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace sql {
-
-// Base class for SQL tests.
-//
-// WARNING: We want to run the same gtest based unit test code both against
-// chromium (which uses this implementation here), and the mojo code (which
-// uses a different class named SQLTestBase). These two classes need to have
-// the same interface because we compile time switch them based on a
-// #define. We need to have two different implementations because the mojo
-// version derives from mojo::test::ApplicationTestBase instead of
-// testing::Test.
-class SQLTestBase : public testing::Test {
- public:
-  SQLTestBase();
-  explicit SQLTestBase(sql::DatabaseOptions options);
-  ~SQLTestBase() override;
-
-  enum WriteJunkType {
-    TYPE_OVERWRITE_AND_TRUNCATE,
-    TYPE_OVERWRITE
-  };
-
-  // Returns the path to the database.
-  base::FilePath db_path();
-
-  // Returns a connection to the database at db_path().
-  sql::Database& db();
-
-  // Closes the current connection to the database and reopens it.
-  bool Reopen();
-
-  // Proxying method around base::PathExists.
-  bool GetPathExists(const base::FilePath& path);
-
-  // SQLite stores the database size in the header, and if the actual
-  // OS-derived size is smaller, the database is considered corrupt.
-  // [This case is actually a common form of corruption in the wild.]
-  // This helper sets the in-header size to one page larger than the
-  // actual file size.  The resulting file will return SQLITE_CORRUPT
-  // for most operations unless PRAGMA writable_schema is turned ON.
-  //
-  // Returns false if any error occurs accessing the file.
-  bool CorruptSizeInHeaderOfDB();
-
-  // Writes junk to the start of the file.
-  void WriteJunkToDatabase(WriteJunkType type);
-
-  // Sets the database file size to 0.
-  void TruncateDatabase();
-
-  // Overridden from testing::Test:
-  void SetUp() override;
-  void TearDown() override;
-
- private:
-  base::ScopedTempDir temp_dir_;
-  sql::Database db_;
-
-  DISALLOW_COPY_AND_ASSIGN(SQLTestBase);
-};
-
-}  // namespace sql
-
-#endif  // SQL_TEST_SQL_TEST_BASE_H_
diff --git a/sql/transaction_unittest.cc b/sql/transaction_unittest.cc
index bcc05f58..48f75da 100644
--- a/sql/transaction_unittest.cc
+++ b/sql/transaction_unittest.cc
@@ -3,38 +3,50 @@
 // found in the LICENSE file.
 
 #include "sql/transaction.h"
+
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "sql/database.h"
 #include "sql/statement.h"
-#include "sql/test/sql_test_base.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/sqlite/sqlite3.h"
 
-class SQLTransactionTest : public sql::SQLTestBase {
- public:
-  void SetUp() override {
-    SQLTestBase::SetUp();
+namespace sql {
 
-    ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
+namespace {
+
+class SQLTransactionTest : public testing::Test {
+ public:
+  ~SQLTransactionTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(
+        db_.Open(temp_dir_.GetPath().AppendASCII("transaction_test.sqlite")));
+
+    ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a, b)"));
   }
 
   // Returns the number of rows in table "foo".
   int CountFoo() {
-    sql::Statement count(db().GetUniqueStatement("SELECT count(*) FROM foo"));
+    Statement count(db_.GetUniqueStatement("SELECT count(*) FROM foo"));
     count.Step();
     return count.ColumnInt(0);
   }
+
+ protected:
+  base::ScopedTempDir temp_dir_;
+  Database db_;
 };
 
 TEST_F(SQLTransactionTest, Commit) {
   {
-    sql::Transaction t(&db());
+    Transaction t(&db_);
     EXPECT_FALSE(t.is_open());
     EXPECT_TRUE(t.Begin());
     EXPECT_TRUE(t.is_open());
 
-    EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
+    EXPECT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
 
     t.Commit();
     EXPECT_FALSE(t.is_open());
@@ -47,23 +59,23 @@
   // Test some basic initialization, and that rollback runs when you exit the
   // scope.
   {
-    sql::Transaction t(&db());
+    Transaction t(&db_);
     EXPECT_FALSE(t.is_open());
     EXPECT_TRUE(t.Begin());
     EXPECT_TRUE(t.is_open());
 
-    EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
+    EXPECT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
   }
 
   // Nothing should have been committed since it was implicitly rolled back.
   EXPECT_EQ(0, CountFoo());
 
   // Test explicit rollback.
-  sql::Transaction t2(&db());
+  Transaction t2(&db_);
   EXPECT_FALSE(t2.is_open());
   EXPECT_TRUE(t2.Begin());
 
-  EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
+  EXPECT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
   t2.Rollback();
   EXPECT_FALSE(t2.is_open());
 
@@ -73,23 +85,23 @@
 
 // Rolling back any part of a transaction should roll back all of them.
 TEST_F(SQLTransactionTest, NestedRollback) {
-  EXPECT_EQ(0, db().transaction_nesting());
+  EXPECT_EQ(0, db_.transaction_nesting());
 
   // Outermost transaction.
   {
-    sql::Transaction outer(&db());
+    Transaction outer(&db_);
     EXPECT_TRUE(outer.Begin());
-    EXPECT_EQ(1, db().transaction_nesting());
+    EXPECT_EQ(1, db_.transaction_nesting());
 
     // The first inner one gets committed.
     {
-      sql::Transaction inner1(&db());
+      Transaction inner1(&db_);
       EXPECT_TRUE(inner1.Begin());
-      EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
-      EXPECT_EQ(2, db().transaction_nesting());
+      EXPECT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
+      EXPECT_EQ(2, db_.transaction_nesting());
 
       inner1.Commit();
-      EXPECT_EQ(1, db().transaction_nesting());
+      EXPECT_EQ(1, db_.transaction_nesting());
     }
 
     // One row should have gotten inserted.
@@ -97,24 +109,28 @@
 
     // The second inner one gets rolled back.
     {
-      sql::Transaction inner2(&db());
+      Transaction inner2(&db_);
       EXPECT_TRUE(inner2.Begin());
-      EXPECT_TRUE(db().Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
-      EXPECT_EQ(2, db().transaction_nesting());
+      EXPECT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)"));
+      EXPECT_EQ(2, db_.transaction_nesting());
 
       inner2.Rollback();
-      EXPECT_EQ(1, db().transaction_nesting());
+      EXPECT_EQ(1, db_.transaction_nesting());
     }
 
     // A third inner one will fail in Begin since one has already been rolled
     // back.
-    EXPECT_EQ(1, db().transaction_nesting());
+    EXPECT_EQ(1, db_.transaction_nesting());
     {
-      sql::Transaction inner3(&db());
+      Transaction inner3(&db_);
       EXPECT_FALSE(inner3.Begin());
-      EXPECT_EQ(1, db().transaction_nesting());
+      EXPECT_EQ(1, db_.transaction_nesting());
     }
   }
-  EXPECT_EQ(0, db().transaction_nesting());
+  EXPECT_EQ(0, db_.transaction_nesting());
   EXPECT_EQ(0, CountFoo());
 }
+
+}  // namespace
+
+}  // namespace sql
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index e693269..86336d70 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -52530,7 +52530,7 @@
   },
   "linux-lacros-version-skew-fyi": {
     "additional_compile_targets": [
-      "chromiumos_preflight"
+      "chrome"
     ],
     "gtest_tests": [
       {
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index c10c2cb..bb515e3 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -3682,7 +3682,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -3725,7 +3724,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -5911,7 +5909,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -5955,7 +5952,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -6963,7 +6959,6 @@
           "--remote=127.0.0.1",
           "--remote-ssh-port=9222"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -7527,7 +7522,6 @@
           "--extra-browser-args=--log-level=0 --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating",
           "--remote=variable_chromeos_device_hostname"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -8071,7 +8065,6 @@
           "--use-weston",
           "--weston-use-gl"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -9278,6 +9271,41 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-20.0.8",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -10027,6 +10055,41 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:2184",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -10868,6 +10931,41 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "1002:7340",
+              "os": "Ubuntu",
+              "pool": "chromium.tests.gpu.experimental"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -11484,6 +11582,40 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:5912-19.0.2",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -12138,6 +12270,41 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "8086:3e92-20.0.8",
+              "os": "Ubuntu-18.04.5",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "expiration": 21600,
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -12790,6 +12957,40 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "gpu": "10de:1cb3-418.56",
+              "os": "Ubuntu-19.04",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -16160,6 +16361,88 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.15.5"
+            },
+            {
+              "cpu": "x86-64",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.14.6",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.15.5"
+            },
+            {
+              "cpu": "x86-64",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.14.6",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -17443,6 +17726,74 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "8086:3e9b",
+              "os": "Mac-10.15.7"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "8086:3e9b",
+              "os": "Mac-10.15.7"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -18672,6 +19023,74 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.15.5"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "8086:0a2e",
+              "os": "Mac-10.15.5"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -21157,6 +21576,78 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.14.6",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "1002:6821",
+              "hidpi": "1",
+              "os": "Mac-10.14.6",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -22460,6 +22951,78 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.14.6",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "gpu": "10de:0fe9",
+              "hidpi": "1",
+              "os": "Mac-10.14.6",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -23609,6 +24172,74 @@
       },
       {
         "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "os": "Mac-11",
+              "pool": "chromium.tests.mac-arm64"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "mediapipe",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "mediapipe_validating_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "cpu": "arm64",
+              "os": "Mac-11",
+              "pool": "chromium.tests.mac-arm64"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
           "pixel",
           "--show-stdout",
           "--browser=release",
@@ -24994,7 +25625,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -25343,7 +25973,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -25853,7 +26482,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -25888,7 +26516,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -26569,7 +27196,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -26606,7 +27232,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -27116,7 +27741,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -27153,7 +27777,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=validating"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -27559,7 +28182,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -28169,7 +28791,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -30032,7 +30653,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -32161,7 +32781,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
@@ -33153,7 +33772,6 @@
           "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_higher_performance_gpu --use-cmd-decoder=passthrough --use-gl=angle"
         ],
-        "experiment_percentage": 100,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "args": [],
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index ae317c8c..37b5047 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3005,9 +3005,6 @@
         'mixins': [
           'enable_resultdb',
         ],
-        # TODO(crbug.com/1201887): Make this non-experimental once tests are
-        # stable.
-        'experiment_percentage': 100,
       },
     },
 
@@ -3031,9 +3028,6 @@
         'mixins': [
           'enable_resultdb',
         ],
-        # TODO(crbug.com/1201887): Make this non-experimental once tests are
-        # stable.
-        'experiment_percentage': 100,
       },
     },
 
@@ -6357,6 +6351,7 @@
 
     'gpu_fyi_linux_release_telemetry_tests': [
       'gpu_common_and_optional_telemetry_tests',
+      'gpu_mediapipe_passthrough_telemetry_tests',
       'gpu_passthrough_telemetry_tests',
       'gpu_webgl2_conformance_gl_passthrough_telemetry_tests',
       'gpu_webgl_conformance_gl_passthrough_telemetry_tests',
@@ -6403,6 +6398,8 @@
 
     'gpu_fyi_mac_release_telemetry_tests': [
       'gpu_common_and_optional_telemetry_tests',
+      'gpu_mediapipe_passthrough_telemetry_tests',
+      'gpu_mediapipe_validating_telemetry_tests',
       'gpu_passthrough_telemetry_tests',
       'gpu_validating_telemetry_tests',
       'gpu_webgl2_conformance_gl_passthrough_telemetry_tests',
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9390e31..92fbbaa 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3332,7 +3332,7 @@
       },
       'linux-lacros-version-skew-fyi': {
         'additional_compile_targets': [
-          'chromiumos_preflight',
+          'chrome',
         ],
         'test_suites': {
           'gtest_tests': 'linux-lacros-version-skew-tests',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 6eb3131..b4ceaab 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2292,7 +2292,7 @@
             ]
         }
     ],
-    "CriticalPersistedTabData": [
+    "CriticalPersistedTabData_V2": [
         {
             "platforms": [
                 "android"
diff --git a/third_party/blink/common/page_state/page_state_serialization_unittest.cc b/third_party/blink/common/page_state/page_state_serialization_unittest.cc
index 3ed40254..da6ef99 100644
--- a/third_party/blink/common/page_state/page_state_serialization_unittest.cc
+++ b/third_party/blink/common/page_state/page_state_serialization_unittest.cc
@@ -197,8 +197,8 @@
     frame_state->document_sequence_number = 456;
     frame_state->page_scale_factor = 2.0f;
 
-    frame_state->document_state.push_back(base::UTF8ToUTF16(
-        "\n\r?% WebKit serialized form state version 8 \n\r=&"));
+    frame_state->document_state.push_back(
+        u"\n\r?% WebKit serialized form state version 8 \n\r=&");
     frame_state->document_state.push_back(u"form key");
     frame_state->document_state.push_back(u"1");
     frame_state->document_state.push_back(u"foo");
@@ -459,9 +459,8 @@
   ExplodedPageState input;
   PopulateFrameState(&input.top);
 
-  std::string excessive_length_string(kMaxScrollAnchorSelectorLength + 1, 'a');
-
-  input.top.scroll_anchor_selector = base::UTF8ToUTF16(excessive_length_string);
+  input.top.scroll_anchor_selector =
+      std::u16string(kMaxScrollAnchorSelectorLength + 1, u'a');
 
   std::string encoded;
   EncodePageState(input, &encoded);
diff --git a/third_party/blink/public/common/input/web_gesture_event.h b/third_party/blink/public/common/input/web_gesture_event.h
index a4fcd34..da6c483 100644
--- a/third_party/blink/public/common/input/web_gesture_event.h
+++ b/third_party/blink/public/common/input/web_gesture_event.h
@@ -98,8 +98,8 @@
       // If true, this event will skip hit testing to find a scroll
       // target and instead just scroll the viewport.
       bool target_viewport;
-      // True if this event is generated from a wheel event with synthetic
-      // phase.
+      // True if this event is generated from a mousewheel or scrollbar.
+      // Synthetic GSB(s) are ignored by the blink::ElasticOverscrollController.
       bool synthetic;
       // If true, this event has been hit tested by the main thread and the
       // result is stored in scrollable_area_element_id. Used only in scroll
diff --git a/third_party/blink/public/web/web_view_client.h b/third_party/blink/public/web/web_view_client.h
index ac6f764..2ff545d 100644
--- a/third_party/blink/public/web/web_view_client.h
+++ b/third_party/blink/public/web/web_view_client.h
@@ -94,8 +94,6 @@
   // should be printed.
   virtual void PrintPage(WebLocalFrame*) {}
 
-  virtual void OnPageVisibilityChanged(mojom::PageVisibilityState visibility) {}
-
   virtual void OnPageFrozenChanged(bool frozen) {}
 
   virtual void DidUpdateRendererPreferences() {}
diff --git a/third_party/blink/renderer/core/app_history/app_history.cc b/third_party/blink/renderer/core/app_history/app_history.cc
index 065d5f4..a3e1414 100644
--- a/third_party/blink/renderer/core/app_history/app_history.cc
+++ b/third_party/blink/renderer/core/app_history/app_history.cc
@@ -204,6 +204,13 @@
                                    const String& url,
                                    AppHistoryNavigateOptions* options,
                                    ExceptionState& exception_state) {
+  if (!GetSupplementable()->GetFrame()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "navigate() may not be called in a "
+                                      "detached window");
+    return ScriptPromise();
+  }
+
   KURL completed_url(GetSupplementable()->Url(), url);
   if (!completed_url.IsValid()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
@@ -232,10 +239,9 @@
   // bypassing ScriptPromiseResolver and managing our own v8::Promise::Resolver,
   // special case detach here.
   if (!GetSupplementable()->GetFrame()) {
-    return ScriptPromise::RejectWithDOMException(
-        script_state,
-        MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError,
-                                           "Navigation was aborted"));
+    exception_state.ThrowDOMException(DOMExceptionCode::kAbortError,
+                                      "Navigation was aborted");
+    return ScriptPromise();
   }
   return navigate_method_call_promise_resolver_->Promise();
 }
diff --git a/third_party/blink/renderer/core/dom/slot_assignment.cc b/third_party/blink/renderer/core/dom/slot_assignment.cc
index 5ac4a53..d375b59 100644
--- a/third_party/blink/renderer/core/dom/slot_assignment.cc
+++ b/third_party/blink/renderer/core/dom/slot_assignment.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/dom/slot_assignment.h"
 
+#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
 #include "third_party/blink/renderer/core/dom/element_traversal.h"
 #include "third_party/blink/renderer/core/dom/flat_tree_traversal_forbidden_scope.h"
@@ -268,6 +269,16 @@
                     *slot, kIncludeSelf));
     }
 
+    // The accessibility cache must be invalidated before flat tree traversal
+    // is forbidden, because the process of invalidation accesses the old flat
+    // tree children in order to clean up soon to be stale relationships.
+    // Any <slot> within this shadow root may lose or gain flat tree children
+    // during slot reassignment, so call ChildrenChanged() on all of them.
+    if (AXObjectCache* cache = owner_->GetDocument().ExistingAXObjectCache()) {
+      for (Member<HTMLSlotElement> slot : Slots())
+        cache->ChildrenChanged(slot);
+    }
+
     FlatTreeTraversalForbiddenScope forbid_flat_tree_traversal(
         owner_->GetDocument());
 
diff --git a/third_party/blink/renderer/core/editing/editing_utilities_test.cc b/third_party/blink/renderer/core/editing/editing_utilities_test.cc
index 3eea9e7..e9e5b4da 100644
--- a/third_party/blink/renderer/core/editing/editing_utilities_test.cc
+++ b/third_party/blink/renderer/core/editing/editing_utilities_test.cc
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/core/editing/testing/editing_test_base.h"
 #include "third_party/blink/renderer/core/editing/visible_position.h"
 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
-#include "third_party/blink/renderer/core/layout//geometry/physical_offset.h"
+#include "third_party/blink/renderer/core/layout/geometry/physical_offset.h"
 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 540b32130..b05d3ca 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -3525,7 +3525,6 @@
   DCHECK(GetPage());
   if (!is_initial_state) {
     // Preserve the side effects of visibility change.
-    web_view_client_->OnPageVisibilityChanged(visibility_state);
     for (auto& observer : observers_)
       observer.OnPageVisibilityChanged(visibility_state);
   }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
index 6ada302..06c889e9 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.cc
@@ -5,11 +5,11 @@
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h"
 
 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
-#include "third_party/blink/renderer/core/layout/ng//ng_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_break_token.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
 #include "third_party/blink/renderer/platform/fonts/character_range.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_buffer.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 54eed64fe..b6b5c05 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -2451,10 +2451,31 @@
       // offset transform for paint_offset_root.
       !context_.current.paint_offset_root->PaintingLayer()
            ->EnclosingPaginationLayer()) {
-    if (object_.StyleRef().GetPosition() == EPosition::kAbsolute)
+    if (object_.StyleRef().GetPosition() == EPosition::kAbsolute) {
+      if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
+        // FIXME(dbaron): When the TransformInteropEnabled flag is removed
+        // because it's always enabled, we should move these variables from
+        // PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext to
+        // PaintPropertyTreeBuilderFragmentContext.
+        context_.absolute_position.should_flatten_inherited_transform =
+            context_.current.should_flatten_inherited_transform;
+        context_.absolute_position.rendering_context_id =
+            context_.current.rendering_context_id;
+      }
       context_.current = context_.absolute_position;
-    else if (object_.StyleRef().GetPosition() == EPosition::kFixed)
+    } else if (object_.StyleRef().GetPosition() == EPosition::kFixed) {
+      if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
+        // FIXME(dbaron): When the TransformInteropEnabled flag is removed
+        // because it's always enabled, we should move these variables from
+        // PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext to
+        // PaintPropertyTreeBuilderFragmentContext.
+        context_.fixed_position.should_flatten_inherited_transform =
+            context_.current.should_flatten_inherited_transform;
+        context_.fixed_position.rendering_context_id =
+            context_.current.rendering_context_id;
+      }
       context_.current = context_.fixed_position;
+    }
 
     // Set fragment visual paint offset.
     PhysicalOffset paint_offset =
@@ -2497,6 +2518,16 @@
       case EPosition::kAbsolute: {
         DCHECK_EQ(full_context_.container_for_absolute_position,
                   box_model_object.Container());
+        if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
+          // FIXME(dbaron): When the TransformInteropEnabled flag is removed
+          // because it's always enabled, we should move these variables from
+          // PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext
+          // to PaintPropertyTreeBuilderFragmentContext.
+          context_.absolute_position.should_flatten_inherited_transform =
+              context_.current.should_flatten_inherited_transform;
+          context_.absolute_position.rendering_context_id =
+              context_.current.rendering_context_id;
+        }
         context_.current = context_.absolute_position;
 
         // Absolutely positioned content in an inline should be positioned
@@ -2517,6 +2548,16 @@
       case EPosition::kFixed: {
         DCHECK_EQ(full_context_.container_for_fixed_position,
                   box_model_object.Container());
+        if (RuntimeEnabledFeatures::TransformInteropEnabled()) {
+          // FIXME(dbaron): When the TransformInteropEnabled flag is removed
+          // because it's always enabled, we should move these variables from
+          // PaintPropertyTreeBuilderFragmentContext::ContainingBlockContext
+          // to PaintPropertyTreeBuilderFragmentContext.
+          context_.fixed_position.should_flatten_inherited_transform =
+              context_.current.should_flatten_inherited_transform;
+          context_.fixed_position.rendering_context_id =
+              context_.current.rendering_context_id;
+        }
         context_.current = context_.fixed_position;
         // Fixed-position elements that are fixed to the viewport have a
         // transform above the scroll of the LayoutView. Child content is
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index a1ff2d6..58dbac3 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -4225,6 +4225,11 @@
                                    << ToString(true, true);
 
   for (const auto& child : children_) {
+    // Check parent first, as the child might be several levels down if there
+    // are unincluded nodes in between, in which case the cached parent will
+    // also be a descendant (unlike children_, parent_ does not skip levels).
+    // Another case where the parent is not the same is when the child has been
+    // reparented using aria-owns.
     if (child->CachedParentObject() == this)
       child->DetachFromParent();
   }
@@ -4234,23 +4239,16 @@
   if (!GetNode())
     return;
 
-  // <slot> content is always included in the tree, so there is no need to
-  // iterate through the nodes. This also protects us against slot use "after
-  // poison", where attempts to access assigned nodes triggers a DCHECK.
-
-  // Detailed explanation:
-  // <slot> elements are placeholders marking locations in a shadow tree where
-  // users of a web component can insert their own custom nodes. Inserted nodes
-  // (also known as distributed nodes) become children of their respective slots
-  // in the accessibility tree. In other words, the accessibility tree mirrors
-  // the flattened DOM tree or the layout tree, not the original DOM tree.
-  // Distributed nodes still maintain their parent relations and computed style
-  // information with their original location in the DOM. Therefore, we need to
-  // ensure that in the accessibility tree no remnant information from the
-  // unflattened DOM tree remains, such as the cached parent.
-  if (IsA<HTMLSlotElement>(GetNode()))
+  if (GetDocument()->IsFlatTreeTraversalForbidden() ||
+      GetDocument()->IsSlotAssignmentRecalcForbidden()) {
+    // Cannot use layout tree builder traversal now, will have to rely on
+    // RepairParent() at a later point.
     return;
+  }
 
+  // Detach children that were not cleared from first loop.
+  // These must have been an unincluded node who's parent is this,
+  // although it may now be included since the children were last updated.
   for (Node* child_node = LayoutTreeBuilderTraversal::FirstChild(*GetNode());
        child_node;
        child_node = LayoutTreeBuilderTraversal::NextSibling(*child_node)) {
@@ -4258,9 +4256,6 @@
     AXObject* ax_child_from_node = AXObjectCache().Get(child_node);
     if (ax_child_from_node &&
         ax_child_from_node->CachedParentObject() == this) {
-      // Child was not cleared from first loop.
-      // It must have been an unincluded node who's parent is this,
-      // although it may now be included since the children were last updated.
       // Check current parent first. It may be owned by another node.
       ax_child_from_node->DetachFromParent();
     }
diff --git a/third_party/blink/renderer/modules/breakout_box/pushable_media_stream_audio_source.cc b/third_party/blink/renderer/modules/breakout_box/pushable_media_stream_audio_source.cc
index 0cc1d49..e31591b 100644
--- a/third_party/blink/renderer/modules/breakout_box/pushable_media_stream_audio_source.cc
+++ b/third_party/blink/renderer/modules/breakout_box/pushable_media_stream_audio_source.cc
@@ -80,22 +80,11 @@
     last_frames_ = frame_count;
   }
 
-  std::unique_ptr<media::AudioBus> audio_bus;
-
-  if (data->sample_format() == media::SampleFormat::kSampleFormatPlanarF32) {
-    // |data| already has the right format for media::AudioBus, and we can
-    // simply wrap the memory without copying it.
-    audio_bus = media::AudioBus::CreateWrapper(channel_count);
-    for (int ch = 0; ch < channel_count; ch++) {
-      audio_bus->SetChannelData(
-          ch, reinterpret_cast<float*>(data->channel_data()[ch]));
-    }
-    audio_bus->set_frames(frame_count);
-  } else {
-    // |data| must be converted into the proper media::AudioBus format.
-    audio_bus = media::AudioBus::Create(channel_count, frame_count);
-    data->ReadFrames(frame_count, 0, 0, audio_bus.get());
-  }
+  // If |data|'s sample format has the same memory layout as a media::AudioBus,
+  // |audio_bus| will simply wrap it. Otherwise, |data| will be copied and
+  // converted into |audio_bus|.
+  std::unique_ptr<media::AudioBus> audio_bus =
+      media::AudioBuffer::WrapOrCopyToAudioBus(data);
 
   DeliverDataToTracks(*audio_bus, base::TimeTicks() + data->timestamp());
 }
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 6210d9b..82b5b1e 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -104,11 +104,59 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 #if defined(OS_ANDROID)
+#include "third_party/blink/public/platform/modules/video_capture/web_video_capture_impl_manager.h"
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.h"
+#include "third_party/blink/renderer/core/page/page_visibility_observer.h"
 #include "third_party/blink/renderer/modules/remote_objects/remote_object_gateway_impl.h"
 #endif
 
 namespace blink {
 
+#if defined(OS_ANDROID)
+namespace {
+
+class SuspendCaptureObserver : public GarbageCollected<SuspendCaptureObserver>,
+                               public Supplement<Page>,
+                               public PageVisibilityObserver {
+ public:
+  static const char kSupplementName[];
+
+  explicit SuspendCaptureObserver(Page& page)
+      : Supplement<Page>(page), PageVisibilityObserver(&page) {}
+
+  // PageVisibilityObserver overrides:
+  void PageVisibilityChanged() override {
+    // TODO(crbug.com/487935): We don't yet suspend video capture devices for
+    // OOPIFs.
+    WebLocalFrameImpl* frame = WebLocalFrameImpl::FromFrame(
+        DynamicTo<LocalFrame>(GetPage()->MainFrame()));
+    if (!frame)
+      return;
+    WebMediaStreamDeviceObserver* media_stream_device_observer =
+        frame->Client()->MediaStreamDeviceObserver();
+    if (!media_stream_device_observer)
+      return;
+
+    bool suspend = !GetPage()->IsPageVisible();
+    MediaStreamDevices video_devices =
+        media_stream_device_observer->GetNonScreenCaptureDevices();
+    Platform::Current()->GetVideoCaptureImplManager()->SuspendDevices(
+        video_devices, suspend);
+  }
+
+  void Trace(Visitor* visitor) const override {
+    Supplement<Page>::Trace(visitor);
+    PageVisibilityObserver::Trace(visitor);
+  }
+};
+
+const char SuspendCaptureObserver::kSupplementName[] = "SuspendCaptureObserver";
+
+}  // namespace
+
+#endif  // OS_ANDROID
+
 void ModulesInitializer::Initialize() {
   // Strings must be initialized before calling CoreInitializer::init().
   const unsigned kModulesStaticStringsCount =
@@ -293,6 +341,9 @@
                                    MakeGarbageCollected<DatabaseClient>());
   StorageNamespace::ProvideSessionStorageNamespaceTo(page, namespace_id);
   AudioGraphTracer::ProvideAudioGraphTracerTo(page);
+#if defined(OS_ANDROID)
+  page.ProvideSupplement(MakeGarbageCollected<SuspendCaptureObserver>(page));
+#endif  // OS_ANDROID
 }
 
 void ModulesInitializer::ForceNextWebGLContextCreationToFail() const {
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
index 290f4f75..be7924fe 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
@@ -204,10 +204,10 @@
 
   auto* frame = request->frame.Release();
 
-  // TODO(crbug.com/1201986): calling buffer() might incur a copy internally,
-  // and we copy the data right back into a media::AudioBus below. We should
-  // replace buffer() with data().
-  auto* buffer = frame->buffer();
+  auto data = frame->data();
+
+  // The frame shouldn't be closed at this point.
+  DCHECK(data);
 
   auto done_callback = [](AudioEncoder* self, uint32_t reset_count,
                           media::Status status) {
@@ -221,11 +221,11 @@
     self->ProcessRequests();
   };
 
-  if (buffer->numberOfChannels() != uint8_t{active_config_->options.channels} ||
-      buffer->sampleRate() != active_config_->options.sample_rate) {
+  if (data->channel_count() != active_config_->options.channels ||
+      data->sample_rate() != active_config_->options.sample_rate) {
     media::Status error(media::StatusCode::kEncoderFailedEncode);
-    error.WithData("channels", int{buffer->numberOfChannels()});
-    error.WithData("sampleRate", buffer->sampleRate());
+    error.WithData("channels", data->channel_count());
+    error.WithData("sampleRate", data->sample_rate());
 
     HandleError(logger_->MakeException(
         "Input audio buffer is incompatible with codec parameters", error));
@@ -233,28 +233,11 @@
     return;
   }
 
-  DCHECK(buffer);
+  // If |data|'s memory layout allows it, |audio_bus| will be a simple wrapper
+  // around it. Otherwise, |audio_bus| will contain a converted copy of |data|.
+  auto audio_bus = media::AudioBuffer::WrapOrCopyToAudioBus(data);
 
-  // TODO(crbug.com/1168418): There are two reasons we need to copy |buffer|
-  // data here:
-  // 1. AudioBus data needs to be 16 bytes aligned and |buffer| data might not
-  // be aligned like that.
-  // 2. The encoder might need to access this data on a different thread, which
-  // is not allowed from blink point of view.
-  //
-  // If we could transfer AudioBuffer's data to another thread, we wouldn't need
-  // to copy it, if alignment happens to be right.
-  auto audio_bus =
-      media::AudioBus::Create(buffer->numberOfChannels(), buffer->length());
-  for (int channel = 0; channel < audio_bus->channels(); channel++) {
-    auto array = buffer->getChannelData(channel);
-    size_t byte_length = array->byteLength();
-    DCHECK_EQ(byte_length, audio_bus->frames() * sizeof(float));
-    memcpy(audio_bus->channel(channel), array->Data(), byte_length);
-  }
-
-  base::TimeTicks timestamp =
-      base::TimeTicks() + base::TimeDelta::FromMicroseconds(frame->timestamp());
+  base::TimeTicks timestamp = base::TimeTicks() + data->timestamp();
   media_encoder_->Encode(
       std::move(audio_bus), timestamp,
       ConvertToBaseOnceCallback(CrossThreadBindOnce(
diff --git a/third_party/blink/renderer/platform/graphics/rw_buffer.cc b/third_party/blink/renderer/platform/graphics/rw_buffer.cc
index 5dc08fa..5e2c90d 100644
--- a/third_party/blink/renderer/platform/graphics/rw_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/rw_buffer.cc
@@ -165,6 +165,10 @@
   return remaining_ != 0;
 }
 
+bool RWBuffer::ROIter::HasNext() const {
+  return block_ && block_->next_;
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // The reader can only access block.capacity_ (which never changes), and cannot
 // access block.used_, which may be updated by the writer.
diff --git a/third_party/blink/renderer/platform/graphics/rw_buffer.h b/third_party/blink/renderer/platform/graphics/rw_buffer.h
index b8c76633..3a357dd4 100644
--- a/third_party/blink/renderer/platform/graphics/rw_buffer.h
+++ b/third_party/blink/renderer/platform/graphics/rw_buffer.h
@@ -28,7 +28,12 @@
     explicit ROIter(RWBuffer*, size_t);
     size_t size() const;
     const void* data() const;
+    // Checks whether there is another block available and advances the iterator
+    // if there is.
     bool Next();
+    // Checks whether there is another block available. Does not advance the
+    // iterator.
+    bool HasNext() const;
 
    private:
     const RWBuffer* rw_buffer_;
diff --git a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
index d1a88ef..678fbaf8 100644
--- a/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/image_decoder.cc
@@ -230,8 +230,9 @@
 #if BUILDFLAG(ENABLE_JXL_DECODER)
   } else if (base::FeatureList::IsEnabled(features::kJXL) &&
              mime_type == "image/jxl") {
-    decoder = std::make_unique<JXLImageDecoder>(alpha_option, color_behavior,
-                                                max_decoded_bytes);
+    decoder = std::make_unique<JXLImageDecoder>(
+        alpha_option, high_bit_depth_decoding_option, color_behavior,
+        max_decoded_bytes);
 #endif
   }
 
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
index b4517330..9c0ffd8 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.cc
@@ -8,11 +8,13 @@
 
 namespace blink {
 
-JXLImageDecoder::JXLImageDecoder(AlphaOption alpha_option,
-                                 const ColorBehavior& color_behavior,
-                                 size_t max_decoded_bytes)
+JXLImageDecoder::JXLImageDecoder(
+    AlphaOption alpha_option,
+    HighBitDepthDecodingOption high_bit_depth_decoding_option,
+    const ColorBehavior& color_behavior,
+    size_t max_decoded_bytes)
     : ImageDecoder(alpha_option,
-                   ImageDecoder::kDefaultBitDepth,
+                   high_bit_depth_decoding_option,
                    color_behavior,
                    max_decoded_bytes) {}
 
@@ -20,6 +22,10 @@
   JxlDecoderDestroy(dec_);
 }
 
+inline bool DecodeToHalfFloat(const JxlPixelFormat& format) {
+  return format.data_type == JXL_TYPE_FLOAT16;
+}
+
 void JXLImageDecoder::Decode(bool only_size) {
   if (IsDecodedSizeAvailable() && only_size) {
     // Also SetEmbeddedProfile is done already if the size was set.
@@ -42,7 +48,6 @@
   }
 
   JxlBasicInfo info;
-  const JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
 
   FastSharedBufferReader reader(data_.get());
   size_t offset = 0;
@@ -53,6 +58,7 @@
   const bool size_available = IsDecodedSizeAvailable();
   // Our API guarantees that we either get JXL_DEC_ERROR, JXL_DEC_SUCCESS or
   // JXL_DEC_NEED_MORE_INPUT, and we exit the loop below in either case.
+
   for (;;) {
     JxlDecoderStatus status = JxlDecoderProcessInput(dec_);
     switch (status) {
@@ -103,27 +109,141 @@
           continue;
         }
 
-        if (!size_available) {
-          size_t icc_size;
-          if (JXL_DEC_SUCCESS !=
-              JxlDecoderGetICCProfileSize(
-                  dec_, &format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size)) {
-            DVLOG(1) << "JxlDecoderGetICCProfileSize failed";
-            SetFailed();
-            return;
-          }
-          std::vector<uint8_t> icc_profile(icc_size);
-          if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
-                                     dec_, &format,
-                                     JXL_COLOR_PROFILE_TARGET_DATA,
-                                     icc_profile.data(), icc_profile.size())) {
-            DVLOG(1) << "JxlDecoderGetColorAsICCProfile failed";
-            SetFailed();
-            return;
-          }
-          std::unique_ptr<ColorProfile> profile =
-              ColorProfile::Create(icc_profile.data(), icc_profile.size());
+        // If the decoder was used before with only_size == true, the color
+        // encoding is already decoded as well, and SetEmbeddedColorProfile
+        // should not be called a second time anymore.
+        if (size_available) {
+          continue;
+        }
 
+        // Detect whether the JXL image is intended to be an HDR image: when it
+        // uses more than 8 bits per pixel, or when it has explicitly marked
+        // PQ or HLG color profile.
+        if (info.bits_per_sample > 8) {
+          is_hdr_ = true;
+        }
+        JxlColorEncoding color_encoding;
+        if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile(
+                                   dec_, &format_,
+                                   JXL_COLOR_PROFILE_TARGET_ORIGINAL,
+                                   &color_encoding)) {
+          if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_PQ ||
+              color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_HLG) {
+            is_hdr_ = true;
+          }
+        }
+
+        std::unique_ptr<ColorProfile> profile;
+
+        if (is_hdr_ &&
+            high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat) {
+          format_.data_type = JXL_TYPE_FLOAT16;
+          format_.endianness = JXL_LITTLE_ENDIAN;
+        }
+
+        bool have_data_profile = false;
+        if (JXL_DEC_SUCCESS ==
+            JxlDecoderGetColorAsEncodedProfile(dec_, &format_,
+                                               JXL_COLOR_PROFILE_TARGET_DATA,
+                                               &color_encoding)) {
+          bool known_transfer_function = true;
+          bool known_gamut = true;
+          skcms_Matrix3x3 gamut;
+          skcms_TransferFunction transfer;
+          // PQ or HLG as the data may occur if the JXL image was lossless, or
+          // not in XYB color space. If the JXL image was lossy with XYB, then
+          // instead linear sRGB is expected here when we treat the image as HDR
+          // (when format_.data_type == JXL_TYPE_FLOAT), nonlinear sRGB for SDR.
+          if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_PQ) {
+            transfer = SkNamedTransferFn::kPQ;
+          } else if (color_encoding.transfer_function ==
+                     JXL_TRANSFER_FUNCTION_HLG) {
+            transfer = SkNamedTransferFn::kHLG;
+          } else if (color_encoding.transfer_function ==
+                     JXL_TRANSFER_FUNCTION_LINEAR) {
+            transfer = SkNamedTransferFn::kLinear;
+          } else if (color_encoding.transfer_function ==
+                     JXL_TRANSFER_FUNCTION_SRGB) {
+            transfer = SkNamedTransferFn::kSRGB;
+          } else {
+            known_transfer_function = false;
+          }
+
+          if (color_encoding.white_point == JXL_WHITE_POINT_D65 &&
+              color_encoding.primaries == JXL_PRIMARIES_2100) {
+            gamut = SkNamedGamut::kRec2020;
+          } else if (color_encoding.white_point == JXL_WHITE_POINT_D65 &&
+                     color_encoding.primaries == JXL_PRIMARIES_SRGB) {
+            gamut = SkNamedGamut::kSRGB;
+          } else {
+            known_gamut = false;
+          }
+
+          have_data_profile = known_transfer_function && known_gamut;
+
+          if (have_data_profile) {
+            skcms_ICCProfile dataProfile;
+            SkColorSpace::MakeRGB(transfer, gamut)->toProfile(&dataProfile);
+            profile = std::make_unique<ColorProfile>(dataProfile);
+          }
+        }
+
+        // Did not handle exact enum values, get as ICC profile instead.
+        if (!have_data_profile) {
+          size_t icc_size;
+          bool got_size =
+              JXL_DEC_SUCCESS ==
+              JxlDecoderGetICCProfileSize(
+                  dec_, &format_, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size);
+          std::vector<uint8_t> icc_profile(icc_size);
+          if (got_size && JXL_DEC_SUCCESS ==
+                              JxlDecoderGetColorAsICCProfile(
+                                  dec_, &format_, JXL_COLOR_PROFILE_TARGET_DATA,
+                                  icc_profile.data(), icc_profile.size())) {
+            profile =
+                ColorProfile::Create(icc_profile.data(), icc_profile.size());
+            have_data_profile = true;
+
+            // Detect whether the ICC profile approximately equals PQ or HLG,
+            // and set the profile to one that indicates this transfer function
+            // more clearly than a raw ICC profile does, so Chrome considers
+            // the profile as HDR.
+            const skcms_ICCProfile* parsed = profile->GetProfile();
+            skcms_ICCProfile parsed_pq = *parsed;
+            parsed_pq.has_trc = true;
+            for (int c = 0; c < 3; c++) {
+              parsed_pq.trc[c].table_entries = 0;
+              skcms_TransferFunction_makePQ(&parsed_pq.trc[c].parametric);
+            }
+            bool approx_pq =
+                skcms_ApproximatelyEqualProfiles(parsed, &parsed_pq);
+
+            skcms_ICCProfile parsed_hlg = *parsed;
+            parsed_hlg.has_trc = true;
+            for (int c = 0; c < 3; c++) {
+              parsed_hlg.trc[c].table_entries = 0;
+              skcms_TransferFunction_makeHLG(&parsed_hlg.trc[c].parametric);
+            }
+            bool approx_hlg =
+                skcms_ApproximatelyEqualProfiles(parsed, &parsed_hlg);
+
+            if (approx_pq) {
+              profile = std::make_unique<ColorProfile>(parsed_pq);
+              is_hdr_ = true;
+            } else if (approx_hlg) {
+              profile = std::make_unique<ColorProfile>(parsed_hlg);
+              is_hdr_ = true;
+            }
+          }
+        }
+
+        if (is_hdr_ &&
+            high_bit_depth_decoding_option_ == kHighBitDepthToHalfFloat) {
+          format_.data_type = JXL_TYPE_FLOAT16;
+          format_.endianness = JXL_LITTLE_ENDIAN;
+        }
+
+        if (have_data_profile) {
           if (profile->GetProfile()->data_color_space == skcms_Signature_RGB) {
             SetEmbeddedColorProfile(std::move(profile));
           }
@@ -134,8 +254,9 @@
         // Progressive is not yet implemented, and we potentially require some
         // color transforms applied on the buffer from the frame. If we let the
         // JPEG XL decoder write to the buffer immediately, Chrome may already
-        // render its intermediate stage with the wrong color format. Hence, for
-        // now, only set the buffer and let JXL decode once we have all data.
+        // render its intermediate stage with the wrong color format_. Hence,
+        // for now, only set the buffer and let JXL decode once we have all
+        // data.
         if (!IsAllDataReceived())
           return;
         // Always 0 because animation is not yet implemented.
@@ -150,19 +271,33 @@
         }
         size_t buffer_size;
         if (JXL_DEC_SUCCESS !=
-            JxlDecoderImageOutBufferSize(dec_, &format, &buffer_size)) {
+            JxlDecoderImageOutBufferSize(dec_, &format_, &buffer_size)) {
           DVLOG(1) << "JxlDecoderImageOutBufferSize failed";
           SetFailed();
           return;
         }
-        if (buffer_size != info.xsize * info.ysize * 4) {
-          DVLOG(1) << "Unexpected buffer size";
-          SetFailed();
-          return;
+
+        void* pixels_buffer;
+
+        if (DecodeToHalfFloat(format_)) {
+          if (buffer_size != info.xsize * info.ysize * 8) {
+            DVLOG(1) << "Unexpected buffer size";
+            SetFailed();
+            return;
+          }
+          pixels_buffer = reinterpret_cast<void*>(frame.GetAddrF16(0, 0));
+        } else {
+          if (buffer_size != info.xsize * info.ysize * 4) {
+            DVLOG(1) << "Unexpected buffer size";
+            SetFailed();
+            return;
+          }
+          pixels_buffer = reinterpret_cast<void*>(frame.GetAddr(0, 0));
         }
-        void* pixels_buffer = reinterpret_cast<void*>(frame.GetAddr(0, 0));
-        if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(
-                                   dec_, &format, pixels_buffer, buffer_size)) {
+
+        if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec_, &format_,
+                                                           pixels_buffer,
+                                                           buffer_size)) {
           DVLOG(1) << "JxlDecoderSetImageOutBuffer failed";
           SetFailed();
           return;
@@ -173,8 +308,12 @@
         ImageFrame& frame = frame_buffer_cache_[0];
         frame.SetHasAlpha(info.alpha_bits != 0);
         ColorProfileTransform* xform = ColorTransform();
-        constexpr skcms_PixelFormat kSrcFormat = skcms_PixelFormat_RGBA_8888;
-        const skcms_PixelFormat kDstFormat = XformColorFormat();
+        const skcms_PixelFormat kSrcFormat = DecodeToHalfFloat(format_)
+                                                 ? skcms_PixelFormat_RGBA_hhhh
+                                                 : skcms_PixelFormat_RGBA_8888;
+        const skcms_PixelFormat kDstFormat = DecodeToHalfFloat(format_)
+                                                 ? skcms_PixelFormat_RGBA_hhhh
+                                                 : XformColorFormat();
 
         if (xform || (kDstFormat != kSrcFormat) ||
             (frame.PremultiplyAlpha() && frame.HasAlpha())) {
@@ -186,13 +325,16 @@
           const auto* src_profile = xform ? xform->SrcProfile() : nullptr;
           const auto* dst_profile = xform ? xform->DstProfile() : nullptr;
           for (size_t y = 0; y < info.ysize; ++y) {
-            ImageFrame::PixelData* row = frame.GetAddr(0, y);
+            void* row = DecodeToHalfFloat(format_)
+                            ? reinterpret_cast<void*>(frame.GetAddrF16(0, y))
+                            : reinterpret_cast<void*>(frame.GetAddr(0, y));
             bool color_conversion_successful =
                 skcms_Transform(row, kSrcFormat, src_alpha, src_profile, row,
                                 kDstFormat, dst_alpha, dst_profile, info.xsize);
             DCHECK(color_conversion_successful);
           }
         }
+
         frame.SetPixelsChanged(true);
         frame.SetStatus(ImageFrame::kFrameComplete);
         // We do not support animated images yet, so return after the first
@@ -229,4 +371,10 @@
   return false;
 }
 
+void JXLImageDecoder::InitializeNewFrame(size_t index) {
+  auto& buffer = frame_buffer_cache_[index];
+  if (DecodeToHalfFloat(format_))
+    buffer.SetPixelFormat(ImageFrame::PixelFormat::kRGBA_F16);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
index 83f65add..c1cecae 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder.h
@@ -41,12 +41,16 @@
 // This class decodes the JXL image format.
 class PLATFORM_EXPORT JXLImageDecoder final : public ImageDecoder {
  public:
-  JXLImageDecoder(AlphaOption, const ColorBehavior&, size_t max_decoded_bytes);
+  JXLImageDecoder(AlphaOption,
+                  HighBitDepthDecodingOption high_bit_depth_decoding_option,
+                  const ColorBehavior&,
+                  size_t max_decoded_bytes);
 
   ~JXLImageDecoder() override;
 
   // ImageDecoder:
   String FilenameExtension() const override { return "jxl"; }
+  bool ImageIsHighBitDepth() override { return is_hdr_; }
 
   // Returns true if the data in fast_reader begins with
   static bool MatchesJXLSignature(const FastSharedBufferReader& fast_reader);
@@ -59,6 +63,7 @@
     return 1;
   }
   void Decode(size_t) override { Decode(false); }
+  void InitializeNewFrame(size_t) override;
 
   // Decodes the image.  If |only_size| is true, stops decoding after
   // calculating the image size. If decoding fails but there is no more
@@ -66,6 +71,12 @@
   void Decode(bool only_size);
 
   JxlDecoder* dec_ = nullptr;
+
+  // The image is considered to be HDR, such as using PQ or HLG transfer
+  // function in the color space.
+  bool is_hdr_ = false;
+
+  JxlPixelFormat format_ = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
index be6d60b..95f49cd2 100644
--- a/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jxl/jxl_image_decoder_test.cc
@@ -16,10 +16,11 @@
 std::unique_ptr<ImageDecoder> CreateJXLDecoderWithArguments(
     const char* jxl_file,
     ImageDecoder::AlphaOption alpha_option,
-    ImageDecoder::HighBitDepthDecodingOption,
+    ImageDecoder::HighBitDepthDecodingOption high_bit_depth_decoding_option,
     ColorBehavior color_behavior) {
   auto decoder = std::make_unique<JXLImageDecoder>(
-      alpha_option, color_behavior, ImageDecoder::kNoDecodedImageByteLimit);
+      alpha_option, high_bit_depth_decoding_option, color_behavior,
+      ImageDecoder::kNoDecodedImageByteLimit);
   scoped_refptr<SharedBuffer> data = ReadFile(jxl_file);
   EXPECT_FALSE(data->IsEmpty());
   decoder->SetData(data.get(), true);
@@ -28,8 +29,8 @@
 
 std::unique_ptr<ImageDecoder> CreateJXLDecoder() {
   return std::make_unique<JXLImageDecoder>(
-      ImageDecoder::kAlphaNotPremultiplied, ColorBehavior::Tag(),
-      ImageDecoder::kNoDecodedImageByteLimit);
+      ImageDecoder::kAlphaNotPremultiplied, ImageDecoder::kDefaultBitDepth,
+      ColorBehavior::Tag(), ImageDecoder::kNoDecodedImageByteLimit);
 }
 
 std::unique_ptr<ImageDecoder> CreateJXLDecoderWithData(const char* jxl_file) {
@@ -40,7 +41,7 @@
   return decoder;
 }
 
-// The expected_color must match the expected top left pixel.
+// expected_color must match the expected top left pixel
 void TestColorProfile(const char* jxl_file,
                       ColorBehavior color_behavior,
                       SkColor expected_color) {
@@ -61,6 +62,71 @@
   }
 }
 
+// Convert from float16 bits in a uint16_t, to 32-bit float, for testing
+static float FromFloat16(uint16_t a) {
+  // 5 bits exponent
+  int exp = (a >> 10) & 31;
+  // 10 bits fractional part
+  float frac = a & 1023;
+  // 1 bit sign
+  int sign = (a & 32768) ? 1 : 0;
+  bool subnormal = exp == 0;
+  // Infinity and NaN are not supported here.
+  exp -= 15;
+  if (subnormal)
+    exp++;
+  frac /= 1024.0;
+  if (!subnormal)
+    frac++;
+  frac *= std::pow(2, exp);
+  if (sign)
+    frac = -frac;
+  return frac;
+}
+
+// expected_color must match the expected top left pixel
+void TestHDR(const char* jxl_file,
+             ColorBehavior color_behavior,
+             bool expect_f16,
+             float expected_r,
+             float expected_g,
+             float expected_b,
+             float expected_a) {
+  auto decoder = CreateJXLDecoderWithArguments(
+      jxl_file, ImageDecoder::AlphaOption::kAlphaPremultiplied,
+      ImageDecoder::kHighBitDepthToHalfFloat, color_behavior);
+  EXPECT_TRUE(decoder->IsSizeAvailable());
+  EXPECT_EQ(1u, decoder->FrameCount());
+  ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+  ASSERT_TRUE(frame);
+  EXPECT_EQ(ImageFrame::kFrameComplete, frame->GetStatus());
+  EXPECT_FALSE(decoder->Failed());
+  float r, g, b, a;
+  if (expect_f16) {
+    EXPECT_EQ(ImageFrame::kRGBA_F16, frame->GetPixelFormat());
+  } else {
+    EXPECT_EQ(ImageFrame::kN32, frame->GetPixelFormat());
+  }
+  if (ImageFrame::kRGBA_F16 == frame->GetPixelFormat()) {
+    uint64_t first_pixel = *frame->GetAddrF16(0, 0);
+    r = FromFloat16(first_pixel >> 0);
+    g = FromFloat16(first_pixel >> 16);
+    b = FromFloat16(first_pixel >> 32);
+    a = FromFloat16(first_pixel >> 48);
+  } else {
+    uint32_t first_pixel = *frame->GetAddr(0, 0);
+    a = ((first_pixel >> 24) & 255) / 255.0;
+    r = ((first_pixel >> 16) & 255) / 255.0;
+    g = ((first_pixel >> 8) & 255) / 255.0;
+    b = ((first_pixel >> 0) & 255) / 255.0;
+  }
+  constexpr float eps = 0.01;
+  EXPECT_NEAR(expected_r, r, eps);
+  EXPECT_NEAR(expected_g, g, eps);
+  EXPECT_NEAR(expected_b, b, eps);
+  EXPECT_NEAR(expected_a, a, eps);
+}
+
 void TestSize(const char* jxl_file, IntSize expected_size) {
   auto decoder = CreateJXLDecoderWithData(jxl_file);
   EXPECT_TRUE(decoder->IsSizeAvailable());
@@ -350,5 +416,54 @@
                    SkColorSetARGB(255, 0xaf, 0xfe, 0x6b));
 }
 
+TEST(JXLTests, JXLHDRTest) {
+  // PQ tests
+  // PQ values, as expected
+  TestHDR("/images/resources/jxl/pq_gradient_lossy.jxl",
+          ColorBehavior::Ignore(), false, 0.58039218187332153,
+          0.73333334922790527, 0.43921568989753723, 1);
+  // sRGB as expected, but not an exact match
+  TestHDR("/images/resources/jxl/pq_gradient_lossy.jxl",
+          ColorBehavior::TransformToSRGB(), true, -0.1437553, 0.3433017,
+          -0.0376128, 1);
+
+  // linear sRGB as expected.
+  TestHDR("/images/resources/jxl/pq_gradient_lossy.jxl", ColorBehavior::Tag(),
+          true, 0.58039218187332153, 0.73333334922790527, 0.43921568989753723,
+          1);
+
+  // correct, original PQ values
+  TestHDR("/images/resources/jxl/pq_gradient_lossless.jxl",
+          ColorBehavior::Ignore(), false, 0.58039218187332153,
+          0.73725491762161255, 0.45098039507865906, 1);
+  TestHDR("/images/resources/jxl/pq_gradient_lossless.jxl",
+          ColorBehavior::TransformToSRGB(), true, -0.1437553, 0.3433017,
+          -0.0376128, 1);
+  // correct, original PQ values
+  TestHDR("/images/resources/jxl/pq_gradient_lossless.jxl",
+          ColorBehavior::Tag(), true, 0.58056640625, 0.7373046875,
+          0.450927734375, 1);
+
+  // with ICC
+  // clipped linear sRGB, as expected from current JXL implementation
+  TestHDR("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
+          ColorBehavior::Ignore(), false, 0, 0.0930381, 0, 1);
+
+  TestHDR("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
+          ColorBehavior::TransformToSRGB(), false, 0, 0.338623046875, 0, 1);
+  TestHDR("/images/resources/jxl/pq_gradient_icc_lossy.jxl",
+          ColorBehavior::Tag(), false, 0, 0.0930381, 0, 1);
+
+  TestHDR("/images/resources/jxl/pq_gradient_icc_lossless.jxl",
+          ColorBehavior::Ignore(), false, 0.58039218187332153,
+          0.73725491762161255, 0.45098039507865906, 1);
+  TestHDR("/images/resources/jxl/pq_gradient_icc_lossless.jxl",
+          ColorBehavior::TransformToSRGB(), true, -0.1437553, 0.34330175,
+          -0.0376128, 1);
+  TestHDR("/images/resources/jxl/pq_gradient_icc_lossless.jxl",
+          ColorBehavior::Tag(), true, 0.58039218187332153, 0.73725491762161255,
+          0.45098039507865906, 1);
+}
+
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/image-decoders/segment_reader.cc b/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
index cd634b9..c0ccf95 100644
--- a/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
+++ b/third_party/blink/renderer/platform/image-decoders/segment_reader.cc
@@ -272,9 +272,25 @@
 
   RWBuffer::ROIter iter(parkable_image_->rw_buffer_.get(), available_);
 
-  // TODO(thiabaud): It should be possible to do this without the extra copy in
-  // the case that everything is already in a single allocation. Double-check
-  // this and fix it to match ROBufferSegmentReader.
+  if (!iter.HasNext()) {  // No need to copy because the data is contiguous.
+    // We lock here so that we don't get a use-after-free. ParkableImage can
+    // not be parked while it is locked, so the buffer is valid for the whole
+    // lifetime of the SkData. We add the ref so that the ParkableImage has a
+    // longer limetime than the SkData.
+    parkable_image_->Lock();
+    parkable_image_->AddRef();
+    return SkData::MakeWithProc(
+        iter.data(), available_,
+        [](const void* ptr, void* context) -> void {
+          auto* parkable_image = static_cast<ParkableImage*>(context);
+          MutexLocker lock(parkable_image->lock_);
+          parkable_image->Unlock();
+          parkable_image->Release();
+        },
+        parkable_image_.get());
+  }
+
+  // Data is not contiguous so we need to copy.
   return BufferCopyAsSkData(iter, available_);
 }
 
diff --git a/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.h b/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.h
index 92472d0..d118d16 100644
--- a/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.h
+++ b/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller.h
@@ -114,6 +114,8 @@
                            VerifyForwardAnimationIsNotPlayed);
   FRIEND_TEST_ALL_PREFIXES(ElasticOverscrollControllerBezierTest,
                            VerifyInitialStretchDelta);
+  FRIEND_TEST_ALL_PREFIXES(ElasticOverscrollControllerBezierTest,
+                           NoSyntheticEventsOverscroll);
 
   enum State {
     // The initial state, during which the overscroll amount is zero and
diff --git a/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller_bezier_unittest.cc b/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller_bezier_unittest.cc
index 36516ef5..3e2241b 100644
--- a/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller_bezier_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/elastic_overscroll_controller_bezier_unittest.cc
@@ -116,6 +116,20 @@
   SendGestureScrollEnd();
 }
 
+// Verify that synthetic gesture events do not trigger an overscroll.
+TEST_F(ElasticOverscrollControllerBezierTest, NoSyntheticEventsOverscroll) {
+  // Test vertical overscroll.
+  WebGestureEvent event(WebInputEvent::Type::kGestureScrollBegin,
+                        WebInputEvent::kNoModifiers, base::TimeTicks(),
+                        WebGestureDevice::kScrollbar);
+  event.data.scroll_begin.inertial_phase = PhaseState::kNonMomentum;
+  event.data.scroll_begin.synthetic = true;
+  controller_.ObserveGestureEventAndResult(event,
+                                           cc::InputHandlerScrollResult());
+  EXPECT_EQ(controller_.state_,
+            ElasticOverscrollController::State::kStateInactive);
+}
+
 // Verify that ReconcileStretchAndScroll reduces the overscrolled delta.
 TEST_F(ElasticOverscrollControllerBezierTest, ReconcileStretchAndScroll) {
   // Test vertical overscroll.
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
index 8e5a65869..fff96b7 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy.cc
@@ -548,9 +548,14 @@
           type, original_timestamp, WebGestureDevice::kScrollbar,
           position_in_widget, scroll_delta, pointer_result.scroll_units);
 
-  // This will avoid hit testing and directly scroll the scroller with the
-  // provided element_id.
   if (type == WebInputEvent::Type::kGestureScrollBegin) {
+    // Gesture events for scrollbars are considered synthetic because they're
+    // created in response to mouse events. Additionally, synthetic GSB(s) are
+    // ignored by the blink::ElasticOverscrollController.
+    synthetic_gesture_event->data.scroll_begin.synthetic = true;
+
+    // This will avoid hit testing and directly scroll the scroller with the
+    // provided element_id.
     synthetic_gesture_event->data.scroll_begin.scrollable_area_element_id =
         pointer_result.target_scroller.GetStableId();
   }
diff --git a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
index c355ef6..740ee23 100644
--- a/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
+++ b/third_party/blink/renderer/platform/widget/input/input_handler_proxy_unittest.cc
@@ -2515,6 +2515,8 @@
   EXPECT_EQ(2ul, event_queue().size());
   EXPECT_EQ(event_queue()[0]->event().GetType(),
             WebInputEvent::Type::kGestureScrollBegin);
+  EXPECT_TRUE(static_cast<const WebGestureEvent&>(event_queue()[0]->event())
+                  .data.scroll_begin.synthetic);
   EXPECT_EQ(event_queue()[1]->event().GetType(),
             WebInputEvent::Type::kGestureScrollUpdate);
   cc::InputHandlerPointerResult pointer_up_result;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 83bc995..e813dea 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1120,7 +1120,6 @@
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/static-child-becomes-fixedpos.html [ Failure ]
 crbug.com/1079031 virtual/layout_ng_block_frag/fast/multicol/tall-line-in-short-block.html [ Failure ]
 crbug.com/1079031 virtual/layout_ng_block_frag/fast/multicol/transform-with-fixedpos.html [ Failure ]
-crbug.com/1058792 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change.html [ Failure ]
 crbug.com/1058792 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change.html [ Failure ]
 
 ### With LayoutNGFragmentTraversal (and LayoutNGFragmentItem) enabled:
@@ -3032,7 +3031,6 @@
 crbug.com/626703 external/wpt/websockets/stream/tentative/close.any.serviceworker.html?wpt_flags=h2 [ Failure Timeout ]
 crbug.com/626703 external/wpt/websockets/Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2 [ Crash Timeout ]
 crbug.com/626703 virtual/layout-ng-grid/external/wpt/css/css-grid/grid-definition/grid-repeat-max-width-001.html [ Crash Timeout Failure ]
-crbug.com/626703 [ Mac ] external/wpt/html/syntax/xmldecl/xmldecl-1.html [ Pass Timeout ]
 crbug.com/626703 external/wpt/css/css-ruby/ruby-intrinsic-isize-003.html [ Failure ]
 crbug.com/626703 [ Mac10.13 ] external/wpt/selection/contenteditable/modifying-selection-with-primary-mouse-button.tentative.html [ Failure Crash ]
 crbug.com/626703 [ Mac10.15 ] external/wpt/streams/readable-streams/tee.any.html [ Failure Crash ]
@@ -5756,12 +5754,16 @@
 crbug.com/1008483 external/wpt/css/css-transforms/backface-visibility-hidden-005.tentative.html [ Failure ]
 crbug.com/1008483 external/wpt/css/css-transforms/backface-visibility-hidden-animated-002.html [ Failure ]
 crbug.com/1008483 external/wpt/css/css-transforms/preserve-3d-flat-grouping-properties-containing-block.tentative.html [ Failure ]
+crbug.com/1008483 external/wpt/css/css-transforms/3d-rendering-context-and-abspos.html [ Failure ]
+crbug.com/1008483 external/wpt/css/css-transforms/3d-rendering-context-and-fixpos.html [ Failure ]
 
 crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/3d-rendering-context-behavior.tentative.html [ Pass ]
 crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/backface-visibility-hidden-004.tentative.html [ Pass ]
 crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/backface-visibility-hidden-005.tentative.html [ Pass ]
 crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/backface-visibility-hidden-animated-002.html [ Pass ]
 crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/preserve-3d-flat-grouping-properties-containing-block.tentative.html [ Pass ]
+crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/3d-rendering-context-and-abspos.html [ Pass ]
+crbug.com/1008483 virtual/transform-interop/external/wpt/css/css-transforms/3d-rendering-context-and-fixpos.html [ Pass ]
 crbug.com/1008483 virtual/transform-interop/compositing/geometry/require-own-backing-recalc-order.html [ Failure ]
 crbug.com/1008483 virtual/transform-interop/compositing/overflow/scroll-parent-with-non-stacking-context-composited-ancestor.html [ Failure ]
 crbug.com/1008483 virtual/transform-interop/paint/invalidation/stacking-context-lost.html [ Failure ]
@@ -6857,4 +6859,3 @@
 
 # Sheriff 2021-05-07
 crbug.com/1206734 [ Mac10.13 ] external/wpt/websockets/Send-binary-blob.any.worker.html?wpt_flags=h2 [ Timeout ]
-crbug.com/1206772 [ Mac ] external/wpt/html/syntax/xmldecl/xmldecl-3.html [ Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 544df26..24b7078 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1019,7 +1019,8 @@
   },
   {
     "prefix": "force-renderer-accessibility",
-    "bases": ["accessibility/slot-poison.html"],
+    "bases": ["accessibility/slot-poison.html",
+              "accessibility/details-summary-crash.html"],
     "args": ["--force-renderer-accessibility"]
   },
   {
diff --git a/third_party/blink/web_tests/accessibility/details-summary-crash-expected.txt b/third_party/blink/web_tests/accessibility/details-summary-crash-expected.txt
new file mode 100644
index 0000000..15d8618
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/details-summary-crash-expected.txt
@@ -0,0 +1,7 @@
+This test passes if it doesn't crash.
+summary 1
+summary 1
+summary 2
+summary 2
+summary 1
+summary 1
diff --git a/third_party/blink/web_tests/accessibility/details-summary-crash.html b/third_party/blink/web_tests/accessibility/details-summary-crash.html
new file mode 100644
index 0000000..fcd5593
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/details-summary-crash.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+
+This test passes if it doesn't crash.
+
+<script>
+  if (window.testRunner)
+    testRunner.dumpAsText();
+</script>
+
+<script>
+class CustomDetails extends HTMLElement {
+  constructor() {
+    super();
+    const shadowRoot = this.attachShadow({mode: 'open', slotAssignment: 'manual'});
+
+    this.summarySlot = document.createElement('slot');
+    this.summarySlot.id = 'details-summary';
+    shadowRoot.appendChild(this.summarySlot);
+
+    const defaultSummary = document.createElement('summary');
+    defaultSummary.textContent = 'Details';
+    this.summarySlot.appendChild(defaultSummary);
+
+    this.contentSlot = document.createElement('slot');
+    this.contentSlot.id = 'details-content';
+    this.contentSlot.style = `content-visibility:hidden; display:block;`;
+    shadowRoot.appendChild(this.contentSlot);
+
+    const style = document.createElement('style');
+    style.textContent = `
+:host summary {
+  display: list-item;
+  counter-increment: list-item 0;
+  list-style: disclosure-closed inside;
+}
+:host([open]) summary {
+  list-style-type: disclosure-open;
+}
+`;
+    shadowRoot.appendChild(style);
+  }
+
+  connectedCallback() {
+    this.updateOpen();
+    this.updateAssignment();
+    this.addEventListener('DOMNodeInserted', this.updateAssignment);
+    this.addEventListener('DOMNodeRemoved', this.updateAssignment);
+  }
+
+  disconnectedCallback() {
+    this.removeEventListener('DOMNodeInserted', this.updateAssignment);
+    this.removeEventListener('DOMNodeRemoved', this.updateAssignment);
+  }
+
+  attributeChangedCallback(name, oldValue, newValue) {
+    this.updateOpen();
+  }
+
+  static get observedAttributes() {
+    return ['open'];
+  }
+
+  updateOpen() {
+    if (this.hasAttribute('open')) {
+      this.contentSlot.style = ``;
+    } else {
+      this.contentSlot.style = `content-visibility: hidden; display:block`;
+    }
+  }
+
+  updateAssignment() {
+    let summary = null;
+    const content = [];
+
+    for (let child = this.firstChild; child; child = child.nextSibling) {
+      if (!summary && child.tagName === 'SUMMARY') {
+        summary = child;
+      } else {
+        content.push(child);
+      }
+    }
+
+    if (summary)
+      this.summarySlot.assign(summary);
+    if (content.length)
+      this.contentSlot.assign(...content);
+  }
+};
+customElements.define('x-details', CustomDetails);
+</script>
+
+<script>
+function clickOn(element, x, y) {
+  const rect = element.getBoundingClientRect();
+  return new Promise((resolve, reject) => {
+    chrome.gpuBenchmarking.pointerActionSequence([
+        {source: 'mouse',
+         actions: [
+             {name: 'pointerMove', x: rect.x + x, y: rect.y + y},
+             {name: 'pointerDown', x: rect.x + x, y: rect.y + y, button: 0},
+             {name: 'pointerUp', button: 0}]
+        }], resolve);
+  });
+}
+
+const $ = document.querySelector.bind(document);
+
+async function runTests() {
+  $('#dt1').removeChild($('#dt1 > summary'));
+
+  $('#dt1c').removeChild($('#dt1c > summary'));
+  await clickOn($('#dt1c'), 2, 2);
+
+  $('#dt2').removeChild($('#dt2 > summary'));
+
+  $('#dt2c').removeChild($('#dt2c > summary'));
+  await clickOn($('#dt2c'), 2, 2);
+
+  $('#dt3').removeChild($('#dt3 > summary:last-of-type'));
+
+  $('#dt3c').removeChild($('#dt3c > summary:last-of-type'));
+  await clickOn($('#dt3c'), 2, 2);
+
+  $('#dt4').removeChild($('#dt4 > summary'));
+
+  $('#dt4c').removeChild($('#dt4c > summary'));
+  await clickOn($('#dt4c'), 2, 2);
+
+  $('#dt5').removeChild($('#dt5 > summary'));
+
+  $('#dt5c').removeChild($('#dt5c > summary'));
+  await clickOn($('#dt5c'), 2, 2);
+
+  $('#dt6').removeChild($('#dt6 > summary:last-of-type'));
+
+  $('#dt6c').removeChild($('#dt6c > summary:last-of-type'));
+  await clickOn($('#dt6c'), 2, 2);
+
+  testRunner.notifyDone();
+}
+
+testRunner.waitUntilDone();
+
+</script>
+<body onload="runTests()">
+<x-details id="dt1"><summary>summary</summary></x-details>
+<x-details id="dt1c"><summary>summary</summary></x-details>
+<x-details id="dt2"><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt2c"><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt3"><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt3c"><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt4" open><summary>summary</summary></x-details>
+<x-details id="dt4c" open><summary>summary</summary></x-details>
+<x-details id="dt5" open><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt5c" open><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt6" open><summary>summary 1</summary><summary>summary 2</summary></x-details>
+<x-details id="dt6c" open><summary>summary 1</summary><summary>summary 2</summary></x-details>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-rendering-context-and-abspos.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-rendering-context-and-abspos.html
new file mode 100644
index 0000000..c5eef46
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-rendering-context-and-abspos.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<title>CSS Test (Transforms): 3D Rendering Context following DOM Tree (absolute positioning)</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#3d-rendering-contexts">
+<meta name="assert" content="Absolutely positioned elements participate in 3D Rendering Contexts based on their parent, not their containing block.">
+<link rel="match" href="reference/green.html">
+
+<style>
+
+div {
+  width: 100px;
+  height: 100px;
+}
+
+.cb {
+  transform-style: preserve-3d;
+  background: red;
+  position: relative;
+}
+
+.parent {
+}
+
+.abspos {
+  position: absolute;
+  top: 0;
+  left: 0;
+  /* Since this element is not in the 3D Rendering Context, this translation
+     does not put it above the <div class="sibling">. */
+  transform: translateZ(20px);
+  background: red;
+}
+
+.sibling {
+  position: absolute;
+  top: 0;
+  left: 0;
+  transform: translateZ(10px);
+  background: green;
+}
+
+</style>
+
+<p>Pass if there is NO red below:</p>
+
+<div class="cb">
+  <div class="parent">
+    <div class="abspos">
+    </div>
+  </div>
+  <div class="sibling">
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-rendering-context-and-fixpos.html b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-rendering-context-and-fixpos.html
new file mode 100644
index 0000000..e763e8b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/3d-rendering-context-and-fixpos.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<title>CSS Test (Transforms): 3D Rendering Context following DOM Tree (fixed positioning)</title>
+<link rel="author" title="L. David Baron" href="https://dbaron.org/">
+<link rel="author" title="Google" href="http://www.google.com/">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#3d-rendering-contexts">
+<meta name="assert" content="Fixed positioned elements participate in 3D Rendering Contexts based on their parent, not their containing block.">
+<link rel="match" href="reference/green.html">
+
+<style>
+
+div {
+  width: 100px;
+  height: 100px;
+}
+
+.cb {
+  transform-style: preserve-3d;
+  transform: translateX(0);
+  background: red;
+  position: relative;
+}
+
+.parent {
+}
+
+.abspos {
+  position: fixed;
+  top: 0;
+  left: 0;
+  /* Since this element is not in the 3D Rendering Context, this translation
+     does not put it above the <div class="sibling">. */
+  transform: translateZ(20px);
+  background: red;
+}
+
+.sibling {
+  position: absolute;
+  top: 0;
+  left: 0;
+  transform: translateZ(10px);
+  background: green;
+}
+
+</style>
+
+<p>Pass if there is NO red below:</p>
+
+<div class="cb">
+  <div class="parent">
+    <div class="abspos">
+    </div>
+  </div>
+  <div class="sibling">
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/syntax/xmldecl/xmldecl-3.html b/third_party/blink/web_tests/external/wpt/html/syntax/xmldecl/xmldecl-3.html
index 4b7aea0d..a9f179d 100644
--- a/third_party/blink/web_tests/external/wpt/html/syntax/xmldecl/xmldecl-3.html
+++ b/third_party/blink/web_tests/external/wpt/html/syntax/xmldecl/xmldecl-3.html
@@ -1,5 +1,6 @@
 <!doctype html>
 <meta charset="windows-1252">
+<meta name="timeout" content="long">
 <title>Bogo-XML declaration</title>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
diff --git a/third_party/blink/web_tests/images/resources/jxl/pq_gradient_icc_lossless.jxl b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_icc_lossless.jxl
new file mode 100644
index 0000000..c420e6e
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_icc_lossless.jxl
Binary files differ
diff --git a/third_party/blink/web_tests/images/resources/jxl/pq_gradient_icc_lossy.jxl b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_icc_lossy.jxl
new file mode 100644
index 0000000..7d7b9ca
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_icc_lossy.jxl
Binary files differ
diff --git a/third_party/blink/web_tests/images/resources/jxl/pq_gradient_lossless.jxl b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_lossless.jxl
new file mode 100644
index 0000000..aa138d16
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_lossless.jxl
Binary files differ
diff --git a/third_party/blink/web_tests/images/resources/jxl/pq_gradient_lossy.jxl b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_lossy.jxl
new file mode 100644
index 0000000..548408d
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/jxl/pq_gradient_lossy.jxl
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
new file mode 100644
index 0000000..1c7811bd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.15/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
new file mode 100644
index 0000000..cf609fc
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.15/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png b/third_party/blink/web_tests/platform/win/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-expected.png
new file mode 100644
index 0000000..2b2083a9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/layout_ng_block_frag/fast/multicol/vertical-lr/composited-relpos-overlapping-will-change-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 5fb151c..2b7f7b8 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/app-history/navigate/navigate-in-detached-window.html b/third_party/blink/web_tests/wpt_internal/app-history/navigate/navigate-in-detached-window.html
new file mode 100644
index 0000000..578c833
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/app-history/navigate/navigate-in-detached-window.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<iframe id="i"></iframe>
+<script>
+promise_test(async t => {
+  let iframe_constructor = i.contentWindow.DOMException;
+  let i_win = i.contentWindow;
+  i.remove();
+  await promise_rejects_dom(t, 'InvalidStateError', iframe_constructor, i_win.appHistory.navigate("#"));
+}, "navigate() in a detached window");
+</script>
diff --git a/third_party/crashpad/README.chromium b/third_party/crashpad/README.chromium
index cd45852..1dc459c 100644
--- a/third_party/crashpad/README.chromium
+++ b/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@
 Short Name: crashpad
 URL: https://crashpad.chromium.org/
 Version: unknown
-Revision: 66eb800996ec4f20048959efb0ecfb6bc67a2e7a
+Revision: d9bc7cf06aef74e928f9afc3dee33b60121b9c73
 License: Apache 2.0
 License File: crashpad/LICENSE
 Security Critical: yes
diff --git a/third_party/crashpad/crashpad/DEPS b/third_party/crashpad/crashpad/DEPS
index e13b5665..db78db2 100644
--- a/third_party/crashpad/crashpad/DEPS
+++ b/third_party/crashpad/crashpad/DEPS
@@ -33,7 +33,7 @@
   },
   'crashpad/third_party/googletest/googletest':
       Var('chromium_git') + '/external/github.com/google/googletest@' +
-      'e589a337170554c48bc658cc857cf15080c9eacc',
+      '11da093e0477185dbd78abaaa9f99db15be498d0',
   'crashpad/third_party/gyp/gyp':
       Var('chromium_git') + '/external/gyp@' +
       '8bee09f4a57807136593ddc906b0b213c21f9014',
@@ -42,7 +42,7 @@
       '7bde79cc274d06451bf65ae82c012a5d3e476b5a',
   'crashpad/third_party/mini_chromium/mini_chromium':
       Var('chromium_git') + '/chromium/mini_chromium@' +
-      '329ca82f73a592d832e79334bed842fba85b9fdd',
+      'ccb198907cecf072d8f5b2543d4d348e834a298a',
   'crashpad/third_party/libfuzzer/src':
       Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
       'fda403cf93ecb8792cb1d061564d89a6553ca020',
diff --git a/third_party/crashpad/crashpad/build/install_linux_sysroot.py b/third_party/crashpad/crashpad/build/install_linux_sysroot.py
index 4e89837c..c05c4d5 100755
--- a/third_party/crashpad/crashpad/build/install_linux_sysroot.py
+++ b/third_party/crashpad/crashpad/build/install_linux_sysroot.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 # Copyright 2018 The Crashpad Authors. All rights reserved.
 #
@@ -21,7 +21,7 @@
 import shutil
 import subprocess
 import sys
-import urllib2
+import urllib.request
 
 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 
@@ -45,16 +45,16 @@
             if s.read() == url:
                 return
 
-    print 'Installing Debian root image from %s' % url
+    print('Installing Debian root image from %s' % url)
 
     if os.path.isdir(sysroot):
         shutil.rmtree(sysroot)
     os.mkdir(sysroot)
     tarball = os.path.join(sysroot, FILENAME)
-    print 'Downloading %s' % url
+    print('Downloading %s' % url)
 
     for _ in range(3):
-        response = urllib2.urlopen(url)
+        response = urllib.request.urlopen(url)
         with open(tarball, 'wb') as f:
             f.write(response.read())
         break
diff --git a/third_party/crashpad/crashpad/client/BUILD.gn b/third_party/crashpad/crashpad/client/BUILD.gn
index e26b965..a53ac46 100644
--- a/third_party/crashpad/crashpad/client/BUILD.gn
+++ b/third_party/crashpad/crashpad/client/BUILD.gn
@@ -16,27 +16,14 @@
 
 crashpad_static_library("client") {
   sources = [
-    "annotation.cc",
-    "annotation.h",
-    "annotation_list.cc",
-    "annotation_list.h",
-    "crash_report_database.cc",
-    "crash_report_database.h",
     "crashpad_client.h",
-    "crashpad_info.cc",
-    "crashpad_info.h",
     "prune_crash_reports.cc",
     "prune_crash_reports.h",
-    "settings.cc",
-    "settings.h",
-    "simple_address_range_bag.h",
-    "simple_string_dictionary.h",
     "simulate_crash.h",
   ]
 
   if (crashpad_is_mac) {
     sources += [
-      "crash_report_database_mac.mm",
       "crashpad_client_mac.cc",
       "simulate_crash_mac.cc",
       "simulate_crash_mac.h",
@@ -45,7 +32,6 @@
 
   if (crashpad_is_ios) {
     sources += [
-      "crash_report_database_mac.mm",
       "crashpad_client_ios.cc",
       "simulate_crash_ios.h",
     ]
@@ -62,13 +48,11 @@
     sources += [
       "client_argv_handling.cc",
       "client_argv_handling.h",
-      "crashpad_info_note.S",
     ]
   }
 
   if (crashpad_is_win) {
     sources += [
-      "crash_report_database_win.cc",
       "crashpad_client_win.cc",
       "simulate_crash_win.h",
     ]
@@ -78,18 +62,18 @@
     sources += [ "crashpad_client_fuchsia.cc" ]
   }
 
-  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
-    sources += [ "crash_report_database_generic.cc" ]
-  }
-
   public_configs = [ "..:crashpad_config" ]
 
   public_deps = [
+    ":common",
     "../third_party/mini_chromium:base",
     "../util",
   ]
 
-  deps = [ "../third_party/mini_chromium:chromeos_buildflags" ]
+  deps = [
+    ":common",
+    "../third_party/mini_chromium:chromeos_buildflags",
+  ]
 
   if (crashpad_is_win) {
     libs = [ "rpcrt4.lib" ]
@@ -99,6 +83,7 @@
   # TODO(justincohen): Temporary dependency to bring up the iOS client.
   if (crashpad_is_ios) {
     deps += [
+      "../handler:common",
       "../minidump",
       "../snapshot",
     ]
@@ -116,6 +101,43 @@
   }
 }
 
+static_library("common") {
+  sources = [
+    "annotation.cc",
+    "annotation.h",
+    "annotation_list.cc",
+    "annotation_list.h",
+    "crash_report_database.cc",
+    "crash_report_database.h",
+    "crashpad_info.cc",
+    "crashpad_info.h",
+    "settings.cc",
+    "settings.h",
+    "simple_address_range_bag.h",
+    "simple_string_dictionary.h",
+  ]
+
+  if (crashpad_is_mac || crashpad_is_ios) {
+    sources += [ "crash_report_database_mac.mm" ]
+  }
+  if (crashpad_is_win) {
+    sources += [ "crash_report_database_win.cc" ]
+  }
+  if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+    sources += [
+      "crash_report_database_generic.cc",
+      "crashpad_info_note.S",
+    ]
+  }
+
+  public_configs = [ "..:crashpad_config" ]
+  public_deps = [
+    "../third_party/mini_chromium:base",
+    "../util",
+  ]
+  deps = [ "../util" ]
+}
+
 source_set("client_test") {
   testonly = true
 
diff --git a/third_party/crashpad/crashpad/client/settings.cc b/third_party/crashpad/crashpad/client/settings.cc
index 0aa525f1..3855b9b 100644
--- a/third_party/crashpad/crashpad/client/settings.cc
+++ b/third_party/crashpad/crashpad/client/settings.cc
@@ -207,8 +207,11 @@
   return ScopedLockedFileHandle(scoped.release(), base::FilePath());
 #else
   if (scoped.is_valid()) {
-    if (!LoggingLockFile(scoped.get(), locking))
+    if (LoggingLockFile(
+            scoped.get(), locking, FileLockingBlocking::kBlocking) !=
+        FileLockingResult::kSuccess) {
       scoped.reset();
+    }
   }
   return ScopedLockedFileHandle(scoped.release());
 #endif
diff --git a/third_party/crashpad/crashpad/handler/BUILD.gn b/third_party/crashpad/crashpad/handler/BUILD.gn
index 1d5951e..a697b33 100644
--- a/third_party/crashpad/crashpad/handler/BUILD.gn
+++ b/third_party/crashpad/crashpad/handler/BUILD.gn
@@ -16,12 +16,8 @@
 
 static_library("handler") {
   sources = [
-    "crash_report_upload_thread.cc",
-    "crash_report_upload_thread.h",
     "handler_main.cc",
     "handler_main.h",
-    "minidump_to_upload_parameters.cc",
-    "minidump_to_upload_parameters.h",
     "prune_crash_reports_thread.cc",
     "prune_crash_reports_thread.h",
     "user_stream_data_source.cc",
@@ -34,8 +30,6 @@
       "mac/crash_report_exception_handler.h",
       "mac/exception_handler_server.cc",
       "mac/exception_handler_server.h",
-      "mac/file_limit_annotation.cc",
-      "mac/file_limit_annotation.h",
     ]
   }
 
@@ -67,12 +61,14 @@
   public_configs = [ "..:crashpad_config" ]
 
   public_deps = [
+    ":common",
     "../client",
     "../third_party/mini_chromium:base",
     "../util",
   ]
 
   deps = [
+    ":common",
     "../minidump",
     "../snapshot",
     "../third_party/mini_chromium:chromeos_buildflags",
@@ -94,6 +90,34 @@
   }
 }
 
+static_library("common") {
+  sources = [
+    "crash_report_upload_thread.cc",
+    "crash_report_upload_thread.h",
+    "minidump_to_upload_parameters.cc",
+    "minidump_to_upload_parameters.h",
+  ]
+  if (crashpad_is_mac || crashpad_is_ios) {
+    sources += [
+      "mac/file_limit_annotation.cc",
+      "mac/file_limit_annotation.h",
+    ]
+  }
+  public_configs = [ "..:crashpad_config" ]
+  public_deps = [
+    "../third_party/mini_chromium:base",
+    "../util",
+  ]
+  deps = [
+    "../client:common",
+    "../snapshot",
+    "../util",
+  ]
+  if (crashpad_is_win) {
+    cflags = [ "/wd4201" ]  # nonstandard extension used : nameless struct/union
+  }
+}
+
 source_set("handler_test") {
   testonly = true
 
diff --git a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
index 6ca1a19..8a25aee 100644
--- a/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
+++ b/third_party/crashpad/crashpad/minidump/minidump_misc_info_writer_test.cc
@@ -193,6 +193,12 @@
   EXPECT_EQ(observed_misc_info.ProcessCookie, expected_misc_info.ProcessCookie);
 }
 
+// Bypass restrictions on conversion of compile-time constants (added for
+// https://crbug.com/1189439).
+const char* Crbug1189439Cast(const char str[]) {
+  return str;
+}
+
 TEST(MinidumpMiscInfoWriter, Empty) {
   MinidumpFileWriter minidump_file_writer;
   auto misc_info_writer = std::make_unique<MinidumpMiscInfoWriter>();
@@ -399,7 +405,8 @@
   expected.Flags1 = MINIDUMP_MISC3_TIMEZONE;
   expected.TimeZoneId = kTimeZoneId;
   expected.TimeZone.Bias = kBias;
-  std::u16string standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
+  std::u16string standard_name_utf16 =
+      base::UTF8ToUTF16(Crbug1189439Cast(kStandardName));
   c16lcpy(AsU16CStr(expected.TimeZone.StandardName),
           standard_name_utf16.c_str(),
           base::size(expected.TimeZone.StandardName));
@@ -407,7 +414,8 @@
          &kStandardDate,
          sizeof(expected.TimeZone.StandardDate));
   expected.TimeZone.StandardBias = kStandardBias;
-  std::u16string daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
+  std::u16string daylight_name_utf16 =
+      base::UTF8ToUTF16(Crbug1189439Cast(kDaylightName));
   c16lcpy(AsU16CStr(expected.TimeZone.DaylightName),
           daylight_name_utf16.c_str(),
           base::size(expected.TimeZone.DaylightName));
@@ -499,12 +507,13 @@
 
   MINIDUMP_MISC_INFO_4 expected = {};
   expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING;
-  std::u16string build_string_utf16 = base::UTF8ToUTF16(kBuildString);
+  std::u16string build_string_utf16 =
+      base::UTF8ToUTF16(Crbug1189439Cast(kBuildString));
   c16lcpy(AsU16CStr(expected.BuildString),
           build_string_utf16.c_str(),
           base::size(expected.BuildString));
   std::u16string debug_build_string_utf16 =
-      base::UTF8ToUTF16(kDebugBuildString);
+      base::UTF8ToUTF16(Crbug1189439Cast(kDebugBuildString));
   c16lcpy(AsU16CStr(expected.DbgBldStr),
           debug_build_string_utf16.c_str(),
           base::size(expected.DbgBldStr));
@@ -681,7 +690,8 @@
   expected.ProtectedProcess = kProtectedProcess;
   expected.TimeZoneId = kTimeZoneId;
   expected.TimeZone.Bias = kBias;
-  std::u16string standard_name_utf16 = base::UTF8ToUTF16(kStandardName);
+  std::u16string standard_name_utf16 =
+      base::UTF8ToUTF16(Crbug1189439Cast(kStandardName));
   c16lcpy(AsU16CStr(expected.TimeZone.StandardName),
           standard_name_utf16.c_str(),
           base::size(expected.TimeZone.StandardName));
@@ -689,7 +699,8 @@
          &kSystemTimeZero,
          sizeof(expected.TimeZone.StandardDate));
   expected.TimeZone.StandardBias = kStandardBias;
-  std::u16string daylight_name_utf16 = base::UTF8ToUTF16(kDaylightName);
+  std::u16string daylight_name_utf16 =
+      base::UTF8ToUTF16(Crbug1189439Cast(kDaylightName));
   c16lcpy(AsU16CStr(expected.TimeZone.DaylightName),
           daylight_name_utf16.c_str(),
           base::size(expected.TimeZone.DaylightName));
@@ -697,12 +708,13 @@
          &kSystemTimeZero,
          sizeof(expected.TimeZone.DaylightDate));
   expected.TimeZone.DaylightBias = kDaylightBias;
-  std::u16string build_string_utf16 = base::UTF8ToUTF16(kBuildString);
+  std::u16string build_string_utf16 =
+      base::UTF8ToUTF16(Crbug1189439Cast(kBuildString));
   c16lcpy(AsU16CStr(expected.BuildString),
           build_string_utf16.c_str(),
           base::size(expected.BuildString));
   std::u16string debug_build_string_utf16 =
-      base::UTF8ToUTF16(kDebugBuildString);
+      base::UTF8ToUTF16(Crbug1189439Cast(kDebugBuildString));
   c16lcpy(AsU16CStr(expected.DbgBldStr),
           debug_build_string_utf16.c_str(),
           base::size(expected.DbgBldStr));
@@ -723,9 +735,9 @@
   static constexpr char kMachineDescription[] =
       "MacBookPro11,3 (Mac-2BD1B31983FE1663)";
   std::u16string standard_time_name_utf16 =
-      base::UTF8ToUTF16(kStandardTimeName);
+      base::UTF8ToUTF16(Crbug1189439Cast(kStandardTimeName));
   std::u16string daylight_time_name_utf16 =
-      base::UTF8ToUTF16(kDaylightTimeName);
+      base::UTF8ToUTF16(Crbug1189439Cast(kDaylightTimeName));
   std::u16string build_string_utf16 = base::UTF8ToUTF16(
       std::string(kOSVersionFull) + "; " + kMachineDescription);
   std::string debug_build_string = internal::MinidumpMiscInfoDebugBuildString();
diff --git a/third_party/crashpad/crashpad/snapshot/BUILD.gn b/third_party/crashpad/crashpad/snapshot/BUILD.gn
index e387e3cb..b9ee959 100644
--- a/third_party/crashpad/crashpad/snapshot/BUILD.gn
+++ b/third_party/crashpad/crashpad/snapshot/BUILD.gn
@@ -242,18 +242,15 @@
   public_deps = [ ":context" ]
 
   deps = [
-    "../client",
+    "../client:common",
     "../compat",
     "../minidump:format",
     "../third_party/mini_chromium:base",
     "../util",
   ]
 
-  if (crashpad_is_ios) {
-    deps -= [ "../client" ]
-  }
-
   if (crashpad_is_win) {
+    deps += [ "../client" ]
     cflags = [ "/wd4201" ]  # nonstandard extension used : nameless struct/union
     libs = [ "powrprof.lib" ]
   }
@@ -423,7 +420,7 @@
 
   deps = [
     ":test_support",
-    "../client",
+    "../client:common",
     "../compat",
     "../minidump:format",
     "../test",
@@ -432,6 +429,10 @@
     "../util",
   ]
 
+  if (crashpad_is_win) {
+    deps += [ "../client" ]
+  }
+
   data_deps = [
     ":crashpad_snapshot_test_module",
     ":crashpad_snapshot_test_module_large",
diff --git a/third_party/crashpad/crashpad/third_party/googletest/BUILD.gn b/third_party/crashpad/crashpad/third_party/googletest/BUILD.gn
index f51db0e..ee3485d9 100644
--- a/third_party/crashpad/crashpad/third_party/googletest/BUILD.gn
+++ b/third_party/crashpad/crashpad/third_party/googletest/BUILD.gn
@@ -113,7 +113,6 @@
       "$googletest_dir/googletest/test/googletest-message-test.cc",
       "$googletest_dir/googletest/test/googletest-options-test.cc",
       "$googletest_dir/googletest/test/googletest-port-test.cc",
-      "$googletest_dir/googletest/test/googletest-printers-test.cc",
       "$googletest_dir/googletest/test/googletest-test-part-test.cc",
       "$googletest_dir/googletest/test/gtest-typed-test2_test.cc",
       "$googletest_dir/googletest/test/gtest-typed-test_test.cc",
@@ -126,6 +125,15 @@
       "$googletest_dir/googletest/test/production.cc",
       "$googletest_dir/googletest/test/production.h",
     ]
+
+    if (!crashpad_is_win) {
+      # TODO: Fix error C2015: too many characters in constant. As this error
+      # cannot be suppressed, removing the test on Windows. See
+      # https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2855854/2
+      # for details.
+      sources +=
+          [ "$googletest_dir/googletest/test/googletest-printers-test.cc" ]
+    }
     configs -= [ "$mini_chromium_dir/build/config:Wexit_time_destructors" ]
     configs += [ ":googletest_private_config" ]
     deps = [
@@ -259,7 +267,6 @@
       "$googletest_dir/googlemock/include/gmock/gmock-actions.h",
       "$googletest_dir/googlemock/include/gmock/gmock-cardinalities.h",
       "$googletest_dir/googlemock/include/gmock/gmock-function-mocker.h",
-      "$googletest_dir/googlemock/include/gmock/gmock-generated-actions.h",
       "$googletest_dir/googlemock/include/gmock/gmock-matchers.h",
       "$googletest_dir/googlemock/include/gmock/gmock-more-actions.h",
       "$googletest_dir/googlemock/include/gmock/gmock-more-matchers.h",
@@ -302,8 +309,6 @@
       "$googletest_dir/googlemock/test/gmock-actions_test.cc",
       "$googletest_dir/googlemock/test/gmock-cardinalities_test.cc",
       "$googletest_dir/googlemock/test/gmock-function-mocker_test.cc",
-      "$googletest_dir/googlemock/test/gmock-generated-actions_test.cc",
-      "$googletest_dir/googlemock/test/gmock-generated-matchers_test.cc",
       "$googletest_dir/googlemock/test/gmock-internal-utils_test.cc",
       "$googletest_dir/googlemock/test/gmock-matchers_test.cc",
       "$googletest_dir/googlemock/test/gmock-more-actions_test.cc",
@@ -331,6 +336,12 @@
         "-Wno-unused-private-field",
       ]
     }
+
+    if (crashpad_is_win) {
+      # TODO: Correct SDK in vc\tools\msvc\14.14.26428\include\functional
+      cflags = [ "/wd4789" ]  # VAR of size N bytes will be overrun
+    }
+
   }
 
   test("gmock_link_test") {
diff --git a/third_party/crashpad/crashpad/util/BUILD.gn b/third_party/crashpad/crashpad/util/BUILD.gn
index 45e069e..2f34d37 100644
--- a/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/third_party/crashpad/crashpad/util/BUILD.gn
@@ -384,6 +384,10 @@
       "ios/exception_processor.mm",
       "ios/ios_system_data_collector.h",
       "ios/ios_system_data_collector.mm",
+      "ios/raw_logging.cc",
+      "ios/raw_logging.h",
+      "ios/scoped_vm_read.cc",
+      "ios/scoped_vm_read.h",
     ]
   }
 
@@ -787,7 +791,10 @@
   }
 
   if (crashpad_is_ios) {
-    sources += [ "ios/exception_processor_test.mm" ]
+    sources += [
+      "ios/exception_processor_test.mm",
+      "ios/scoped_vm_read_test.cc",
+    ]
 
     sources -= [
       "process/process_memory_range_test.cc",
diff --git a/third_party/crashpad/crashpad/util/file/file_io.h b/third_party/crashpad/crashpad/util/file/file_io.h
index 1f502ad..3b6d767 100644
--- a/third_party/crashpad/crashpad/util/file/file_io.h
+++ b/third_party/crashpad/crashpad/util/file/file_io.h
@@ -96,6 +96,28 @@
   kExclusive,
 };
 
+//! \brief Determines if LoggingLockFile will block.
+enum class FileLockingBlocking : bool {
+  //! \brief Block until the lock is acquired
+  kBlocking = false,
+
+  //! \brief Do not block when attempting to acquire a lock.
+  kNonBlocking = true,
+};
+
+//! \brief The return value for LoggingLockFile.
+enum class FileLockingResult : int {
+  //! \brief The lock was acquired successfully.
+  kSuccess,
+
+  //! \brief In non-blocking mode only, the file was already locked. Locking
+  //!     would block, so the lock was not acquired.
+  kWouldBlock,
+
+  //! \brief The lock was not acquired.
+  kFailure,
+};
+
 //! \brief Determines the FileHandle that StdioFileHandle() returns.
 enum class StdioStream {
   //! \brief Standard input, or `stdin`.
@@ -443,8 +465,9 @@
 //!     Windows.
 //!
 //! It is an error to attempt to lock a file in a different mode when it is
-//! already locked. This call will block until the lock is acquired. The
-//! entire file is locked.
+//! already locked. This call will block until the lock is acquired unless
+//! \a blocking is FileLockingBlocking::kNonBlocking. The entire file is
+//! locked.
 //!
 //! If \a locking is FileLocking::kShared, \a file must have been opened for
 //! reading, and if it's FileLocking::kExclusive, \a file must have been opened
@@ -453,9 +476,16 @@
 //! \param[in] file The open file handle to be locked.
 //! \param[in] locking Controls whether the lock is a shared reader lock, or an
 //!     exclusive writer lock.
+//! \param[in] blocking Controls whether a locked file will result in blocking
+//!     or an immediate return.
 //!
-//! \return `true` on success, or `false` and a message will be logged.
-bool LoggingLockFile(FileHandle file, FileLocking locking);
+//! \return kSuccess if a lock is acquired. If a lock could not be acquired
+//!     because \a blocking is FileLockingBlocking::kNonBlocking and acquiring
+//!     the lock would block, returns kWouldBlock. If a lock fails for any other
+//!     reason, returns kFailure and a message will be logged.
+FileLockingResult LoggingLockFile(FileHandle file,
+                                  FileLocking locking,
+                                  FileLockingBlocking blocking);
 
 //! \brief Unlocks a file previously locked with LoggingLockFile().
 //!
diff --git a/third_party/crashpad/crashpad/util/file/file_io_posix.cc b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
index 2baa811..119a2fc 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_posix.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_posix.cc
@@ -210,11 +210,22 @@
 
 #if !defined(OS_FUCHSIA)
 
-bool LoggingLockFile(FileHandle file, FileLocking locking) {
+FileLockingResult LoggingLockFile(FileHandle file,
+                                  FileLocking locking,
+                                  FileLockingBlocking blocking) {
   int operation = (locking == FileLocking::kShared) ? LOCK_SH : LOCK_EX;
+  if (blocking == FileLockingBlocking::kNonBlocking)
+    operation |= LOCK_NB;
+
   int rv = HANDLE_EINTR(flock(file, operation));
-  PLOG_IF(ERROR, rv != 0) << "flock";
-  return rv == 0;
+  if (rv != 0) {
+    if (errno == EWOULDBLOCK) {
+      return FileLockingResult::kWouldBlock;
+    }
+    PLOG(ERROR) << "flock";
+    return FileLockingResult::kFailure;
+  }
+  return FileLockingResult::kSuccess;
 }
 
 bool LoggingUnlockFile(FileHandle file) {
diff --git a/third_party/crashpad/crashpad/util/file/file_io_test.cc b/third_party/crashpad/crashpad/util/file/file_io_test.cc
index 52bb6ee..553d9d6 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_test.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_test.cc
@@ -561,11 +561,17 @@
 
   auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));
   ASSERT_NE(handle1, kInvalidFileHandle);
-  EXPECT_TRUE(LoggingLockFile(handle1.get(), FileLocking::kShared));
+  EXPECT_EQ(
+      LoggingLockFile(
+          handle1.get(), FileLocking::kShared, FileLockingBlocking::kBlocking),
+      FileLockingResult::kSuccess);
 
   auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(shared_file));
   ASSERT_NE(handle1, kInvalidFileHandle);
-  EXPECT_TRUE(LoggingLockFile(handle2.get(), FileLocking::kShared));
+  EXPECT_EQ(
+      LoggingLockFile(
+          handle2.get(), FileLocking::kShared, FileLockingBlocking::kBlocking),
+      FileLockingResult::kSuccess);
 
   EXPECT_TRUE(LoggingUnlockFile(handle1.get()));
   EXPECT_TRUE(LoggingUnlockFile(handle2.get()));
@@ -590,7 +596,9 @@
  private:
   void ThreadMain() override {
     for (int i = 0; i < iterations_; ++i) {
-      EXPECT_TRUE(LoggingLockFile(file_.get(), lock_type_));
+      EXPECT_EQ(LoggingLockFile(
+                    file_.get(), lock_type_, FileLockingBlocking::kBlocking),
+                FileLockingResult::kSuccess);
       base::subtle::NoBarrier_AtomicIncrement(actual_iterations_, 1);
       EXPECT_TRUE(LoggingUnlockFile(file_.get()));
     }
@@ -624,7 +632,9 @@
                                     FileWriteMode::kReuseOrCreate,
                                     FilePermissions::kOwnerOnly));
   ASSERT_NE(initial, kInvalidFileHandle);
-  ASSERT_TRUE(LoggingLockFile(initial.get(), main_lock));
+  EXPECT_EQ(
+      LoggingLockFile(initial.get(), main_lock, FileLockingBlocking::kBlocking),
+      FileLockingResult::kSuccess);
 
   base::subtle::Atomic32 actual_iterations = 0;
 
@@ -671,6 +681,43 @@
   LockingTest(FileLocking::kShared, FileLocking::kExclusive);
 }
 
+TEST(FileIO, ExclusiveVsExclusivesNonBlocking) {
+  ScopedTempDir temp_dir;
+  base::FilePath exclusive_file =
+      temp_dir.path().Append(FILE_PATH_LITERAL("file_to_lock"));
+
+  {
+    // Create an empty file to lock.
+    ScopedFileHandle create(
+        LoggingOpenFileForWrite(exclusive_file,
+                                FileWriteMode::kCreateOrFail,
+                                FilePermissions::kOwnerOnly));
+  }
+
+  auto handle1 = ScopedFileHandle(LoggingOpenFileForRead(exclusive_file));
+  ASSERT_NE(handle1, kInvalidFileHandle);
+  EXPECT_EQ(LoggingLockFile(handle1.get(),
+                            FileLocking::kExclusive,
+                            FileLockingBlocking::kBlocking),
+            FileLockingResult::kSuccess);
+
+  // Non-blocking lock should fail.
+  auto handle2 = ScopedFileHandle(LoggingOpenFileForRead(exclusive_file));
+  ASSERT_NE(handle2, kInvalidFileHandle);
+  EXPECT_EQ(LoggingLockFile(handle2.get(),
+                            FileLocking::kExclusive,
+                            FileLockingBlocking::kNonBlocking),
+            FileLockingResult::kWouldBlock);
+
+  // After unlocking, non-blocking lock should succeed.
+  EXPECT_TRUE(LoggingUnlockFile(handle1.get()));
+  EXPECT_EQ(LoggingLockFile(handle2.get(),
+                            FileLocking::kExclusive,
+                            FileLockingBlocking::kNonBlocking),
+            FileLockingResult::kSuccess);
+  EXPECT_TRUE(LoggingUnlockFile(handle2.get()));
+}
+
 #endif  // !OS_FUCHSIA
 
 TEST(FileIO, FileSizeByHandle) {
diff --git a/third_party/crashpad/crashpad/util/file/file_io_win.cc b/third_party/crashpad/crashpad/util/file/file_io_win.cc
index 8586619..93a1229 100644
--- a/third_party/crashpad/crashpad/util/file/file_io_win.cc
+++ b/third_party/crashpad/crashpad/util/file/file_io_win.cc
@@ -182,19 +182,26 @@
   return file;
 }
 
-bool LoggingLockFile(FileHandle file, FileLocking locking) {
+FileLockingResult LoggingLockFile(FileHandle file,
+                                  FileLocking locking,
+                                  FileLockingBlocking blocking) {
   DWORD flags =
       (locking == FileLocking::kExclusive) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
+  if (blocking == FileLockingBlocking::kNonBlocking)
+    flags |= LOCKFILE_FAIL_IMMEDIATELY;
 
   // Note that the `Offset` fields of overlapped indicate the start location for
   // locking (beginning of file in this case), and `hEvent` must be also be set
   // to 0.
   OVERLAPPED overlapped = {0};
   if (!LockFileEx(file, flags, 0, MAXDWORD, MAXDWORD, &overlapped)) {
+    if (GetLastError() == ERROR_LOCK_VIOLATION) {
+      return FileLockingResult::kWouldBlock;
+    }
     PLOG(ERROR) << "LockFileEx";
-    return false;
+    return FileLockingResult::kFailure;
   }
-  return true;
+  return FileLockingResult::kSuccess;
 }
 
 bool LoggingUnlockFile(FileHandle file) {
diff --git a/third_party/crashpad/crashpad/util/ios/raw_logging.cc b/third_party/crashpad/crashpad/util/ios/raw_logging.cc
new file mode 100644
index 0000000..f037c45
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/ios/raw_logging.cc
@@ -0,0 +1,66 @@
+// Copyright 2021 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/ios/raw_logging.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+
+namespace crashpad {
+namespace internal {
+
+void RawLogString(const char* message) {
+  const size_t message_len = strlen(message);
+  size_t bytes_written = 0;
+  while (bytes_written < message_len) {
+    int rv = HANDLE_EINTR(write(
+        STDERR_FILENO, message + bytes_written, message_len - bytes_written));
+    if (rv < 0) {
+      // Give up, nothing we can do now.
+      break;
+    }
+    bytes_written += rv;
+  }
+}
+
+void RawLogInt(unsigned int number) {
+  char buffer[20];
+  char* digit = &buffer[sizeof(buffer) - 1];
+  *digit = '\0';
+  do {
+    *(--digit) = (number % 10) + '0';
+    number /= 10;
+  } while (number != 0);
+  RawLogString(digit);
+}
+
+// Prints `path:linenum message:error` (with optional `:error`).
+void RawLog(const char* file, int line, const char* message, int error) {
+  RawLogString(file);
+  HANDLE_EINTR(write(STDERR_FILENO, ":", 1));
+  RawLogInt(line);
+  HANDLE_EINTR(write(STDERR_FILENO, " ", 1));
+  RawLogString(message);
+  if (error) {
+    RawLogString(": ");
+    RawLogInt(error);
+  }
+  HANDLE_EINTR(write(STDERR_FILENO, "\n", 1));
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/ios/raw_logging.h b/third_party/crashpad/crashpad/util/ios/raw_logging.h
new file mode 100644
index 0000000..505aa671
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/ios/raw_logging.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_
+#define CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Log \a message to stderr in a way that is safe to run during an
+//!     in-process crash.  Also prints the given file, line number and an
+//!     optional error code.
+//!
+//! Note: RUNS-DURING-CRASH.
+void RawLog(const char* file, int line, const char* message, int error);
+
+}  // namespace internal
+}  // namespace crashpad
+
+#define RAW_LOG(message) \
+  ::crashpad::internal::RawLog(__FILE__, __LINE__, message, 0)
+
+#define RAW_LOG_ERROR(error, message) \
+  ::crashpad::internal::RawLog(__FILE__, __LINE__, message, error)
+
+#endif  // CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_
diff --git a/third_party/crashpad/crashpad/util/ios/scoped_vm_read.cc b/third_party/crashpad/crashpad/util/ios/scoped_vm_read.cc
new file mode 100644
index 0000000..33c2ad0
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/ios/scoped_vm_read.cc
@@ -0,0 +1,62 @@
+// Copyright 2021 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/ios/scoped_vm_read.h"
+
+#include "util/ios/raw_logging.h"
+
+namespace crashpad {
+namespace internal {
+
+ScopedVMReadInternal::ScopedVMReadInternal()
+    : data_(0), vm_read_data_(0), vm_read_data_count_(0) {}
+
+ScopedVMReadInternal::~ScopedVMReadInternal() {
+  if (data_) {
+    kern_return_t kr =
+        vm_deallocate(mach_task_self(), vm_read_data_, vm_read_data_count_);
+    if (kr != KERN_SUCCESS)
+      RAW_LOG_ERROR(kr, "vm_deallocate");
+  }
+}
+
+bool ScopedVMReadInternal::Read(const void* data, const size_t data_length) {
+  if (data_) {
+    kern_return_t kr =
+        vm_deallocate(mach_task_self(), vm_read_data_, vm_read_data_count_);
+    if (kr != KERN_SUCCESS)
+      RAW_LOG_ERROR(kr, "vm_deallocate");
+    data_ = 0;
+  }
+  vm_address_t data_address = reinterpret_cast<vm_address_t>(data);
+  vm_address_t page_region_address = trunc_page(data_address);
+  vm_size_t page_region_size =
+      round_page(data_address - page_region_address + data_length);
+  kern_return_t kr = vm_read(mach_task_self(),
+                             page_region_address,
+                             page_region_size,
+                             &vm_read_data_,
+                             &vm_read_data_count_);
+
+  if (kr == KERN_SUCCESS) {
+    data_ = vm_read_data_ + (data_address - page_region_address);
+    return true;
+  } else {
+    RAW_LOG_ERROR(kr, "vm_read");
+    return false;
+  }
+}
+
+}  // namespace internal
+}  // namespace crashpad
diff --git a/third_party/crashpad/crashpad/util/ios/scoped_vm_read.h b/third_party/crashpad/crashpad/util/ios/scoped_vm_read.h
new file mode 100644
index 0000000..86d8039
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/ios/scoped_vm_read.h
@@ -0,0 +1,96 @@
+// Copyright 2021 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_
+#define CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_
+
+#include <mach/mach.h>
+
+#include "base/macros.h"
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Non-templated internal class to be used by ScopedVMRead.
+//!
+//! Note: RUNS-DURING-CRASH.
+class ScopedVMReadInternal {
+ public:
+  ScopedVMReadInternal();
+  ~ScopedVMReadInternal();
+
+  //! \brief Releases any previously read data and vm_reads \a data. Logs an
+  //!     error on failure.
+  //!
+  //! \param[in] data Memory to be read by vm_read.
+  //! \param[in] data_length Length of \a data.
+  //!
+  //! \return `true` if all the data was read. Logs an error and returns false
+  //!   on failure
+  bool Read(const void* data, size_t data_length);
+
+  vm_address_t data() const { return data_; }
+
+ private:
+  // The address of the requested data.
+  vm_address_t data_;
+
+  // The rounded down page boundary of the requested data.
+  vm_address_t vm_read_data_;
+
+  // The size of the pages that were actually read.
+  mach_msg_type_number_t vm_read_data_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedVMReadInternal);
+};
+
+//! \brief A scoped wrapper for calls to `vm_read` and `vm_deallocate`.  Allows
+//!     in-process handler to safely read memory for the intermediate dump.
+//!
+//! Note: RUNS-DURING-CRASH.
+template <typename T>
+class ScopedVMRead {
+ public:
+  ScopedVMRead() : internal_() {}
+  ~ScopedVMRead() {}
+
+  //! \brief Releases any previously read data and vm_reads data.
+  //!
+  //! \param[in] data Memory to be read by vm_read.
+  //! \param[in] count Length of \a data.
+  //!
+  //! \return `true` if all the data was read. Logs an error and returns false
+  //!   on failure
+  bool Read(const void* data, size_t count = 1) {
+    size_t data_length = count * sizeof(T);
+    return internal_.Read(data, data_length);
+  }
+
+  //! \brief Returns the pointer to memory safe to read during the in-process
+  //!   crash handler.
+  T* operator->() const { return get(); }
+
+  //! \brief Returns the pointer to memory safe to read during the in-process
+  //!   crash handler.
+  T* get() const { return reinterpret_cast<T*>(internal_.data()); }
+
+ private:
+  ScopedVMReadInternal internal_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedVMRead);
+};
+
+}  // namespace internal
+}  // namespace crashpad
+
+#endif  // CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_
diff --git a/third_party/crashpad/crashpad/util/ios/scoped_vm_read_test.cc b/third_party/crashpad/crashpad/util/ios/scoped_vm_read_test.cc
new file mode 100644
index 0000000..80edf04
--- /dev/null
+++ b/third_party/crashpad/crashpad/util/ios/scoped_vm_read_test.cc
@@ -0,0 +1,83 @@
+// Copyright 2021 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/ios/scoped_vm_read.h"
+
+#include <sys/time.h>
+
+#include "base/mac/scoped_mach_vm.h"
+#include "gtest/gtest.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(ScopedVMReadTest, BasicFunctionality) {
+  // bad data or count.
+  internal::ScopedVMRead<vm_address_t> vmread_bad;
+  ASSERT_FALSE(vmread_bad.Read(nullptr, 100));
+  ASSERT_FALSE(vmread_bad.Read(reinterpret_cast<void*>(0x1000), 100));
+  vm_address_t address = 1;
+  ASSERT_FALSE(vmread_bad.Read(&address, 1000000000));
+
+  // array
+  constexpr char read_me[] = "read me";
+  internal::ScopedVMRead<char> vmread_string;
+  ASSERT_TRUE(vmread_string.Read(read_me, strlen(read_me)));
+  EXPECT_STREQ(read_me, vmread_string.get());
+
+  // struct
+  timeval time_of_day;
+  EXPECT_TRUE(gettimeofday(&time_of_day, nullptr) == 0);
+  internal::ScopedVMRead<timeval> vmread_time;
+  ASSERT_TRUE(vmread_time.Read(&time_of_day));
+  EXPECT_EQ(time_of_day.tv_sec, vmread_time->tv_sec);
+  EXPECT_EQ(time_of_day.tv_usec, vmread_time->tv_usec);
+
+  // reset.
+  timeval time_of_day2;
+  EXPECT_TRUE(gettimeofday(&time_of_day2, nullptr) == 0);
+  ASSERT_TRUE(vmread_time.Read(&time_of_day2));
+  EXPECT_EQ(time_of_day2.tv_sec, vmread_time->tv_sec);
+  EXPECT_EQ(time_of_day2.tv_usec, vmread_time->tv_usec);
+}
+
+TEST(ScopedVMReadTest, MissingMiddleVM) {
+  char* region;
+  vm_size_t page_size = getpagesize();
+  vm_size_t region_size = page_size * 3;
+  ASSERT_EQ(vm_allocate(mach_task_self(),
+                        reinterpret_cast<vm_address_t*>(&region),
+                        region_size,
+                        VM_FLAGS_ANYWHERE),
+            0);
+  base::mac::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),
+                                   region_size);
+
+  internal::ScopedVMRead<char> vmread_missing_middle;
+  ASSERT_TRUE(vmread_missing_middle.Read(region, region_size));
+
+  // Dealloc middle page.
+  ASSERT_EQ(vm_deallocate(mach_task_self(),
+                          reinterpret_cast<vm_address_t>(region + page_size),
+                          page_size),
+            0);
+
+  ASSERT_FALSE(vmread_missing_middle.Read(region, region_size));
+  ASSERT_TRUE(vmread_missing_middle.Read(region, page_size));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace crashpad
diff --git a/third_party/ink/.gitignore b/third_party/ink/.gitignore
deleted file mode 100644
index 802fd26..0000000
--- a/third_party/ink/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-build/**/*.js
-build/**/*.wasm
-build/**/*.mem
diff --git a/third_party/ink/BUILD.gn b/third_party/ink/BUILD.gn
deleted file mode 100644
index add6e9f..0000000
--- a/third_party/ink/BUILD.gn
+++ /dev/null
@@ -1,21 +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.
-
-import("//build/config/chromeos/ui_mode.gni")
-import("//tools/grit/grit_rule.gni")
-
-if (is_chromeos_ash) {
-  grit("ink_resources") {
-    source = "ink_resources.grd"
-
-    use_brotli = true
-
-    outputs = [
-      "grit/ink_resources.h",
-      "grit/ink_resources_map.cc",
-      "grit/ink_resources_map.h",
-      "ink_resources.pak",
-    ]
-  }
-}
diff --git a/third_party/ink/DIR_METADATA b/third_party/ink/DIR_METADATA
deleted file mode 100644
index 14b5edb..0000000
--- a/third_party/ink/DIR_METADATA
+++ /dev/null
@@ -1,3 +0,0 @@
-monorail {
-  component: "Internals"
-}
diff --git a/third_party/ink/LICENSE b/third_party/ink/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/third_party/ink/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/third_party/ink/OWNERS b/third_party/ink/OWNERS
deleted file mode 100644
index 99e13a15..0000000
--- a/third_party/ink/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-dstockwell@google.com
-dvallet@chromium.org
-martiw@chromium.org
diff --git a/third_party/ink/README.chromium b/third_party/ink/README.chromium
deleted file mode 100644
index d19aeef4..0000000
--- a/third_party/ink/README.chromium
+++ /dev/null
@@ -1,8 +0,0 @@
-Name: Google Ink
-Short Name: ink
-URL: https://github.com/google/ink
-Version: 311554669
-License: Apache 2.0
-Security Critical: yes
-
-The build artifacts from Ink are downloaded to this directory via DEPS.
diff --git a/third_party/ink/README.md b/third_party/ink/README.md
deleted file mode 100644
index 73f75c5f..0000000
--- a/third_party/ink/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Ink
-
-Ink is a software library enabling Google applications to let their users
-express themselves using freehand drawing and handwriting.
-
-https://github.com/google/ink
diff --git a/third_party/ink/build/ink_lib_binary.js.sha1 b/third_party/ink/build/ink_lib_binary.js.sha1
deleted file mode 100644
index bd03968..0000000
--- a/third_party/ink/build/ink_lib_binary.js.sha1
+++ /dev/null
@@ -1 +0,0 @@
-32284c19757f1ab24b4198a97b6719b0a284c574
\ No newline at end of file
diff --git a/third_party/ink/build/ink_lib_externs.js.sha1 b/third_party/ink/build/ink_lib_externs.js.sha1
deleted file mode 100644
index 4f52b9b1..0000000
--- a/third_party/ink/build/ink_lib_externs.js.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b36c93a7821f30214e8c4b68e2f3fabd58d9b0e1
\ No newline at end of file
diff --git a/third_party/ink/build/ink_loader.js.sha1 b/third_party/ink/build/ink_loader.js.sha1
deleted file mode 100644
index e9fd2842..0000000
--- a/third_party/ink/build/ink_loader.js.sha1
+++ /dev/null
@@ -1 +0,0 @@
-becce94030213629f3919efce7c236759d5999ac
\ No newline at end of file
diff --git a/third_party/ink/build/wasm_ink.wasm.sha1 b/third_party/ink/build/wasm_ink.wasm.sha1
deleted file mode 100644
index 6797116e3..0000000
--- a/third_party/ink/build/wasm_ink.wasm.sha1
+++ /dev/null
@@ -1 +0,0 @@
-60b4e019170aafb36432429ea5023ebd48efe9a4
\ No newline at end of file
diff --git a/third_party/ink/build/wasm_ink.worker.js.sha1 b/third_party/ink/build/wasm_ink.worker.js.sha1
deleted file mode 100644
index 535ca47..0000000
--- a/third_party/ink/build/wasm_ink.worker.js.sha1
+++ /dev/null
@@ -1 +0,0 @@
-06d9cbef83c63e3f2a14f365b3042118f0f04dd9
\ No newline at end of file
diff --git a/third_party/ink/ink_resources.grd b/third_party/ink/ink_resources.grd
deleted file mode 100644
index 70330b5..0000000
--- a/third_party/ink/ink_resources.grd
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
-  <outputs>
-    <output filename="grit/ink_resources.h" type="rc_header">
-      <emit emit_type='prepend'></emit>
-    </output>
-    <output filename="grit/ink_resources_map.cc" type="resource_file_map_source" />
-    <output filename="grit/ink_resources_map.h" type="resource_map_header" />
-    <output filename="ink_resources.pak" type="data_package" />
-  </outputs>
-  <release seq="1">
-    <includes>
-      <if expr="chromeos">
-        <include name="IDR_INK_LIB_BINARY_JS" file="build/ink_lib_binary.js" type="BINDATA" />
-        <include name="IDR_INK_WORKER_JS" file="build/wasm_ink.worker.js" type="BINDATA" />
-        <include name="IDR_INK_WASM" file="build/wasm_ink.wasm" compress="brotli" type="BINDATA" />
-        <include name="IDR_INK_LOADER_JS" file="build/ink_loader.js" compress="brotli" type="BINDATA" />
-      </if>
-    </includes>
-  </release>
-</grit>
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index c56848f..bf45df8 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -700,9 +700,6 @@
   "third_party/blink/public/strings/blink_strings.grd": {
     "messages": [3560],
   },
-  "third_party/ink/ink_resources.grd": {
-    "includes": [3580],
-  },
   "third_party/libaddressinput/chromium/address_input_strings.grd": {
     "messages": [3600],
   },
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6ed6bada..7210e7b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -24380,6 +24380,7 @@
   <int value="856" label="DataLeakPreventionReportingEnabled"/>
   <int value="857" label="AdditionalDnsQueryTypesEnabled"/>
   <int value="858" label="TripleDESEnabled"/>
+  <int value="859" label="CloudUserPolicyMerge"/>
 </enum>
 
 <enum name="EnterprisePolicyDeviceIdValidity">
diff --git a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
index fee65cc..8efed59 100644
--- a/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/accessibility/histograms.xml
@@ -788,6 +788,28 @@
 
 <histogram name="Accessibility.LiveCaption.Duration.CaptionBubble{Visibility}"
     units="ms" expires_after="2021-09-05">
+  <obsolete>
+    Removed in M92, replaced with
+    Accessibility.LiveCaption.Duration.CaptionBubble{Visibility}2.
+  </obsolete>
+  <owner>abigailbklein@google.com</owner>
+  <owner>evliu@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Measures how long SODA was running while the Live Caption UI was
+    {Visibility}. Logged once on the destruction of the
+    SpeechRecognitionRecognizerImpl.
+  </summary>
+  <token key="Visibility">
+    <variant name="Hidden"
+        summary="hidden. This might be because it was closed by user or
+                 because there was an error passing transcriptions to the UI"/>
+    <variant name="Visible" summary="visible and showing transcriptions"/>
+  </token>
+</histogram>
+
+<histogram name="Accessibility.LiveCaption.Duration.CaptionBubble{Visibility}2"
+    units="ms" expires_after="2021-09-05">
   <owner>abigailbklein@google.com</owner>
   <owner>evliu@google.com</owner>
   <owner>chrome-a11y-core@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/blink/histograms.xml b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
index d5155a5..ac46323 100644
--- a/tools/metrics/histograms/histograms_xml/blink/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/blink/histograms.xml
@@ -2559,6 +2559,17 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Sms.WebContentsVisibleOnReceive" enum="Boolean"
+    expires_after="2021-10-10">
+  <owner>yigu@chromium.org</owner>
+  <owner>web-identity@google.com</owner>
+  <summary>
+    Records whether the web contents that receives the OTP is visible or not for
+    the UserConsent backend. Recorded at most once per API call when Chrome sees
+    the OTP from the expected origin.
+  </summary>
+</histogram>
+
 <histogram name="Blink.SpatialNavigation.Advance" units="microseconds"
     expires_after="M85">
   <obsolete>
diff --git a/tools/metrics/histograms/histograms_xml/holding_space/histograms.xml b/tools/metrics/histograms/histograms_xml/holding_space/histograms.xml
index 9a31016b..76cf35c 100644
--- a/tools/metrics/histograms/histograms_xml/holding_space/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/holding_space/histograms.xml
@@ -141,6 +141,16 @@
   </summary>
 </histogram>
 
+<histogram name="HoldingSpace.Item.FailureToLaunch.Extension"
+    enum="HoldingSpaceExtension" expires_after="2021-09-24">
+  <owner>dmblack@google.com</owner>
+  <owner>gzadina@google.com</owner>
+  <summary>
+    Records a failure to launch a holding space item backed by file of a
+    specific extension at the moment of failure.
+  </summary>
+</histogram>
+
 <histogram name="HoldingSpace.Pod.Action.All" enum="HoldingSpacePodAction"
     expires_after="2021-09-24">
   <owner>dmblack@google.com</owner>
diff --git a/tools/metrics/histograms/histograms_xml/tab/histograms.xml b/tools/metrics/histograms/histograms_xml/tab/histograms.xml
index c1e8b9d..b3acfce 100644
--- a/tools/metrics/histograms/histograms_xml/tab/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/tab/histograms.xml
@@ -1957,6 +1957,17 @@
   </summary>
 </histogram>
 
+<histogram name="Tabs.Startup.TabCount.{TabType}" units="count"
+    expires_after="2022-05-04">
+  <owner>davidjm@chromium.org</owner>
+  <owner>nyquist@chromium.org</owner>
+  <summary>Records the number of {TabType} Tabs loaded on startup.</summary>
+  <token key="TabType">
+    <variant name="Incognito" summary="Incognito"/>
+    <variant name="Regular" summary="Regular"/>
+  </token>
+</histogram>
+
 <histogram name="Tabs.StateTransfer.NumberOfOtherTabsActivatedBeforeMadeActive"
     units="tabs" expires_after="M77">
   <owner>kouhei@chromium.org</owner>
diff --git a/tools/perf/benchmarks/rendering.py b/tools/perf/benchmarks/rendering.py
index ff293f4..faa4189 100644
--- a/tools/perf/benchmarks/rendering.py
+++ b/tools/perf/benchmarks/rendering.py
@@ -34,8 +34,7 @@
 
 class _RenderingBenchmark(perf_benchmark.PerfBenchmark):
   options = {
-      'capture_screen_video': True,
-      'periodic_screenshot_frequency_ms': 1000
+      'capture_screen_video': True
   }
 
   @classmethod
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index f5af82b..efddd70 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,16 +1,16 @@
 {
     "trace_processor_shell": {
         "win": {
-            "hash": "a44d572b16def9b17bb4e1a15615d4f6477aabdc",
-            "remote_path": "perfetto_binaries/trace_processor_shell/win/2b0fcfa349ac5ccd7851e459ec683a8e687f7c75/trace_processor_shell.exe"
+            "hash": "23cff3007ff25abed77bdaa20c104a0cfdb12219",
+            "remote_path": "perfetto_binaries/trace_processor_shell/win/2e55bf4f07c1dd4ab28fff9f468b4eb33731670f/trace_processor_shell.exe"
         },
         "mac": {
-            "hash": "9071b3f46a0807212bc99e56c7a794d17cb17fdc",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/2b0fcfa349ac5ccd7851e459ec683a8e687f7c75/trace_processor_shell"
+            "hash": "72fc3915dddc48d4d1a256acf8eada25c740eb9e",
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/78e179f910b7cb65044a25f0723a0b263d010974/trace_processor_shell"
         },
         "linux": {
             "hash": "b95b2b75294ef7c5ed96e53582c5d8f0d4e575bd",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/ba54d8c034c447a84194dd2a30379ea2a0afc36d/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/78e179f910b7cb65044a25f0723a0b263d010974/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/accessibility/ax_text_utils_unittest.cc b/ui/accessibility/ax_text_utils_unittest.cc
index c644941..8fd1b90 100644
--- a/ui/accessibility/ax_text_utils_unittest.cc
+++ b/ui/accessibility/ax_text_utils_unittest.cc
@@ -290,14 +290,14 @@
 }
 
 TEST(AXTextUtils, GetSentenceStartOffsetsBasicTest) {
-  const std::u16string text = base::UTF8ToUTF16(
-      "This is the first sentence. This is the second sentence");
+  const std::u16string text =
+      u"This is the first sentence. This is the second sentence";
   EXPECT_THAT(GetSentenceStartOffsets(text), testing::ElementsAre(0, 28));
 }
 
 TEST(AXTextUtils, GetSentenceEndOffsetsBasicTest) {
-  const std::u16string text = base::UTF8ToUTF16(
-      "This is the first sentence. This is the second sentence");
+  const std::u16string text =
+      u"This is the first sentence. This is the second sentence";
   EXPECT_THAT(GetSentenceEndOffsets(text), testing::ElementsAre(28, 55));
 }
 
diff --git a/ui/base/accelerators/accelerator.h b/ui/base/accelerators/accelerator.h
index 59008a4..780a45f9 100644
--- a/ui/base/accelerators/accelerator.h
+++ b/ui/base/accelerators/accelerator.h
@@ -93,6 +93,7 @@
 
 #if defined(OS_CHROMEOS)
   DomCode code() const { return code_; }
+  void reset_code() { code_ = DomCode::NONE; }
 #endif
 
   // Sets the key state that triggers the accelerator. Default is PRESSED.
diff --git a/ui/base/accelerators/accelerator_map.h b/ui/base/accelerators/accelerator_map.h
index 0399409..e64eb5f 100644
--- a/ui/base/accelerators/accelerator_map.h
+++ b/ui/base/accelerators/accelerator_map.h
@@ -79,8 +79,13 @@
 
   V& GetOrInsertDefault(const Accelerator& accelerator) {
 #if defined(OS_CHROMEOS)
-    // Cannot explicitly register an accelerator with a DomCode.
-    DCHECK_EQ(DomCode::NONE, accelerator.code());
+    // Ensure the DomCode is NONE before registering. The DomCode is only
+    // used during lookup to select the correct VKEY.
+    if (accelerator.code() != DomCode::NONE) {
+      Accelerator accelerator_copy = accelerator;
+      accelerator_copy.reset_code();
+      return map_[accelerator_copy];
+    }
 #endif
     return map_[accelerator];
   }
@@ -94,8 +99,16 @@
   // accelerator was already in the map.
   void InsertNew(const std::pair<const Accelerator, V>& value) {
 #if defined(OS_CHROMEOS)
-    // Cannot explicitly register an accelerator with a DomCode.
-    DCHECK_EQ(DomCode::NONE, value.first.code());
+    // Ensure the DomCode is NONE before registering. The DomCode is only
+    // used during lookup to select the correct VKEY.
+    if (value.first.code() != DomCode::NONE) {
+      Accelerator accelerator_copy = value.first;
+      accelerator_copy.reset_code();
+      auto value_copy = std::make_pair(accelerator_copy, value.second);
+      auto result = map_.insert(value_copy);
+      DCHECK(result.second);
+      return;
+    }
 #endif
     auto result = map_.insert(value);
     DCHECK(result.second);
@@ -154,12 +167,15 @@
     return Accelerator(lookup_key_code, DomCode::NONE, accelerator.modifiers(),
                        accelerator.key_state());
   }
-#endif
+#endif  // defined(OS_CHROMEOS)
 
   const_iterator FindImpl(const Accelerator& accelerator) const {
 #if defined(OS_CHROMEOS)
-    return map_.find(RemapAcceleratorForLookup(accelerator));
-#endif
+    auto iter = map_.find(RemapAcceleratorForLookup(accelerator));
+    // Sanity check that a DomCode was never inserted into the map.
+    DCHECK(iter == map_.end() || iter->first.code() == DomCode::NONE);
+    return iter;
+#endif  // defined(OS_CHROMEOS)
 
     return map_.find(accelerator);
   }
diff --git a/ui/base/accelerators/accelerator_map_unittest.cc b/ui/base/accelerators/accelerator_map_unittest.cc
index 79d11a9..a89715d4 100644
--- a/ui/base/accelerators/accelerator_map_unittest.cc
+++ b/ui/base/accelerators/accelerator_map_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/accelerators/accelerator.h"
+#include "ui/events/event.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 
 namespace ui {
@@ -206,6 +207,27 @@
   EXPECT_TRUE(IsValidMatch(&m, pressed, expected));
 }
 
+// When an accelerator is inserted to the map, if it contains a DomCode it
+// should be stripped out.
+TEST(AcceleratorMapTest, DomCodesStrippedWhenInserted) {
+  AcceleratorMap<int> m;
+  m.set_use_positional_lookup(true);
+
+  // Verify the accelerator has a DomCode and insert it.
+  Accelerator accelerator(ui::VKEY_F, DomCode::US_F,
+                          ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN);
+  EXPECT_EQ(accelerator.code(), DomCode::US_F);
+  const int expected = 77;
+  m.InsertNew(std::make_pair(accelerator, expected));
+
+  // Reset the DomCode on the accelerator and perform a lookup and verify
+  // that it can still be found.
+  accelerator.reset_code();
+  auto* value = m.Find(accelerator);
+  ASSERT_TRUE(value);
+  EXPECT_EQ(*value, expected);
+}
+
 #endif  // defined(OS_CHROMEOS)
 
 }  // namespace
diff --git a/ui/base/accelerators/accelerator_unittest.cc b/ui/base/accelerators/accelerator_unittest.cc
index 7428200..e2e8e6e 100644
--- a/ui/base/accelerators/accelerator_unittest.cc
+++ b/ui/base/accelerators/accelerator_unittest.cc
@@ -7,8 +7,10 @@
 #include <string>
 
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/event.h"
 
 namespace ui {
@@ -74,4 +76,35 @@
   EXPECT_EQ(std::u16string(), accelerator.GetShortcutText());
 }
 
+TEST(AcceleratorTest, ConversionFromKeyEvent) {
+  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_F,
+                         ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN);
+  Accelerator accelerator(key_event);
+
+  EXPECT_EQ(accelerator.key_code(), ui::VKEY_F);
+  EXPECT_EQ(accelerator.modifiers(), ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN);
+}
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+TEST(AcceleratorTest, ConversionFromKeyEvent_Ash) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      ::features::kImprovedKeyboardShortcuts);
+
+  ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_F,
+                         ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN);
+  Accelerator accelerator(key_event);
+
+  EXPECT_EQ(accelerator.key_code(), ui::VKEY_F);
+  EXPECT_EQ(accelerator.modifiers(), ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN);
+
+  // Code is set when converting from a KeyEvent.
+  EXPECT_EQ(accelerator.code(), DomCode::US_F);
+
+  // Test resetting code.
+  accelerator.reset_code();
+  EXPECT_EQ(accelerator.code(), DomCode::NONE);
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace ui
diff --git a/ui/base/clipboard/clipboard_test_template.h b/ui/base/clipboard/clipboard_test_template.h
index bf608e2c..e89e626 100644
--- a/ui/base/clipboard/clipboard_test_template.h
+++ b/ui/base/clipboard/clipboard_test_template.h
@@ -63,7 +63,6 @@
 
 using base::ASCIIToUTF16;
 using base::UTF16ToUTF8;
-using base::UTF8ToUTF16;
 
 using testing::Contains;
 
@@ -366,8 +365,7 @@
 // Some platforms store HTML as UTF-8 internally. Make sure fragment indices are
 // adjusted appropriately when converting back to UTF-16.
 TYPED_TEST(ClipboardTest, UnicodeHTMLTest) {
-  std::u16string markup(UTF8ToUTF16("<div>A \xc3\xb8 \xe6\xb0\xb4</div>")),
-      markup_result;
+  std::u16string markup(u"<div>A ø 水</div>"), markup_result;
   std::string url, url_result;
 
   {
@@ -443,7 +441,7 @@
   this->clipboard().ReadAvailableTypes(ClipboardBuffer::kCopyPaste,
                                        /* data_dst = */ nullptr, &types);
   EXPECT_EQ(1u, types.size());
-  EXPECT_EQ("text/uri-list", base::UTF16ToUTF8(types[0]));
+  EXPECT_EQ(u"text/uri-list", types[0]);
 
   std::vector<ui::FileInfo> filenames;
   this->clipboard().ReadFilenames(ClipboardBuffer::kCopyPaste,
@@ -708,6 +706,7 @@
 
 TYPED_TEST(ClipboardTest, DataTest) {
   const std::string kFormatString = "chromium/x-test-format";
+  const std::u16string kFormatString16 = u"chromium/x-test-format";
   const ClipboardFormatType kFormat =
       ClipboardFormatType::GetType(kFormatString);
   const std::string payload = "test string";
@@ -716,7 +715,7 @@
 
   {
     ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste);
-    clipboard_writer.WriteData(UTF8ToUTF16(kFormatString),
+    clipboard_writer.WriteData(kFormatString16,
                                mojo_base::BigBuffer(payload_span));
   }
 
@@ -736,6 +735,7 @@
     !BUILDFLAG(IS_CHROMEOS_ASH)
 TYPED_TEST(ClipboardTest, MultipleDataTest) {
   const std::string kFormatString1 = "chromium/x-test-format1";
+  const std::u16string kFormatString116 = u"chromium/x-test-format1";
   const ClipboardFormatType kFormat1 =
       ClipboardFormatType::GetType(kFormatString1);
   const std::string payload1("test string1");
@@ -743,6 +743,7 @@
       reinterpret_cast<const uint8_t*>(payload1.data()), payload1.size());
 
   const std::string kFormatString2 = "chromium/x-test-format2";
+  const std::u16string kFormatString216 = u"chromium/x-test-format2";
   const ClipboardFormatType kFormat2 =
       ClipboardFormatType::GetType(kFormatString2);
   const std::string payload2("test string2");
@@ -752,16 +753,16 @@
   {
     ScopedClipboardWriter clipboard_writer(ClipboardBuffer::kCopyPaste);
     // Both payloads should write successfully and not overwrite one another.
-    clipboard_writer.WriteData(UTF8ToUTF16(kFormatString1),
+    clipboard_writer.WriteData(kFormatString116,
                                mojo_base::BigBuffer(payload_span1));
-    clipboard_writer.WriteData(UTF8ToUTF16(kFormatString2),
+    clipboard_writer.WriteData(kFormatString216,
                                mojo_base::BigBuffer(payload_span2));
   }
 
   // Check format 1.
   EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
                   ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
-              Contains(ASCIIToUTF16(kFormatString1)));
+              Contains(kFormatString116));
   EXPECT_TRUE(this->clipboard().IsFormatAvailable(
       kFormat1, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
   std::string output1;
@@ -771,7 +772,7 @@
   // Check format 2.
   EXPECT_THAT(this->clipboard().ReadAvailablePlatformSpecificFormatNames(
                   ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr),
-              Contains(ASCIIToUTF16(kFormatString2)));
+              Contains(kFormatString216));
   EXPECT_TRUE(this->clipboard().IsFormatAvailable(
       kFormat2, ClipboardBuffer::kCopyPaste, /* data_dst = */ nullptr));
   std::string output2;
@@ -904,9 +905,9 @@
 TYPED_TEST(ClipboardTest, HyperlinkTest) {
   const std::string kTitle("The <Example> Company's \"home page\"");
   const std::string kUrl("http://www.example.com?x=3&lt=3#\"'<>");
-  const std::u16string kExpectedHtml(UTF8ToUTF16(
-      "<a href=\"http://www.example.com?x=3&amp;lt=3#&quot;&#39;&lt;&gt;\">"
-      "The &lt;Example&gt; Company&#39;s &quot;home page&quot;</a>"));
+  const std::u16string kExpectedHtml(
+      u"<a href=\"http://www.example.com?x=3&amp;lt=3#&quot;&#39;&lt;&gt;\">"
+      u"The &lt;Example&gt; Company&#39;s &quot;home page&quot;</a>");
 
   std::string url_result;
   std::u16string html_result;
diff --git a/ui/chromeos/events/event_rewriter_chromeos.cc b/ui/chromeos/events/event_rewriter_chromeos.cc
index 9591eff0..20c9911 100644
--- a/ui/chromeos/events/event_rewriter_chromeos.cc
+++ b/ui/chromeos/events/event_rewriter_chromeos.cc
@@ -618,20 +618,20 @@
 // "six pack" eg. Home, End, PageUp, PageDown, Delete, Insert.
 void RecordSixPackEventRewrites(ui::EventType event_type,
                                 ui::KeyboardCode key_code,
-                                bool search_variant) {
+                                bool legacy_variant) {
   if (event_type != ET_KEY_PRESSED) {
     return;
   }
 
-  if (search_variant) {
+  if (!legacy_variant) {
     switch (key_code) {
       case ui::VKEY_DELETE:
         base::RecordAction(
             base::UserMetricsAction("SearchBasedKeyRewrite_Delete"));
         break;
       case ui::VKEY_INSERT:
-        base::RecordAction(
-            base::UserMetricsAction("SearchBasedKeyRewrite_Insert"));
+        base::RecordAction(base::UserMetricsAction(
+            "SearchBasedKeyRewrite_Insert_ViaSearchShiftBackspace"));
         break;
       case ui::VKEY_HOME:
         base::RecordAction(
@@ -659,6 +659,10 @@
         base::RecordAction(
             base::UserMetricsAction("AltBasedKeyRewrite_Delete"));
         break;
+      case ui::VKEY_INSERT:
+        base::RecordAction(
+            base::UserMetricsAction("SearchBasedKeyRewrite_Insert"));
+        break;
       case ui::VKEY_HOME:
         base::RecordAction(base::UserMetricsAction("AltBasedKeyRewrite_Home"));
         break;
@@ -1414,7 +1418,52 @@
       }
     }
 
-    static const KeyboardRemapping kSearchRemappings[] = {
+    // The new Search+Shift+Backspace rewrite is only active when
+    // IsImprovedKeyboardShortcutsEnabled() is true.
+    // TODO(crbug.com/1179893): Merge this entry into kSixPackRemappings
+    // once the flag is removed.
+    static const KeyboardRemapping kOldInsertRemapping[] = {
+        {// Search+Period -> Insert
+         {EF_COMMAND_DOWN, VKEY_OEM_PERIOD},
+         {EF_NONE, DomCode::INSERT, DomKey::INSERT, VKEY_INSERT}},
+    };
+
+    if (::features::IsImprovedKeyboardShortcutsEnabled()) {
+      static const KeyboardRemapping kNewInsertRemapping[] = {
+          {// Search+Shift+BackSpace -> Insert
+           {EF_COMMAND_DOWN | EF_SHIFT_DOWN, VKEY_BACK},
+           {EF_NONE, DomCode::INSERT, DomKey::INSERT, VKEY_INSERT}},
+      };
+
+      if (!skip_search_key_remapping &&
+          RewriteWithKeyboardRemappings(kNewInsertRemapping,
+                                        base::size(kNewInsertRemapping),
+                                        incoming, state, strict)) {
+        RecordSixPackEventRewrites(key_event.type(), state->key_code,
+                                   /*legacy_variant=*/false);
+        return;
+      }
+
+      // Test for the deprecated insert rewrite in order to show a notification.
+      const ui::KeyboardCode deprecated_key = MatchedDeprecatedRemapping(
+          kOldInsertRemapping, base::size(kOldInsertRemapping), incoming);
+      if (deprecated_key != VKEY_UNKNOWN) {
+        // If the key would have matched prior to being deprecated then notify
+        // the delegate to show a notification.
+        delegate_->NotifyDeprecatedSixPackKeyRewrite(deprecated_key);
+      }
+    } else {
+      if (!skip_search_key_remapping &&
+          RewriteWithKeyboardRemappings(kOldInsertRemapping,
+                                        base::size(kOldInsertRemapping),
+                                        incoming, state, strict)) {
+        RecordSixPackEventRewrites(key_event.type(), state->key_code,
+                                   /*legacy_variant=*/true);
+        return;
+      }
+    }
+
+    static const KeyboardRemapping kSixPackRemappings[] = {
         {// Search+BackSpace -> Delete
          {EF_COMMAND_DOWN, VKEY_BACK},
          {EF_NONE, DomCode::DEL, DomKey::DEL, VKEY_DELETE}},
@@ -1429,23 +1478,21 @@
          {EF_NONE, DomCode::END, DomKey::END, VKEY_END}},
         {// Search+Down -> Next (aka PageDown)
          {EF_COMMAND_DOWN, VKEY_DOWN},
-         {EF_NONE, DomCode::PAGE_DOWN, DomKey::PAGE_DOWN, VKEY_NEXT}},
-        {// Search+Period -> Insert
-         {EF_COMMAND_DOWN, VKEY_OEM_PERIOD},
-         {EF_NONE, DomCode::INSERT, DomKey::INSERT, VKEY_INSERT}}};
+         {EF_NONE, DomCode::PAGE_DOWN, DomKey::PAGE_DOWN, VKEY_NEXT}}};
+
     if (!skip_search_key_remapping &&
-        RewriteWithKeyboardRemappings(kSearchRemappings,
-                                      base::size(kSearchRemappings), incoming,
+        RewriteWithKeyboardRemappings(kSixPackRemappings,
+                                      base::size(kSixPackRemappings), incoming,
                                       state, strict)) {
       RecordSixPackEventRewrites(key_event.type(), state->key_code,
-                                 /*search_variant=*/true);
+                                 /*legacy_variant=*/false);
       return;
     }
   }
 
   // TODO(crbug.com/1179893): Remove block once Alt rewrites are deprecated.
   if ((incoming.flags & EF_ALT_DOWN) && is_alt_down_remapping_enabled_) {
-    static const KeyboardRemapping kNonSearchRemappings[] = {
+    static const KeyboardRemapping kLegacySixPackRemappings[] = {
         {// Alt+BackSpace -> Delete
          {EF_ALT_DOWN, VKEY_BACK},
          {EF_NONE, DomCode::DEL, DomKey::DEL, VKEY_DELETE}},
@@ -1462,20 +1509,21 @@
          {EF_ALT_DOWN, VKEY_DOWN},
          {EF_NONE, DomCode::PAGE_DOWN, DomKey::PAGE_DOWN, VKEY_NEXT}}};
     if (!::features::IsImprovedKeyboardShortcutsEnabled()) {
-      if (RewriteWithKeyboardRemappings(kNonSearchRemappings,
-                                        base::size(kNonSearchRemappings),
+      if (RewriteWithKeyboardRemappings(kLegacySixPackRemappings,
+                                        base::size(kLegacySixPackRemappings),
                                         incoming, state)) {
         RecordSixPackEventRewrites(key_event.type(), state->key_code,
-                                   /*search_variant=*/false);
+                                   /*legacy_variant=*/true);
         return;
       }
     } else {
       const ui::KeyboardCode deprecated_key = MatchedDeprecatedRemapping(
-          kNonSearchRemappings, base::size(kNonSearchRemappings), incoming);
+          kLegacySixPackRemappings, base::size(kLegacySixPackRemappings),
+          incoming);
       if (deprecated_key != VKEY_UNKNOWN) {
         // If the key would have matched prior to being deprecated then notify
         // the delegate to show a notification.
-        delegate_->NotifyDeprecatedAltBasedKeyRewrite(deprecated_key);
+        delegate_->NotifyDeprecatedSixPackKeyRewrite(deprecated_key);
       }
     }
   }
diff --git a/ui/chromeos/events/event_rewriter_chromeos.h b/ui/chromeos/events/event_rewriter_chromeos.h
index 6be3748..616fc11 100644
--- a/ui/chromeos/events/event_rewriter_chromeos.h
+++ b/ui/chromeos/events/event_rewriter_chromeos.h
@@ -138,10 +138,11 @@
     // and this function returns true if the notification was shown.
     virtual bool NotifyDeprecatedFKeyRewrite() = 0;
 
-    // Used to send a notification about Alt based key rewrite being deprecated.
-    // The notification is only sent once per user session, and this function
-    // returns true if the notification was shown.
-    virtual bool NotifyDeprecatedAltBasedKeyRewrite(KeyboardCode key_code) = 0;
+    // Used to send a notification about a Six Pack (PageUp, PageDown, Home,
+    // End, Insert, Delete) key rewrite being deprecated. The notification
+    // is only sent once per user session, and this function returns true if
+    // the notification was shown.
+    virtual bool NotifyDeprecatedSixPackKeyRewrite(KeyboardCode key_code) = 0;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(Delegate);
diff --git a/ui/gfx/bidi_line_iterator_unittest.cc b/ui/gfx/bidi_line_iterator_unittest.cc
index d8dbc51..73032f2 100644
--- a/ui/gfx/bidi_line_iterator_unittest.cc
+++ b/ui/gfx/bidi_line_iterator_unittest.cc
@@ -121,9 +121,10 @@
 
 TEST_P(BiDiLineIteratorTest, RTLPunctuationNoCustomBehavior) {
   // This string features Hebrew characters interleaved with ASCII punctuation.
-  iterator()->Open(base::UTF8ToUTF16("א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/"
-                                     "ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו"),
-                   GetParam());
+  iterator()->Open(
+      u"א!ב\"ג#ד$ה%ו&ז'ח(ט)י*ך+כ,ל-ם.מ/"
+      u"ן:נ;ס<ע=ף>פ?ץ@צ[ק\\ר]ש^ת_א`ב{ג|ד}ה~ו",
+      GetParam());
 
   // Expect a single RTL run.
   ASSERT_EQ(1, iterator()->CountRuns());
diff --git a/ui/gfx/image/image_skia_unittest.cc b/ui/gfx/image/image_skia_unittest.cc
index 854a063..79218684 100644
--- a/ui/gfx/image/image_skia_unittest.cc
+++ b/ui/gfx/image/image_skia_unittest.cc
@@ -316,7 +316,7 @@
   ImageSkia image_skia(std::make_unique<DynamicSource>(Size(100, 200)),
                        Size(100, 200));
   const SkBitmap* bitmap = image_skia.bitmap();
-  EXPECT_NE(static_cast<SkBitmap*>(NULL), bitmap);
+  ASSERT_NE(nullptr, bitmap);
   EXPECT_FALSE(bitmap->isNull());
 }
 
@@ -330,7 +330,7 @@
   // Check that ImageSkia::bitmap() still returns a valid SkBitmap pointer for
   // the image and all its copies.
   const SkBitmap* bitmap = empty_image_copy.bitmap();
-  ASSERT_NE(static_cast<SkBitmap*>(NULL), bitmap);
+  ASSERT_NE(nullptr, bitmap);
   EXPECT_TRUE(bitmap->isNull());
   EXPECT_TRUE(bitmap->empty());
 }
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc
index 132b257..3d0981b1 100644
--- a/ui/gfx/render_text_unittest.cc
+++ b/ui/gfx/render_text_unittest.cc
@@ -60,22 +60,18 @@
 #include "base/mac/mac_util.h"
 #endif
 
-using base::ASCIIToUTF16;
-using base::UTF8ToUTF16;
-using base::WideToUTF16;
-
 namespace gfx {
 
 namespace {
 
 // Various weak, LTR, RTL, and Bidi string cases with three characters each.
-const char kWeak[] = " . ";
-const char kLtr[] = "abc";
-const char kRtl[] = "\u05d0\u05d1\u05d2";
-const char kLtrRtl[] = "a\u05d0\u05d1";
-const char kLtrRtlLtr[] = "a\u05d1b";
-const char kRtlLtr[] = "\u05d0\u05d1a";
-const char kRtlLtrRtl[] = "\u05d0a\u05d1";
+const char16_t kWeak[] = u" . ";
+const char16_t kLtr[] = u"abc";
+const char16_t kRtl[] = u"אבג";
+const char16_t kLtrRtl[] = u"aאב";
+const char16_t kLtrRtlLtr[] = u"aבb";
+const char16_t kRtlLtr[] = u"אבa";
+const char16_t kRtlLtrRtl[] = u"אaב";
 
 constexpr bool kUseWordWrap = true;
 constexpr bool kUseObscuredText = true;
@@ -257,15 +253,6 @@
   return std::u16string(length, RenderText::kPasswordReplacementChar);
 }
 
-// Converts a vector of UTF8 literals into a vector of (UTF16) string16.
-std::vector<std::u16string> ToString16Vec(
-    const std::vector<const char*>& utf8_literals) {
-  std::vector<std::u16string> vec;
-  for (auto* const literal : utf8_literals)
-    vec.push_back(UTF8ToUTF16(literal));
-  return vec;
-}
-
 // Returns the combined character range from all text runs on |line|.
 Range LineCharRange(const internal::Line& line) {
   if (line.segments.empty())
@@ -676,14 +663,14 @@
   // Check the default styles applied to new instances and adjusted text.
   RenderText* render_text = GetRenderText();
   EXPECT_TRUE(render_text->text().empty());
-  const char* const cases[] = {kWeak, kLtr, "Hello", kRtl, "", ""};
+  const char16_t* const cases[] = {kWeak, kLtr, u"Hello", kRtl, u"", u""};
   for (size_t i = 0; i < base::size(cases); ++i) {
     EXPECT_TRUE(test_api()->colors().EqualsValueForTesting(kPlaceholderColor));
     EXPECT_TRUE(test_api()->baselines().EqualsValueForTesting(NORMAL_BASELINE));
     EXPECT_TRUE(test_api()->font_size_overrides().EqualsValueForTesting(0));
     for (size_t style = 0; style < static_cast<int>(TEXT_STYLE_COUNT); ++style)
       EXPECT_TRUE(test_api()->styles()[style].EqualsValueForTesting(false));
-    render_text->SetText(UTF8ToUTF16(cases[i]));
+    render_text->SetText(cases[i]);
   }
 }
 
@@ -695,7 +682,7 @@
   render_text->SetBaselineStyle(SUPERSCRIPT);
   render_text->SetWeight(Font::Weight::BOLD);
   render_text->SetStyle(TEXT_STYLE_UNDERLINE, false);
-  const char* const cases[] = {kWeak, kLtr, "Hello", kRtl, "", ""};
+  const char16_t* const cases[] = {kWeak, kLtr, u"Hello", kRtl, u"", u""};
   for (size_t i = 0; i < base::size(cases); ++i) {
     EXPECT_TRUE(test_api()->colors().EqualsValueForTesting(color));
     EXPECT_TRUE(test_api()->baselines().EqualsValueForTesting(SUPERSCRIPT));
@@ -704,7 +691,7 @@
     EXPECT_TRUE(
         test_api()->styles()[TEXT_STYLE_UNDERLINE].EqualsValueForTesting(
             false));
-    render_text->SetText(UTF8ToUTF16(cases[i]));
+    render_text->SetText(cases[i]);
 
     // Ensure custom default styles can be applied after text has been set.
     if (i == 1)
@@ -821,7 +808,7 @@
 
 TEST_F(RenderTextTest, ApplyStyleGrapheme) {
   RenderText* render_text = GetRenderText();
-  render_text->SetText(u"\u0065\u0301");
+  render_text->SetText(u"e\u0301");
   render_text->ApplyStyle(TEXT_STYLE_ITALIC, true, gfx::Range(1, 2));
   render_text->ApplyStyle(TEXT_STYLE_UNDERLINE, true, gfx::Range(0, 1));
   Draw();
@@ -832,7 +819,7 @@
 
 TEST_F(RenderTextTest, ApplyStyleMultipleGraphemes) {
   RenderText* render_text = GetRenderText();
-  render_text->SetText(u"xx\u0065\u0301x");
+  render_text->SetText(u"xxe\u0301x");
   // Apply the style in the middle of a grapheme.
   gfx::Range range(1, 3);
   render_text->ApplyStyle(TEXT_STYLE_ITALIC, true, range);
@@ -943,14 +930,14 @@
 
   // Render the isolated form of the first glyph.
   RenderText* render_text = GetRenderText();
-  render_text->SetText(u"\u0628");
+  render_text->SetText(u"ب");
   Draw();
   ASSERT_EQ(1u, text_log().size());
   ASSERT_EQ(1u, text_log()[0].glyphs().size());
   uint16_t isolated_first_glyph = text_log()[0].glyphs()[0];
 
   // Render a pair of glyphs (initial form and final form).
-  render_text->SetText(u"\u0628\u0645");
+  render_text->SetText(u"بم");
   Draw();
   ASSERT_EQ(1u, text_log().size());
   ASSERT_LE(2u, text_log()[0].glyphs().size());
@@ -1068,7 +1055,8 @@
   constexpr float kGlyphWidth = 5.5f;
   constexpr Size kCanvasSize(300, 50);
   constexpr SkColor kTranslucentBlue = SkColorSetARGB(0x7F, 0x00, 0x00, 0xFF);
-  const char* kTestString{"A B C D"};
+  const char* const kTestString{"A B C D"};
+  const char16_t* const kTestString16{u"A B C D"};
 
   SkBitmap bitmap;
   bitmap.allocPixels(
@@ -1082,7 +1070,7 @@
   render_text->set_selection_background_focused_color(kTranslucentBlue);
   render_text->set_focused(true);
 
-  render_text->SetText(UTF8ToUTF16(kTestString));
+  render_text->SetText(kTestString16);
   render_text->SelectRange(Range(0, 7));
   const Rect text_rect = Rect(render_text->GetStringSize());
   render_text->SetDisplayRect(text_rect);
@@ -1098,7 +1086,7 @@
 
 TEST_F(RenderTextTest, SelectRangeColoredGrapheme) {
   RenderText* render_text = GetRenderText();
-  render_text->SetText(u"x\u0065\u0301y");
+  render_text->SetText(u"xe\u0301y");
   render_text->SetColor(SK_ColorBLACK);
   render_text->set_selection_color(SK_ColorGREEN);
   render_text->set_focused(true);
@@ -1161,7 +1149,7 @@
 
 TEST_F(RenderTextTest, SetCompositionRangeColoredGrapheme) {
   RenderText* render_text = GetRenderText();
-  render_text->SetText(u"x\u0065\u0301y");
+  render_text->SetText(u"xe\u0301y");
 
   render_text->SetCompositionRange(Range(0, 1));
   Draw();
@@ -1263,15 +1251,16 @@
             std::ceil(render_text->GetCursorSpan({0, 2}).length()));
 
   // Cursoring is independent of underlying characters when text is obscured.
-  const char* const texts[] = {
-      kWeak, kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl,
-      "hop on pop",                              // Check LTR word boundaries.
-      "\u05d0\u05d1 \u05d0\u05d2 \u05d1\u05d2",  // Check RTL word boundaries.
+  const char16_t* const texts[] = {
+      kWeak,         kLtr, kLtrRtl, kLtrRtlLtr, kRtl, kRtlLtr, kRtlLtrRtl,
+      u"hop on pop",  // Check LTR word boundaries.
+      u"אב אג בג",    // Check RTL word boundaries.
   };
   for (size_t i = 0; i < base::size(texts); ++i) {
-    std::u16string text = UTF8ToUTF16(texts[i]);
-    TestVisualCursorMotionInObscuredField(render_text, text, SELECTION_NONE);
-    TestVisualCursorMotionInObscuredField(render_text, text, SELECTION_RETAIN);
+    TestVisualCursorMotionInObscuredField(render_text, texts[i],
+                                          SELECTION_NONE);
+    TestVisualCursorMotionInObscuredField(render_text, texts[i],
+                                          SELECTION_RETAIN);
   }
 }
 
@@ -1348,8 +1337,7 @@
 
   // Text with invalid surrogates (surrogates low 0xDC00 and high 0xD800).
   // Invalid surrogates are replaced by replacement character (e.g. 0xFFFD).
-  const char16_t invalid_surrogates[] = {0xDC00, 0xD800, 'h', 'o', 'p', 0};
-  render_text->SetText(invalid_surrogates);
+  render_text->SetText(u"\xDC00\xD800hop");
   EXPECT_EQ(GetObscuredString(5), render_text->GetDisplayText());
   render_text->RenderText::SetObscuredRevealIndex(0);
   EXPECT_EQ(GetObscuredString(5, 0, 0xFFFD), render_text->GetDisplayText());
@@ -1359,9 +1347,7 @@
   EXPECT_EQ(GetObscuredString(5, 2, 'h'), render_text->GetDisplayText());
 
   // Text with valid surrogates before and after the reveal index.
-  const char16_t valid_surrogates[] = {0xD800, 0xDC00, 'h',    'o',
-                                       'p',    0xD800, 0xDC00, 0};
-  render_text->SetText(valid_surrogates);
+  render_text->SetText(u"\xD800\xDC00hop\xD800\xDC00");
   EXPECT_EQ(GetObscuredString(5), render_text->GetDisplayText());
   render_text->RenderText::SetObscuredRevealIndex(0);
   const char16_t valid_expect_0_and_1[] = {0xD800,
@@ -1394,12 +1380,11 @@
   RenderText* render_text = GetRenderText();
   render_text->SetObscured(true);
   // Test U+1F601 😁 "Grinning face with smiling eyes", followed by 'y'.
-  // Windows requires wide strings for \Unnnnnnnn universal character names.
-  render_text->SetText(u"\U0001F601y");
+  render_text->SetText(u"😁y");
   render_text->Draw(canvas());
 
-  // Emoji codepoints are replaced by bullets (e.g. "\u2022\u2022").
-  EXPECT_EQ(u"\u2022\u2022", render_text->GetDisplayText());
+  // Emoji codepoints are replaced by bullets.
+  EXPECT_EQ(u"••", render_text->GetDisplayText());
   EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(0U));
   EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(1U));
   EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(2U));
@@ -1412,12 +1397,11 @@
   EXPECT_EQ(3U, test_api()->DisplayIndexToTextIndex(2U));
 
   // Test two U+1F4F7 📷 "Camera" characters in a row.
-  // Windows requires wide strings for \Unnnnnnnn universal character names.
-  render_text->SetText(u"\U0001F4F7\U0001F4F7");
+  render_text->SetText(u"📷📷");
   render_text->Draw(canvas());
 
-  // Emoji codepoints are replaced by bullets (e.g. "\u2022\u2022").
-  EXPECT_EQ(u"\u2022\u2022", render_text->GetDisplayText());
+  // Emoji codepoints are replaced by bullets.
+  EXPECT_EQ(u"••", render_text->GetDisplayText());
   EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(0U));
   EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(1U));
   EXPECT_EQ(1U, test_api()->TextIndexToDisplayIndex(2U));
@@ -1430,7 +1414,7 @@
   render_text->SetObscuredRevealIndex(0);
   render_text->Draw(canvas());
 
-  EXPECT_EQ(u"\U0001F4F7\u2022", render_text->GetDisplayText());
+  EXPECT_EQ(u"📷•", render_text->GetDisplayText());
   EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(0U));
   EXPECT_EQ(0U, test_api()->TextIndexToDisplayIndex(1U));
   EXPECT_EQ(2U, test_api()->TextIndexToDisplayIndex(2U));
@@ -1444,7 +1428,7 @@
 TEST_F(RenderTextTest, ObscuredEmojiRevealed) {
   RenderText* render_text = GetRenderText();
 
-  std::u16string text = u"123\U0001F4F7\U0001F4F7x\U0001F601-";
+  std::u16string text = u"123📷📷x😁-";
   for (size_t i = 0; i < text.length(); ++i) {
     render_text->SetText(text);
     render_text->SetObscured(true);
@@ -1455,7 +1439,7 @@
 
 struct TextIndexConversionCase {
   const char* test_name;
-  const wchar_t* text;
+  const char16_t* text;
 };
 
 using TextIndexConversionParam =
@@ -1481,7 +1465,7 @@
   size_t reveal_index = std::get<2>(GetParam());
 
   RenderText* render_text = GetRenderText();
-  render_text->SetText(WideToUTF16(param.text));
+  render_text->SetText(param.text);
   render_text->SetObscured(obscured);
   render_text->SetObscuredRevealIndex(reveal_index);
   render_text->Draw(canvas());
@@ -1509,15 +1493,15 @@
 }
 
 const TextIndexConversionCase kTextIndexConversionCases[] = {
-    {"simple", L"abc"},
-    {"simple_obscured1", L"abc"},
-    {"simple_obscured2", L"abc"},
-    {"emoji_asc", L"\U0001F6281234"},
-    {"emoji_asc_obscured0", L"\U0001F6281234"},
-    {"emoji_asc_obscured2", L"\U0001F6281234"},
-    {"picto_title", L"x☛"},
-    {"simple_mixed", L"aaڭڭcc"},
-    {"simple_rtl", L"أسكي"},
+    {"simple", u"abc"},
+    {"simple_obscured1", u"abc"},
+    {"simple_obscured2", u"abc"},
+    {"emoji_asc", u"😨1234"},
+    {"emoji_asc_obscured0", u"😨1234"},
+    {"emoji_asc_obscured2", u"😨1234"},
+    {"picto_title", u"x☛"},
+    {"simple_mixed", u"aaڭڭcc"},
+    {"simple_rtl", u"أسكي"},
 };
 
 // Validate that conversion text and between display text indexes are consistent
@@ -1527,12 +1511,12 @@
     RenderTextTestWithTextIndexConversionCase,
     ::testing::Combine(::testing::ValuesIn(kTextIndexConversionCases),
                        testing::Values(false, true),
-                       testing::Values(0, 1, 4)),
+                       testing::Values(0, 1, 3)),
     RenderTextTestWithTextIndexConversionCase::ParamInfoToString);
 
 struct RunListCase {
   const char* test_name;
-  const wchar_t* text;
+  const char16_t* text;
   const char* expected;
   const bool multiline = false;
 };
@@ -1551,40 +1535,39 @@
   RunListCase param = GetParam();
   RenderTextHarfBuzz* render_text = GetRenderText();
   render_text->SetMultiline(param.multiline);
-  render_text->SetText(WideToUTF16(param.text));
+  render_text->SetText(param.text);
   EXPECT_EQ(param.expected, GetRunListStructureString());
 }
 
 const RunListCase kBasicsRunListCases[] = {
-    {"simpleLTR", L"abc", "[0->2]"},
-    {"simpleRTL", L"ښڛڜ", "[2<-0]"},
-    {"asc_arb", L"abcښڛڜdef", "[0->2][5<-3][6->8]"},
-    {"asc_dev_asc", L"abcऔकखdefڜ", "[0->2][3->5][6->8][9]"},
-    {"phone", L"1-(800)-xxx-xxxx", "[0][1][2][3->5][6][7][8->10][11][12->15]"},
-    {"dev_ZWS", L"क\u200Bख", "[0][1][2]"},
-    {"numeric", L"1 2 3 4", "[0][1][2][3][4][5][6]"},
-    {"joiners1", L"1\u200C2\u200C3\u200C4", "[0->6]"},
-    {"joiners2", L"\u060F\u200C\u060F", "[0->2]"},
-    {"combining_accents1", L"a\u0300e\u0301", "[0->3]"},
-    {"combining_accents2", L"\u0065\u0308\u0435\u0308", "[0->1][2->3]"},
-    {"picto_title", L"☞☛test☚☜", "[0->1][2->5][6->7]"},
-    {"picto_LTR", L"☺☺☺!", "[0->2][3]"},
-    {"picto_RTL", L"☺☺☺ښ", "[3][2<-0]"},
-    {"paren_picto", L"(☾☹☽)", "[0][1][2][3][4]"},
-    {"emoji_asc", L"\U0001F6281234",
-     "[0->1][2->5]"},  // http://crbug.com/530021
-    {"emoji_title", L"▶Feel goods",
+    {"simpleLTR", u"abc", "[0->2]"},
+    {"simpleRTL", u"ښڛڜ", "[2<-0]"},
+    {"asc_arb", u"abcښڛڜdef", "[0->2][5<-3][6->8]"},
+    {"asc_dev_asc", u"abcऔकखdefڜ", "[0->2][3->5][6->8][9]"},
+    {"phone", u"1-(800)-xxx-xxxx", "[0][1][2][3->5][6][7][8->10][11][12->15]"},
+    {"dev_ZWS", u"क\u200Bख", "[0][1][2]"},
+    {"numeric", u"1 2 3 4", "[0][1][2][3][4][5][6]"},
+    {"joiners1", u"1\u200C2\u200C3\u200C4", "[0->6]"},
+    {"joiners2", u"؏\u200C؏", "[0->2]"},
+    {"combining_accents1", u"àé", "[0->3]"},
+    {"combining_accents2", u"ëё", "[0->1][2->3]"},
+    {"picto_title", u"☞☛test☚☜", "[0->1][2->5][6->7]"},
+    {"picto_LTR", u"☺☺☺!", "[0->2][3]"},
+    {"picto_RTL", u"☺☺☺ښ", "[3][2<-0]"},
+    {"paren_picto", u"(☾☹☽)", "[0][1][2][3][4]"},
+    {"emoji_asc", u"😨1234", "[0->1][2->5]"},  // http://crbug.com/530021
+    {"emoji_title", u"▶Feel goods",
      "[0][1->4][5][6->10]"},  // http://crbug.com/278913
-    {"jap_paren1", L"ぬ「シ」ほ",
+    {"jap_paren1", u"ぬ「シ」ほ",
      "[0][1][2][3][4]"},  // http://crbug.com/396776
-    {"jap_paren2", L"國哲(c)1",
+    {"jap_paren2", u"國哲(c)1",
      "[0->1][2][3][4][5]"},  // http://crbug.com/125792
-    {"newline1", L"\n\n", "[0->1]"},
-    {"newline2", L"\r\n\r\n", "[0->3]"},
-    {"newline3", L"\r\r\n", "[0->2]"},
-    {"multiline_newline1", L"\n\n", "[0][1]", true},
-    {"multiline_newline2", L"\r\n\r\n", "[0->1][2->3]", true},
-    {"multiline_newline3", L"\r\r\n", "[0][1->2]", true},
+    {"newline1", u"\n\n", "[0->1]"},
+    {"newline2", u"\r\n\r\n", "[0->3]"},
+    {"newline3", u"\r\r\n", "[0->2]"},
+    {"multiline_newline1", u"\n\n", "[0][1]", true},
+    {"multiline_newline2", u"\r\n\r\n", "[0->1][2->3]", true},
+    {"multiline_newline3", u"\r\r\n", "[0][1->2]", true},
 };
 
 INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsBasics,
@@ -1594,38 +1577,38 @@
 
 // see 'Unicode Bidirectional Algorithm': http://unicode.org/reports/tr9/
 const RunListCase kBidiRunListCases[] = {
-    {"simple_ltr", L"ascii", "[0->4]"},
-    {"simple_rtl", L"أسكي", "[3<-0]"},
-    {"simple_mixed", L"aaڭڭcc", "[0->1][3<-2][4->5]"},
-    {"simple_mixed_LRE", L"\u202Aaaڭڭcc\u202C", "[0][1->2][4<-3][5->6][7]"},
-    {"simple_mixed_RLE", L"\u202Baaڭڭcc\u202C", "[7][5->6][4<-3][0][1->2]"},
-    {"sequence_RLE", L"\u202Baa\u202C\u202Bbb\u202C",
+    {"simple_ltr", u"ascii", "[0->4]"},
+    {"simple_rtl", u"أسكي", "[3<-0]"},
+    {"simple_mixed", u"aaڭڭcc", "[0->1][3<-2][4->5]"},
+    {"simple_mixed_LRE", u"\u202Aaaڭڭcc\u202C", "[0][1->2][4<-3][5->6][7]"},
+    {"simple_mixed_RLE", u"\u202Baaڭڭcc\u202C", "[7][5->6][4<-3][0][1->2]"},
+    {"sequence_RLE", u"\u202Baa\u202C\u202Bbb\u202C",
      "[7][0][1->2][3->4][5->6]"},
-    {"simple_mixed_LRI", L"\u2066aaڭڭcc\u2069", "[0][1->2][4<-3][5->6][7]"},
-    {"simple_mixed_RLI", L"\u2067aaڭڭcc\u2069", "[0][5->6][4<-3][1->2][7]"},
-    {"sequence_RLI", L"\u2067aa\u2069\u2067bb\u2069",
+    {"simple_mixed_LRI", u"\u2066aaڭڭcc\u2069", "[0][1->2][4<-3][5->6][7]"},
+    {"simple_mixed_RLI", u"\u2067aaڭڭcc\u2069", "[0][5->6][4<-3][1->2][7]"},
+    {"sequence_RLI", u"\u2067aa\u2069\u2067bb\u2069",
      "[0][1->2][3->4][5->6][7]"},
-    {"override_ltr_RLO", L"\u202Eaaa\u202C", "[4][3<-1][0]"},
-    {"override_rtl_LRO", L"\u202Dڭڭڭ\u202C", "[0][1->3][4]"},
-    {"neutral_strong_ltr", L"a!!a", "[0][1->2][3]"},
-    {"neutral_strong_rtl", L"ڭ!!ڭ", "[3][2<-1][0]"},
-    {"neutral_strong_both", L"a a ڭ ڭ", "[0][1][2][3][6][5][4]"},
-    {"neutral_strong_both_RLE", L"\u202Ba a ڭ ڭ\u202C",
+    {"override_ltr_RLO", u"\u202Eaaa\u202C", "[4][3<-1][0]"},
+    {"override_rtl_LRO", u"\u202Dڭڭڭ\u202C", "[0][1->3][4]"},
+    {"neutral_strong_ltr", u"a!!a", "[0][1->2][3]"},
+    {"neutral_strong_rtl", u"ڭ!!ڭ", "[3][2<-1][0]"},
+    {"neutral_strong_both", u"a a ڭ ڭ", "[0][1][2][3][6][5][4]"},
+    {"neutral_strong_both_RLE", u"\u202Ba a ڭ ڭ\u202C",
      "[8][7][6][5][4][0][1][2][3]"},
-    {"weak_numbers", L"one ڭ222ڭ", "[0->2][3][8][5->7][4]"},
-    {"not_weak_letters", L"one ڭabcڭ", "[0->2][3][4][5->7][8]"},
-    {"weak_arabic_numbers", L"one ڭ١٢٣ڭ", "[0->2][3][8][5->7][4]"},
-    {"neutral_LRM_pre", L"\u200E\u2026\u2026", "[0->2]"},
-    {"neutral_LRM_post", L"\u2026\u2026\u200E", "[0->2]"},
-    {"neutral_RLM_pre", L"\u200F\u2026\u2026", "[2<-0]"},
-    {"neutral_RLM_post", L"\u2026\u2026\u200F", "[2<-0]"},
-    {"brackets_ltr", L"aa(ڭڭ)\u2026\u2026", "[0->1][2][4<-3][5][6->7]"},
-    {"brackets_rtl", L"ڭڭ(aa)\u2026\u2026", "[7<-6][5][3->4][2][1<-0]"},
-    {"mixed_with_punct", L"aa \"ڭڭ!\", aa",
+    {"weak_numbers", u"one ڭ222ڭ", "[0->2][3][8][5->7][4]"},
+    {"not_weak_letters", u"one ڭabcڭ", "[0->2][3][4][5->7][8]"},
+    {"weak_arabic_numbers", u"one ڭ١٢٣ڭ", "[0->2][3][8][5->7][4]"},
+    {"neutral_LRM_pre", u"\u200E……", "[0->2]"},
+    {"neutral_LRM_post", u"……\u200E", "[0->2]"},
+    {"neutral_RLM_pre", u"\u200F……", "[2<-0]"},
+    {"neutral_RLM_post", u"……\u200F", "[2<-0]"},
+    {"brackets_ltr", u"aa(ڭڭ)……", "[0->1][2][4<-3][5][6->7]"},
+    {"brackets_rtl", u"ڭڭ(aa)……", "[7<-6][5][3->4][2][1<-0]"},
+    {"mixed_with_punct", u"aa \"ڭڭ!\", aa",
      "[0->1][2][3][5<-4][6->8][9][10->11]"},
-    {"mixed_with_punct_RLI", L"aa \"\u2067ڭڭ!\u2069\", aa",
+    {"mixed_with_punct_RLI", u"aa \"\u2067ڭڭ!\u2069\", aa",
      "[0->1][2][3][4][7][6<-5][8][9->10][11][12->13]"},
-    {"mixed_with_punct_RLM", L"aa \"ڭڭ!\u200F\", aa",
+    {"mixed_with_punct_RLM", u"aa \"ڭڭ!\u200F\", aa",
      "[0->1][2][3][7][6][5<-4][8->9][10][11->12]"},
 };
 
@@ -1635,18 +1618,17 @@
                          RenderTextTestWithRunListCase::ParamInfoToString);
 
 const RunListCase kBracketsRunListCases[] = {
-    {"matched_parens", L"(a)", "[0][1][2]"},
-    {"double_matched_parens", L"((a))", "[0->1][2][3->4]"},
-    {"double_matched_parens2", L"((aaa))", "[0->1][2->4][5->6]"},
-    {"square_brackets", L"[...]x", "[0][1->3][4][5]"},
-    {"curly_brackets", L"{}x{}", "[0->1][2][3->4]"},
-    {"style_brackets", L"\u300c...\u300dx", "[0][1->3][4][5]"},
-    {"tibetan_brackets", L"\u0f3a\u0f3b\u0f20\u0f20\u0f3c\u0f3d",
-     "[0->1][2->3][4->5]"},
-    {"angle_brackets", L"\u3008\u3007\u3007\u3009", "[0][1->2][3]"},
-    {"double_angle_brackets", L"\u300A\u3007\u3007\u300B", "[0][1->2][3]"},
-    {"corner_angle_brackets", L"\u300C\u3007\u3007\u300D", "[0][1->2][3]"},
-    {"fullwidth_parens", L"\uff08\uff01\uff09", "[0][1][2]"},
+    {"matched_parens", u"(a)", "[0][1][2]"},
+    {"double_matched_parens", u"((a))", "[0->1][2][3->4]"},
+    {"double_matched_parens2", u"((aaa))", "[0->1][2->4][5->6]"},
+    {"square_brackets", u"[...]x", "[0][1->3][4][5]"},
+    {"curly_brackets", u"{}x{}", "[0->1][2][3->4]"},
+    {"style_brackets", u"「...」x", "[0][1->3][4][5]"},
+    {"tibetan_brackets", u"༺༻༠༠༼༽", "[0->1][2->3][4->5]"},
+    {"angle_brackets", u"〈〇〇〉", "[0][1->2][3]"},
+    {"double_angle_brackets", u"《〇〇》", "[0][1->2][3]"},
+    {"corner_angle_brackets", u"「〇〇」", "[0][1->2][3]"},
+    {"fullwidth_parens", u"(!)", "[0][1][2]"},
 };
 
 INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsBrackets,
@@ -1658,15 +1640,15 @@
 // account while performing the text itemization.
 // See table 7 from http://www.unicode.org/reports/tr24/tr24-29.html
 const RunListCase kScriptExtensionRunListCases[] = {
-    {"implicit_com_inherited", L"a\u0301", "[0->1]"},
-    {"explicit_lat", L"\u0061d", "[0->1]"},
-    {"explicit_inherited_lat", L"x\u0363d", "[0->2]"},
-    {"explicit_inherited_dev", L"क\u1CD1क", "[0->2]"},
-    {"multi_explicit_hira", L"は\u30FCz", "[0->1][2]"},
-    {"multi_explicit_kana", L"ハ\u30FCz", "[0->1][2]"},
-    {"multi_explicit_lat", L"a\u30FCz", "[0][1][2]"},
-    {"multi_explicit_impl_dev", L"क\u1CD0z", "[0->1][2]"},
-    {"multi_explicit_expl_dev", L"क\u096Fz", "[0->1][2]"},
+    {"implicit_com_inherited", u"a\u0301", "[0->1]"},
+    {"explicit_lat", u"\u0061d", "[0->1]"},
+    {"explicit_inherited_lat", u"x\u0363d", "[0->2]"},
+    {"explicit_inherited_dev", u"क\u1CD1क", "[0->2]"},
+    {"multi_explicit_hira", u"は\u30FCz", "[0->1][2]"},
+    {"multi_explicit_kana", u"ハ\u30FCz", "[0->1][2]"},
+    {"multi_explicit_lat", u"a\u30FCz", "[0][1][2]"},
+    {"multi_explicit_impl_dev", u"क\u1CD0z", "[0->1][2]"},
+    {"multi_explicit_expl_dev", u"क\u096Fz", "[0->1][2]"},
 };
 
 INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsScriptExtension,
@@ -1679,111 +1661,111 @@
 // See ScriptExtensions.txt and Scripts.txt from
 // http://www.unicode.org/reports/tr24/tr24-29.html
 const RunListCase kScriptsRunListCases[] = {
-    {"lat", L"abc", "[0->2]"},
-    {"lat_diac", L"e\u0308f", "[0->2]"},
+    {"lat", u"abc", "[0->2]"},
+    {"lat_diac", u"e\u0308f", "[0->2]"},
     // Indic Fraction codepoints have large set of script extensions.
-    {"indic_fraction", L"\uA830\uA832\uA834\uA835", "[0->3]"},
+    {"indic_fraction", u"\uA830\uA832\uA834\uA835", "[0->3]"},
     // Devanagari Danda codepoints have large set of script extensions.
-    {"dev_danda", L"\u0964\u0965", "[0->1]"},
+    {"dev_danda", u"\u0964\u0965", "[0->1]"},
     // Combining Diacritical Marks (inherited) should only merge with preceding.
-    {"diac_lat", L"\u0308fg", "[0][1->2]"},
-    {"diac_dev", L"क\u0308f", "[0->1][2]"},
+    {"diac_lat", u"\u0308fg", "[0][1->2]"},
+    {"diac_dev", u"क\u0308f", "[0->1][2]"},
     // ZWJW has the inherited script.
-    {"lat_ZWNJ", L"ab\u200Ccd", "[0->4]"},
-    {"dev_ZWNJ", L"क\u200Cक", "[0->2]"},
-    {"lat_dev_ZWNJ", L"a\u200Cक", "[0->1][2]"},
+    {"lat_ZWNJ", u"ab\u200Ccd", "[0->4]"},
+    {"dev_ZWNJ", u"क\u200Cक", "[0->2]"},
+    {"lat_dev_ZWNJ", u"a\u200Cक", "[0->1][2]"},
     // Invalid codepoints.
-    {"invalid_cp", L"\uFFFE", "[0]"},
-    {"invalid_cps", L"\uFFFE\uFFFF", "[0->1]"},
-    {"unknown", L"a\u243Fb", "[0][1][2]"},
+    {"invalid_cp", u"\uFFFE", "[0]"},
+    {"invalid_cps", u"\uFFFE\uFFFF", "[0->1]"},
+    {"unknown", u"a\u243Fb", "[0][1][2]"},
 
     // Codepoints from different code block should be in same run when part of
     // the same script.
-    {"blocks_lat", L"aɒɠƉĚÑ", "[0->5]"},
-    {"blocks_lat_paren", L"([_!_])", "[0->1][2->4][5->6]"},
-    {"blocks_lat_sub", L"ₐₑaeꬱ", "[0->4]"},
-    {"blocks_lat_smallcap", L"ꟺM", "[0->1]"},
-    {"blocks_lat_small_letter", L"ᶓᶍᶓᴔᴟ", "[0->4]"},
-    {"blocks_lat_acc", L"eéěĕȩɇḕẻếⱻꞫ", "[0->10]"},
-    {"blocks_com", L"⟦ℳ¥¾⟾⁸⟧Ⓔ", "[0][1][2->3][4][5][6][7]"},
+    {"blocks_lat", u"aɒɠƉĚÑ", "[0->5]"},
+    {"blocks_lat_paren", u"([_!_])", "[0->1][2->4][5->6]"},
+    {"blocks_lat_sub", u"ₐₑaeꬱ", "[0->4]"},
+    {"blocks_lat_smallcap", u"ꟺM", "[0->1]"},
+    {"blocks_lat_small_letter", u"ᶓᶍᶓᴔᴟ", "[0->4]"},
+    {"blocks_lat_acc", u"eéěĕȩɇḕẻếⱻꞫ", "[0->10]"},
+    {"blocks_com", u"⟦ℳ¥¾⟾⁸⟧Ⓔ", "[0][1][2->3][4][5][6][7]"},
 
     // Latin script.
-    {"latin_numbers", L"a1b2c3", "[0][1][2][3][4][5]"},
-    {"latin_puncts1", L"a,b,c.", "[0][1][2][3][4][5]"},
-    {"latin_puncts2", L"aa,bb,cc!!", "[0->1][2][3->4][5][6->7][8->9]"},
-    {"latin_diac_multi", L"a\u0300e\u0352i", "[0->4]"},
+    {"latin_numbers", u"a1b2c3", "[0][1][2][3][4][5]"},
+    {"latin_puncts1", u"a,b,c.", "[0][1][2][3][4][5]"},
+    {"latin_puncts2", u"aa,bb,cc!!", "[0->1][2][3->4][5][6->7][8->9]"},
+    {"latin_diac_multi", u"a\u0300e\u0352i", "[0->4]"},
 
     // Common script.
-    {"common_tm", L"•bug™", "[0][1->3][4]"},
-    {"common_copyright", L"chromium©", "[0->7][8]"},
-    {"common_math1", L"ℳ: ¬ƒ(x)=½×¾", "[0][1][2][3][4][5][6][7][8][9->11]"},
-    {"common_math2", L"𝟏×𝟑", "[0->1][2][3->4]"},
-    {"common_numbers", L"🄀𝟭𝟐⒓¹²", "[0->1][2->5][6][7->8]"},
-    {"common_puncts", L",.\u0083!", "[0->1][2][3]"},
+    {"common_tm", u"•bug™", "[0][1->3][4]"},
+    {"common_copyright", u"chromium©", "[0->7][8]"},
+    {"common_math1", u"ℳ: ¬ƒ(x)=½×¾", "[0][1][2][3][4][5][6][7][8][9->11]"},
+    {"common_math2", u"𝟏×𝟑", "[0->1][2][3->4]"},
+    {"common_numbers", u"🄀𝟭𝟐⒓¹²", "[0->1][2->5][6][7->8]"},
+    {"common_puncts", u",.\u0083!", "[0->1][2][3]"},
 
     // Arabic script.
-    {"arabic", L"\u0633\u069b\u0763\u077f\u08A2\uFB53", "[5<-0]"},
-    {"arabic_lat", L"\u0633\u069b\u0763\u077f\u08A2\uFB53abc", "[6->8][5<-0]"},
-    {"arabic_word_ligatures", L"\uFDFD\uFDF3", "[1<-0]"},
-    {"arabic_diac", L"\u069D\u0300", "[1<-0]"},
-    {"arabic_diac_lat", L"\u069D\u0300abc", "[2->4][1<-0]"},
-    {"arabic_diac_lat2", L"abc\u069D\u0300abc", "[0->2][4<-3][5->7]"},
-    {"arabic_lyd", L"\U00010935\U00010930\u06B0\u06B1", "[5<-4][3<-0]"},
-    {"arabic_numbers", L"12\u06D034", "[3->4][2][0->1]"},
-    {"arabic_letters", L"ab\u06D0cd", "[0->1][2][3->4]"},
-    {"arabic_mixed", L"a1\u06D02d", "[0][1][3][2][4]"},
-    {"arabic_coptic1", L"\u06D0\U000102E2\u2CB2", "[1->3][0]"},
-    {"arabic_coptic2", L"\u2CB2\U000102E2\u06D0", "[0->2][3]"},
+    {"arabic", u"\u0633\u069b\u0763\u077f\u08A2\uFB53", "[5<-0]"},
+    {"arabic_lat", u"\u0633\u069b\u0763\u077f\u08A2\uFB53abc", "[6->8][5<-0]"},
+    {"arabic_word_ligatures", u"\uFDFD\uFDF3", "[1<-0]"},
+    {"arabic_diac", u"\u069D\u0300", "[1<-0]"},
+    {"arabic_diac_lat", u"\u069D\u0300abc", "[2->4][1<-0]"},
+    {"arabic_diac_lat2", u"abc\u069D\u0300abc", "[0->2][4<-3][5->7]"},
+    {"arabic_lyd", u"\U00010935\U00010930\u06B0\u06B1", "[5<-4][3<-0]"},
+    {"arabic_numbers", u"12\u06D034", "[3->4][2][0->1]"},
+    {"arabic_letters", u"ab\u06D0cd", "[0->1][2][3->4]"},
+    {"arabic_mixed", u"a1\u06D02d", "[0][1][3][2][4]"},
+    {"arabic_coptic1", u"\u06D0\U000102E2\u2CB2", "[1->3][0]"},
+    {"arabic_coptic2", u"\u2CB2\U000102E2\u06D0", "[0->2][3]"},
 
     // Devanagari script.
-    {"devanagari1", L"ञटठडढणतथ", "[0->7]"},
-    {"devanagari2", L"ढ꣸ꣴ", "[0->2]"},
-    {"devanagari_vowels", L"\u0915\u093F\u0915\u094C", "[0->3]"},
-    {"devanagari_consonants", L"\u0915\u094D\u0937", "[0->2]"},
+    {"devanagari1", u"ञटठडढणतथ", "[0->7]"},
+    {"devanagari2", u"ढ꣸ꣴ", "[0->2]"},
+    {"devanagari_vowels", u"\u0915\u093F\u0915\u094C", "[0->3]"},
+    {"devanagari_consonants", u"\u0915\u094D\u0937", "[0->2]"},
 
     // Ethiopic script.
-    {"ethiopic", L"መጩጪᎅⶹⶼꬣꬦ", "[0->7]"},
-    {"ethiopic_numbers", L"1ቨቤ2", "[0][1->2][3]"},
-    {"ethiopic_mixed1", L"abቨቤ12", "[0->1][2->3][4->5]"},
-    {"ethiopic_mixed2", L"a1ቨቤb2", "[0][1][2->3][4][5]"},
+    {"ethiopic", u"መጩጪᎅⶹⶼꬣꬦ", "[0->7]"},
+    {"ethiopic_numbers", u"1ቨቤ2", "[0][1->2][3]"},
+    {"ethiopic_mixed1", u"abቨቤ12", "[0->1][2->3][4->5]"},
+    {"ethiopic_mixed2", u"a1ቨቤb2", "[0][1][2->3][4][5]"},
 
     // Georgian script.
-    {"georgian1", L"ႼႽႾႿჀჁჂჳჴჵ", "[0->9]"},
-    {"georgian2", L"ლⴊⴅ", "[0->2]"},
-    {"georgian_numbers", L"1ლⴊⴅ2", "[0][1->3][4]"},
-    {"georgian_mixed", L"a1ლⴊⴅb2", "[0][1][2->4][5][6]"},
+    {"georgian1", u"ႼႽႾႿჀჁჂჳჴჵ", "[0->9]"},
+    {"georgian2", u"ლⴊⴅ", "[0->2]"},
+    {"georgian_numbers", u"1ლⴊⴅ2", "[0][1->3][4]"},
+    {"georgian_mixed", u"a1ლⴊⴅb2", "[0][1][2->4][5][6]"},
 
     // Telugu script.
-    {"telugu_lat", L"aaఉయ!", "[0->1][2->3][4]"},
-    {"telugu_numbers", L"123౦౧౨456౩౪౫", "[0->2][3->5][6->8][9->11]"},
-    {"telugu_puncts", L"కురుచ, చిఱుత, చేరువ, చెఱువు!",
+    {"telugu_lat", u"aaఉయ!", "[0->1][2->3][4]"},
+    {"telugu_numbers", u"123౦౧౨456౩౪౫", "[0->2][3->5][6->8][9->11]"},
+    {"telugu_puncts", u"కురుచ, చిఱుత, చేరువ, చెఱువు!",
      "[0->4][5][6][7->11][12][13][14->18][19][20][21->26][27]"},
 
     // Control Pictures.
-    {"control_pictures", L"␑␒␓␔␕␖␗␘␙␚␛", "[0->10]"},
-    {"control_pictures_rewrite", L"␑\t␛", "[0->2]"},
+    {"control_pictures", u"␑␒␓␔␕␖␗␘␙␚␛", "[0->10]"},
+    {"control_pictures_rewrite", u"␑\t␛", "[0->2]"},
 
     // Unicode art.
-    {"unicode_emoticon1", L"(▀̿ĺ̯▀̿ ̿)", "[0][1->2][3->4][5->6][7->8][9]"},
-    {"unicode_emoticon2", L"▀̿̿Ĺ̯̿▀̿ ", "[0->2][3->5][6->7][8]"},
-    {"unicode_emoticon3", L"( ͡° ͜ʖ ͡°)", "[0][1->2][3][4->5][6][7->8][9][10]"},
-    {"unicode_emoticon4", L"✩·͙*̩̩͙˚̩̥̩̥( ͡ᵔ ͜ʖ ͡ᵔ )*̩̩͙✩·͙˚̩̥̩̥.",
+    {"unicode_emoticon1", u"(▀̿ĺ̯▀̿ ̿)", "[0][1->2][3->4][5->6][7->8][9]"},
+    {"unicode_emoticon2", u"▀̿̿Ĺ̯̿▀̿ ", "[0->2][3->5][6->7][8]"},
+    {"unicode_emoticon3", u"( ͡° ͜ʖ ͡°)", "[0][1->2][3][4->5][6][7->8][9][10]"},
+    {"unicode_emoticon4", u"✩·͙*̩̩͙˚̩̥̩̥( ͡ᵔ ͜ʖ ͡ᵔ )*̩̩͙✩·͙˚̩̥̩̥.",
      "[0][1->2][3->6][7->11][12][13->14][15][16->17][18][19->20][21][22][23]["
      "24->27][28][29->30][31->35][36]"},
-    {"unicode_emoticon5", L"ヽ(͡◕ ͜ʖ ͡◕)ノ",
+    {"unicode_emoticon5", u"ヽ(͡◕ ͜ʖ ͡◕)ノ",
      "[0][1->2][3][4->5][6][7->8][9][10][11]"},
-    {"unicode_art1", L"꧁༒✧ Great ✧༒꧂", "[0][1][2][3][4->8][9][10][11][12]"},
-    {"unicode_art2", L"t͎e͎s͎t͎", "[0->7]"},
+    {"unicode_art1", u"꧁༒✧ Great ✧༒꧂", "[0][1][2][3][4->8][9][10][11][12]"},
+    {"unicode_art2", u"t͎e͎s͎t͎", "[0->7]"},
 
     // Combining diacritical sequences.
-    {"unicode_diac1", L"\u2123\u0336", "[0->1]"},
-    {"unicode_diac2", L"\u273c\u0325", "[0->1]"},
-    {"unicode_diac3", L"\u2580\u033f", "[0->1]"},
-    {"unicode_diac4", L"\u2022\u0325\u0329", "[0->2]"},
-    {"unicode_diac5", L"\u2022\u0325", "[0->1]"},
-    {"unicode_diac6", L"\u00b7\u0359\u0325", "[0->2]"},
-    {"unicode_diac7", L"\u2027\u0329\u0325", "[0->2]"},
-    {"unicode_diac8", L"\u0332\u0305\u03c1", "[0->1][2]"},
+    {"unicode_diac1", u"\u2123\u0336", "[0->1]"},
+    {"unicode_diac2", u"\u273c\u0325", "[0->1]"},
+    {"unicode_diac3", u"\u2580\u033f", "[0->1]"},
+    {"unicode_diac4", u"\u2022\u0325\u0329", "[0->2]"},
+    {"unicode_diac5", u"\u2022\u0325", "[0->1]"},
+    {"unicode_diac6", u"\u00b7\u0359\u0325", "[0->2]"},
+    {"unicode_diac7", u"\u2027\u0329\u0325", "[0->2]"},
+    {"unicode_diac8", u"\u0332\u0305\u03c1", "[0->1][2]"},
 };
 
 INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsScripts,
@@ -1797,96 +1779,96 @@
 const RunListCase kEmojiRunListCases[] = {
     // Samples from
     // https://www.unicode.org/Public/emoji/12.0/emoji-data.txt
-    {"number_sign", L"\u0023", "[0]"},
-    {"keyboard", L"\u2328", "[0]"},
-    {"aries", L"\u2648", "[0]"},
-    {"candle", L"\U0001F56F", "[0->1]"},
-    {"anchor", L"\u2693", "[0]"},
-    {"grinning_face", L"\U0001F600", "[0->1]"},
-    {"face_with_monocle", L"\U0001F9D0", "[0->1]"},
-    {"light_skin_tone", L"\U0001F3FB", "[0->1]"},
-    {"index_pointing_up", L"\u261D", "[0]"},
-    {"horse_racing", L"\U0001F3C7", "[0->1]"},
-    {"kiss", L"\U0001F48F", "[0->1]"},
-    {"couple_with_heart", L"\U0001F491", "[0->1]"},
-    {"people_wrestling", L"\U0001F93C", "[0->1]"},
-    {"eject_button", L"\u23CF", "[0]"},
+    {"number_sign", u"\u0023", "[0]"},
+    {"keyboard", u"\u2328", "[0]"},
+    {"aries", u"\u2648", "[0]"},
+    {"candle", u"\U0001F56F", "[0->1]"},
+    {"anchor", u"\u2693", "[0]"},
+    {"grinning_face", u"\U0001F600", "[0->1]"},
+    {"face_with_monocle", u"\U0001F9D0", "[0->1]"},
+    {"light_skin_tone", u"\U0001F3FB", "[0->1]"},
+    {"index_pointing_up", u"\u261D", "[0]"},
+    {"horse_racing", u"\U0001F3C7", "[0->1]"},
+    {"kiss", u"\U0001F48F", "[0->1]"},
+    {"couple_with_heart", u"\U0001F491", "[0->1]"},
+    {"people_wrestling", u"\U0001F93C", "[0->1]"},
+    {"eject_button", u"\u23CF", "[0]"},
 
     // Samples from
     // https://unicode.org/Public/emoji/12.0/emoji-sequences.txt
-    {"watch", L"\u231A", "[0]"},
-    {"cross_mark", L"\u274C", "[0]"},
-    {"copyright", L"\u00A9\uFE0F", "[0->1]"},
-    {"stop_button", L"\u23F9\uFE0F", "[0->1]"},
-    {"passenger_ship", L"\U0001F6F3\uFE0F", "[0->2]"},
-    {"keycap_star", L"\u002A\uFE0F\u20E3", "[0->2]"},
-    {"keycap_6", L"\u0036\uFE0F\u20E3", "[0->2]"},
-    {"flag_andorra", L"\U0001F1E6\U0001F1E9", "[0->3]"},
-    {"flag_egypt", L"\U0001F1EA\U0001F1EC", "[0->3]"},
+    {"watch", u"\u231A", "[0]"},
+    {"cross_mark", u"\u274C", "[0]"},
+    {"copyright", u"\u00A9\uFE0F", "[0->1]"},
+    {"stop_button", u"\u23F9\uFE0F", "[0->1]"},
+    {"passenger_ship", u"\U0001F6F3\uFE0F", "[0->2]"},
+    {"keycap_star", u"\u002A\uFE0F\u20E3", "[0->2]"},
+    {"keycap_6", u"\u0036\uFE0F\u20E3", "[0->2]"},
+    {"flag_andorra", u"\U0001F1E6\U0001F1E9", "[0->3]"},
+    {"flag_egypt", u"\U0001F1EA\U0001F1EC", "[0->3]"},
     {"flag_england",
-     L"\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F",
+     u"\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F",
      "[0->13]"},
-    {"index_up_light", L"\u261D\U0001F3FB", "[0->2]"},
-    {"person_bouncing_ball_light", L"\u26F9\U0001F3FC", "[0->2]"},
-    {"victory_hand_med_light", L"\u270C\U0001F3FC", "[0->2]"},
-    {"horse_racing_med_dark", L"\U0001F3C7\U0001F3FE", "[0->3]"},
-    {"woman_man_hands_light", L"\U0001F46B\U0001F3FB", "[0->3]"},
-    {"person_haircut_med_light", L"\U0001F487\U0001F3FC", "[0->3]"},
-    {"pinching_hand_light", L"\U0001F90F\U0001F3FB", "[0->3]"},
-    {"love_you_light", L"\U0001F91F\U0001F3FB", "[0->3]"},
-    {"leg_dark", L"\U0001F9B5\U0001F3FF", "[0->3]"},
+    {"index_up_light", u"\u261D\U0001F3FB", "[0->2]"},
+    {"person_bouncing_ball_light", u"\u26F9\U0001F3FC", "[0->2]"},
+    {"victory_hand_med_light", u"\u270C\U0001F3FC", "[0->2]"},
+    {"horse_racing_med_dark", u"\U0001F3C7\U0001F3FE", "[0->3]"},
+    {"woman_man_hands_light", u"\U0001F46B\U0001F3FB", "[0->3]"},
+    {"person_haircut_med_light", u"\U0001F487\U0001F3FC", "[0->3]"},
+    {"pinching_hand_light", u"\U0001F90F\U0001F3FB", "[0->3]"},
+    {"love_you_light", u"\U0001F91F\U0001F3FB", "[0->3]"},
+    {"leg_dark", u"\U0001F9B5\U0001F3FF", "[0->3]"},
 
     // Samples from
     // https://unicode.org/Public/emoji/12.0/emoji-variation-sequences.txt
-    {"number_sign_text", L"\u0023\uFE0E", "[0->1]"},
-    {"number_sign_emoji", L"\u0023\uFE0F", "[0->1]"},
-    {"digit_eight_text", L"\u0038\uFE0E", "[0->1]"},
-    {"digit_eight_emoji", L"\u0038\uFE0F", "[0->1]"},
-    {"up_down_arrow_text", L"\u2195\uFE0E", "[0->1]"},
-    {"up_down_arrow_emoji", L"\u2195\uFE0F", "[0->1]"},
-    {"stopwatch_text", L"\u23F1\uFE0E", "[0->1]"},
-    {"stopwatch_emoji", L"\u23F1\uFE0F", "[0->1]"},
-    {"thermometer_text", L"\U0001F321\uFE0E", "[0->2]"},
-    {"thermometer_emoji", L"\U0001F321\uFE0F", "[0->2]"},
-    {"thumbs_up_text", L"\U0001F44D\uFE0E", "[0->2]"},
-    {"thumbs_up_emoji", L"\U0001F44D\uFE0F", "[0->2]"},
-    {"hole_text", L"\U0001F573\uFE0E", "[0->2]"},
-    {"hole_emoji", L"\U0001F573\uFE0F", "[0->2]"},
+    {"number_sign_text", u"\u0023\uFE0E", "[0->1]"},
+    {"number_sign_emoji", u"\u0023\uFE0F", "[0->1]"},
+    {"digit_eight_text", u"\u0038\uFE0E", "[0->1]"},
+    {"digit_eight_emoji", u"\u0038\uFE0F", "[0->1]"},
+    {"up_down_arrow_text", u"\u2195\uFE0E", "[0->1]"},
+    {"up_down_arrow_emoji", u"\u2195\uFE0F", "[0->1]"},
+    {"stopwatch_text", u"\u23F1\uFE0E", "[0->1]"},
+    {"stopwatch_emoji", u"\u23F1\uFE0F", "[0->1]"},
+    {"thermometer_text", u"\U0001F321\uFE0E", "[0->2]"},
+    {"thermometer_emoji", u"\U0001F321\uFE0F", "[0->2]"},
+    {"thumbs_up_text", u"\U0001F44D\uFE0E", "[0->2]"},
+    {"thumbs_up_emoji", u"\U0001F44D\uFE0F", "[0->2]"},
+    {"hole_text", u"\U0001F573\uFE0E", "[0->2]"},
+    {"hole_emoji", u"\U0001F573\uFE0F", "[0->2]"},
 
     // Samples from
     // https://unicode.org/Public/emoji/12.0/emoji-zwj-sequences.txt
-    {"couple_man_man", L"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F468",
+    {"couple_man_man", u"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F468",
      "[0->7]"},
     {"kiss_man_man",
-     L"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468",
+     u"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468",
      "[0->10]"},
     {"family_man_woman_girl_boy",
-     L"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", "[0->10]"},
+     u"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", "[0->10]"},
     {"men_hands_dark_medium",
-     L"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FD",
+     u"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FD",
      "[0->11]"},
     {"people_hands_dark",
-     L"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF",
+     u"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF",
      "[0->11]"},
-    {"man_pilot", L"\U0001F468\u200D\u2708\uFE0F", "[0->4]"},
-    {"man_scientist", L"\U0001F468\u200D\U0001F52C", "[0->4]"},
-    {"man_mechanic_light", L"\U0001F468\U0001F3FB\u200D\U0001F527", "[0->6]"},
-    {"man_judge_medium", L"\U0001F468\U0001F3FD\u200D\u2696\uFE0F", "[0->6]"},
-    {"woman_cane_dark", L"\U0001F469\U0001F3FF\u200D\U0001F9AF", "[0->6]"},
-    {"woman_ball_light", L"\u26F9\U0001F3FB\u200D\u2640\uFE0F", "[0->5]"},
-    {"woman_running", L"\U0001F3C3\u200D\u2640\uFE0F", "[0->4]"},
-    {"woman_running_dark", L"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F", "[0->6]"},
-    {"woman_turban", L"\U0001F473\u200D\u2640\uFE0F", "[0->4]"},
-    {"woman_detective", L"\U0001F575\uFE0F\u200D\u2640\uFE0F", "[0->5]"},
-    {"man_facepalming", L"\U0001F926\u200D\u2642\uFE0F", "[0->4]"},
-    {"man_red_hair", L"\U0001F468\u200D\U0001F9B0", "[0->4]"},
-    {"man_medium_curly", L"\U0001F468\U0001F3FD\u200D\U0001F9B1", "[0->6]"},
-    {"woman_dark_white_hair", L"\U0001F469\U0001F3FF\u200D\U0001F9B3",
+    {"man_pilot", u"\U0001F468\u200D\u2708\uFE0F", "[0->4]"},
+    {"man_scientist", u"\U0001F468\u200D\U0001F52C", "[0->4]"},
+    {"man_mechanic_light", u"\U0001F468\U0001F3FB\u200D\U0001F527", "[0->6]"},
+    {"man_judge_medium", u"\U0001F468\U0001F3FD\u200D\u2696\uFE0F", "[0->6]"},
+    {"woman_cane_dark", u"\U0001F469\U0001F3FF\u200D\U0001F9AF", "[0->6]"},
+    {"woman_ball_light", u"\u26F9\U0001F3FB\u200D\u2640\uFE0F", "[0->5]"},
+    {"woman_running", u"\U0001F3C3\u200D\u2640\uFE0F", "[0->4]"},
+    {"woman_running_dark", u"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F", "[0->6]"},
+    {"woman_turban", u"\U0001F473\u200D\u2640\uFE0F", "[0->4]"},
+    {"woman_detective", u"\U0001F575\uFE0F\u200D\u2640\uFE0F", "[0->5]"},
+    {"man_facepalming", u"\U0001F926\u200D\u2642\uFE0F", "[0->4]"},
+    {"man_red_hair", u"\U0001F468\u200D\U0001F9B0", "[0->4]"},
+    {"man_medium_curly", u"\U0001F468\U0001F3FD\u200D\U0001F9B1", "[0->6]"},
+    {"woman_dark_white_hair", u"\U0001F469\U0001F3FF\u200D\U0001F9B3",
      "[0->6]"},
-    {"rainbow_flag", L"\U0001F3F3\uFE0F\u200D\U0001F308", "[0->5]"},
-    {"pirate_flag", L"\U0001F3F4\u200D\u2620\uFE0F", "[0->4]"},
-    {"service_dog", L"\U0001F415\u200D\U0001F9BA", "[0->4]"},
-    {"eye_bubble", L"\U0001F441\uFE0F\u200D\U0001F5E8\uFE0F", "[0->6]"},
+    {"rainbow_flag", u"\U0001F3F3\uFE0F\u200D\U0001F308", "[0->5]"},
+    {"pirate_flag", u"\U0001F3F4\u200D\u2620\uFE0F", "[0->4]"},
+    {"service_dog", u"\U0001F415\u200D\U0001F9BA", "[0->4]"},
+    {"eye_bubble", u"\U0001F441\uFE0F\u200D\U0001F5E8\uFE0F", "[0->6]"},
 };
 
 INSTANTIATE_TEST_SUITE_P(ItemizeTextToRunsEmoji,
@@ -1903,12 +1885,12 @@
 
 struct ElideTextCase {
   const char* test_name;
-  const wchar_t* text;
-  const wchar_t* display_text;
+  const char16_t* text;
+  const char16_t* display_text;
   // The available width, specified as a number of fixed-width glyphs. If no
   // value is specified, the width of the resulting |display_text| is used. This
   // helps test available widths larger than the resulting test; e.g. "a  b"
-  // should yield "a\u2026" even if 3 glyph widths are available, when
+  // should yield "a…" even if 3 glyph widths are available, when
   // whitespace elision is enabled.
   const base::Optional<size_t> available_width_as_glyph_count = base::nullopt;
   const base::Optional<bool> whitespace_elision = base::nullopt;
@@ -1933,8 +1915,8 @@
 
   const ElideTextTestOptions options = std::get<0>(GetParam());
   const ElideTextCase param = std::get<1>(GetParam());
-  const std::u16string text = WideToUTF16(param.text);
-  const std::u16string display_text = WideToUTF16(param.display_text);
+  const std::u16string text = param.text;
+  const std::u16string display_text = param.display_text;
 
   // Retrieve the display_text width without eliding.
   RenderTextHarfBuzz* render_text = GetRenderText();
@@ -1970,68 +1952,64 @@
 }
 
 const ElideTextCase kElideHeadTextCases[] = {
-    {"empty", L"", L""},
-    {"letter_m_tail0", L"M", L""},
-    {"letter_m_tail1", L"M", L"M"},
-    {"no_eliding", L"012ab", L"012ab"},
-    {"ltr_3", L"abc", L"abc"},
-    {"ltr_2", L"abc", L"\u2026c"},
-    {"ltr_1", L"abc", L"\u2026"},
-    {"ltr_0", L"abc", L""},
-    {"rtl_3", L"\u05d0\u05d1\u05d2", L"\u05d0\u05d1\u05d2"},
-    {"rtl_2", L"\u05d0\u05d1\u05d2", L"\u2026\u05d2"},
-    {"rtl_1", L"\u05d0\u05d1\u05d2", L"\u2026"},
-    {"rtl_0", L"\u05d0\u05d1\u05d2", L""},
-    {"ltr_rtl_5", L"abc\u05d0\u05d1\u05d2", L"\u2026c\u05d0\u05d1\u05d2"},
-    {"ltr_rtl_4", L"abc\u05d0\u05d1\u05d2", L"\u2026\u05d0\u05d1\u05d2"},
-    {"ltr_rtl_3", L"abc\u05d0\u05d1\u05d2", L"\u2026\u05d1\u05d2"},
-    {"rtl_ltr_5", L"\u05d0\u05d1\u05d2abc", L"\u2026\u05d2abc"},
-    {"rtl_ltr_4", L"\u05d0\u05d1\u05d2abc", L"\u2026abc"},
-    {"rtl_ltr_3", L"\u05d0\u05d1\u05d2abc", L"\u2026bc"},
-    {"bidi_1", L"a\u05d1b\u05d1c012", L"\u2026b\u05d1c012"},
-    {"bidi_2", L"a\u05d1b\u05d1c012", L"\u2026\u05d1c012"},
-    {"bidi_3", L"a\u05d1b\u05d1c012", L"\u2026c012"},
+    {"empty", u"", u""},
+    {"letter_m_tail0", u"M", u""},
+    {"letter_m_tail1", u"M", u"M"},
+    {"no_eliding", u"012ab", u"012ab"},
+    {"ltr_3", u"abc", u"abc"},
+    {"ltr_2", u"abc", u"…c"},
+    {"ltr_1", u"abc", u"…"},
+    {"ltr_0", u"abc", u""},
+    {"rtl_3", u"אבג", u"אבג"},
+    {"rtl_2", u"אבג", u"…ג"},
+    {"rtl_1", u"אבג", u"…"},
+    {"rtl_0", u"אבג", u""},
+    {"ltr_rtl_5", u"abcאבג", u"…cאבג"},
+    {"ltr_rtl_4", u"abcאבג", u"…אבג"},
+    {"ltr_rtl_3", u"abcאבג", u"…בג"},
+    {"rtl_ltr_5", u"אבגabc", u"…גabc"},
+    {"rtl_ltr_4", u"אבגabc", u"…abc"},
+    {"rtl_ltr_3", u"אבגabc", u"…bc"},
+    {"bidi_1", u"aבbבc012", u"…bבc012"},
+    {"bidi_2", u"aבbבc012", u"…בc012"},
+    {"bidi_3", u"aבbבc012", u"…c012"},
     // Test surrogate pairs. No surrogate pair should be partially elided.
-    {"surrogate1", L"abc\U0001D11E\U0001D122x", L"\u2026\U0001D11E\U0001D122x"},
-    {"surrogate2", L"abc\U0001D11E\U0001D122x", L"\u2026\U0001D122x"},
-    {"surrogate3", L"abc\U0001D11E\U0001D122x", L"\u2026x"},
+    {"surrogate1", u"abc\U0001D11E\U0001D122x", u"…\U0001D11E\U0001D122x"},
+    {"surrogate2", u"abc\U0001D11E\U0001D122x", u"…\U0001D122x"},
+    {"surrogate3", u"abc\U0001D11E\U0001D122x", u"…x"},
     // Test combining character sequences. U+0915 U+093F forms a compound
     // glyph, as does U+0915 U+0942. No combining sequence should be partially
     // elided.
-    {"combining1", L"0123\u0915\u093f\u0915\u0942456",
-     L"\u2026\u0915\u0942456"},
-    {"combining2", L"0123\u0915\u093f\u0915\u0942456", L"\u2026456"},
+    {"combining1", u"0123\u0915\u093f\u0915\u0942456", u"…\u0915\u0942456"},
+    {"combining2", u"0123\u0915\u093f\u0915\u0942456", u"…456"},
     // 𝄞 (U+1D11E, MUSICAL SYMBOL G CLEF) should be fully elided.
-    {"emoji1", L"012\U0001D11Ex", L"\u2026\U0001D11Ex"},
-    {"emoji2", L"012\U0001D11Ex", L"\u2026x"},
+    {"emoji1", u"012\U0001D11Ex", u"…\U0001D11Ex"},
+    {"emoji2", u"012\U0001D11Ex", u"…x"},
 
     // Whitespace elision tests.
-    {"empty_no_elision", L"", L"", 0, kForceNoWhitespaceElision},
-    {"empty_elision", L"", L"", 0, kForceWhitespaceElision},
-    {"xyz_no_elision", L"  x  xyz", L"\u2026 xyz", 5,
-     kForceNoWhitespaceElision},
-    {"xyz_elision", L"  x  xyz", L"\u2026xyz", 5, kForceWhitespaceElision},
-    {"ltr_rtl_elision3", L"x  \u05d1  y    \u05d2", L"\u2026\u05d2", 3,
+    {"empty_no_elision", u"", u"", 0, kForceNoWhitespaceElision},
+    {"empty_elision", u"", u"", 0, kForceWhitespaceElision},
+    {"xyz_no_elision", u"  x  xyz", u"… xyz", 5, kForceNoWhitespaceElision},
+    {"xyz_elision", u"  x  xyz", u"…xyz", 5, kForceWhitespaceElision},
+    {"ltr_rtl_elision3", u"x  ב  y    ג", u"…ג", 3, kForceWhitespaceElision},
+    {"ltr_rtl_elision6", u"x  ב  y    ג", u"…ג", 6, kForceWhitespaceElision},
+    {"ltr_rtl_elision7", u"x  ב  y    ג", u"…y    ג", 7,
      kForceWhitespaceElision},
-    {"ltr_rtl_elision6", L"x  \u05d1  y    \u05d2", L"\u2026\u05d2", 6,
+    {"ltr_rtl_elision10", u"x  ב  y    ג", u"…ב  y    ג", 10,
      kForceWhitespaceElision},
-    {"ltr_rtl_elision7", L"x  \u05d1  y    \u05d2", L"\u2026y    \u05d2", 7,
+    {"ltr_rtl_elision11", u"x  ב  y    ג", u"…ב  y    ג", 11,
      kForceWhitespaceElision},
-    {"ltr_rtl_elision10", L"x  \u05d1  y    \u05d2",
-     L"\u2026\u05d1  y    \u05d2", 10, kForceWhitespaceElision},
-    {"ltr_rtl_elision11", L"x  \u05d1  y    \u05d2",
-     L"\u2026\u05d1  y    \u05d2", 11, kForceWhitespaceElision},
     // Emoji U+1F601 and emoji U+1F321 U+FE0E are graphemes that result in
     // one glyph each. Eliding a glyph must remove the whole grapheme. It is
     // invalid to break a grapheme in pieces.
-    {"graphemes_elision3", L"  \U0001F601  \U0001F321\uFE0E  ", L"\u2026", 3,
+    {"graphemes_elision3", u"  \U0001F601  \U0001F321\uFE0E  ", u"…", 3,
      kForceWhitespaceElision},
-    {"graphemes_elision4", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"\u2026\U0001F321\uFE0E  ", 4, kForceWhitespaceElision},
-    {"graphemes_elision6", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"\u2026\U0001F321\uFE0E  ", 6, kForceWhitespaceElision},
-    {"graphemes_elision7", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"\u2026\U0001F601  \U0001F321\uFE0E  ", 7, kForceWhitespaceElision},
+    {"graphemes_elision4", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"…\U0001F321\uFE0E  ", 4, kForceWhitespaceElision},
+    {"graphemes_elision6", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"…\U0001F321\uFE0E  ", 6, kForceWhitespaceElision},
+    {"graphemes_elision7", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"…\U0001F601  \U0001F321\uFE0E  ", 7, kForceWhitespaceElision},
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -2042,75 +2020,71 @@
     RenderTextTestWithElideTextCase::ParamInfoToString);
 
 const ElideTextCase kElideTailTextCases[] = {
-    {"empty", L"", L""},
-    {"letter_m_tail0", L"M", L""},
-    {"letter_m_tail1", L"M", L"M"},
-    {"letter_weak_3", L" . ", L" . "},
-    {"letter_weak_2", L" . ", L"\u2026"},
-    {"no_eliding", L"012ab", L"012ab"},
-    {"ltr_3", L"abc", L"abc"},
-    {"ltr_2", L"abc", L"a\u2026"},
-    {"ltr_1", L"abc", L"\u2026"},
-    {"ltr_0", L"abc", L""},
-    {"rtl_3", L"\u05d0\u05d1\u05d2", L"\u05d0\u05d1\u05d2"},
-    {"rtl_2", L"\u05d0\u05d1\u05d2", L"\u05d0\u2026"},
-    {"rtl_1", L"\u05d0\u05d1\u05d2", L"\u2026"},
-    {"rtl_0", L"\u05d0\u05d1\u05d2", L""},
-    {"ltr_rtl_5", L"abc\u05d0\u05d1\u05d2", L"abc\u05d0\u2026\u200F"},
-    {"ltr_rtl_4", L"abc\u05d0\u05d1\u05d2", L"abc\u2026"},
-    {"ltr_rtl_3", L"abc\u05d0\u05d1\u05d2", L"ab\u2026"},
-    {"rtl_ltr_5", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1\u05d2a\u2026\u200E"},
-    {"rtl_ltr_4", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1\u05d2\u2026"},
-    {"rtl_ltr_3", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1\u2026"},
-    {"bidi_1", L"012a\u05d1b\u05d1c", L"012a\u2026"},
-    {"bidi_2", L"012a\u05d1b\u05d1c", L"012a\u05d1\u2026\u200F"},
-    {"bidi_3", L"012a\u05d1b\u05d1c", L"012a\u05d1b\u2026"},
+    {"empty", u"", u""},
+    {"letter_m_tail0", u"M", u""},
+    {"letter_m_tail1", u"M", u"M"},
+    {"letter_weak_3", u" . ", u" . "},
+    {"letter_weak_2", u" . ", u"…"},
+    {"no_eliding", u"012ab", u"012ab"},
+    {"ltr_3", u"abc", u"abc"},
+    {"ltr_2", u"abc", u"a…"},
+    {"ltr_1", u"abc", u"…"},
+    {"ltr_0", u"abc", u""},
+    {"rtl_3", u"אבג", u"אבג"},
+    {"rtl_2", u"אבג", u"א…"},
+    {"rtl_1", u"אבג", u"…"},
+    {"rtl_0", u"אבג", u""},
+    {"ltr_rtl_5", u"abcאבג", u"abcא…\u200F"},
+    {"ltr_rtl_4", u"abcאבג", u"abc…"},
+    {"ltr_rtl_3", u"abcאבג", u"ab…"},
+    {"rtl_ltr_5", u"אבגabc", u"אבגa…\u200E"},
+    {"rtl_ltr_4", u"אבגabc", u"אבג…"},
+    {"rtl_ltr_3", u"אבגabc", u"אב…"},
+    {"bidi_1", u"012aבbבc", u"012a…"},
+    {"bidi_2", u"012aבbבc", u"012aב…\u200F"},
+    {"bidi_3", u"012aבbבc", u"012aבb…"},
     // No RLM marker added as digits (012) have weak directionality.
-    {"no_rlm", L"01\u05d0\u05d1\u05d2", L"01\u05d0\u2026"},
+    {"no_rlm", u"01אבג", u"01א…"},
     // RLM marker added as "ab" have strong LTR directionality.
-    {"with_rlm", L"ab\u05d0\u05d1\u05d2cd", L"ab\u05d0\u05d1\u2026\u200f"},
+    {"with_rlm", u"abאבגcd", u"abאב…\u200f"},
     // Test surrogate pairs. The first pair 𝄞 'MUSICAL SYMBOL G CLEF' U+1D11E
     // should be kept, and the second pair 𝄢 'MUSICAL SYMBOL F CLEF' U+1D122
     // should be removed. No surrogate pair should be partially elided.
-    {"surrogate", L"0123\U0001D11E\U0001D122x", L"0123\U0001D11E\u2026"},
+    {"surrogate", u"0123\U0001D11E\U0001D122x", u"0123\U0001D11E…"},
     // Test combining character sequences. U+0915 U+093F forms a compound
     // glyph, as does U+0915 U+0942. The first should be kept; the second
     // removed. No combining sequence should be partially elided.
-    {"combining", L"0123\u0915\u093f\u0915\u0942456",
-     L"0123\u0915\u093f\u2026"},
+    {"combining", u"0123\u0915\u093f\u0915\u0942456", u"0123\u0915\u093f…"},
     // U+05E9 U+05BC U+05C1 U+05B8 forms a four-character compound glyph.
     // It should be either fully elided, or not elided at all. If completely
     // elided, an LTR Mark (U+200E) should be added.
-    {"grapheme1", L"0\u05e9\u05bc\u05c1\u05b8", L"0\u05e9\u05bc\u05c1\u05b8"},
-    {"grapheme2", L"0\u05e9\u05bc\u05c1\u05b8abc", L"0\u2026\u200E"},
-    {"grapheme3", L"01\u05e9\u05bc\u05c1\u05b8abc", L"01\u2026\u200E"},
-    {"grapheme4", L"012\u05e9\u05bc\u05c1\u05b8abc", L"012\u2026\u200E"},
+    {"grapheme1", u"0\u05e9\u05bc\u05c1\u05b8", u"0\u05e9\u05bc\u05c1\u05b8"},
+    {"grapheme2", u"0\u05e9\u05bc\u05c1\u05b8abc", u"0…\u200E"},
+    {"grapheme3", u"01\u05e9\u05bc\u05c1\u05b8abc", u"01…\u200E"},
+    {"grapheme4", u"012\u05e9\u05bc\u05c1\u05b8abc", u"012…\u200E"},
     // 𝄞 (U+1D11E, MUSICAL SYMBOL G CLEF) should be fully elided.
-    {"emoji", L"012\U0001D11Ex", L"012\u2026"},
+    {"emoji", u"012\U0001D11Ex", u"012…"},
 
     // Whitespace elision tests.
-    {"empty_no_elision", L"", L"", 0, kForceNoWhitespaceElision},
-    {"empty_elision", L"", L"", 0, kForceWhitespaceElision},
-    {"letter_weak_2_no_elision", L" . ", L" \u2026", 2,
-     kForceNoWhitespaceElision},
-    {"xyz_no_elision", L"  x  xyz", L"  x \u2026", 5,
-     kForceNoWhitespaceElision},
-    {"xyz_elision", L"  x  xyz", L"  x\u2026", 5, kForceWhitespaceElision},
-    {"ltr_rtl_elision4", L"x  \u05d1  y    \u05d2", L"x\u2026", 4,
+    {"empty_no_elision", u"", u"", 0, kForceNoWhitespaceElision},
+    {"empty_elision", u"", u"", 0, kForceWhitespaceElision},
+    {"letter_weak_2_no_elision", u" . ", u" …", 2, kForceNoWhitespaceElision},
+    {"xyz_no_elision", u"  x  xyz", u"  x …", 5, kForceNoWhitespaceElision},
+    {"xyz_elision", u"  x  xyz", u"  x…", 5, kForceWhitespaceElision},
+    {"ltr_rtl_elision4", u"x  ב  y    ג", u"x…", 4, kForceWhitespaceElision},
+    {"ltr_rtl_elision5", u"x  ב  y    ג", u"x  ב…\u200F", 5,
      kForceWhitespaceElision},
-    {"ltr_rtl_elision5", L"x  \u05d1  y    \u05d2", L"x  \u05d1\u2026\u200F", 5,
-     kForceWhitespaceElision},
-    {"ltr_rtl_elision9", L"x  \u05d1  y    \u05d2", L"x  \u05d1  y\u2026", 9,
+    {"ltr_rtl_elision9", u"x  ב  y    ג", u"x  ב  y…", 9,
      kForceWhitespaceElision},
     // Emoji U+1F601 and emoji U+1F321 U+FE0E are graphemes that result in
     // one glyph each. Eliding a glyph must remove the whole grapheme. It is
     // invalid to break a grapheme in pieces.
-    {"graphemes_elision3", L"  \U0001F601  \U0001F321\uFE0E  ", L"\u2026", 3,
+    {"graphemes_elision3", u"  \U0001F601  \U0001F321\uFE0E  ", u"…", 3,
      kForceWhitespaceElision},
-    {"graphemes_elision6", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"  \U0001F601\u2026", 6, kForceWhitespaceElision},
-    {"graphemes_elision7", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"  \U0001F601  \U0001F321\uFE0E\u2026", 7, kForceWhitespaceElision},
+    {"graphemes_elision6", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"  \U0001F601…", 6, kForceWhitespaceElision},
+    {"graphemes_elision7", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"  \U0001F601  \U0001F321\uFE0E…", 7, kForceWhitespaceElision},
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -2121,70 +2095,67 @@
     RenderTextTestWithElideTextCase::ParamInfoToString);
 
 const ElideTextCase kElideTruncateTextCases[] = {
-    {"empty", L"", L""},
-    {"letter_m_tail0", L"M", L""},
-    {"letter_m_tail1", L"M", L"M"},
-    {"no_eliding", L"012ab", L"012ab"},
-    {"ltr_3", L"abc", L"abc"},
-    {"ltr_2", L"abc", L"ab"},
-    {"ltr_1", L"abc", L"a"},
-    {"ltr_0", L"abc", L""},
-    {"rtl_3", L"\u05d0\u05d1\u05d2", L"\u05d0\u05d1\u05d2"},
-    {"rtl_2", L"\u05d0\u05d1\u05d2", L"\u05d0\u05d1"},
-    {"rtl_1", L"\u05d0\u05d1\u05d2", L"\u05d0"},
-    {"rtl_0", L"\u05d0\u05d1\u05d2", L""},
-    {"ltr_rtl_5", L"abc\u05d0\u05d1\u05d2", L"abc\u05d0\u05d1"},
-    {"ltr_rtl_4", L"abc\u05d0\u05d1\u05d2", L"abc\u05d0"},
-    {"ltr_rtl_3", L"abc\u05d0\u05d1\u05d2", L"abc"},
-    {"ltr_rtl_2", L"abc\u05d0\u05d1\u05d2", L"ab"},
-    {"rtl_ltr_5", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1\u05d2ab"},
-    {"rtl_ltr_4", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1\u05d2a"},
-    {"rtl_ltr_3", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1\u05d2"},
-    {"rtl_ltr_2", L"\u05d0\u05d1\u05d2abc", L"\u05d0\u05d1"},
-    {"bidi_1", L"012a\u05d1b\u05d1c", L"012a\u05d1b\u05d1"},
-    {"bidi_2", L"012a\u05d1b\u05d1c", L"012a\u05d1b"},
-    {"bidi_3", L"012a\u05d1b\u05d1c", L"012a\u05d1"},
-    {"bidi_4", L"012a\u05d1b\u05d1c", L"012a\u05d1"},
+    {"empty", u"", u""},
+    {"letter_m_tail0", u"M", u""},
+    {"letter_m_tail1", u"M", u"M"},
+    {"no_eliding", u"012ab", u"012ab"},
+    {"ltr_3", u"abc", u"abc"},
+    {"ltr_2", u"abc", u"ab"},
+    {"ltr_1", u"abc", u"a"},
+    {"ltr_0", u"abc", u""},
+    {"rtl_3", u"אבג", u"אבג"},
+    {"rtl_2", u"אבג", u"אב"},
+    {"rtl_1", u"אבג", u"א"},
+    {"rtl_0", u"אבג", u""},
+    {"ltr_rtl_5", u"abcאבג", u"abcאב"},
+    {"ltr_rtl_4", u"abcאבג", u"abcא"},
+    {"ltr_rtl_3", u"abcאבג", u"abc"},
+    {"ltr_rtl_2", u"abcאבג", u"ab"},
+    {"rtl_ltr_5", u"אבגabc", u"אבגab"},
+    {"rtl_ltr_4", u"אבגabc", u"אבגa"},
+    {"rtl_ltr_3", u"אבגabc", u"אבג"},
+    {"rtl_ltr_2", u"אבגabc", u"אב"},
+    {"bidi_1", u"012aבbבc", u"012aבbב"},
+    {"bidi_2", u"012aבbבc", u"012aבb"},
+    {"bidi_3", u"012aבbבc", u"012aב"},
+    {"bidi_4", u"012aבbבc", u"012aב"},
     // Test surrogate pairs. The first pair 𝄞 'MUSICAL SYMBOL G CLEF' U+1D11E
     // should be kept, and the second pair 𝄢 'MUSICAL SYMBOL F CLEF' U+1D122
     // should be removed. No surrogate pair should be partially elided.
-    {"surrogate1", L"0123\U0001D11E\U0001D122x", L"0123\U0001D11E\U0001D122"},
-    {"surrogate2", L"0123\U0001D11E\U0001D122x", L"0123\U0001D11E"},
-    {"surrogate3", L"0123\U0001D11E\U0001D122x", L"0123"},
+    {"surrogate1", u"0123\U0001D11E\U0001D122x", u"0123\U0001D11E\U0001D122"},
+    {"surrogate2", u"0123\U0001D11E\U0001D122x", u"0123\U0001D11E"},
+    {"surrogate3", u"0123\U0001D11E\U0001D122x", u"0123"},
     // Test combining character sequences. U+0915 U+093F forms a compound
     // glyph, as does U+0915 U+0942. The first should be kept; the second
     // removed. No combining sequence should be partially elided.
-    {"combining", L"0123\u0915\u093f\u0915\u0942456", L"0123\u0915\u093f"},
+    {"combining", u"0123\u0915\u093f\u0915\u0942456", u"0123\u0915\u093f"},
     // 𝄞 (U+1D11E, MUSICAL SYMBOL G CLEF) should be fully elided.
-    {"emoji1", L"012\U0001D11Ex", L"012\U0001D11E"},
-    {"emoji2", L"012\U0001D11Ex", L"012"},
+    {"emoji1", u"012\U0001D11Ex", u"012\U0001D11E"},
+    {"emoji2", u"012\U0001D11Ex", u"012"},
 
     // Whitespace elision tests.
-    {"empty_no_elision", L"", L"", 0, kForceNoWhitespaceElision},
-    {"empty_elision", L"", L"", 0, kForceWhitespaceElision},
-    {"xyz_no_elision", L"  x  xyz", L"  x  ", 5, kForceNoWhitespaceElision},
-    {"xyz_elision", L"  x  xyz", L"  x", 5, kForceWhitespaceElision},
-    {"ltr_rtl_elision3", L"x  \u05d1  y    \u05d2", L"x", 3,
-     kForceWhitespaceElision},
-    {"ltr_rtl_elision4", L"x  \u05d1  y    \u05d2", L"x  \u05d1", 4,
-     kForceWhitespaceElision},
-    {"ltr_rtl_elision5", L"x  \u05d1  y    \u05d2", L"x  \u05d1", 5,
-     kForceWhitespaceElision},
-    {"ltr_rtl_elision9", L"x  \u05d1  y    \u05d2", L"x  \u05d1  y", 9,
+    {"empty_no_elision", u"", u"", 0, kForceNoWhitespaceElision},
+    {"empty_elision", u"", u"", 0, kForceWhitespaceElision},
+    {"xyz_no_elision", u"  x  xyz", u"  x  ", 5, kForceNoWhitespaceElision},
+    {"xyz_elision", u"  x  xyz", u"  x", 5, kForceWhitespaceElision},
+    {"ltr_rtl_elision3", u"x  ב  y    ג", u"x", 3, kForceWhitespaceElision},
+    {"ltr_rtl_elision4", u"x  ב  y    ג", u"x  ב", 4, kForceWhitespaceElision},
+    {"ltr_rtl_elision5", u"x  ב  y    ג", u"x  ב", 5, kForceWhitespaceElision},
+    {"ltr_rtl_elision9", u"x  ב  y    ג", u"x  ב  y", 9,
      kForceWhitespaceElision},
     // Emoji U+1F601 and emoji U+1F321 U+FE0E are graphemes that result in
     // one glyph each. Eliding a glyph must remove the whole grapheme. It is
     // invalid to break a grapheme in pieces.
-    {"graphemes_elision2", L"  \U0001F601  \U0001F321\uFE0E  ", L"", 2,
+    {"graphemes_elision2", u"  \U0001F601  \U0001F321\uFE0E  ", u"", 2,
      kForceWhitespaceElision},
-    {"graphemes_elision3", L"  \U0001F601  \U0001F321\uFE0E  ", L"  \U0001F601",
+    {"graphemes_elision3", u"  \U0001F601  \U0001F321\uFE0E  ", u"  \U0001F601",
      3, kForceWhitespaceElision},
-    {"graphemes_elision5", L"  \U0001F601  \U0001F321\uFE0E  ", L"  \U0001F601",
+    {"graphemes_elision5", u"  \U0001F601  \U0001F321\uFE0E  ", u"  \U0001F601",
      5, kForceWhitespaceElision},
-    {"graphemes_elision6", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"  \U0001F601  \U0001F321\uFE0E", 6, kForceWhitespaceElision},
-    {"graphemes_elision7", L"  \U0001F601  \U0001F321\uFE0E  ",
-     L"  \U0001F601  \U0001F321\uFE0E", 7, kForceWhitespaceElision},
+    {"graphemes_elision6", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"  \U0001F601  \U0001F321\uFE0E", 6, kForceWhitespaceElision},
+    {"graphemes_elision7", u"  \U0001F601  \U0001F321\uFE0E  ",
+     u"  \U0001F601  \U0001F321\uFE0E", 7, kForceWhitespaceElision},
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -2196,44 +2167,44 @@
 
 const ElideTextCase kElideEmailTextCases[] = {
     // Invalid email text.
-    {"empty", L"", L""},
-    {"invalid_char1", L"x", L""},
-    {"invalid_char3", L"xyz", L"x\u2026"},
-    {"invalid_amp", L"@", L""},
-    {"invalid_no_prefix0", L"@y", L""},
-    {"invalid_no_prefix1", L"@y", L"\u2026"},
-    {"invalid_no_prefix2", L"@xyz", L"@x\u2026"},
-    {"invalid_no_suffix0", L"x@", L""},
-    {"invalid_no_suffix1", L"x@", L"\u2026"},
-    {"invalid_no_suffix2", L"xyz@", L"x\u2026@"},
+    {"empty", u"", u""},
+    {"invalid_char1", u"x", u""},
+    {"invalid_char3", u"xyz", u"x…"},
+    {"invalid_amp", u"@", u""},
+    {"invalid_no_prefix0", u"@y", u""},
+    {"invalid_no_prefix1", u"@y", u"…"},
+    {"invalid_no_prefix2", u"@xyz", u"@x…"},
+    {"invalid_no_suffix0", u"x@", u""},
+    {"invalid_no_suffix1", u"x@", u"…"},
+    {"invalid_no_suffix2", u"xyz@", u"x…@"},
 
-    {"at1", L"@", L"@"},
-    {"at2", L"@@", L"\u2026", 1},
-    {"at3", L"@@@", L"\u2026", 2},
-    {"at4", L"@@@@", L"@\u2026@", 3},
+    {"at1", u"@", u"@"},
+    {"at2", u"@@", u"…", 1},
+    {"at3", u"@@@", u"…", 2},
+    {"at4", u"@@@@", u"@…@", 3},
 
-    {"small1", L"a@b", L"\u2026", 1},
-    {"small2", L"a@b", L"\u2026", 2},
-    {"small3", L"a@b", L"a@b", 3},
-    {"small_username3", L"xyz@b", L"\u2026", 3},
-    {"small_username4", L"xyz@b", L"x\u2026@b", 4},
-    {"small_username5", L"xyz@b", L"xyz@b", 5},
-    {"small_domain3", L"a@xyz", L"\u2026", 3},
-    {"small_domain4", L"a@xyz", L"a@x\u2026", 4},
-    {"small_domain5", L"a@xyz", L"a@xyz", 5},
+    {"small1", u"a@b", u"…", 1},
+    {"small2", u"a@b", u"…", 2},
+    {"small3", u"a@b", u"a@b", 3},
+    {"small_username3", u"xyz@b", u"…", 3},
+    {"small_username4", u"xyz@b", u"x…@b", 4},
+    {"small_username5", u"xyz@b", u"xyz@b", 5},
+    {"small_domain3", u"a@xyz", u"…", 3},
+    {"small_domain4", u"a@xyz", u"a@x…", 4},
+    {"small_domain5", u"a@xyz", u"a@xyz", 5},
 
     // Valid email.
-    {"email_small", L"a@b.com", L"\u2026"},
-    {"email_nobody3", L"nobody@gmail.com", L"\u2026", 3},
-    {"email_nobody4", L"nobody@gmail.com", L"\u2026", 4},
-    {"email_nobody5", L"nobody@gmail.com", L"n\u2026@g\u2026", 5},
-    {"email_nobody6", L"nobody@gmail.com", L"no\u2026@g\u2026", 6},
-    {"email_nobody7", L"nobody@gmail.com", L"no\u2026@g\u2026m", 7},
-    {"email_nobody8", L"nobody@gmail.com", L"nob\u2026@g\u2026m", 8},
-    {"email_nobody9", L"nobody@gmail.com", L"nob\u2026@gm\u2026m", 9},
-    {"email_nobody10", L"nobody@gmail.com", L"nobo\u2026@gm\u2026m", 10},
-    {"email_root", L"root@localhost", L"r\u2026@l\u2026", 5},
-    {"email_myself", L"myself@127.0.0.1", L"my\u2026@1\u2026", 6},
+    {"email_small", u"a@b.com", u"…"},
+    {"email_nobody3", u"nobody@gmail.com", u"…", 3},
+    {"email_nobody4", u"nobody@gmail.com", u"…", 4},
+    {"email_nobody5", u"nobody@gmail.com", u"n…@g…", 5},
+    {"email_nobody6", u"nobody@gmail.com", u"no…@g…", 6},
+    {"email_nobody7", u"nobody@gmail.com", u"no…@g…m", 7},
+    {"email_nobody8", u"nobody@gmail.com", u"nob…@g…m", 8},
+    {"email_nobody9", u"nobody@gmail.com", u"nob…@gm…m", 9},
+    {"email_nobody10", u"nobody@gmail.com", u"nobo…@gm…m", 10},
+    {"email_root", u"root@localhost", u"r…@l…", 5},
+    {"email_myself", u"myself@127.0.0.1", u"my…@1…", 6},
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -2288,11 +2259,11 @@
   render_text->SetCursorEnabled(false);
   render_text->SetDisplayRect(Rect(0, 0, 3 * kGlyphWidth, 100));
   render_text->SetElideBehavior(ELIDE_TAIL);
-  EXPECT_EQ(u"ab\u2026", render_text->GetDisplayText());
+  EXPECT_EQ(u"ab…", render_text->GetDisplayText());
 
   // Setting a different eliding behavior must trigger a relayout.
   render_text->SetElideBehavior(ELIDE_HEAD);
-  EXPECT_EQ(u"\u2026ef", render_text->GetDisplayText());
+  EXPECT_EQ(u"…ef", render_text->GetDisplayText());
 }
 
 TEST_F(RenderTextTest, SetWhitespaceElision) {
@@ -2306,11 +2277,11 @@
   render_text->SetDisplayRect(Rect(0, 0, 3 * kGlyphWidth, 100));
   render_text->SetElideBehavior(ELIDE_TAIL);
   render_text->SetWhitespaceElision(false);
-  EXPECT_EQ(u"a \u2026", render_text->GetDisplayText());
+  EXPECT_EQ(u"a …", render_text->GetDisplayText());
 
   // Setting a different whitespace elision must trigger a relayout.
   render_text->SetWhitespaceElision(true);
-  EXPECT_EQ(u"a\u2026", render_text->GetDisplayText());
+  EXPECT_EQ(u"a…", render_text->GetDisplayText());
 }
 
 TEST_F(RenderTextTest, ElidedObscuredText) {
@@ -2560,23 +2531,22 @@
 }
 
 TEST_F(RenderTextTest, ElidedStyledTextRtl) {
-  static const char* kInputTexts[] = {
-      "http://ar.wikipedia.com/فحص",
-      "testحص,",
-      "حص,test",
-      "…",
-      "…test",
-      "test…",
-      "حص,test…",
-      "ٱ",
-      "\uFEFF",  // BOM: Byte Order Marker
-      "…\u200F",  // Right to left marker.
+  static const char16_t* kInputTexts[] = {
+      u"http://ar.wikipedia.com/فحص",
+      u"testحص,",
+      u"حص,test",
+      u"…",
+      u"…test",
+      u"test…",
+      u"حص,test…",
+      u"ٱ",
+      u"\uFEFF",   // BOM: Byte Order Marker
+      u"…\u200F",  // Right to left marker.
   };
 
   for (const auto* raw_text : kInputTexts) {
-    SCOPED_TRACE(
-        base::StringPrintf("ElidedStyledTextRtl text = %s", raw_text));
-    std::u16string input_text(UTF8ToUTF16(raw_text));
+    std::u16string input_text(raw_text);
+    SCOPED_TRACE(u"ElidedStyledTextRtl text = " + input_text);
 
     RenderText* render_text = GetRenderText();
     render_text->SetText(input_text);
@@ -2614,61 +2584,61 @@
 
 TEST_F(RenderTextTest, TruncatedText) {
   struct {
-    const wchar_t* text;
-    const wchar_t* display_text;
+    const char16_t* text;
+    const char16_t* display_text;
   } cases[] = {
       // Strings shorter than the truncation length should be laid out in full.
-      {L"", L""},
-      {L" . ", L" . "},                                // a wide kWeak
-      {L"abc", L"abc"},                                // a wide kLtr
-      {L"\u05d0\u05d1\u05d2", L"\u05d0\u05d1\u05d2"},  // a wide kRtl
-      {L"a\u05d0\u05d1", L"a\u05d0\u05d1"},            // a wide kLtrRtl
-      {L"a\u05d1b", L"a\u05d1b"},                      // a wide kLtrRtlLtr
-      {L"\u05d0\u05d1a", L"\u05d0\u05d1a"},            // a wide kRtlLtr
-      {L"\u05d0a\u05d1", L"\u05d0a\u05d1"},            // a wide kRtlLtrRtl
-      {L"01234", L"01234"},
+      {u"", u""},
+      {u" . ", u" . "},  // a wide kWeak
+      {u"abc", u"abc"},  // a wide kLtr
+      {u"אבג", u"אבג"},  // a wide kRtl
+      {u"aאב", u"aאב"},  // a wide kLtrRtl
+      {u"aבb", u"aבb"},  // a wide kLtrRtlLtr
+      {u"אבa", u"אבa"},  // a wide kRtlLtr
+      {u"אaב", u"אaב"},  // a wide kRtlLtrRtl
+      {u"01234", u"01234"},
       // Long strings should be truncated with an ellipsis appended at the end.
-      {L"012345", L"0123\u2026"},
-      {L"012 . ", L"012 \u2026"},
-      {L"012abc", L"012a\u2026"},
-      {L"012a\u05d0\u05d1", L"012a\u2026"},
-      {L"012a\u05d1b", L"012a\u2026"},
-      {L"012\u05d0\u05d1\u05d2", L"012\u05d0\u2026"},
-      {L"012\u05d0\u05d1a", L"012\u05d0\u2026"},
-      {L"012\u05d0a\u05d1", L"012\u05d0\u2026"},
+      {u"012345", u"0123…"},
+      {u"012 . ", u"012 …"},
+      {u"012abc", u"012a…"},
+      {u"012aאב", u"012a…"},
+      {u"012aבb", u"012a…"},
+      {u"012אבג", u"012א…"},
+      {u"012אבa", u"012א…"},
+      {u"012אaב", u"012א…"},
       // Surrogate pairs should be truncated reasonably enough.
-      {L"0123\u0915\u093f", L"0123\u2026"},
-      {L"\u05e9\u05bc\u05c1\u05b8", L"\u05e9\u05bc\u05c1\u05b8"},
-      {L"0\u05e9\u05bc\u05c1\u05b8", L"0\u05e9\u05bc\u05c1\u05b8"},
-      {L"01\u05e9\u05bc\u05c1\u05b8", L"01\u2026"},
-      {L"012\u05e9\u05bc\u05c1\u05b8", L"012\u2026"},
+      {u"0123\u0915\u093f", u"0123…"},
+      {u"\u05e9\u05bc\u05c1\u05b8", u"\u05e9\u05bc\u05c1\u05b8"},
+      {u"0\u05e9\u05bc\u05c1\u05b8", u"0\u05e9\u05bc\u05c1\u05b8"},
+      {u"01\u05e9\u05bc\u05c1\u05b8", u"01…"},
+      {u"012\u05e9\u05bc\u05c1\u05b8", u"012…"},
       // Codepoint U+0001D11E is using 2x 16-bit characters.
-      {L"0\U0001D11Eaaa", L"0\U0001D11Ea\u2026"},
-      {L"01\U0001D11Eaaa", L"01\U0001D11E\u2026"},
-      {L"012\U0001D11Eaaa", L"012\u2026"},
-      {L"0123\U0001D11Eaaa", L"0123\u2026"},
-      {L"01234\U0001D11Eaaa", L"0123\u2026"},
+      {u"0\U0001D11Eaaa", u"0\U0001D11Ea…"},
+      {u"01\U0001D11Eaaa", u"01\U0001D11E…"},
+      {u"012\U0001D11Eaaa", u"012…"},
+      {u"0123\U0001D11Eaaa", u"0123…"},
+      {u"01234\U0001D11Eaaa", u"0123…"},
       // Combining codepoint should stay together.
       // (Letter 'e' U+0065 and acute accent U+0301).
-      {L"0e\u0301aaa", L"0e\u0301a\u2026"},
-      {L"01e\u0301aaa", L"01e\u0301\u2026"},
-      {L"012e\u0301aaa", L"012\u2026"},
+      {u"0e\u0301aaa", u"0e\u0301a…"},
+      {u"01e\u0301aaa", u"01e\u0301…"},
+      {u"012e\u0301aaa", u"012…"},
       // Emoji 'keycap letter 6'.
-      {L"\u0036\uFE0F\u20E3aaa", L"\u0036\uFE0F\u20E3a\u2026"},
-      {L"0\u0036\uFE0F\u20E3aaa", L"0\u0036\uFE0F\u20E3\u2026"},
-      {L"01\u0036\uFE0F\u20E3aaa", L"01\u2026"},
+      {u"\u0036\uFE0F\u20E3aaa", u"\u0036\uFE0F\u20E3a…"},
+      {u"0\u0036\uFE0F\u20E3aaa", u"0\u0036\uFE0F\u20E3…"},
+      {u"01\u0036\uFE0F\u20E3aaa", u"01…"},
       // Emoji 'pilot'.
-      {L"\U0001F468\u200D\u2708\uFE0F", L"\U0001F468\u200D\u2708\uFE0F"},
-      {L"\U0001F468\u200D\u2708\uFE0F0", L"\u2026"},
-      {L"0\U0001F468\u200D\u2708\uFE0F", L"0\u2026"},
+      {u"\U0001F468\u200D\u2708\uFE0F", u"\U0001F468\u200D\u2708\uFE0F"},
+      {u"\U0001F468\u200D\u2708\uFE0F0", u"…"},
+      {u"0\U0001F468\u200D\u2708\uFE0F", u"0…"},
   };
 
   RenderText* render_text = GetRenderText();
   render_text->set_truncate_length(5);
   for (size_t i = 0; i < base::size(cases); i++) {
-    render_text->SetText(WideToUTF16(cases[i].text));
-    EXPECT_EQ(WideToUTF16(cases[i].text), render_text->text());
-    EXPECT_EQ(WideToUTF16(cases[i].display_text), render_text->GetDisplayText())
+    render_text->SetText(cases[i].text);
+    EXPECT_EQ(cases[i].text, render_text->text());
+    EXPECT_EQ(cases[i].display_text, render_text->GetDisplayText())
         << "For case " << i << ": " << cases[i].text;
   }
 }
@@ -2691,13 +2661,13 @@
   EXPECT_EQ(GetObscuredString(3), render_text->GetDisplayText());
 
   render_text->SetObscuredRevealIndex(0);
-  EXPECT_EQ(u"e\u0301\u2026", render_text->GetDisplayText());
+  EXPECT_EQ(u"e\u0301…", render_text->GetDisplayText());
 
   render_text->SetObscuredRevealIndex(2);
-  EXPECT_EQ(u"\u2022\u2026", render_text->GetDisplayText());
+  EXPECT_EQ(u"\u2022…", render_text->GetDisplayText());
 
   render_text->SetObscuredRevealIndex(7);
-  EXPECT_EQ(u"\u2022\u2022\u2026", render_text->GetDisplayText());
+  EXPECT_EQ(u"\u2022\u2022…", render_text->GetDisplayText());
 }
 
 TEST_F(RenderTextTest, TruncatedCursorMovementLTR) {
@@ -2731,7 +2701,7 @@
 TEST_F(RenderTextTest, TruncatedCursorMovementRTL) {
   RenderText* render_text = GetRenderText();
   render_text->set_truncate_length(2);
-  render_text->SetText(u"\u05d0\u05d1\u05d2\u05d3");
+  render_text->SetText(u"אבגד");
 
   EXPECT_EQ(SelectionModel(0, CURSOR_BACKWARD), render_text->selection_model());
   render_text->MoveCursor(LINE_BREAK, CURSOR_LEFT, SELECTION_NONE);
@@ -3253,18 +3223,18 @@
   render_text->SetText(u"a");
   EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
 
-  render_text->SetText(u"\u05d0");
+  render_text->SetText(u"א");
   EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
 
   // The codepoints u+2026 (ellipsis) has no strong direction.
-  render_text->SetText(u"\u2026");
+  render_text->SetText(u"…");
   EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
   render_text->AppendText(u"a");
   EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetTextDirection());
 
-  render_text->SetText(u"\u2026");
+  render_text->SetText(u"…");
   EXPECT_EQ(original_text_direction, render_text->GetTextDirection());
-  render_text->AppendText(u"\u05d0");
+  render_text->AppendText(u"א");
   EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
 }
 
@@ -3278,18 +3248,18 @@
   render_text->SetText(u"a");
   EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
 
-  render_text->SetText(u"\u05d0");
+  render_text->SetText(u"א");
   EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
 
   // The codepoints u+2026 (ellipsis) has no strong direction.
-  render_text->SetText(u"\u2026");
+  render_text->SetText(u"…");
   EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
   render_text->AppendText(u"a");
   EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
 
-  render_text->SetText(u"\u2026");
+  render_text->SetText(u"…");
   EXPECT_EQ(original_text_direction, render_text->GetDisplayTextDirection());
-  render_text->AppendText(u"\u05d0");
+  render_text->AppendText(u"א");
   EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetDisplayTextDirection());
 }
 
@@ -3304,7 +3274,7 @@
 
   // The elided text is an ellipsis with neutral directionality, and a 'z' with
   // a strong LTR directionality.
-  EXPECT_EQ(u"\u2026z", render_text->GetDisplayText());
+  EXPECT_EQ(u"…z", render_text->GetDisplayText());
   EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, render_text->GetTextDirection());
   EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, render_text->GetDisplayTextDirection());
 }
@@ -3314,7 +3284,7 @@
   ASSERT_EQ(render_text->directionality_mode(), DIRECTIONALITY_FROM_TEXT);
 
   // The codepoints u+2026 (ellipsis) has weak directionality.
-  render_text->SetText(u"\u2026");
+  render_text->SetText(u"…");
   const base::i18n::TextDirection original_text_direction =
       render_text->GetTextDirection();
 
@@ -3338,11 +3308,11 @@
   render_text->SetVerticalAlignment(ALIGN_TOP);
 
   const size_t kLineSize = 50;
-  std::string text;
+  std::u16string text;
   for (size_t i = 0; i < kLineSize - 1; ++i)
-    text += "a\n";
+    text += u"a\n";
 
-  render_text->SetText(ASCIIToUTF16(text));
+  render_text->SetText(text);
   EXPECT_EQ(kLineSize, render_text->GetNumLines());
 
   // Move cursor down with scroll.
@@ -3383,11 +3353,11 @@
 
 TEST_F(RenderTextTest, GetDisplayTextDirection) {
   struct {
-    const char* text;
+    const char16_t* text;
     const base::i18n::TextDirection text_direction;
   } cases[] = {
       // Blank strings and those with no/weak directionality default to LTR.
-      {"", base::i18n::LEFT_TO_RIGHT},
+      {u"", base::i18n::LEFT_TO_RIGHT},
       {kWeak, base::i18n::LEFT_TO_RIGHT},
       // Strings that begin with strong LTR characters.
       {kLtr, base::i18n::LEFT_TO_RIGHT},
@@ -3410,7 +3380,7 @@
 
     // Ensure that directionality modes yield the correct text directions.
     for (size_t j = 0; j < base::size(cases); j++) {
-      render_text->SetText(UTF8ToUTF16(cases[j].text));
+      render_text->SetText(cases[j].text);
       render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
       EXPECT_EQ(render_text->GetDisplayTextDirection(),cases[j].text_direction);
       render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_UI);
@@ -3431,15 +3401,15 @@
 
   // Ensure that text changes update the direction for DIRECTIONALITY_FROM_TEXT.
   render_text->SetDirectionalityMode(DIRECTIONALITY_FROM_TEXT);
-  render_text->SetText(UTF8ToUTF16(kLtr));
+  render_text->SetText(kLtr);
   EXPECT_EQ(render_text->GetDisplayTextDirection(), base::i18n::LEFT_TO_RIGHT);
-  render_text->SetText(UTF8ToUTF16(kRtl));
+  render_text->SetText(kRtl);
   EXPECT_EQ(render_text->GetDisplayTextDirection(), base::i18n::RIGHT_TO_LEFT);
 }
 
 struct GetTextIndexOfLineCase {
   const char* test_name;
-  const wchar_t* const text;
+  const char16_t* const text;
   const std::vector<size_t> line_breaks;
   const bool set_word_wrap = false;
   const bool set_obscured = false;
@@ -3465,60 +3435,60 @@
     render_text->SetWordWrapBehavior(WRAP_LONG_WORDS);
   }
   render_text->SetObscured(param.set_obscured);
-  render_text->SetText(base::WideToUTF16(param.text));
+  render_text->SetText(param.text);
   for (size_t i = 0; i < param.line_breaks.size(); ++i) {
     EXPECT_EQ(param.line_breaks[i], render_text->GetTextIndexOfLine(i));
   }
 }
 
 const GetTextIndexOfLineCase kGetTextIndexOfLineCases[] = {
-    {"emptyString", L"", {0}},
+    {"emptyString", u"", {0}},
     // The following test strings are three character strings.
     // The word wrap makes each character fall on a new line.
-    {"kWeak_minWidth", L" . ", {0, 1, 2}, kUseWordWrap},
-    {"kLtr_minWidth", L"abc", {0, 1, 2}, kUseWordWrap},
-    {"kLtrRtl_minWidth", L"a\u05d0\u05d1", {0, 1, 2}, kUseWordWrap},
-    {"kLtrRtlLtr_minWidth", L"a\u05d1b", {0, 1, 2}, kUseWordWrap},
-    {"kRtl_minWidth", L"\u05d0\u05d1\u05d2", {0, 1, 2}, kUseWordWrap},
-    {"kRtlLtr_minWidth", L"\u05d0\u05d1a", {0, 1, 2}, kUseWordWrap},
-    {"kRtlLtrRtl_minWidth", L"\u05d0a\u05d1", {0, 1, 2}, kUseWordWrap},
+    {"kWeak_minWidth", u" . ", {0, 1, 2}, kUseWordWrap},
+    {"kLtr_minWidth", u"abc", {0, 1, 2}, kUseWordWrap},
+    {"kLtrRtl_minWidth", u"aאב", {0, 1, 2}, kUseWordWrap},
+    {"kLtrRtlLtr_minWidth", u"aבb", {0, 1, 2}, kUseWordWrap},
+    {"kRtl_minWidth", u"אבג", {0, 1, 2}, kUseWordWrap},
+    {"kRtlLtr_minWidth", u"אבa", {0, 1, 2}, kUseWordWrap},
+    {"kRtlLtrRtl_minWidth", u"אaב", {0, 1, 2}, kUseWordWrap},
     // The following test strings have 2 graphemes separated by a newline.
     // The obscured text replace each grapheme by a single codepoint.
     {"grapheme_unobscured",
-     L"\U0001F601\n\U0001F468\u200D\u2708\uFE0F\nx",
+     u"\U0001F601\n\U0001F468\u200D\u2708\uFE0F\nx",
      {0, 3, 9}},
     {"grapheme_obscured",
-     L"\U0001F601\n\U0001F468\u200D\u2708\uFE0F\nx",
+     u"\U0001F601\n\U0001F468\u200D\u2708\uFE0F\nx",
      {0, 3, 9},
      !kUseWordWrap,
      kUseObscuredText},
     // The following test strings have a new line character.
-    {"basic_newLine", L"abc\ndef", {0, 4}},
-    {"basic_newLineWindows", L"abc\r\ndef", {0, 5}},
-    {"spaces_newLine", L"a \n b ", {0, 3}},
-    {"spaces_newLineWindows", L"a \r\n b ", {0, 4}},
-    {"double_newLine", L"a\n\nb", {0, 2, 3}},
-    {"double_newLineWindows", L"a\r\n\r\nb", {0, 3, 5}},
-    {"start_newLine", L"\nab", {0, 1}},
-    {"start_newLineWindows", L"\r\nab", {0, 2}},
-    {"end_newLine", L"ab\n", {0}},
-    {"end_newLineWindows", L"ab\r\n", {0}},
-    {"isolated_newLine", L"\n", {0}},
-    {"isolated_newLineWindows", L"\r\n", {0}},
-    {"isolatedDouble_newLine", L"\n\n", {0, 1}},
-    {"isolatedDouble_newLineWindows", L"\r\n\r\n", {0, 2}},
+    {"basic_newLine", u"abc\ndef", {0, 4}},
+    {"basic_newLineWindows", u"abc\r\ndef", {0, 5}},
+    {"spaces_newLine", u"a \n b ", {0, 3}},
+    {"spaces_newLineWindows", u"a \r\n b ", {0, 4}},
+    {"double_newLine", u"a\n\nb", {0, 2, 3}},
+    {"double_newLineWindows", u"a\r\n\r\nb", {0, 3, 5}},
+    {"start_newLine", u"\nab", {0, 1}},
+    {"start_newLineWindows", u"\r\nab", {0, 2}},
+    {"end_newLine", u"ab\n", {0}},
+    {"end_newLineWindows", u"ab\r\n", {0}},
+    {"isolated_newLine", u"\n", {0}},
+    {"isolated_newLineWindows", u"\r\n", {0}},
+    {"isolatedDouble_newLine", u"\n\n", {0, 1}},
+    {"isolatedDouble_newLineWindows", u"\r\n\r\n", {0, 2}},
     // The following test strings have unicode characters.
-    {"playSymbol_unicode", L"x\n\u25B6\ny", {0, 2, 4}},
-    {"emoji_unicode", L"x\n\U0001F601\ny\n\u2728\nz", {0, 2, 5, 7, 9}},
-    {"flag_unicode", L"🇬🇧\n🇯🇵", {0, 5}, false, false},
+    {"playSymbol_unicode", u"x\n\u25B6\ny", {0, 2, 4}},
+    {"emoji_unicode", u"x\n\U0001F601\ny\n\u2728\nz", {0, 2, 5, 7, 9}},
+    {"flag_unicode", u"🇬🇧\n🇯🇵", {0, 5}, false, false},
     // The following cases test that GetTextIndexOfLine returns the length of
     // the text when passed a line index larger than the number of lines.
-    {"basic_outsideRange", L"abc", {0, 1, 2, 3, 3}, kUseWordWrap},
-    {"emptyString_outsideRange", L"", {0, 0, 0}},
-    {"newLine_outsideRange", L"\n", {0, 1, 1}},
-    {"newLineWindows_outsideRange", L"\r\n", {0, 2, 2, 2}},
-    {"doubleNewLine_outsideRange", L"\n\n", {0, 1, 2, 2}},
-    {"doubleNewLineWindows_outsideRange", L"\r\n\r\n", {0, 2, 4, 4}},
+    {"basic_outsideRange", u"abc", {0, 1, 2, 3, 3}, kUseWordWrap},
+    {"emptyString_outsideRange", u"", {0, 0, 0}},
+    {"newLine_outsideRange", u"\n", {0, 1, 1}},
+    {"newLineWindows_outsideRange", u"\r\n", {0, 2, 2, 2}},
+    {"doubleNewLine_outsideRange", u"\n\n", {0, 1, 2, 2}},
+    {"doubleNewLineWindows_outsideRange", u"\r\n\r\n", {0, 2, 4, 4}},
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -3553,7 +3523,7 @@
 TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtl) {
   RenderText* render_text = GetRenderText();
   // LTR-RTL
-  render_text->SetText(u"abc\u05d0\u05d1\u05d2");
+  render_text->SetText(u"abcאבג");
   // The last one is the expected END position.
   std::vector<SelectionModel> expected;
   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
@@ -3581,7 +3551,7 @@
 TEST_F(RenderTextTest, MoveCursorLeftRightInLtrRtlLtr) {
   RenderText* render_text = GetRenderText();
   // LTR-RTL-LTR.
-  render_text->SetText(u"a\u05d1b");
+  render_text->SetText(u"aבb");
   std::vector<SelectionModel> expected;
   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
   expected.push_back(SelectionModel(1, CURSOR_BACKWARD));
@@ -3602,7 +3572,7 @@
 TEST_F(RenderTextTest, MoveCursorLeftRightInRtl) {
   RenderText* render_text = GetRenderText();
   // Pure RTL.
-  render_text->SetText(u"\u05d0\u05d1\u05d2");
+  render_text->SetText(u"אבג");
   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, SELECTION_NONE);
   std::vector<SelectionModel> expected;
 
@@ -3626,7 +3596,7 @@
 TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtr) {
   RenderText* render_text = GetRenderText();
   // RTL-LTR
-  render_text->SetText(u"\u05d0\u05d1\u05d2abc");
+  render_text->SetText(u"אבגabc");
   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, SELECTION_NONE);
   std::vector<SelectionModel> expected;
   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
@@ -3654,7 +3624,7 @@
 TEST_F(RenderTextTest, MoveCursorLeftRightInRtlLtrRtl) {
   RenderText* render_text = GetRenderText();
   // RTL-LTR-RTL.
-  render_text->SetText(u"\u05d0a\u05d1");
+  render_text->SetText(u"אaב");
   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, SELECTION_NONE);
   std::vector<SelectionModel> expected;
   expected.push_back(SelectionModel(0, CURSOR_BACKWARD));
@@ -3956,12 +3926,12 @@
 }
 
 TEST_F(RenderTextTest, FindCursorPosition) {
-  const char* kTestStrings[] = {kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl};
+  const char16_t* kTestStrings[] = {kLtrRtl, kLtrRtlLtr, kRtlLtr, kRtlLtrRtl};
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(Rect(0, 0, 100, 20));
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("Testing case[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
     for (size_t j = 0; j < render_text->text().length(); ++j) {
       gfx::RangeF cursor_span = render_text->GetCursorSpan(Range(j, j + 1));
       // Test a point just inside the leading edge of the glyph bounds.
@@ -3974,8 +3944,7 @@
 
 // Tests that FindCursorPosition behaves correctly for multi-line text.
 TEST_F(RenderTextTest, FindCursorPositionMultiline) {
-  const char* kTestStrings[] = {"abc def",
-                                "\u05d0\u05d1\u05d2 \u05d3\u05d4\u05d5"};
+  const char16_t* kTestStrings[] = {u"abc def", u"אבג דהו"};
 
   SetGlyphWidth(5);
   RenderText* render_text = GetRenderText();
@@ -3983,7 +3952,7 @@
   render_text->SetMultiline(true);
 
   for (size_t i = 0; i < base::size(kTestStrings); i++) {
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
     EXPECT_EQ(2u, render_text->GetNumLines());
 
     const bool is_ltr =
@@ -4075,8 +4044,8 @@
 }
 
 TEST_F(RenderTextTest, SelectAll) {
-  const char* const cases[] = {kWeak, kLtr,    kLtrRtl,   kLtrRtlLtr,
-                               kRtl,  kRtlLtr, kRtlLtrRtl};
+  const char16_t* const cases[] = {kWeak, kLtr,    kLtrRtl,   kLtrRtlLtr,
+                                   kRtl,  kRtlLtr, kRtlLtrRtl};
 
   // Ensure that SelectAll respects the |reversed| argument regardless of
   // application locale and text content directionality.
@@ -4093,7 +4062,7 @@
 
     // Test the weak, LTR, RTL, and Bidi string cases.
     for (size_t j = 0; j < base::size(cases); j++) {
-      render_text->SetText(UTF8ToUTF16(cases[j]));
+      render_text->SetText(cases[j]);
       render_text->SelectAll(false);
       EXPECT_EQ(render_text->selection_model(), expected_forwards);
       render_text->SelectAll(true);
@@ -4106,7 +4075,7 @@
 
 TEST_F(RenderTextTest, MoveCursorLeftRightWithSelection) {
   RenderText* render_text = GetRenderText();
-  render_text->SetText(u"abc\u05d0\u05d1\u05d2");
+  render_text->SetText(u"abcאבג");
   // Left arrow on select ranging (6, 4).
   render_text->MoveCursor(LINE_BREAK, CURSOR_RIGHT, SELECTION_NONE);
   EXPECT_EQ(Range(6), render_text->selection());
@@ -4270,9 +4239,9 @@
             render_text->GetUpdatedCursorBounds().right());
 }
 
-void MoveLeftRightByWordVerifier(RenderText* render_text, const char* str) {
+void MoveLeftRightByWordVerifier(RenderText* render_text, const char16_t* str) {
   SCOPED_TRACE(str);
-  const std::u16string str16(UTF8ToUTF16(str));
+  const std::u16string str16(str);
   render_text->SetText(str16);
 
   // Test moving by word from left to right.
@@ -4344,37 +4313,37 @@
 TEST_F(RenderTextTest, MAYBE_MoveLeftRightByWordInBidiText) {
   RenderText* render_text = GetRenderText();
   // For testing simplicity, each word is a 3-character word.
-  std::vector<const char*> test;
-  test.push_back("abc");
-  test.push_back("abc def");
-  test.push_back("\u05E1\u05E2\u05E3");
-  test.push_back("\u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6");
-  test.push_back("abc \u05E1\u05E2\u05E3");
-  test.push_back("abc def \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6");
+  std::vector<const char16_t*> test;
+  test.push_back(u"abc");
+  test.push_back(u"abc def");
+  test.push_back(u"\u05E1\u05E2\u05E3");
+  test.push_back(u"\u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6");
+  test.push_back(u"abc \u05E1\u05E2\u05E3");
+  test.push_back(u"abc def \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6");
   test.push_back(
-      "abc def hij \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6"
-      " \u05E7\u05E8\u05E9");
+      u"abc def hij \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6"
+      u" \u05E7\u05E8\u05E9");
 
-  test.push_back("abc \u05E1\u05E2\u05E3 hij");
-  test.push_back("abc def \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6 hij opq");
+  test.push_back(u"abc \u05E1\u05E2\u05E3 hij");
+  test.push_back(u"abc def \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6 hij opq");
   test.push_back(
-      "abc def hij \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6"
-      " \u05E7\u05E8\u05E9 opq rst uvw");
+      u"abc def hij \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6"
+      u" \u05E7\u05E8\u05E9 opq rst uvw");
 
-  test.push_back("\u05E1\u05E2\u05E3 abc");
-  test.push_back("\u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6 abc def");
+  test.push_back(u"\u05E1\u05E2\u05E3 abc");
+  test.push_back(u"\u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6 abc def");
   test.push_back(
-      "\u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6 \u05E7\u05E8\u05E9"
-      " abc def hij");
+      u"\u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6 \u05E7\u05E8\u05E9"
+      u" abc def hij");
 
-  test.push_back("\u05D1\u05D2\u05D3 abc \u05E1\u05E2\u05E3");
+  test.push_back(u"בגד abc \u05E1\u05E2\u05E3");
   test.push_back(
-      "\u05D1\u05D2\u05D3 \u05D4\u05D5\u05D6 abc def"
-      " \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6");
+      u"בגד הוז abc def"
+      u" \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6");
   test.push_back(
-      "\u05D1\u05D2\u05D3 \u05D4\u05D5\u05D6 \u05D7\u05D8\u05D9"
-      " abc def hij \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6"
-      " \u05E7\u05E8\u05E9");
+      u"בגד הוז חטי"
+      u" abc def hij \u05E1\u05E2\u05E3 \u05E4\u05E5\u05E6"
+      u" \u05E7\u05E8\u05E9");
 
   for (size_t i = 0; i < test.size(); ++i)
     MoveLeftRightByWordVerifier(render_text, test[i]);
@@ -4711,15 +4680,13 @@
   Font cjk_font(kCJKFontName, 16);
   ASSERT_EQ(base::ToLowerASCII(kCJKFontName),
             base::ToLowerASCII(cjk_font.GetActualFontName()));
-  // "a" should be rendered with the test font, not with the CJK font.
-  const char* test_font_text = "a";
-  // "円" (U+5168 Han character YEN) should render with the CJK font, not
-  // the test font.
-  const char* cjk_font_text = "\u5168";
   Font smaller_font = test_font;
   Font larger_font = cjk_font;
-  const char* smaller_font_text = test_font_text;
-  const char* larger_font_text = cjk_font_text;
+  // "a" should be rendered with the test font, not with the CJK font.
+  const char16_t* smaller_font_text = u"a";
+  // "円" (U+5168 Han character YEN) should render with the CJK font, not
+  // the test font.
+  const char16_t* larger_font_text = u"\u5168";
   if (cjk_font.GetHeight() < test_font.GetHeight() &&
       cjk_font.GetBaseline() < test_font.GetBaseline()) {
     std::swap(smaller_font, larger_font);
@@ -4730,7 +4697,7 @@
 
   // Check |smaller_font_text| is rendered with the smaller font.
   RenderText* render_text = GetRenderText();
-  render_text->SetText(UTF8ToUTF16(smaller_font_text));
+  render_text->SetText(smaller_font_text);
   render_text->SetFontList(FontList(smaller_font));
   render_text->SetDisplayRect(Rect(0, 0, 0,
                                    render_text->font_list().GetHeight()));
@@ -4840,7 +4807,7 @@
 
   RenderText* render_text = GetRenderText();
   for (size_t text_length = 0; text_length < 10; ++text_length) {
-    render_text->SetText(ASCIIToUTF16(std::string(text_length, 'x')));
+    render_text->SetText(std::u16string(text_length, u'x'));
 
     // Ensures that conversion from float to integer ceils the values.
     const float expected_width = text_length * kGlyphWidth;
@@ -4881,7 +4848,7 @@
     if (line != 0)
       render_text->AppendText(u"\n");
     const int text_length = line;
-    render_text->AppendText(ASCIIToUTF16(std::string(text_length, 'x')));
+    render_text->AppendText(std::u16string(text_length, u'x'));
 
     // Ensures that conversion from float to integer ceils the values.
     const float expected_width = text_length * kGlyphWidth;
@@ -4945,7 +4912,7 @@
   const int kGlyphCount = 3;
 
   RenderText* render_text = GetRenderText();
-  render_text->SetText(ASCIIToUTF16(std::string(kGlyphCount, 'x')));
+  render_text->SetText(std::u16string(kGlyphCount, u'x'));
   render_text->SetDisplayRect(Rect(1, 1, 25, 12));
   render_text->SetCursorEnabled(false);
   render_text->SetVerticalAlignment(ALIGN_TOP);
@@ -5444,18 +5411,18 @@
 
 // Make sure the last word is selected when the cursor is at text.length().
 TEST_F(RenderTextTest, LastWordSelected) {
-  const std::string kTestURL1 = "http://www.google.com";
-  const std::string kTestURL2 = "http://www.google.com/something/";
+  const std::u16string kTestURL1 = u"http://www.google.com";
+  const std::u16string kTestURL2 = u"http://www.google.com/something/";
 
   RenderText* render_text = GetRenderText();
 
-  render_text->SetText(ASCIIToUTF16(kTestURL1));
+  render_text->SetText(kTestURL1);
   render_text->SetCursorPosition(kTestURL1.length());
   render_text->SelectWord();
   EXPECT_EQ(u"com", GetSelectedText(render_text));
   EXPECT_FALSE(render_text->selection().is_reversed());
 
-  render_text->SetText(ASCIIToUTF16(kTestURL2));
+  render_text->SetText(kTestURL2);
   render_text->SetCursorPosition(kTestURL2.length());
   render_text->SelectWord();
   EXPECT_EQ(u"/", GetSelectedText(render_text));
@@ -5465,11 +5432,11 @@
 // When given a non-empty selection, SelectWord should expand the selection to
 // nearest word boundaries.
 TEST_F(RenderTextTest, SelectMultipleWords) {
-  const std::string kTestURL = "http://www.google.com";
+  const std::u16string kTestURL = u"http://www.google.com";
 
   RenderText* render_text = GetRenderText();
 
-  render_text->SetText(ASCIIToUTF16(kTestURL));
+  render_text->SetText(kTestURL);
   render_text->SelectRange(Range(16, 20));
   render_text->SelectWord();
   EXPECT_EQ(u"google.com", GetSelectedText(render_text));
@@ -5511,9 +5478,7 @@
   EXPECT_EQ(width, render_text->GetUpdatedCursorBounds().x());
 
   // Repeat the test with RTL text.
-  render_text->SetText(
-      UTF8ToUTF16("\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7"
-                  "\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df"));
+  render_text->SetText(u"אבגדהוזחטיךכלםמן");
   render_text->SetCursorPosition(0);
   width = render_text->GetStringSize().width();
   ASSERT_GT(width, 10);
@@ -5569,9 +5534,7 @@
             render_text->GetUpdatedCursorBounds().x());
 
   // Repeat the test with RTL text.
-  render_text->SetText(
-      UTF8ToUTF16("\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7"
-                  "\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df"));
+  render_text->SetText(u"אבגדהוזחטיךכלםמן");
   render_text->SetCursorPosition(render_text->text().length());
   width = render_text->GetStringSize().width();
   ASSERT_GT(width, 10);
@@ -5601,12 +5564,12 @@
 
 // Changing colors between or inside ligated glyphs should not break shaping.
 TEST_F(RenderTextTest, SelectionKeepsLigatures) {
-  const char* kTestStrings[] = {"\u0644\u0623", "\u0633\u0627"};
+  const char16_t* const kTestStrings[] = {u"\u0644\u0623", u"\u0633\u0627"};
   RenderText* render_text = GetRenderText();
   render_text->set_selection_color(SK_ColorGREEN);
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
     const int expected_width = render_text->GetStringSize().width();
     render_text->SelectRange({0, 1});
     EXPECT_EQ(expected_width, render_text->GetStringSize().width());
@@ -5633,7 +5596,8 @@
   EXPECT_EQ(std::vector<std::u16string>({ramen_katakana}),
             RunsFor(ramen_katakana));
 
-  EXPECT_EQ(ToString16Vec({"らあ", "メン"}), RunsFor(ramen_mixed));
+  EXPECT_EQ(std::vector<std::u16string>({u"らあ", u"メン"}),
+            RunsFor(ramen_mixed));
 }
 
 // Test that whitespace breaks runs of text. E.g. this can permit better fonts
@@ -5649,18 +5613,20 @@
   // This says "thank you very much" with a full-width non-ascii space (U+3000).
   const std::u16string full_width_space = u"ども ありがと";
 
-  EXPECT_EQ(ToString16Vec({"סיבית", " ", "–", " ", "ויקיפדיה"}),
-            RunsFor(ascii_space_he));
-  EXPECT_EQ(ToString16Vec({"Bit", " ", "-", " ", "Wikipedia"}),
-            RunsFor(ascii_space_en));
-  EXPECT_EQ(ToString16Vec({"ども", " ", "ありがと"}),
+  EXPECT_EQ(
+      std::vector<std::u16string>({u"סיבית", u" ", u"–", u" ", u"ויקיפדיה"}),
+      RunsFor(ascii_space_he));
+  EXPECT_EQ(
+      std::vector<std::u16string>({u"Bit", u" ", u"-", u" ", u"Wikipedia"}),
+      RunsFor(ascii_space_en));
+  EXPECT_EQ(std::vector<std::u16string>({u"ども", u" ", u"ありがと"}),
             RunsFor(full_width_space));
 }
 
 // Ensure strings wrap onto multiple lines for a small available width.
 TEST_F(RenderTextTest, Multiline_MinWidth) {
-  const char* kTestStrings[] = {kWeak, kLtr,    kLtrRtl,   kLtrRtlLtr,
-                                kRtl,  kRtlLtr, kRtlLtrRtl};
+  const char16_t* kTestStrings[] = {kWeak, kLtr,    kLtrRtl,   kLtrRtlLtr,
+                                    kRtl,  kRtlLtr, kRtlLtrRtl};
 
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(Rect(1, 1000));
@@ -5669,7 +5635,7 @@
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
     render_text->Draw(canvas());
     EXPECT_GT(test_api()->lines().size(), 1U);
   }
@@ -5680,7 +5646,7 @@
   // Should RenderText suppress drawing whitespace at the end of a line?
   // Currently it does not.
   const struct {
-    const char* const text;
+    const char16_t* const text;
     const Range first_line_char_range;
     const Range second_line_char_range;
 
@@ -5692,22 +5658,20 @@
 
     bool is_ltr;
   } kTestStrings[] = {
-      {"abc defg hijkl", Range(0, 9), Range(9, 14), {3, 1, 4, 1, 5}, 4, true},
-      {"qwertyzxcvbn", Range(0, 10), Range(10, 12), {10, 2}, 1, true},
+      {u"abc defg hijkl", Range(0, 9), Range(9, 14), {3, 1, 4, 1, 5}, 4, true},
+      {u"qwertyzxcvbn", Range(0, 10), Range(10, 12), {10, 2}, 1, true},
       // RTL: should render left-to-right as "<space>43210 \n cba9876".
       // Note this used to say "Arabic language", in Arabic, but the last
       // character in the string (\u0629) got fancy in an updated Mac font, so
       // now the penultimate character repeats. (See "NOTE" below).
-      {"\u0627\u0644\u0644\u063A\u0629 "
-       "\u0627\u0644\u0639\u0631\u0628\u064A\u064A",
+      {u"اللغة العربيي",
        Range(0, 6),
        Range(6, 13),
        {1 /* space first */, 5, 7},
        2,
        false},
       // RTL: should render left-to-right as "<space>3210 \n cba98765".
-      {"\u062A\u0641\u0627\u062D \u05EA\u05E4\u05D5\u05D6\u05D9"
-       "\u05DA\u05DB\u05DD",
+      {u"تفاح תפוזיךכם",
        Range(0, 5),
        Range(5, 13),
        {1 /* space first */, 5, 8},
@@ -5726,7 +5690,7 @@
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i].text));
+    render_text->SetText(kTestStrings[i].text);
     DrawVisualText();
 
     ASSERT_EQ(2U, test_api()->lines().size());
@@ -5760,8 +5724,14 @@
 // Ensure strings don't wrap onto multiple lines for a sufficient available
 // width.
 TEST_F(RenderTextTest, Multiline_SufficientWidth) {
-  const char* kTestStrings[] = {"", " ", ".", " . ", "abc", "a b c",
-                                "\u062E\u0628\u0632", "\u062E \u0628 \u0632"};
+  const char16_t* kTestStrings[] = {u"",
+                                    u" ",
+                                    u".",
+                                    u" . ",
+                                    u"abc",
+                                    u"a b c",
+                                    u"\u062E\u0628\u0632",
+                                    u"\u062E \u0628 \u0632"};
 
   RenderText* render_text = GetRenderText();
   render_text->SetDisplayRect(Rect(1000, 1000));
@@ -5769,7 +5739,7 @@
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
     render_text->Draw(canvas());
     EXPECT_EQ(1U, test_api()->lines().size());
   }
@@ -5777,17 +5747,17 @@
 
 TEST_F(RenderTextTest, Multiline_Newline) {
   const struct {
-    const char* const text;
+    const char16_t* const text;
     const size_t lines_count;
     // Ranges of the characters on each line.
     const Range line_char_ranges[3];
   } kTestStrings[] = {
-      {"abc\ndef", 2ul, {Range(0, 4), Range(4, 7), Range::InvalidRange()}},
-      {"a \n b ", 2ul, {Range(0, 3), Range(3, 6), Range::InvalidRange()}},
-      {"ab\n", 2ul, {Range(0, 3), Range(), Range::InvalidRange()}},
-      {"a\n\nb", 3ul, {Range(0, 2), Range(2, 3), Range(3, 4)}},
-      {"\nab", 2ul, {Range(0, 1), Range(1, 3), Range::InvalidRange()}},
-      {"\n", 2ul, {Range(0, 1), Range(), Range::InvalidRange()}},
+      {u"abc\ndef", 2ul, {Range(0, 4), Range(4, 7), Range::InvalidRange()}},
+      {u"a \n b ", 2ul, {Range(0, 3), Range(3, 6), Range::InvalidRange()}},
+      {u"ab\n", 2ul, {Range(0, 3), Range(), Range::InvalidRange()}},
+      {u"a\n\nb", 3ul, {Range(0, 2), Range(2, 3), Range(3, 4)}},
+      {u"\nab", 2ul, {Range(0, 1), Range(1, 3), Range::InvalidRange()}},
+      {u"\n", 2ul, {Range(0, 1), Range(), Range::InvalidRange()}},
   };
 
   RenderText* render_text = GetRenderText();
@@ -5796,7 +5766,7 @@
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i].text));
+    render_text->SetText(kTestStrings[i].text);
     render_text->Draw(canvas());
     EXPECT_EQ(kTestStrings[i].lines_count, test_api()->lines().size());
     if (kTestStrings[i].lines_count != test_api()->lines().size())
@@ -5821,23 +5791,20 @@
 TEST_F(RenderTextTest, Multiline_IgnoreElide) {
   const char16_t kTestString[] =
       u"very very very long string xxxxxxxxxxxxxxxxxxxxxxxxxx";
-  const char kEllipsis[] = "\u2026";
 
   RenderText* render_text = GetRenderText();
   render_text->SetElideBehavior(ELIDE_TAIL);
   render_text->SetDisplayRect(Rect(20, 1000));
   render_text->SetText(kTestString);
-  EXPECT_NE(std::u16string::npos,
-            render_text->GetDisplayText().find(base::UTF8ToUTF16(kEllipsis)));
+  EXPECT_NE(std::u16string::npos, render_text->GetDisplayText().find(u"…"));
 
   render_text->SetMultiline(true);
-  EXPECT_EQ(std::u16string::npos,
-            render_text->GetDisplayText().find(base::UTF8ToUTF16(kEllipsis)));
+  EXPECT_EQ(std::u16string::npos, render_text->GetDisplayText().find(u"…"));
 }
 
 TEST_F(RenderTextTest, Multiline_NewlineCharacterReplacement) {
-  const char* kTestStrings[] = {
-      "abc\ndef", "a \n b ", "ab\n", "a\n\nb", "\nab", "\n",
+  const char16_t* kTestStrings[] = {
+      u"abc\ndef", u"a \n b ", u"ab\n", u"a\n\nb", u"\nab", u"\n",
   };
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
@@ -5845,49 +5812,45 @@
     ResetRenderTextInstance();
     RenderText* render_text = GetRenderText();
     render_text->SetDisplayRect(Rect(200, 1000));
-    render_text->SetText(ASCIIToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
 
     std::u16string display_text = render_text->GetDisplayText();
     // If RenderText is not multiline, the newline characters are replaced
     // by symbols, therefore the character should be changed.
-    EXPECT_NE(ASCIIToUTF16(kTestStrings[i]), render_text->GetDisplayText());
+    EXPECT_NE(kTestStrings[i], render_text->GetDisplayText());
 
     // Setting multiline will fix this, the newline characters will be back
     // to the original text.
     render_text->SetMultiline(true);
-    EXPECT_EQ(ASCIIToUTF16(kTestStrings[i]), render_text->GetDisplayText());
+    EXPECT_EQ(kTestStrings[i], render_text->GetDisplayText());
   }
 }
 
 // Ensure horizontal alignment works in multiline mode.
 TEST_F(RenderTextTest, Multiline_HorizontalAlignment) {
   constexpr struct {
-    const char* const text;
+    const char16_t* const text;
     const HorizontalAlignment alignment;
     const base::i18n::TextDirection display_text_direction;
   } kTestStrings[] = {
-      {"abcdefghi\nhijk", ALIGN_LEFT, base::i18n::LEFT_TO_RIGHT},
-      {"nhij\nabcdefghi", ALIGN_LEFT, base::i18n::LEFT_TO_RIGHT},
+      {u"abcdefghi\nhijk", ALIGN_LEFT, base::i18n::LEFT_TO_RIGHT},
+      {u"nhij\nabcdefghi", ALIGN_LEFT, base::i18n::LEFT_TO_RIGHT},
       // Hebrew, 2nd line shorter
-      {"\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\n"
-       "\u05d0\u05d1\u05d2\u05d3",
-       ALIGN_RIGHT,
-       base::i18n::RIGHT_TO_LEFT},
+      {u"אבגדהוזח\n"
+       u"אבגד",
+       ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT},
       // Hebrew, 2nd line longer
-      {"\u05d0\u05d1\u05d2\u05d3\n"
-       "\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7",
-       ALIGN_RIGHT,
-       base::i18n::RIGHT_TO_LEFT},
+      {u"אבגד\n"
+       u"אבגדהוזח",
+       ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT},
       // Arabic, 2nd line shorter.
-      {"\u0627\u0627\u0627\u0627\u0627\u0627\u0627\u0627\n"
-       "\u0627\u0644\u0644\u063A",
-       ALIGN_RIGHT,
-       base::i18n::RIGHT_TO_LEFT},
+      {u"\u0627\u0627\u0627\u0627\u0627\u0627\u0627\u0627\n"
+       u"\u0627\u0644\u0644\u063A",
+       ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT},
       // Arabic, 2nd line longer.
-      {"\u0627\u0644\u0644\u063A\n"
-       "\u0627\u0627\u0627\u0627\u0627\u0627\u0627\u0627",
-       ALIGN_RIGHT,
-       base::i18n::RIGHT_TO_LEFT},
+      {u"\u0627\u0644\u0644\u063A\n"
+       u"\u0627\u0627\u0627\u0627\u0627\u0627\u0627\u0627",
+       ALIGN_RIGHT, base::i18n::RIGHT_TO_LEFT},
   };
   const int kGlyphSize = 5;
   RenderTextHarfBuzz* render_text = GetRenderText();
@@ -5899,7 +5862,7 @@
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(testing::Message("kTestStrings[")
                  << i << "] = " << kTestStrings[i].text);
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i].text));
+    render_text->SetText(kTestStrings[i].text);
     EXPECT_EQ(kTestStrings[i].display_text_direction,
               render_text->GetDisplayTextDirection());
     render_text->Draw(canvas());
@@ -5908,9 +5871,9 @@
       EXPECT_EQ(0, test_api()->GetAlignmentOffset(0).x());
       EXPECT_EQ(0, test_api()->GetAlignmentOffset(1).x());
     } else {
-      std::vector<std::u16string> lines = base::SplitString(
-          base::UTF8ToUTF16(kTestStrings[i].text), std::u16string(1, '\n'),
-          base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+      std::vector<std::u16string> lines =
+          base::SplitString(kTestStrings[i].text, std::u16string(1, '\n'),
+                            base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
       ASSERT_EQ(2u, lines.size());
 
       // Sanity check the input string lengths match the glyph lengths.
@@ -5970,36 +5933,46 @@
 TEST_F(RenderTextTest, Multiline_LineBreakerBehavior) {
   const int kGlyphSize = 5;
   const struct {
-    const char* const text;
+    const char16_t* const text;
     const WordWrapBehavior behavior;
     const Range char_ranges[3];
   } kTestScenarios[] = {
-      {"a single run",
+      {u"a single run",
        IGNORE_LONG_WORDS,
        {Range(0, 2), Range(2, 9), Range(9, 12)}},
       // 3 words: "That's ", ""good". ", "aaa" and 7 runs: "That", "'", "s ",
       // """, "good", "". ", "aaa". They all mixed together.
-      {"That's \"good\". aaa", IGNORE_LONG_WORDS,
+      {u"That's \"good\". aaa",
+       IGNORE_LONG_WORDS,
        {Range(0, 7), Range(7, 15), Range(15, 18)}},
       // Test "\"" should be put into a new line correctly.
-      {"a \"good\" one.", IGNORE_LONG_WORDS,
+      {u"a \"good\" one.",
+       IGNORE_LONG_WORDS,
        {Range(0, 2), Range(2, 9), Range(9, 13)}},
       // Test for full-width space.
-      {"That's\u3000good.\u3000yyy", IGNORE_LONG_WORDS,
+      {u"That's\u3000good.\u3000yyy",
+       IGNORE_LONG_WORDS,
        {Range(0, 7), Range(7, 13), Range(13, 16)}},
-      {"a single run", TRUNCATE_LONG_WORDS,
+      {u"a single run",
+       TRUNCATE_LONG_WORDS,
        {Range(0, 2), Range(2, 6), Range(9, 12)}},
-      {"That's \"good\". aaa", TRUNCATE_LONG_WORDS,
+      {u"That's \"good\". aaa",
+       TRUNCATE_LONG_WORDS,
        {Range(0, 4), Range(7, 11), Range(15, 18)}},
-      {"That's good. aaa", TRUNCATE_LONG_WORDS,
+      {u"That's good. aaa",
+       TRUNCATE_LONG_WORDS,
        {Range(0, 4), Range(7, 11), Range(13, 16)}},
-      {"a \"good\" one.", TRUNCATE_LONG_WORDS,
+      {u"a \"good\" one.",
+       TRUNCATE_LONG_WORDS,
        {Range(0, 2), Range(2, 6), Range(9, 13)}},
-      {"asingleword", WRAP_LONG_WORDS,
+      {u"asingleword",
+       WRAP_LONG_WORDS,
        {Range(0, 4), Range(4, 8), Range(8, 11)}},
-      {"That's good", WRAP_LONG_WORDS,
+      {u"That's good",
+       WRAP_LONG_WORDS,
        {Range(0, 4), Range(4, 7), Range(7, 11)}},
-      {"That's \"g\".", WRAP_LONG_WORDS,
+      {u"That's \"g\".",
+       WRAP_LONG_WORDS,
        {Range(0, 4), Range(4, 7), Range(7, 11)}},
   };
 
@@ -6010,7 +5983,7 @@
 
   for (size_t i = 0; i < base::size(kTestScenarios); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestScenarios[i].text));
+    render_text->SetText(kTestScenarios[i].text);
     render_text->SetWordWrapBehavior(kTestScenarios[i].behavior);
     render_text->Draw(canvas());
 
@@ -6180,8 +6153,8 @@
 }
 
 TEST_F(RenderTextTest, NewlineWithoutMultilineFlag) {
-  const char* kTestStrings[] = {
-      "abc\ndef", "a \n b ", "ab\n", "a\n\nb", "\nab", "\n",
+  const char16_t* kTestStrings[] = {
+      u"abc\ndef", u"a \n b ", u"ab\n", u"a\n\nb", u"\nab", u"\n",
   };
 
   RenderText* render_text = GetRenderText();
@@ -6189,7 +6162,7 @@
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(ASCIIToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
     render_text->Draw(canvas());
 
     EXPECT_EQ(1U, test_api()->lines().size());
@@ -6251,18 +6224,18 @@
 // LTR languages and right-to-left for RTL languages).
 TEST_F(RenderTextTest, HarfBuzz_HorizontalPositions) {
   const struct {
-    const char* const text;
+    const char16_t* const text;
     const char* expected_runs;
   } kTestStrings[] = {
-      {"abc\u3042\u3044\u3046\u3048\u304A", "[0->2][3->7]"},
-      {"\u062A\u0641\u0627\u062D\u05EA\u05E4\u05D5\u05D6", "[7<-4][3<-0]"},
+      {u"abc\u3042\u3044\u3046\u3048\u304A", "[0->2][3->7]"},
+      {u"\u062A\u0641\u0627\u062D\u05EA\u05E4וז", "[7<-4][3<-0]"},
   };
 
   RenderTextHarfBuzz* render_text = GetRenderText();
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
     SCOPED_TRACE(base::StringPrintf("kTestStrings[%" PRIuS "]", i));
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i].text));
+    render_text->SetText(kTestStrings[i].text);
 
     EXPECT_EQ(kTestStrings[i].expected_runs, GetRunListStructureString());
 
@@ -6355,13 +6328,13 @@
 
 // Ensure that graphemes with multiple code points do not get split.
 TEST_F(RenderTextTest, HarfBuzz_SubglyphGraphemeCases) {
-  const char* cases[] = {
+  const char16_t* cases[] = {
       // Ä (A with combining umlaut), followed by a "B".
-      "A\u0308B",
+      u"A\u0308B",
       // कि (Devangari letter KA with vowel I), followed by an "a".
-      "\u0915\u093f\u0905",
-      // จำ (Thai charcters CHO CHAN and SARA AM, followed by Thai digit 0.
-      "\u0e08\u0e33\u0E50",
+      u"\u0915\u093f\u0905",
+      // จำ (Thai characters CHO CHAN and SARA AM, followed by Thai digit 0.
+      u"\u0e08\u0e33\u0E50",
   };
 
   RenderTextHarfBuzz* render_text = GetRenderText();
@@ -6369,7 +6342,7 @@
   for (size_t i = 0; i < base::size(cases); ++i) {
     SCOPED_TRACE(base::StringPrintf("Case %" PRIuS, i));
 
-    std::u16string text = UTF8ToUTF16(cases[i]);
+    std::u16string text = cases[i];
     render_text->SetText(text);
     const internal::TextRunList* run_list = GetHarfBuzzRunList();
     ASSERT_EQ(1U, run_list->size());
@@ -6439,7 +6412,7 @@
 
 TEST_F(RenderTextTest, HarfBuzz_RunDirection) {
   RenderTextHarfBuzz* render_text = GetRenderText();
-  const std::u16string mixed = u"\u05D0\u05D11234\u05D2\u05D3abc";
+  const std::u16string mixed = u"אב1234גדabc";
   render_text->SetText(mixed);
 
   // Get the run list for both display directions.
@@ -6454,9 +6427,9 @@
   RenderTextHarfBuzz* render_text = GetRenderText();
   // This string, unescaped (logical order):
   // ‭www.אב.גד/הוabc/def?זח=טי‬
-  const std::u16string mixed = UTF8ToUTF16(
-      "www.\u05D0\u05D1.\u05D2\u05D3/\u05D4\u05D5"
-      "abc/def?\u05D6\u05D7=\u05D8\u05D9");
+  const std::u16string mixed =
+      u"www.אב.גד/הו"
+      u"abc/def?זח=טי";
   render_text->SetText(mixed);
 
   // Normal LTR text should treat URL syntax as weak (as per the normal Bidi
@@ -6482,11 +6455,13 @@
 
   // The ▶ (U+25B6) "play character" should break runs. http://crbug.com/278913
   render_text->SetText(u"x\u25B6y");
-  EXPECT_EQ(ToString16Vec({"x", "▶", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"x", u"▶", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1][2]", GetRunListStructureString());
 
   render_text->SetText(u"x \u25B6 y");
-  EXPECT_EQ(ToString16Vec({"x", " ", "▶", " ", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"x", u" ", u"▶", u" ", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1][2][3][4]", GetRunListStructureString());
 }
 
@@ -6497,13 +6472,15 @@
   // drawn with color emoji fonts, so runs should be separated. crbug.com/448909
   // Windows requires wide strings for \Unnnnnnnn universal character names.
   render_text->SetText(u"x\U0001F601y\u2728");
-  EXPECT_EQ(ToString16Vec({"x", "😁", "y", "✨"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"x", u"😁", u"y", u"✨"}),
+            GetRunListStrings());
   // U+1F601 is represented as a surrogate pair in UTF-16.
   EXPECT_EQ("[0][1->2][3][4]", GetRunListStructureString());
 
   // Ensure non-latin 「foo」 brackets around Emoji correctly break runs.
   render_text->SetText(u"「🦋」「");
-  EXPECT_EQ(ToString16Vec({"「", "🦋", "」「"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"「", u"🦋", u"」「"}),
+            GetRunListStrings());
   // Note 🦋 is a surrogate pair [1->2].
   EXPECT_EQ("[0][1->2][3->4]", GetRunListStructureString());
 }
@@ -6512,7 +6489,8 @@
   RenderText* render_text = GetRenderText();
   render_text->SetMultiline(true);
   render_text->SetText(u"x\ny");
-  EXPECT_EQ(ToString16Vec({"x", "\n", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"x", u"\n", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1][2]", GetRunListStructureString());
 
   // Validate that the character newline is an unknown glyph
@@ -6547,7 +6525,8 @@
   // not break between the codepoints, or the incorrect glyph will be chosen.
   render_text->SetText(u"z\u260E\uFE0Fy");
   render_text->SetDisplayRect(Rect(1000, 50));
-  EXPECT_EQ(ToString16Vec({"z", "☎\uFE0F", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"z", u"☎\uFE0F", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1->2][3]", GetRunListStructureString());
 
   // Also test moving the cursor across the telephone.
@@ -6588,7 +6567,7 @@
   // It should never happen in normal usage, but a variation selector can appear
   // by itself. In this case, it can form its own text run, with no glyphs.
   render_text->SetText(u"\uFE0F");
-  EXPECT_EQ(ToString16Vec({"\uFE0F"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"\uFE0F"}), GetRunListStrings());
   EXPECT_EQ("[0]", GetRunListStructureString());
   CheckBoundsForCursorPositions();
 }
@@ -6604,7 +6583,7 @@
   // cause the typesetter to render tofu in this case, but it should not break
   // a text run.
   render_text->SetText(u"z\uFE0Fy");
-  EXPECT_EQ(ToString16Vec({"z\uFE0Fy"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"z\uFE0Fy"}), GetRunListStrings());
   EXPECT_EQ("[0->2]", GetRunListStructureString());
   CheckBoundsForCursorPositions();
 }
@@ -6615,7 +6594,8 @@
   // When a variation selector appears either side of an emoji, ensure the one
   // after is in the same run.
   render_text->SetText(u"\uFE0F\u260E\uFE0Fy");
-  EXPECT_EQ(ToString16Vec({"\uFE0F", "☎\uFE0F", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"\uFE0F", u"☎\uFE0F", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1->2][3]", GetRunListStructureString());
   CheckBoundsForCursorPositions();
 }
@@ -6628,7 +6608,8 @@
   // ultimately up to the typeface but, however it choses, cursor and glyph
   // positions should behave.
   render_text->SetText(u"z\u260E\uFE0F\uFE0Fy");
-  EXPECT_EQ(ToString16Vec({"z", "☎\uFE0F\uFE0F", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"z", u"☎\uFE0F\uFE0F", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1->3][4]", GetRunListStructureString());
   CheckBoundsForCursorPositions();
 }
@@ -6639,7 +6620,8 @@
   // Two emoji with variation selectors appearing in a correct sequence should
   // be in the same run.
   render_text->SetText(u"z\u260E\uFE0F\u260E\uFE0Fy");
-  EXPECT_EQ(ToString16Vec({"z", "☎\uFE0F☎\uFE0F", "y"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"z", u"☎\uFE0F☎\uFE0F", u"y"}),
+            GetRunListStrings());
   EXPECT_EQ("[0][1->4][5]", GetRunListStructureString());
   CheckBoundsForCursorPositions();
 }
@@ -6650,26 +6632,26 @@
   // ▶ (U+25B6, Geometric Shapes) and an ascii character should have
   // different runs.
   render_text->SetText(u"▶z");
-  EXPECT_EQ(ToString16Vec({"▶", "z"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"▶", u"z"}), GetRunListStrings());
   EXPECT_EQ("[0][1]", GetRunListStructureString());
 
   // ★ (U+2605, Miscellaneous Symbols) and an ascii character should have
   // different runs.
   render_text->SetText(u"★1");
-  EXPECT_EQ(ToString16Vec({"★", "1"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"★", u"1"}), GetRunListStrings());
   EXPECT_EQ("[0][1]", GetRunListStructureString());
 
   // 🐱 (U+1F431, a cat face, Miscellaneous Symbols and Pictographs) and an
   // ASCII period should have separate runs.
   render_text->SetText(u"🐱.");
-  EXPECT_EQ(ToString16Vec({"🐱", "."}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"🐱", u"."}), GetRunListStrings());
   // U+1F431 is represented as a surrogate pair in UTF-16.
   EXPECT_EQ("[0->1][2]", GetRunListStructureString());
 
   // 🥴 (U+1f974, Supplemental Symbols and Pictographs) and an ascii character
   // should have different runs.
   render_text->SetText(u"🥴$");
-  EXPECT_EQ(ToString16Vec({"🥴", "$"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"🥴", u"$"}), GetRunListStrings());
   EXPECT_EQ("[0->1][2]", GetRunListStructureString());
 }
 
@@ -6733,12 +6715,12 @@
 }
 
 TEST_F(RenderTextTest, GlyphBounds) {
-  const char* kTestStrings[] = {"asdf 1234 qwer", "\u0647\u0654",
-                                "\u0645\u0631\u062D\u0628\u0627"};
+  const char16_t* kTestStrings[] = {u"asdf 1234 qwer", u"\u0647\u0654",
+                                    u"\u0645\u0631\u062D\u0628\u0627"};
   RenderText* render_text = GetRenderText();
 
   for (size_t i = 0; i < base::size(kTestStrings); ++i) {
-    render_text->SetText(UTF8ToUTF16(kTestStrings[i]));
+    render_text->SetText(kTestStrings[i]);
 
     for (size_t j = 0; j < render_text->text().length(); ++j)
       EXPECT_FALSE(render_text->GetCursorSpan(Range(j, j + 1)).is_empty());
@@ -6852,13 +6834,13 @@
 // for different languages.
 TEST_F(RenderTextTest, HarfBuzz_FallbackFontsSupportGlyphs) {
   // The word 'test' in different languages.
-  static const wchar_t* kLanguageTests[] = {
-      L"test", L"اختبار", L"Δοκιμή", L"परीक्षा", L"تست", L"Փորձարկում",
+  static const char16_t* kLanguageTests[] = {
+      u"test", u"اختبار", u"Δοκιμή", u"परीक्षा", u"تست", u"Փորձարկում",
   };
 
-  for (const wchar_t* text : kLanguageTests) {
+  for (const auto* text : kLanguageTests) {
     RenderTextHarfBuzz* render_text = GetRenderText();
-    render_text->SetText(WideToUTF16(text));
+    render_text->SetText(text);
 
     const internal::TextRunList* run_list = GetHarfBuzzRunList();
     ASSERT_EQ(1U, run_list->size());
@@ -6873,15 +6855,15 @@
 // Ensure that the fallback fonts offered by GetFallbackFont() support glyphs
 // for different languages.
 TEST_F(RenderTextTest, HarfBuzz_MultiRunsSupportGlyphs) {
-  static const wchar_t* kLanguageTests[] = {
-      L"www.اختبار.com",
-      L"(اختبار)",
-      L"/ זה (מבחן) /",
+  static const char16_t* kLanguageTests[] = {
+      u"www.اختبار.com",
+      u"(اختبار)",
+      u"/ זה (מבחן) /",
   };
 
-  for (const wchar_t* text : kLanguageTests) {
+  for (const auto* text : kLanguageTests) {
     RenderTextHarfBuzz* render_text = GetRenderText();
-    render_text->SetText(WideToUTF16(text));
+    render_text->SetText(text);
 
     int missing_glyphs = 0;
     const internal::TextRunList* run_list = GetHarfBuzzRunList();
@@ -6897,7 +6879,7 @@
 
 struct FallbackFontCase {
   const char* test_name;
-  const wchar_t* text;
+  const char16_t* text;
 };
 
 class RenderTextTestWithFallbackFontCase
@@ -6913,7 +6895,7 @@
 TEST_P(RenderTextTestWithFallbackFontCase, FallbackFont) {
   FallbackFontCase param = GetParam();
   RenderTextHarfBuzz* render_text = GetRenderText();
-  render_text->SetText(WideToUTF16(param.text));
+  render_text->SetText(param.text);
 
   int missing_glyphs = 0;
   const internal::TextRunList* run_list = GetHarfBuzzRunList();
@@ -6925,17 +6907,17 @@
 
 const FallbackFontCase kUnicodeDecomposeCases[] = {
     // Decompose to "\u0041\u0300".
-    {"letter_A_with_grave", L"\u00c0"},
+    {"letter_A_with_grave", u"\u00c0"},
     // Decompose to "\u004f\u0328\u0304".
-    {"letter_O_with_ogonek_macron", L"\u01ec"},
+    {"letter_O_with_ogonek_macron", u"\u01ec"},
     // Decompose to "\u0041\u030a".
-    {"angstrom_sign", L"\u212b"},
+    {"angstrom_sign", u"\u212b"},
     // Decompose to "\u1100\u1164\u11b6".
-    {"hangul_syllable_gyaelh", L"\uac63"},
+    {"hangul_syllable_gyaelh", u"\uac63"},
     // Decompose to "\u1107\u1170\u11af".
-    {"hangul_syllable_bwel", L"\ubdc0"},
+    {"hangul_syllable_bwel", u"\ubdc0"},
     // Decompose to "\U00044039".
-    {"cjk_ideograph_fad4", L"\ufad4"},
+    {"cjk_ideograph_fad4", u"\ufad4"},
 };
 
 INSTANTIATE_TEST_SUITE_P(FallbackFontUnicodeDecompose,
@@ -6947,64 +6929,64 @@
 // codepoint can be rendered by the font. An error here can be by an incorrect
 // ItemizeText(...) leading to an invalid fallback font.
 const FallbackFontCase kComplexTextCases[] = {
-    {"simple1", L"test"},
-    {"simple2", L"اختبار"},
-    {"simple3", L"Δοκιμή"},
-    {"simple4", L"परीक्षा"},
-    {"simple5", L"تست"},
-    {"simple6", L"Փորձարկում"},
-    {"mixed1", L"www.اختبار.com"},
-    {"mixed2", L"(اختبار)"},
-    {"mixed3", L"/ זה (מבחן) /"},
+    {"simple1", u"test"},
+    {"simple2", u"اختبار"},
+    {"simple3", u"Δοκιμή"},
+    {"simple4", u"परीक्षा"},
+    {"simple5", u"تست"},
+    {"simple6", u"Փորձարկում"},
+    {"mixed1", u"www.اختبار.com"},
+    {"mixed2", u"(اختبار)"},
+    {"mixed3", u"/ זה (מבחן) /"},
 #if defined(OS_WIN)
-    {"asc_arb", L"abcښڛڜdef"},
-    {"devanagari", L"ञटठडढणतथ"},
-    {"ethiopic", L"መጩጪᎅⶹⶼ"},
-    {"greek", L"ξοπρς"},
-    {"kannada", L"ಠಡಢಣತಥ"},
-    {"lao", L"ປຝພຟມ"},
-    {"oriya", L"ଔକଖଗଘଙ"},
-    {"telugu_lat", L"aaఉయ!"},
-    {"common_math", L"ℳ: ¬ƒ(x)=½×¾"},
-    {"picto_title", L"☞☛test☚☜"},
-    {"common_numbers", L"𝟭𝟐⒓¹²"},
-    {"common_puncts", L",.!"},
-    {"common_space_math1", L" 𝓐"},
-    {"common_space_math2", L" 𝓉"},
-    {"common_split_spaces", L"♬  𝓐"},
-    {"common_mixed", L"\U0001d4c9\u24d4\U0001d42c"},
-    {"arrows", L"↰↱↲↳↴↵⇚⇛⇜⇝⇞⇟"},
-    {"arrows_space", L"↰ ↱ ↲ ↳ ↴ ↵ ⇚ ⇛ ⇜ ⇝ ⇞ ⇟"},
-    {"emoji_title", L"▶Feel goods"},
-    {"enclosed_alpha", L"ⒶⒷⒸⒹⒺⒻⒼ"},
-    {"shapes", L" ▶▷▸▹►▻◀◁◂◃◄◅"},
-    {"symbols", L"☂☎☏☝☫☬☭☮☯"},
-    {"symbols_space", L"☂ ☎ ☏ ☝ ☫ ☬ ☭ ☮ ☯"},
-    {"dingbats", L"✂✃✄✆✇✈"},
-    {"cjk_compatibility_ideographs", L"賈滑串句龜"},
-    {"lat_dev_ZWNJ", L"a\u200Cक"},
-    {"paren_picto", L"(☾☹☽)"},
-    {"emoji1", L"This is 💩!"},
-    {"emoji2", L"Look [🔝]"},
-    {"strange1", L"💔♬  𝓐 𝓉ⓔ𝐬т FỖ𝕣 c卄尺𝕆ᵐ€  ♘👹"},
-    {"strange2", L"˜”*°•.˜”*°• A test for chrome •°*”˜.•°*”˜"},
-    {"strange3", L"𝐭єⓢт fσ𝐑 𝔠ʰ𝕣ό𝐌𝔢"},
-    {"strange4", L"тẸⓈ𝔱 𝔽𝕠ᖇ 𝕔𝐡ŕ𝔬ⓜẸ"},
-    {"url1", L"http://www.google.com"},
-    {"url2", L"http://www.nowhere.com/Lörick.html"},
-    {"url3", L"http://www.nowhere.com/تسجيل الدخول"},
-    {"url4", L"https://xyz.com:8080/تس(1)جيل الدخول"},
-    {"url5", L"http://www.script.com/test.php?abc=42&cde=12&f=%20%20"},
-    {"punct1", L"This‐is‑a‒test–for—punctuations"},
-    {"punct2", L"⁅All ‷magic‴ comes with a ‶price″⁆"},
-    {"punct3", L"⍟ Complete my sentence… †"},
-    {"parens", L"❝This❞ 「test」 has ((a)) 【lot】 [{of}] 〚parentheses〛"},
-    {"games", L"Let play: ♗♘⚀⚁♠♣"},
-    {"braille", L"⠞⠑⠎⠞ ⠋⠕⠗ ⠉⠓⠗⠕⠍⠑"},
-    {"emoticon1", L"¯\\_(ツ)_/¯"},
-    {"emoticon2", L"٩(⁎❛ᴗ❛⁎)۶"},
-    {"emoticon3", L"(͡° ͜ʖ ͡°)"},
-    {"emoticon4", L"[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]"},
+    {"asc_arb", u"abcښڛڜdef"},
+    {"devanagari", u"ञटठडढणतथ"},
+    {"ethiopic", u"መጩጪᎅⶹⶼ"},
+    {"greek", u"ξοπρς"},
+    {"kannada", u"ಠಡಢಣತಥ"},
+    {"lao", u"ປຝພຟມ"},
+    {"oriya", u"ଔକଖଗଘଙ"},
+    {"telugu_lat", u"aaఉయ!"},
+    {"common_math", u"ℳ: ¬ƒ(x)=½×¾"},
+    {"picto_title", u"☞☛test☚☜"},
+    {"common_numbers", u"𝟭𝟐⒓¹²"},
+    {"common_puncts", u",.!"},
+    {"common_space_math1", u" 𝓐"},
+    {"common_space_math2", u" 𝓉"},
+    {"common_split_spaces", u"♬  𝓐"},
+    {"common_mixed", u"\U0001d4c9\u24d4\U0001d42c"},
+    {"arrows", u"↰↱↲↳↴↵⇚⇛⇜⇝⇞⇟"},
+    {"arrows_space", u"↰ ↱ ↲ ↳ ↴ ↵ ⇚ ⇛ ⇜ ⇝ ⇞ ⇟"},
+    {"emoji_title", u"▶Feel goods"},
+    {"enclosed_alpha", u"ⒶⒷⒸⒹⒺⒻⒼ"},
+    {"shapes", u" ▶▷▸▹►▻◀◁◂◃◄◅"},
+    {"symbols", u"☂☎☏☝☫☬☭☮☯"},
+    {"symbols_space", u"☂ ☎ ☏ ☝ ☫ ☬ ☭ ☮ ☯"},
+    {"dingbats", u"✂✃✄✆✇✈"},
+    {"cjk_compatibility_ideographs", u"賈滑串句龜"},
+    {"lat_dev_ZWNJ", u"a\u200Cक"},
+    {"paren_picto", u"(☾☹☽)"},
+    {"emoji1", u"This is 💩!"},
+    {"emoji2", u"Look [🔝]"},
+    {"strange1", u"💔♬  𝓐 𝓉ⓔ𝐬т FỖ𝕣 c卄尺𝕆ᵐ€  ♘👹"},
+    {"strange2", u"˜”*°•.˜”*°• A test for chrome •°*”˜.•°*”˜"},
+    {"strange3", u"𝐭єⓢт fσ𝐑 𝔠ʰ𝕣ό𝐌𝔢"},
+    {"strange4", u"тẸⓈ𝔱 𝔽𝕠ᖇ 𝕔𝐡ŕ𝔬ⓜẸ"},
+    {"url1", u"http://www.google.com"},
+    {"url2", u"http://www.nowhere.com/Lörick.html"},
+    {"url3", u"http://www.nowhere.com/تسجيل الدخول"},
+    {"url4", u"https://xyz.com:8080/تس(1)جيل الدخول"},
+    {"url5", u"http://www.script.com/test.php?abc=42&cde=12&f=%20%20"},
+    {"punct1", u"This‐is‑a‒test–for—punctuations"},
+    {"punct2", u"⁅All ‷magic‴ comes with a ‶price″⁆"},
+    {"punct3", u"⍟ Complete my sentence… †"},
+    {"parens", u"❝This❞ 「test」 has ((a)) 【lot】 [{of}] 〚parentheses〛"},
+    {"games", u"Let play: ♗♘⚀⚁♠♣"},
+    {"braille", u"⠞⠑⠎⠞ ⠋⠕⠗ ⠉⠓⠗⠕⠍⠑"},
+    {"emoticon1", u"¯\\_(ツ)_/¯"},
+    {"emoticon2", u"٩(⁎❛ᴗ❛⁎)۶"},
+    {"emoticon3", u"(͡° ͜ʖ ͡°)"},
+    {"emoticon4", u"[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]"},
 #endif
 };
 
@@ -7019,110 +7001,110 @@
 const FallbackFontCase kCommonScriptCases[] = {
 #if defined(OS_WIN)
     // The following tests are made to work on win7 and win10.
-    {"common00", L"\u237b\u2ac1\u24f5\u259f\u2a87\u23ea\u25d4\u2220"},
-    {"common01", L"\u2303\u2074\u2988\u32b6\u26a2\u24e5\u2a53\u2219"},
-    {"common02", L"\u29b2\u25fc\u2366\u24ae\u2647\u258e\u2654\u25fe"},
-    {"common03", L"\u21ea\u22b4\u29b0\u2a84\u0008\u2657\u2731\u2697"},
-    {"common04", L"\u2b3c\u2932\u21c8\u23cf\u20a1\u2aa2\u2344\u0011"},
-    {"common05", L"\u22c3\u2a56\u2340\u21b7\u26ba\u2798\u220f\u2404"},
-    {"common06", L"\u21f9\u25fd\u008e\u21e6\u2686\u21e4\u259f\u29ee"},
-    {"common07", L"\u231e\ufe39\u0008\u2349\u2262\u2270\uff09\u2b3b"},
-    {"common08", L"\u24a3\u236e\u29b2\u2259\u26ea\u2705\u00ae\u2a23"},
-    {"common09", L"\u33bd\u235e\u2018\u32ba\u2973\u02c1\u20b9\u25b4"},
-    {"common10", L"\u2245\u2a4d\uff19\u2042\u2aa9\u2658\u276e\uff40"},
-    {"common11", L"\u0007\u21b4\u23c9\u2593\u21ba\u00a0\u258f\u23b3"},
-    {"common12", L"\u2938\u250c\u2240\u2676\u2297\u2b07\u237e\u2a04"},
-    {"common13", L"\u2520\u233a\u20a5\u2744\u2445\u268a\u2716\ufe62"},
-    {"common14", L"\ufe4d\u25d5\u2ae1\u2a35\u2323\u273c\u26be\u2a3b"},
-    {"common15", L"\u2aa2\u0000\ufe65\u2962\u2573\u21f8\u2651\u02d2"},
-    {"common16", L"\u225c\u2283\u2960\u4de7\uff12\uffe1\u0016\u2905"},
-    {"common17", L"\uff07\u25aa\u2076\u259e\u226c\u2568\u0026\u2691"},
-    {"common18", L"\u2388\u21c2\u208d\u2a7f\u22d0\u2583\u2ad5\u240f"},
-    {"common19", L"\u230a\u27ac\u001e\u261e\u259d\u25c3\u33a5\u0011"},
-    {"common20", L"\ufe54\u29c7\u2477\u21ed\u2069\u4dfc\u2ae2\u21e8"},
-    {"common21", L"\u2131\u2ab7\u23b9\u2660\u2083\u24c7\u228d\u2a01"},
-    {"common22", L"\u2587\u2572\u21df\uff3c\u02cd\ufffd\u2404\u22b3"},
-    {"common23", L"\u4dc3\u02fe\uff09\u25a3\ufe14\u255c\u2128\u2698"},
-    {"common24", L"\u2b36\u3382\u02f6\u2752\uff16\u22cf\u00b0\u21d6"},
-    {"common25", L"\u2561\u23db\u2958\u2782\u22af\u2621\u24a3\u29ae"},
-    {"common26", L"\u2693\u22e2\u2988\u2987\u33ba\u2a94\u298e\u2328"},
-    {"common27", L"\u266c\u2aa5\u2405\uffeb\uff5c\u2902\u291e\u02e6"},
-    {"common28", L"\u2634\u32b2\u3385\u2032\u33be\u2366\u2ac7\u23cf"},
-    {"common29", L"\u2981\ua721\u25a9\u2320\u21cf\u295a\u2273\u2ac2"},
-    {"common30", L"\u22d9\u2465\u2347\u2a94\u4dca\u2389\u23b0\u208d"},
-    {"common31", L"\u21cc\u2af8\u2912\u23a4\u2271\u2303\u241e\u33a1"},
+    {"common00", u"\u237b\u2ac1\u24f5\u259f\u2a87\u23ea\u25d4\u2220"},
+    {"common01", u"\u2303\u2074\u2988\u32b6\u26a2\u24e5\u2a53\u2219"},
+    {"common02", u"\u29b2\u25fc\u2366\u24ae\u2647\u258e\u2654\u25fe"},
+    {"common03", u"\u21ea\u22b4\u29b0\u2a84\u0008\u2657\u2731\u2697"},
+    {"common04", u"\u2b3c\u2932\u21c8\u23cf\u20a1\u2aa2\u2344\u0011"},
+    {"common05", u"\u22c3\u2a56\u2340\u21b7\u26ba\u2798\u220f\u2404"},
+    {"common06", u"\u21f9\u25fd\u008e\u21e6\u2686\u21e4\u259f\u29ee"},
+    {"common07", u"\u231e\ufe39\u0008\u2349\u2262\u2270\uff09\u2b3b"},
+    {"common08", u"\u24a3\u236e\u29b2\u2259\u26ea\u2705\u00ae\u2a23"},
+    {"common09", u"\u33bd\u235e\u2018\u32ba\u2973\u02c1\u20b9\u25b4"},
+    {"common10", u"\u2245\u2a4d\uff19\u2042\u2aa9\u2658\u276e\uff40"},
+    {"common11", u"\u0007\u21b4\u23c9\u2593\u21ba\u00a0\u258f\u23b3"},
+    {"common12", u"\u2938\u250c\u2240\u2676\u2297\u2b07\u237e\u2a04"},
+    {"common13", u"\u2520\u233a\u20a5\u2744\u2445\u268a\u2716\ufe62"},
+    {"common14", u"\ufe4d\u25d5\u2ae1\u2a35\u2323\u273c\u26be\u2a3b"},
+    {"common15", u"\u2aa2\u0000\ufe65\u2962\u2573\u21f8\u2651\u02d2"},
+    {"common16", u"\u225c\u2283\u2960\u4de7\uff12\uffe1\u0016\u2905"},
+    {"common17", u"\uff07\u25aa\u2076\u259e\u226c\u2568\u0026\u2691"},
+    {"common18", u"\u2388\u21c2\u208d\u2a7f\u22d0\u2583\u2ad5\u240f"},
+    {"common19", u"\u230a\u27ac\u001e\u261e\u259d\u25c3\u33a5\u0011"},
+    {"common20", u"\ufe54\u29c7\u2477\u21ed\u2069\u4dfc\u2ae2\u21e8"},
+    {"common21", u"\u2131\u2ab7\u23b9\u2660\u2083\u24c7\u228d\u2a01"},
+    {"common22", u"\u2587\u2572\u21df\uff3c\u02cd\ufffd\u2404\u22b3"},
+    {"common23", u"\u4dc3\u02fe\uff09\u25a3\ufe14\u255c\u2128\u2698"},
+    {"common24", u"\u2b36\u3382\u02f6\u2752\uff16\u22cf\u00b0\u21d6"},
+    {"common25", u"\u2561\u23db\u2958\u2782\u22af\u2621\u24a3\u29ae"},
+    {"common26", u"\u2693\u22e2\u2988\u2987\u33ba\u2a94\u298e\u2328"},
+    {"common27", u"\u266c\u2aa5\u2405\uffeb\uff5c\u2902\u291e\u02e6"},
+    {"common28", u"\u2634\u32b2\u3385\u2032\u33be\u2366\u2ac7\u23cf"},
+    {"common29", u"\u2981\ua721\u25a9\u2320\u21cf\u295a\u2273\u2ac2"},
+    {"common30", u"\u22d9\u2465\u2347\u2a94\u4dca\u2389\u23b0\u208d"},
+    {"common31", u"\u21cc\u2af8\u2912\u23a4\u2271\u2303\u241e\u33a1"},
 #elif defined(OS_ANDROID)
-    {"common00", L"\u2497\uff04\u277c\u21b6\u2076\u21e4\u2068\u21b3"},
-    {"common01", L"\u2663\u2466\u338e\u226b\u2734\u21be\u3389\u00ab"},
-    {"common02", L"\u2062\u2197\u3392\u2681\u33be\u206d\ufe10\ufe34"},
-    {"common03", L"\u02db\u00b0\u02d3\u2745\u33d1\u21e4\u24e4\u33d6"},
-    {"common04", L"\u21da\u261f\u26a1\u2586\u27af\u2560\u21cd\u25c6"},
-    {"common05", L"\ufe51\uff17\u0027\u21fd\u24de\uff5e\u2606\u251f"},
-    {"common06", L"\u2493\u2466\u21fc\u226f\u202d\u21a9\u0040\u265d"},
-    {"common07", L"\u2103\u255a\u2153\u26be\u27ac\u222e\u2490\u21a4"},
-    {"common08", L"\u270b\u2486\u246b\u263c\u27b6\u21d9\u219d\u25a9"},
-    {"common09", L"\u002d\u2494\u25fd\u2321\u2111\u2511\u00d7\u2535"},
-    {"common10", L"\u2523\u203e\u25b2\ufe18\u2499\u2229\ufd3e\ufe16"},
-    {"common11", L"\u2133\u2716\u273f\u2064\u2248\u005c\u265f\u21e6"},
-    {"common12", L"\u2060\u246a\u231b\u2726\u25bd\ufe40\u002e\u25ca"},
-    {"common13", L"\ufe39\u24a2\ufe18\u254b\u249c\u3396\ua71f\u2466"},
-    {"common14", L"\u21b8\u2236\u251a\uff11\u2077\u0035\u27bd\u2013"},
-    {"common15", L"\u2668\u2551\u221a\u02bc\u2741\u2649\u2192\u00a1"},
-    {"common16", L"\u2211\u21ca\u24dc\u2536\u201b\u21c8\u2530\u25fb"},
-    {"common17", L"\u231a\u33d8\u2934\u27bb\u2109\u23ec\u20a9\u3000"},
-    {"common18", L"\u2069\u205f\u33d3\u2466\u24a1\u24dd\u21ac\u21e3"},
-    {"common19", L"\u2737\u219a\u21f1\u2285\u226a\u00b0\u27b2\u2746"},
-    {"common20", L"\u264f\u2539\u2202\u264e\u2548\u2530\u2111\u2007"},
-    {"common21", L"\u2799\u0035\u25e4\u265b\u24e2\u2044\u222b\u0021"},
-    {"common22", L"\u2728\u00a2\u2533\ufe43\u33c9\u27a2\u02f9\u005d"},
-    {"common23", L"\ufe68\u256c\u25b6\u276c\u2771\u33c4\u2712\u24b3"},
-    {"common24", L"\ufe5d\ufe31\ufe3d\u205e\u2512\u33b8\u272b\ufe4f"},
-    {"common25", L"\u24e7\u25fc\u2582\u2743\u2010\u2474\u2262\u251a"},
-    {"common26", L"\u2020\u211c\u24b4\u33c7\u2007\uff0f\u267f\u00b4"},
-    {"common27", L"\u266c\u3399\u2570\u33a4\u276e\u00a8\u2506\u24dc"},
-    {"common28", L"\u2202\ufe43\u2511\u2191\u339a\u33b0\u02d7\u2473"},
-    {"common29", L"\u2517\u2297\u2762\u2460\u25bd\u24a9\u21a7\ufe64"},
-    {"common30", L"\u2105\u2722\u275d\u249c\u21a2\u2590\u2260\uff5d"},
-    {"common31", L"\u33ba\u21c6\u2706\u02cb\ufe64\u02e6\u0374\u2493"},
+    {"common00", u"\u2497\uff04\u277c\u21b6\u2076\u21e4\u2068\u21b3"},
+    {"common01", u"\u2663\u2466\u338e\u226b\u2734\u21be\u3389\u00ab"},
+    {"common02", u"\u2062\u2197\u3392\u2681\u33be\u206d\ufe10\ufe34"},
+    {"common03", u"\u02db\u00b0\u02d3\u2745\u33d1\u21e4\u24e4\u33d6"},
+    {"common04", u"\u21da\u261f\u26a1\u2586\u27af\u2560\u21cd\u25c6"},
+    {"common05", u"\ufe51\uff17\u0027\u21fd\u24de\uff5e\u2606\u251f"},
+    {"common06", u"\u2493\u2466\u21fc\u226f\u202d\u21a9\u0040\u265d"},
+    {"common07", u"\u2103\u255a\u2153\u26be\u27ac\u222e\u2490\u21a4"},
+    {"common08", u"\u270b\u2486\u246b\u263c\u27b6\u21d9\u219d\u25a9"},
+    {"common09", u"\u002d\u2494\u25fd\u2321\u2111\u2511\u00d7\u2535"},
+    {"common10", u"\u2523\u203e\u25b2\ufe18\u2499\u2229\ufd3e\ufe16"},
+    {"common11", u"\u2133\u2716\u273f\u2064\u2248\u005c\u265f\u21e6"},
+    {"common12", u"\u2060\u246a\u231b\u2726\u25bd\ufe40\u002e\u25ca"},
+    {"common13", u"\ufe39\u24a2\ufe18\u254b\u249c\u3396\ua71f\u2466"},
+    {"common14", u"\u21b8\u2236\u251a\uff11\u2077\u0035\u27bd\u2013"},
+    {"common15", u"\u2668\u2551\u221a\u02bc\u2741\u2649\u2192\u00a1"},
+    {"common16", u"\u2211\u21ca\u24dc\u2536\u201b\u21c8\u2530\u25fb"},
+    {"common17", u"\u231a\u33d8\u2934\u27bb\u2109\u23ec\u20a9\u3000"},
+    {"common18", u"\u2069\u205f\u33d3\u2466\u24a1\u24dd\u21ac\u21e3"},
+    {"common19", u"\u2737\u219a\u21f1\u2285\u226a\u00b0\u27b2\u2746"},
+    {"common20", u"\u264f\u2539\u2202\u264e\u2548\u2530\u2111\u2007"},
+    {"common21", u"\u2799\u0035\u25e4\u265b\u24e2\u2044\u222b\u0021"},
+    {"common22", u"\u2728\u00a2\u2533\ufe43\u33c9\u27a2\u02f9\u005d"},
+    {"common23", u"\ufe68\u256c\u25b6\u276c\u2771\u33c4\u2712\u24b3"},
+    {"common24", u"\ufe5d\ufe31\ufe3d\u205e\u2512\u33b8\u272b\ufe4f"},
+    {"common25", u"\u24e7\u25fc\u2582\u2743\u2010\u2474\u2262\u251a"},
+    {"common26", u"\u2020\u211c\u24b4\u33c7\u2007\uff0f\u267f\u00b4"},
+    {"common27", u"\u266c\u3399\u2570\u33a4\u276e\u00a8\u2506\u24dc"},
+    {"common28", u"\u2202\ufe43\u2511\u2191\u339a\u33b0\u02d7\u2473"},
+    {"common29", u"\u2517\u2297\u2762\u2460\u25bd\u24a9\u21a7\ufe64"},
+    {"common30", u"\u2105\u2722\u275d\u249c\u21a2\u2590\u2260\uff5d"},
+    {"common31", u"\u33ba\u21c6\u2706\u02cb\ufe64\u02e6\u0374\u2493"},
 #elif defined(OS_APPLE)
-    {"common00", L"\u2153\u24e0\u2109\u02f0\u2a8f\u25ed\u02c5\u2716"},
-    {"common01", L"\u02f0\u208c\u2203\u2518\u2067\u2270\u21f1\ufe66"},
-    {"common02", L"\u2686\u2585\u2b15\u246f\u23e3\u21b4\u2394\ufe31"},
-    {"common03", L"\u23c1\u2a97\u201e\u2200\u3389\u25d3\u02c2\u259d"},
+    {"common00", u"\u2153\u24e0\u2109\u02f0\u2a8f\u25ed\u02c5\u2716"},
+    {"common01", u"\u02f0\u208c\u2203\u2518\u2067\u2270\u21f1\ufe66"},
+    {"common02", u"\u2686\u2585\u2b15\u246f\u23e3\u21b4\u2394\ufe31"},
+    {"common03", u"\u23c1\u2a97\u201e\u2200\u3389\u25d3\u02c2\u259d"},
 #else
     // The following tests are made for the mock fonts (see test_fonts).
-    {"common00", L"\u2153\u24e0\u2109\u02f0\u2a8f\u25ed\u02c5\u2716"},
-    {"common01", L"\u02f0\u208c\u2203\u2518\u2067\u2270\u21f1\ufe66"},
-    {"common02", L"\u2686\u2585\u2b15\u246f\u23e3\u21b4\u2394\ufe31"},
-    {"common03", L"\u23c1\u2a97\u201e\u2200\u3389\u25d3\u02c2\u259d"},
-    {"common04", L"\u2075\u4dec\u252a\uff15\u4df6\u2668\u27fa\ufe17"},
-    {"common05", L"\u260b\u2049\u3036\u2a85\u2b15\u23c7\u230a\u2374"},
-    {"common06", L"\u2771\u27fa\u255d\uff0b\u2213\u3396\u2a85\u2276"},
-    {"common07", L"\u211e\u2b06\u2255\u2727\u26c3\u33cf\u267d\u2ab2"},
-    {"common08", L"\u2373\u20b3\u22b8\u2a0f\u02fd\u2585\u3036\ufe48"},
-    {"common09", L"\u256d\u2940\u21d8\u4dde\u23a1\u226b\u3374\u2a99"},
-    {"common10", L"\u270f\u24e5\u26c1\u2131\u21f5\u25af\u230f\u27fe"},
-    {"common11", L"\u27aa\u23a2\u02ef\u2373\u2257\u2749\u2496\ufe31"},
-    {"common12", L"\u230a\u25fb\u2117\u3386\u32cc\u21c5\u24c4\u207e"},
-    {"common13", L"\u2467\u2791\u3393\u33bb\u02ca\u25de\ua788\u278f"},
-    {"common14", L"\ua719\u25ed\u20a8\u20a1\u4dd8\u2295\u24eb\u02c8"},
-    {"common15", L"\u22b6\u2520\u2036\uffee\u21df\u002d\u277a\u2b24"},
-    {"common16", L"\u21f8\u211b\u22a0\u25b6\u263e\u2704\u221a\u2758"},
-    {"common17", L"\ufe10\u2060\u24ac\u3385\u27a1\u2059\u2689\u2278"},
-    {"common18", L"\u269b\u211b\u33a4\ufe36\u239e\u267f\u2423\u24a2"},
-    {"common19", L"\u4ded\u262d\u225e\u248b\u21df\u279d\u2518\u21ba"},
-    {"common20", L"\u225a\uff16\u21d4\u21c6\u02ba\u2545\u23aa\u005e"},
-    {"common21", L"\u20a5\u265e\u3395\u2a6a\u2555\u22a4\u2086\u23aa"},
-    {"common22", L"\u203f\u3250\u2240\u24e9\u21cb\u258f\u24b1\u3259"},
-    {"common23", L"\u27bd\u263b\uff1f\u2199\u2547\u258d\u201f\u2507"},
-    {"common24", L"\u2482\u2548\u02dc\u231f\u24cd\u2198\u220e\u20ad"},
-    {"common25", L"\u2ff7\u2540\ufe48\u2197\u276b\u2574\u2062\u3398"},
-    {"common26", L"\u2663\u21cd\u263f\u23e5\u22d7\u2518\u21b9\u2628"},
-    {"common27", L"\u21fa\ufe66\u2739\u2051\u21f4\u3399\u2599\u25f7"},
-    {"common28", L"\u29d3\u25ec\u27a6\u24e0\u2735\u25b4\u2737\u25db"},
-    {"common29", L"\u2622\u22e8\u33d2\u21d3\u2502\u2153\u2669\u25f2"},
-    {"common30", L"\u2121\u21af\u2729\u203c\u337a\u2464\u2b08\u2e24"},
-    {"common31", L"\u33cd\u007b\u02d2\u22cc\u32be\u2ffa\u2787\u02e9"},
+    {"common00", u"\u2153\u24e0\u2109\u02f0\u2a8f\u25ed\u02c5\u2716"},
+    {"common01", u"\u02f0\u208c\u2203\u2518\u2067\u2270\u21f1\ufe66"},
+    {"common02", u"\u2686\u2585\u2b15\u246f\u23e3\u21b4\u2394\ufe31"},
+    {"common03", u"\u23c1\u2a97\u201e\u2200\u3389\u25d3\u02c2\u259d"},
+    {"common04", u"\u2075\u4dec\u252a\uff15\u4df6\u2668\u27fa\ufe17"},
+    {"common05", u"\u260b\u2049\u3036\u2a85\u2b15\u23c7\u230a\u2374"},
+    {"common06", u"\u2771\u27fa\u255d\uff0b\u2213\u3396\u2a85\u2276"},
+    {"common07", u"\u211e\u2b06\u2255\u2727\u26c3\u33cf\u267d\u2ab2"},
+    {"common08", u"\u2373\u20b3\u22b8\u2a0f\u02fd\u2585\u3036\ufe48"},
+    {"common09", u"\u256d\u2940\u21d8\u4dde\u23a1\u226b\u3374\u2a99"},
+    {"common10", u"\u270f\u24e5\u26c1\u2131\u21f5\u25af\u230f\u27fe"},
+    {"common11", u"\u27aa\u23a2\u02ef\u2373\u2257\u2749\u2496\ufe31"},
+    {"common12", u"\u230a\u25fb\u2117\u3386\u32cc\u21c5\u24c4\u207e"},
+    {"common13", u"\u2467\u2791\u3393\u33bb\u02ca\u25de\ua788\u278f"},
+    {"common14", u"\ua719\u25ed\u20a8\u20a1\u4dd8\u2295\u24eb\u02c8"},
+    {"common15", u"\u22b6\u2520\u2036\uffee\u21df\u002d\u277a\u2b24"},
+    {"common16", u"\u21f8\u211b\u22a0\u25b6\u263e\u2704\u221a\u2758"},
+    {"common17", u"\ufe10\u2060\u24ac\u3385\u27a1\u2059\u2689\u2278"},
+    {"common18", u"\u269b\u211b\u33a4\ufe36\u239e\u267f\u2423\u24a2"},
+    {"common19", u"\u4ded\u262d\u225e\u248b\u21df\u279d\u2518\u21ba"},
+    {"common20", u"\u225a\uff16\u21d4\u21c6\u02ba\u2545\u23aa\u005e"},
+    {"common21", u"\u20a5\u265e\u3395\u2a6a\u2555\u22a4\u2086\u23aa"},
+    {"common22", u"\u203f\u3250\u2240\u24e9\u21cb\u258f\u24b1\u3259"},
+    {"common23", u"\u27bd\u263b\uff1f\u2199\u2547\u258d\u201f\u2507"},
+    {"common24", u"\u2482\u2548\u02dc\u231f\u24cd\u2198\u220e\u20ad"},
+    {"common25", u"\u2ff7\u2540\ufe48\u2197\u276b\u2574\u2062\u3398"},
+    {"common26", u"\u2663\u21cd\u263f\u23e5\u22d7\u2518\u21b9\u2628"},
+    {"common27", u"\u21fa\ufe66\u2739\u2051\u21f4\u3399\u2599\u25f7"},
+    {"common28", u"\u29d3\u25ec\u27a6\u24e0\u2735\u25b4\u2737\u25db"},
+    {"common29", u"\u2622\u22e8\u33d2\u21d3\u2502\u2153\u2669\u25f2"},
+    {"common30", u"\u2121\u21af\u2729\u203c\u337a\u2464\u2b08\u2e24"},
+    {"common31", u"\u33cd\u007b\u02d2\u22cc\u32be\u2ffa\u2787\u02e9"},
 #endif
 };
 
@@ -7175,16 +7157,16 @@
 }
 
 TEST_F(RenderTextTest, ZeroWidthCharacters) {
-  static const wchar_t* kEmptyText[] = {
-      L"\u200C",  // ZERO WIDTH NON-JOINER
-      L"\u200D",  // ZERO WIDTH JOINER
-      L"\u200B",  // ZERO WIDTH SPACE
-      L"\uFEFF",  // ZERO WIDTH NO-BREAK SPACE
+  static const char16_t* kEmptyText[] = {
+      u"\u200C",  // ZERO WIDTH NON-JOINER
+      u"\u200D",  // ZERO WIDTH JOINER
+      u"\u200B",  // ZERO WIDTH SPACE
+      u"\uFEFF",  // ZERO WIDTH NO-BREAK SPACE
   };
 
-  for (const wchar_t* text : kEmptyText) {
+  for (const auto* text : kEmptyText) {
     RenderTextHarfBuzz* render_text = GetRenderText();
-    render_text->SetText(WideToUTF16(text));
+    render_text->SetText(text);
 
     const internal::TextRunList* run_list = GetHarfBuzzRunList();
     EXPECT_EQ(0, run_list->width());
@@ -7202,10 +7184,8 @@
       // crbug.com/459812.  This appears to be a preexisting issue that wasn't
       // revealed by the prior unit tests.
       // "TEST_______",
-      "TEST some stuff", "WWWWWWWWWW", "gAXAXAXAXAXAXA",
-      "g\u00C5X\u00C5X\u00C5X\u00C5X\u00C5X\u00C5X\u00C5",
-      ("\u0647\u0654\u0647\u0654\u0647\u0654\u0647\u0654\u0645\u0631\u062D"
-       "\u0628\u0627")};
+      "TEST some stuff", "WWWWWWWWWW", "gAXAXAXAXAXAXA", "gÅXÅXÅXÅXÅXÅXÅ",
+      "هٔهٔهٔهٔمرحبا"};
   const Size kCanvasSize(300, 50);
   const int kTestSize = 10;
 
@@ -7220,7 +7200,7 @@
 
   for (auto* string : kTestStrings) {
     paint_canvas.clear(SK_ColorWHITE);
-    render_text->SetText(UTF8ToUTF16(string));
+    render_text->SetText(base::UTF8ToUTF16(string));
     render_text->ApplyBaselineStyle(SUPERSCRIPT, Range(1, 2));
     render_text->ApplyBaselineStyle(SUPERIOR, Range(3, 4));
     render_text->ApplyBaselineStyle(INFERIOR, Range(5, 6));
@@ -7291,7 +7271,7 @@
 
   for (auto* string : kTestStrings) {
     paint_canvas.clear(SK_ColorWHITE);
-    render_text->SetText(UTF8ToUTF16(string));
+    render_text->SetText(base::UTF8ToUTF16(string));
     const Size string_size = render_text->GetStringSize();
     int fake_width = string_size.width() / 2;
     int fake_height = string_size.height() / 2;
@@ -7798,37 +7778,37 @@
 // Tests text selection made at end points of individual lines of multiline
 // text.
 TEST_F(RenderTextTest, LineEndSelections) {
-  const char* const ltr = "abc\n\ndef";
-  const char* const rtl = "שנב\n\nגקכ";
-  const char* const ltr_single = "abc def ghi";
-  const char* const rtl_single = "שנב גקכ עין";
+  const char16_t* const ltr = u"abc\n\ndef";
+  const char16_t* const rtl = u"שנב\n\nגקכ";
+  const char16_t* const ltr_single = u"abc def ghi";
+  const char16_t* const rtl_single = u"שנב גקכ עין";
   const int left_x = -100;
   const int right_x = 200;
   struct {
-    const char* const text;
+    const char16_t* const text;
     const int line_num;
     const int x;
-    const char* const selected_text;
+    const char16_t* const selected_text;
   } cases[] = {
-      {ltr, 1, left_x, "abc\n"},
-      {ltr, 1, right_x, "abc\n"},
-      {ltr, 2, left_x, "abc\n\n"},
+      {ltr, 1, left_x, u"abc\n"},
+      {ltr, 1, right_x, u"abc\n"},
+      {ltr, 2, left_x, u"abc\n\n"},
       {ltr, 2, right_x, ltr},
 
-      {rtl, 1, left_x, "שנב\n"},
-      {rtl, 1, right_x, "שנב\n"},
+      {rtl, 1, left_x, u"שנב\n"},
+      {rtl, 1, right_x, u"שנב\n"},
       {rtl, 2, left_x, rtl},
-      {rtl, 2, right_x, "שנב\n\n"},
+      {rtl, 2, right_x, u"שנב\n\n"},
 
-      {ltr_single, 1, left_x, "abc "},
-      {ltr_single, 1, right_x, "abc def "},
-      {ltr_single, 2, left_x, "abc def "},
+      {ltr_single, 1, left_x, u"abc "},
+      {ltr_single, 1, right_x, u"abc def "},
+      {ltr_single, 2, left_x, u"abc def "},
       {ltr_single, 2, right_x, ltr_single},
 
-      {rtl_single, 1, left_x, "שנב גקכ "},
-      {rtl_single, 1, right_x, "שנב "},
+      {rtl_single, 1, left_x, u"שנב גקכ "},
+      {rtl_single, 1, right_x, u"שנב "},
       {rtl_single, 2, left_x, rtl_single},
-      {rtl_single, 2, right_x, "שנב גקכ "},
+      {rtl_single, 2, right_x, u"שנב גקכ "},
   };
 
   SetGlyphWidth(5);
@@ -7838,7 +7818,7 @@
 
   for (size_t i = 0; i < base::size(cases); i++) {
     SCOPED_TRACE(base::StringPrintf("Testing case %" PRIuS "", i));
-    render_text->SetText(UTF8ToUTF16(cases[i].text));
+    render_text->SetText(cases[i].text);
 
     EXPECT_EQ(3u, render_text->GetNumLines());
     // Position the cursor at the logical beginning of text.
@@ -7846,8 +7826,7 @@
 
     render_text->MoveCursorToPoint(
         Point(cases[i].x, GetCursorYForTesting(cases[i].line_num)), true);
-    EXPECT_EQ(UTF8ToUTF16(cases[i].selected_text),
-              GetSelectedText(render_text));
+    EXPECT_EQ(cases[i].selected_text, GetSelectedText(render_text));
   }
 }
 
@@ -8263,7 +8242,8 @@
   const int test_font_size_override = default_font_size + 5;
   render_text->SetText(u"0123456789");
   render_text->ApplyFontSizeOverride(test_font_size_override, gfx::Range(3, 7));
-  EXPECT_EQ(ToString16Vec({"012", "3456", "789"}), GetRunListStrings());
+  EXPECT_EQ(std::vector<std::u16string>({u"012", u"3456", u"789"}),
+            GetRunListStrings());
 
   const internal::TextRunList* run_list = GetHarfBuzzRunList();
   ASSERT_EQ(3U, run_list->size());
diff --git a/ui/gfx/text_elider_unittest.cc b/ui/gfx/text_elider_unittest.cc
index d1b5017..4df7150 100644
--- a/ui/gfx/text_elider_unittest.cc
+++ b/ui/gfx/text_elider_unittest.cc
@@ -27,44 +27,26 @@
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/text_utils.h"
 
-using base::ASCIIToUTF16;
-using base::UTF16ToUTF8;
-using base::UTF16ToWide;
-using base::UTF8ToUTF16;
-
 namespace gfx {
 
 namespace {
 
-struct Testcase {
-  const std::string input;
-  const std::string output;
-};
-
 struct FileTestcase {
   const base::FilePath::StringType input;
-  const std::string output;
+  const std::u16string output;
   // If this value is specified, we will try to cut the path down to the render
   // width of this string; if not specified, output will be used.
-  const std::string using_width_of = std::string();
+  const std::u16string using_width_of = std::u16string();
 };
 
-struct UTF16Testcase {
+struct Testcase {
   const std::u16string input;
   const std::u16string output;
 };
 
-struct TestData {
-  const std::string a;
-  const std::string b;
-  const int compare_result;
-};
-
 }  // namespace
 
 TEST(TextEliderTest, ElideEmail) {
-  const std::string kEllipsisStr(kEllipsis);
-
   // Test emails and their expected elided forms (from which the available
   // widths will be derived).
   // For elided forms in which both the username and domain must be elided:
@@ -72,48 +54,43 @@
   // dependent. To avoid this, the username is prefixed with the characters
   // expected to remain in the domain.
   Testcase testcases[] = {
-      {"g@g.c", "g@g.c"},
-      {"g@g.c", kEllipsisStr},
-      {"ga@co.ca", "ga@c" + kEllipsisStr + "a"},
-      {"short@small.com", "s" + kEllipsisStr + "@s" + kEllipsisStr},
-      {"short@small.com", "s" + kEllipsisStr + "@small.com"},
-      {"short@longbutlotsofspace.com", "short@longbutlotsofspace.com"},
-      {"short@longbutnotverymuchspace.com",
-       "short@long" + kEllipsisStr + ".com"},
-      {"la_short@longbutverytightspace.ca",
-       "la" + kEllipsisStr + "@l" + kEllipsisStr + "a"},
-      {"longusername@gmail.com", "long" + kEllipsisStr + "@gmail.com"},
-      {"elidetothemax@justfits.com", "e" + kEllipsisStr + "@justfits.com"},
-      {"thatom_somelongemail@thatdoesntfit.com",
-       "thatom" + kEllipsisStr + "@tha" + kEllipsisStr + "om"},
-      {"namefits@butthedomaindoesnt.com",
-       "namefits@butthedo" + kEllipsisStr + "snt.com"},
-      {"widthtootight@nospace.com", kEllipsisStr},
-      {"nospaceforusername@l", kEllipsisStr},
-      {"little@littlespace.com", "l" + kEllipsisStr + "@l" + kEllipsisStr},
-      {"l@llllllllllllllllllllllll.com", "l@lllll" + kEllipsisStr + ".com"},
-      {"messed\"up@whyanat\"++@notgoogley.com",
-       "messed\"up@whyanat\"++@notgoogley.com"},
-      {"messed\"up@whyanat\"++@notgoogley.com",
-       "messed\"up@why" + kEllipsisStr + "@notgoogley.com"},
-      {"noca_messed\"up@whyanat\"++@notgoogley.ca",
-       "noca" + kEllipsisStr + "@no" + kEllipsisStr + "ca"},
-      {"at\"@@@@@@@@@...@@.@.@.@@@\"@madness.com",
-       "at\"@@@@@@@@@...@@.@." + kEllipsisStr + "@madness.com"},
+      {u"g@g.c", u"g@g.c"},
+      {u"g@g.c", u"…"},
+      {u"ga@co.ca", u"ga@c…a"},
+      {u"short@small.com", u"s…@s…"},
+      {u"short@small.com", u"s…@small.com"},
+      {u"short@longbutlotsofspace.com", u"short@longbutlotsofspace.com"},
+      {u"short@longbutnotverymuchspace.com", u"short@long….com"},
+      {u"la_short@longbutverytightspace.ca", u"la…@l…a"},
+      {u"longusername@gmail.com", u"long…@gmail.com"},
+      {u"elidetothemax@justfits.com", u"e…@justfits.com"},
+      {u"thatom_somelongemail@thatdoesntfit.com", u"thatom…@tha…om"},
+      {u"namefits@butthedomaindoesnt.com", u"namefits@butthedo…snt.com"},
+      {u"widthtootight@nospace.com", u"…"},
+      {u"nospaceforusername@l", u"…"},
+      {u"little@littlespace.com", u"l…@l…"},
+      {u"l@llllllllllllllllllllllll.com", u"l@lllll….com"},
+      {u"messed\"up@whyanat\"++@notgoogley.com",
+       u"messed\"up@whyanat\"++@notgoogley.com"},
+      {u"messed\"up@whyanat\"++@notgoogley.com",
+       u"messed\"up@why…@notgoogley.com"},
+      {u"noca_messed\"up@whyanat\"++@notgoogley.ca", u"noca…@no…ca"},
+      {u"at\"@@@@@@@@@...@@.@.@.@@@\"@madness.com",
+       u"at\"@@@@@@@@@...@@.@.…@madness.com"},
       // Special case: "m..." takes more than half of the available width; thus
       // the domain must elide to "l..." and not "l...l" as it must allow enough
       // space for the minimal username elision although its half of the
       // available width would normally allow it to elide to "l...l".
-      {"mmmmm@llllllllll", "m" + kEllipsisStr + "@l" + kEllipsisStr},
+      {u"mmmmm@llllllllll", u"m…@l…"},
   };
 
   const FontList font_list;
   for (size_t i = 0; i < base::size(testcases); ++i) {
-    const std::u16string expected_output = UTF8ToUTF16(testcases[i].output);
-    EXPECT_EQ(expected_output,
-              ElideText(UTF8ToUTF16(testcases[i].input), font_list,
-                        GetStringWidthF(expected_output, font_list),
-                        ELIDE_EMAIL));
+    const std::u16string expected_output = testcases[i].output;
+    EXPECT_EQ(
+        expected_output,
+        ElideText(testcases[i].input, font_list,
+                  GetStringWidthF(expected_output, font_list), ELIDE_EMAIL));
   }
 }
 
@@ -121,74 +98,69 @@
   const int test_widths_extra_spaces[] = {
       10,
       1000,
-      100000,
+      100'000,
   };
-  const char* test_emails[] = {
-      "a@c",
-      "test@email.com",
-      "short@verysuperdupperlongdomain.com",
-      "supermegalongusername@withasuperlonnnggggdomain.gouv.qc.ca",
+  const char16_t* const test_emails[] = {
+      u"a@c",
+      u"test@email.com",
+      u"short@verysuperdupperlongdomain.com",
+      u"supermegalongusername@withasuperlonnnggggdomain.gouv.qc.ca",
   };
 
   const FontList font_list;
   for (const auto* test_email : test_emails) {
-    const std::u16string test_email16 = UTF8ToUTF16(test_email);
-    const int mimimum_width = GetStringWidth(test_email16, font_list);
+    const int mimimum_width = GetStringWidth(test_email, font_list);
     for (int extra_space : test_widths_extra_spaces) {
       // Extra space is available: the email should not be elided.
-      EXPECT_EQ(test_email16,
-                ElideText(test_email16, font_list, mimimum_width + extra_space,
+      EXPECT_EQ(test_email,
+                ElideText(test_email, font_list, mimimum_width + extra_space,
                           ELIDE_EMAIL));
     }
   }
 }
 
 TEST(TextEliderTest, TestFilenameEliding) {
-  const std::string kEllipsisStr(kEllipsis);
   const base::FilePath::StringType kPathSeparator =
       base::FilePath::StringType().append(1, base::FilePath::kSeparators[0]);
 
   FileTestcase testcases[] = {
-      {FILE_PATH_LITERAL(""), ""},
-      {FILE_PATH_LITERAL("."), "."},
-      {FILE_PATH_LITERAL("filename.exe"), "filename.exe"},
-      {FILE_PATH_LITERAL(".longext"), ".longext"},
-      {FILE_PATH_LITERAL("pie"), "pie"},
+      {FILE_PATH_LITERAL(""), u""},
+      {FILE_PATH_LITERAL("."), u"."},
+      {FILE_PATH_LITERAL("filename.exe"), u"filename.exe"},
+      {FILE_PATH_LITERAL(".longext"), u".longext"},
+      {FILE_PATH_LITERAL("pie"), u"pie"},
       {FILE_PATH_LITERAL("c:") + kPathSeparator + FILE_PATH_LITERAL("path") +
            kPathSeparator + FILE_PATH_LITERAL("filename.pie"),
-       "filename.pie"},
+       u"filename.pie"},
       {FILE_PATH_LITERAL("c:") + kPathSeparator + FILE_PATH_LITERAL("path") +
            kPathSeparator + FILE_PATH_LITERAL("longfilename.pie"),
-       "long" + kEllipsisStr + ".pie"},
-      {FILE_PATH_LITERAL("http://path.com/filename.pie"), "filename.pie"},
-      {FILE_PATH_LITERAL("http://path.com/longfilename.pie"),
-       "long" + kEllipsisStr + ".pie"},
-      {FILE_PATH_LITERAL("piesmashingtacularpants"), "pie" + kEllipsisStr},
-      {FILE_PATH_LITERAL(".piesmashingtacularpants"), ".pie" + kEllipsisStr},
-      {FILE_PATH_LITERAL("cheese."), "cheese."},
-      {FILE_PATH_LITERAL("file name.longext"),
-       "file" + kEllipsisStr + ".longext"},
-      {FILE_PATH_LITERAL("fil ename.longext"),
-       "fil" + kEllipsisStr + ".longext", "fil " + kEllipsisStr + ".longext"},
-      {FILE_PATH_LITERAL("filename.longext"),
-       "file" + kEllipsisStr + ".longext"},
+       u"long….pie"},
+      {FILE_PATH_LITERAL("http://path.com/filename.pie"), u"filename.pie"},
+      {FILE_PATH_LITERAL("http://path.com/longfilename.pie"), u"long….pie"},
+      {FILE_PATH_LITERAL("piesmashingtacularpants"), u"pie…"},
+      {FILE_PATH_LITERAL(".piesmashingtacularpants"), u".pie…"},
+      {FILE_PATH_LITERAL("cheese."), u"cheese."},
+      {FILE_PATH_LITERAL("file name.longext"), u"file….longext"},
+      {FILE_PATH_LITERAL("fil ename.longext"), u"fil….longext",
+       u"fil ….longext"},
+      {FILE_PATH_LITERAL("filename.longext"), u"file….longext"},
       {FILE_PATH_LITERAL("filename.middleext.longext"),
-       "filename.mid" + kEllipsisStr + ".longext"},
+       u"filename.mid….longext"},
       {FILE_PATH_LITERAL("filename.superduperextremelylongext"),
-       "filename.sup" + kEllipsisStr + "emelylongext"},
+       u"filename.sup…emelylongext"},
       {FILE_PATH_LITERAL("filenamereallylongtext.superdeduperextremelylongext"),
-       "filenamereall" + kEllipsisStr + "emelylongext"},
+       u"filenamereall…emelylongext"},
       {FILE_PATH_LITERAL(
            "file.name.really.long.text.superduperextremelylongext"),
-       "file.name.re" + kEllipsisStr + "emelylongext"}};
+       u"file.name.re…emelylongext"}};
 
   static const FontList font_list;
   for (size_t i = 0; i < base::size(testcases); ++i) {
     base::FilePath filepath(testcases[i].input);
-    std::u16string expected = UTF8ToUTF16(testcases[i].output);
-    std::u16string using_width_of = UTF8ToUTF16(
-        testcases[i].using_width_of.empty() ? testcases[i].output
-                                            : testcases[i].using_width_of);
+    std::u16string expected = testcases[i].output;
+    std::u16string using_width_of = testcases[i].using_width_of.empty()
+                                        ? testcases[i].output
+                                        : testcases[i].using_width_of;
     expected = base::i18n::GetDisplayStringInLTRDirectionality(expected);
     EXPECT_EQ(expected,
               ElideFilename(filepath, font_list,
@@ -200,76 +172,71 @@
   const FontList font_list;
   const float kTestWidth = GetStringWidthF(u"Test", font_list);
   struct TestData {
-    const char* input;
+    const char16_t* input;
     float width;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "", 0, "" },
-    { "Test", 0, "" },
-    { "", kTestWidth, "" },
-    { "Tes", kTestWidth, "Tes" },
-    { "Test", kTestWidth, "Test" },
-    { "Tests", kTestWidth, "Test" },
+      {u"", 0, u""},
+      {u"Test", 0, u""},
+      {u"", kTestWidth, u""},
+      {u"Tes", kTestWidth, u"Tes"},
+      {u"Test", kTestWidth, u"Test"},
+      {u"Tests", kTestWidth, u"Test"},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
-    std::u16string result = ElideText(UTF8ToUTF16(cases[i].input), font_list,
-                                      cases[i].width, TRUNCATE);
-    EXPECT_EQ(cases[i].output, UTF16ToUTF8(result));
+    std::u16string result =
+        ElideText(cases[i].input, font_list, cases[i].width, TRUNCATE);
+    EXPECT_EQ(cases[i].output, result);
   }
 }
 
 TEST(TextEliderTest, ElideTextEllipsis) {
   const FontList font_list;
   const float kTestWidth = GetStringWidthF(u"Test", font_list);
-  const char* kEllipsis = "\xE2\x80\xA6";
-  const float kEllipsisWidth =
-      GetStringWidthF(UTF8ToUTF16(kEllipsis), font_list);
+  const float kEllipsisWidth = GetStringWidthF(u"…", font_list);
   struct TestData {
-    const char* input;
+    const char16_t* input;
     float width;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "", 0, "" },
-    { "Test", 0, "" },
-    { "Test", kEllipsisWidth, kEllipsis },
-    { "", kTestWidth, "" },
-    { "Tes", kTestWidth, "Tes" },
-    { "Test", kTestWidth, "Test" },
+      {u"", 0, u""},
+      {u"Test", 0, u""},
+      {u"Test", kEllipsisWidth, u"…"},
+      {u"", kTestWidth, u""},
+      {u"Tes", kTestWidth, u"Tes"},
+      {u"Test", kTestWidth, u"Test"},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
-    std::u16string result = ElideText(UTF8ToUTF16(cases[i].input), font_list,
-                                      cases[i].width, ELIDE_TAIL);
-    EXPECT_EQ(cases[i].output, UTF16ToUTF8(result));
+    std::u16string result =
+        ElideText(cases[i].input, font_list, cases[i].width, ELIDE_TAIL);
+    EXPECT_EQ(cases[i].output, result);
   }
 }
 
 TEST(TextEliderTest, ElideTextEllipsisFront) {
   const FontList font_list;
   const float kTestWidth = GetStringWidthF(u"Test", font_list);
-  const std::string kEllipsisStr(kEllipsis);
-  const float kEllipsisWidth =
-      GetStringWidthF(UTF8ToUTF16(kEllipsis), font_list);
-  const float kEllipsis23Width =
-      GetStringWidthF(UTF8ToUTF16(kEllipsisStr + "23"), font_list);
+  const float kEllipsisWidth = GetStringWidthF(u"…", font_list);
+  const float kEllipsis23Width = GetStringWidthF(u"…23", font_list);
   struct TestData {
-    const char* input;
+    const char16_t* input;
     float width;
     const std::u16string output;
   } cases[] = {
-      {"", 0, std::u16string()},
-      {"Test", 0, std::u16string()},
-      {"Test", kEllipsisWidth, UTF8ToUTF16(kEllipsisStr)},
-      {"", kTestWidth, std::u16string()},
-      {"Tes", kTestWidth, u"Tes"},
-      {"Test", kTestWidth, u"Test"},
-      {"Test123", kEllipsis23Width, UTF8ToUTF16(kEllipsisStr + "23")},
+      {u"", 0, std::u16string()},
+      {u"Test", 0, std::u16string()},
+      {u"Test", kEllipsisWidth, u"…"},
+      {u"", kTestWidth, std::u16string()},
+      {u"Tes", kTestWidth, u"Tes"},
+      {u"Test", kTestWidth, u"Test"},
+      {u"Test123", kEllipsis23Width, u"…23"},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
-    std::u16string result = ElideText(UTF8ToUTF16(cases[i].input), font_list,
-                                      cases[i].width, ELIDE_HEAD);
+    std::u16string result =
+        ElideText(cases[i].input, font_list, cases[i].width, ELIDE_HEAD);
     EXPECT_EQ(cases[i].output, result);
   }
 }
@@ -298,15 +265,13 @@
       base::test::SingleThreadTaskEnvironment::MainThreadType::UI);
 #endif
   const FontList font_list;
+  std::vector<std::u16string> pairs;
   // The below is 'MUSICAL SYMBOL G CLEF' (U+1D11E), which is represented in
   // UTF-16 as two code units forming a surrogate pair: 0xD834 0xDD1E.
-  const char16_t kSurrogate[] = {0xD834, 0xDD1E, 0};
+  pairs.push_back(u"\U0001d11e");
   // The below is a Devanagari two-character combining sequence U+0921 U+093F.
   // The sequence forms a single display character and should not be separated.
-  const char16_t kCombiningSequence[] = {0x921, 0x93F, 0};
-  std::vector<std::u16string> pairs;
-  pairs.push_back(kSurrogate);
-  pairs.push_back(kCombiningSequence);
+  pairs.push_back(u"\u0921\u093f");
 
   for (const std::u16string& pair : pairs) {
     char16_t first_char = pair[0];
@@ -335,34 +300,33 @@
 }
 
 TEST(TextEliderTest, ElideTextLongStrings) {
-  const std::u16string kEllipsisStr = UTF8ToUTF16(kEllipsis);
   std::u16string data_scheme(u"data:text/plain,");
   size_t data_scheme_length = data_scheme.length();
 
   std::u16string ten_a(10, 'a');
   std::u16string hundred_a(100, 'a');
   std::u16string thousand_a(1000, 'a');
-  std::u16string ten_thousand_a(10000, 'a');
-  std::u16string hundred_thousand_a(100000, 'a');
-  std::u16string million_a(1000000, 'a');
+  std::u16string ten_thousand_a(10'000, 'a');
+  std::u16string hundred_thousand_a(100'000, 'a');
+  std::u16string million_a(1'000'000, 'a');
 
   // TODO(gbillock): Improve these tests by adding more string diversity and
   // doing string compares instead of length compares. See bug 338836.
 
   size_t number_of_as = 156;
-  std::u16string long_string_end(
-      data_scheme + std::u16string(number_of_as, 'a') + kEllipsisStr);
-  UTF16Testcase testcases_end[] = {
-     { data_scheme + ten_a,              data_scheme + ten_a },
-     { data_scheme + hundred_a,          data_scheme + hundred_a },
-     { data_scheme + thousand_a,         long_string_end },
-     { data_scheme + ten_thousand_a,     long_string_end },
-     { data_scheme + hundred_thousand_a, long_string_end },
-     { data_scheme + million_a,          long_string_end },
+  std::u16string long_string_end(data_scheme +
+                                 std::u16string(number_of_as, 'a') + u"…");
+  Testcase testcases_end[] = {
+      {data_scheme + ten_a, data_scheme + ten_a},
+      {data_scheme + hundred_a, data_scheme + hundred_a},
+      {data_scheme + thousand_a, long_string_end},
+      {data_scheme + ten_thousand_a, long_string_end},
+      {data_scheme + hundred_thousand_a, long_string_end},
+      {data_scheme + million_a, long_string_end},
   };
 
   const FontList font_list;
-  float ellipsis_width = GetStringWidthF(kEllipsisStr, font_list);
+  float ellipsis_width = GetStringWidthF(u"…", font_list);
   for (size_t i = 0; i < base::size(testcases_end); ++i) {
     // Compare sizes rather than actual contents because if the test fails,
     // output is rather long.
@@ -370,20 +334,19 @@
               ElideText(testcases_end[i].input, font_list,
                         GetStringWidthF(testcases_end[i].output, font_list),
                         ELIDE_TAIL).size());
-    EXPECT_EQ(kEllipsisStr,
-              ElideText(testcases_end[i].input, font_list, ellipsis_width,
-                        ELIDE_TAIL));
+    EXPECT_EQ(u"…", ElideText(testcases_end[i].input, font_list, ellipsis_width,
+                              ELIDE_TAIL));
   }
 
   size_t number_of_trailing_as = (data_scheme_length + number_of_as) / 2;
   std::u16string long_string_middle(
       data_scheme + std::u16string(number_of_as - number_of_trailing_as, 'a') +
-      kEllipsisStr + std::u16string(number_of_trailing_as, 'a'));
+      u"…" + std::u16string(number_of_trailing_as, 'a'));
 #if !defined(OS_IOS)
-  long_string_middle += kEllipsisStr;
+  long_string_middle += u"…";
 #endif
 
-  UTF16Testcase testcases_middle[] = {
+  Testcase testcases_middle[] = {
       {data_scheme + ten_a, data_scheme + ten_a},
       {data_scheme + hundred_a, data_scheme + hundred_a},
       {data_scheme + thousand_a, long_string_middle},
@@ -400,17 +363,17 @@
                         GetStringWidthF(testcases_middle[i].output, font_list),
                         ELIDE_MIDDLE)
                   .size());
-    EXPECT_EQ(kEllipsisStr, ElideText(testcases_middle[i].input, font_list,
-                                      ellipsis_width, ELIDE_MIDDLE));
+    EXPECT_EQ(u"…", ElideText(testcases_middle[i].input, font_list,
+                              ellipsis_width, ELIDE_MIDDLE));
   }
 
-  std::u16string long_string_beginning(kEllipsisStr +
+  std::u16string long_string_beginning(u"…" +
                                        std::u16string(number_of_as, 'a'));
 #if !defined(OS_IOS)
-  long_string_beginning += kEllipsisStr;
+  long_string_beginning += u"…";
 #endif
 
-  UTF16Testcase testcases_beginning[] = {
+  Testcase testcases_beginning[] = {
       {data_scheme + ten_a, data_scheme + ten_a},
       {data_scheme + hundred_a, data_scheme + hundred_a},
       {data_scheme + thousand_a, long_string_beginning},
@@ -424,8 +387,8 @@
                   testcases_beginning[i].input, font_list,
                   GetStringWidthF(testcases_beginning[i].output, font_list),
                   ELIDE_HEAD).size());
-    EXPECT_EQ(kEllipsisStr, ElideText(testcases_beginning[i].input, font_list,
-                                      ellipsis_width, ELIDE_HEAD));
+    EXPECT_EQ(u"…", ElideText(testcases_beginning[i].input, font_list,
+                              ellipsis_width, ELIDE_HEAD));
   }
 }
 
@@ -435,11 +398,11 @@
 TEST(TextEliderTest, StringSlicerBasicTest) {
   // Must store strings in variables (StringSlicer retains a reference to them).
   std::u16string text(u"Hello, world!");
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string ellipsis(u"…");
   StringSlicer slicer(text, ellipsis, false, false);
 
   EXPECT_EQ(u"", slicer.CutString(0, false));
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(0, true));
+  EXPECT_EQ(u"…", slicer.CutString(0, true));
 
   EXPECT_EQ(u"Hell", slicer.CutString(4, false));
   EXPECT_EQ(u"Hell…", slicer.CutString(4, true));
@@ -459,7 +422,7 @@
 TEST(TextEliderTest, StringSlicerWhitespace_UseDefault) {
   // Must store strings in variables (StringSlicer retains a reference to them).
   std::u16string text(u"Hello, world!");
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string ellipsis(u"…");
 
   // Eliding the end of a string should result in whitespace being removed
   // before the ellipsis by default.
@@ -487,7 +450,7 @@
 TEST(TextEliderTest, StringSlicerWhitespace_NoTrim) {
   // Must store strings in variables (StringSlicer retains a reference to them).
   std::u16string text(u"Hello, world!");
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string ellipsis(u"…");
 
   // Eliding the end of a string should not result in whitespace being removed
   // before the ellipsis in no-trim mode.
@@ -515,7 +478,7 @@
 TEST(TextEliderTest, StringSlicerWhitespace_Trim) {
   // Must store strings in variables (StringSlicer retains a reference to them).
   std::u16string text(u"Hello, world!");
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string ellipsis(u"…");
 
   // Eliding the end of a string should result in whitespace being removed
   // before the ellipsis in trim mode.
@@ -543,7 +506,7 @@
 TEST(TextEliderTest, StringSlicer_ElideMiddle_MultipleWhitespace) {
   // Must store strings in variables (StringSlicer retains a reference to them).
   std::u16string text(u"Hello  world!");
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string ellipsis(u"…");
 
   // Eliding the middle of a string should not result in whitespace being
   // removed around the ellipsis in default whitespace mode.
@@ -579,24 +542,23 @@
 TEST(TextEliderTest, StringSlicerSurrogate) {
   // The below is 'MUSICAL SYMBOL G CLEF' (U+1D11E), which is represented in
   // UTF-16 as two code units forming a surrogate pair: 0xD834 0xDD1E.
-  const std::u16string kSurrogate = u"𝄞";
+  const std::u16string kSurrogate = u"\U0001d11e";
   ASSERT_EQ(2u, kSurrogate.size());
   ASSERT_EQ(u'\xD834', kSurrogate[0]);
   ASSERT_EQ(u'\xDD1E', kSurrogate[1]);
 
   std::u16string text(u"abc" + kSurrogate + u"xyz");
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string ellipsis(u"…");
   StringSlicer slicer(text, ellipsis, false, false);
 
   // Cut surrogate on the right. Should round left and exclude the surrogate.
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(0, true));
+  EXPECT_EQ(u"…", slicer.CutString(0, true));
   EXPECT_EQ(u"abc…", slicer.CutString(4, true));
-  EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(text.length(), true));
+  EXPECT_EQ(text + u"…", slicer.CutString(text.length(), true));
 
   // Cut surrogate on the left. Should round right and exclude the surrogate.
   StringSlicer slicer_begin(text, ellipsis, false, true);
-  EXPECT_EQ(std::u16string(kEllipsisUTF16) + u"xyz",
-            slicer_begin.CutString(4, true));
+  EXPECT_EQ(u"…xyz", slicer_begin.CutString(4, true));
 
   // Cut surrogate in the middle. Should round right and exclude the surrogate.
   std::u16string short_text(u"abc" + kSurrogate);
@@ -607,9 +569,8 @@
   std::u16string dangling_trailing_text = kSurrogate.substr(1);
   StringSlicer slicer_dangling_trailing(dangling_trailing_text, ellipsis, false,
                                         false);
-  EXPECT_EQ(std::u16string(kEllipsisUTF16),
-            slicer_dangling_trailing.CutString(0, true));
-  EXPECT_EQ(dangling_trailing_text + kEllipsisUTF16,
+  EXPECT_EQ(u"…", slicer_dangling_trailing.CutString(0, true));
+  EXPECT_EQ(dangling_trailing_text + u"…",
             slicer_dangling_trailing.CutString(1, true));
 }
 
@@ -619,10 +580,8 @@
   // LATIN SMALL LETTER E + COMBINING ACUTE ACCENT + COMBINING CEDILLA
   // LATIN SMALL LETTER X + COMBINING ENCLOSING KEYCAP
   // DEVANAGARI LETTER DDA + DEVANAGARI VOWEL SIGN I
-  const char16_t kText[] = {'e',    0x301, 0x327, ' ',   'x',
-                            0x20E3, ' ',   0x921, 0x93F, 0};
-  std::u16string text(kText);
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string text(u"e\u0301\u0327 x\u20e3 \u0921\u093f");
+  std::u16string ellipsis(u"…");
   StringSlicer slicer(text, ellipsis, false, false);
 
   // Attempt to cut the string for all lengths. When a combining sequence is
@@ -630,33 +589,31 @@
   // Whitespace is also cut adjacent to the ellipsis.
 
   // First sequence:
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(0, true));
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(1, true));
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(2, true));
-  EXPECT_EQ(text.substr(0, 3) + kEllipsisUTF16, slicer.CutString(3, true));
+  EXPECT_EQ(u"…", slicer.CutString(0, true));
+  EXPECT_EQ(u"…", slicer.CutString(1, true));
+  EXPECT_EQ(u"…", slicer.CutString(2, true));
+  EXPECT_EQ(text.substr(0, 3) + u"…", slicer.CutString(3, true));
   // Second sequence:
-  EXPECT_EQ(text.substr(0, 3) + kEllipsisUTF16, slicer.CutString(4, true));
-  EXPECT_EQ(text.substr(0, 3) + kEllipsisUTF16, slicer.CutString(5, true));
-  EXPECT_EQ(text.substr(0, 6) + kEllipsisUTF16, slicer.CutString(6, true));
+  EXPECT_EQ(text.substr(0, 3) + u"…", slicer.CutString(4, true));
+  EXPECT_EQ(text.substr(0, 3) + u"…", slicer.CutString(5, true));
+  EXPECT_EQ(text.substr(0, 6) + u"…", slicer.CutString(6, true));
   // Third sequence:
-  EXPECT_EQ(text.substr(0, 6) + kEllipsisUTF16, slicer.CutString(7, true));
-  EXPECT_EQ(text.substr(0, 6) + kEllipsisUTF16, slicer.CutString(8, true));
-  EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(9, true));
+  EXPECT_EQ(text.substr(0, 6) + u"…", slicer.CutString(7, true));
+  EXPECT_EQ(text.substr(0, 6) + u"…", slicer.CutString(8, true));
+  EXPECT_EQ(text + u"…", slicer.CutString(9, true));
 
   // Cut string in the middle, splitting the second sequence in half. Should
   // round both left and right, excluding the second sequence.
   StringSlicer slicer_mid(text, ellipsis, true, false);
-  EXPECT_EQ(text.substr(0, 4) + kEllipsisUTF16 + text.substr(6),
+  EXPECT_EQ(text.substr(0, 4) + u"…" + text.substr(6),
             slicer_mid.CutString(9, true));
 
   // String that starts with a dangling combining mark.
   char16_t dangling_mark_chars[] = {text[1], 0};
   std::u16string dangling_mark_text(dangling_mark_chars);
   StringSlicer slicer_dangling_mark(dangling_mark_text, ellipsis, false, false);
-  EXPECT_EQ(std::u16string(kEllipsisUTF16),
-            slicer_dangling_mark.CutString(0, true));
-  EXPECT_EQ(dangling_mark_text + kEllipsisUTF16,
-            slicer_dangling_mark.CutString(1, true));
+  EXPECT_EQ(u"…", slicer_dangling_mark.CutString(0, true));
+  EXPECT_EQ(dangling_mark_text + u"…", slicer_dangling_mark.CutString(1, true));
 }
 
 TEST(TextEliderTest, StringSlicerCombiningSurrogate) {
@@ -664,49 +621,46 @@
   // The following string contains a single combining character sequence:
   // MUSICAL SYMBOL G CLEF (U+1D11E) + MUSICAL SYMBOL COMBINING FLAG-1 (U+1D16E)
   // Represented as four UTF-16 code units.
-  const char16_t kText[] = {0xD834, 0xDD1E, 0xD834, 0xDD6E, 0};
-  std::u16string text(kText);
-  std::u16string ellipsis(kEllipsisUTF16);
+  std::u16string text(u"\U0001d11e\U0001d16e");
+  std::u16string ellipsis(u"…");
   StringSlicer slicer(text, ellipsis, false, false);
 
   // Attempt to cut the string for all lengths. Should always round left and
   // exclude the combining sequence.
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(0, true));
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(1, true));
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(2, true));
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer.CutString(3, true));
-  EXPECT_EQ(text + kEllipsisUTF16, slicer.CutString(4, true));
+  EXPECT_EQ(u"…", slicer.CutString(0, true));
+  EXPECT_EQ(u"…", slicer.CutString(1, true));
+  EXPECT_EQ(u"…", slicer.CutString(2, true));
+  EXPECT_EQ(u"…", slicer.CutString(3, true));
+  EXPECT_EQ(text + u"…", slicer.CutString(4, true));
 
   // Cut string in the middle. Should exclude the sequence.
   StringSlicer slicer_mid(text, ellipsis, true, false);
-  EXPECT_EQ(std::u16string(kEllipsisUTF16), slicer_mid.CutString(4, true));
+  EXPECT_EQ(u"…", slicer_mid.CutString(4, true));
 }
 
 TEST(TextEliderTest, ElideString) {
   struct TestData {
-    const char* input;
+    const char16_t* input;
     size_t max_len;
     bool result;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "Hello", 0, true, "" },
-    { "", 0, false, "" },
-    { "Hello, my name is Tom", 1, true, "H" },
-    { "Hello, my name is Tom", 2, true, "He" },
-    { "Hello, my name is Tom", 3, true, "H.m" },
-    { "Hello, my name is Tom", 4, true, "H..m" },
-    { "Hello, my name is Tom", 5, true, "H...m" },
-    { "Hello, my name is Tom", 6, true, "He...m" },
-    { "Hello, my name is Tom", 7, true, "He...om" },
-    { "Hello, my name is Tom", 10, true, "Hell...Tom" },
-    { "Hello, my name is Tom", 100, false, "Hello, my name is Tom" }
-  };
+      {u"Hello", 0, true, u""},
+      {u"", 0, false, u""},
+      {u"Hello, my name is Tom", 1, true, u"H"},
+      {u"Hello, my name is Tom", 2, true, u"He"},
+      {u"Hello, my name is Tom", 3, true, u"H.m"},
+      {u"Hello, my name is Tom", 4, true, u"H..m"},
+      {u"Hello, my name is Tom", 5, true, u"H...m"},
+      {u"Hello, my name is Tom", 6, true, u"He...m"},
+      {u"Hello, my name is Tom", 7, true, u"He...om"},
+      {u"Hello, my name is Tom", 10, true, u"Hell...Tom"},
+      {u"Hello, my name is Tom", 100, false, u"Hello, my name is Tom"}};
   for (size_t i = 0; i < base::size(cases); ++i) {
     std::u16string output;
     EXPECT_EQ(cases[i].result,
-              ElideString(UTF8ToUTF16(cases[i].input),
-                          cases[i].max_len, &output));
-    EXPECT_EQ(cases[i].output, UTF16ToUTF8(output));
+              ElideString(cases[i].input, cases[i].max_len, &output));
+    EXPECT_EQ(cases[i].output, output);
   }
 }
 
@@ -716,50 +670,51 @@
   const float test_width = GetStringWidthF(u"Test", font_list);
 
   struct TestData {
-    const char* input;
+    const char16_t* input;
     float available_pixel_width;
     int available_pixel_height;
     bool truncated_y;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-      {"", 0, 0, false, nullptr},
-      {"", 1, 1, false, nullptr},
-      {"Test", test_width, 0, true, nullptr},
-      {"Test", test_width, 1, false, "Test"},
-      {"Test", test_width, line_height, false, "Test"},
-      {"Test Test", test_width, line_height, true, "Test"},
-      {"Test Test", test_width, line_height + 1, false, "Test|Test"},
-      {"Test Test", test_width, line_height * 2, false, "Test|Test"},
-      {"Test Test", test_width, line_height * 3, false, "Test|Test"},
-      {"Test Test", test_width * 2, line_height * 2, false, "Test|Test"},
-      {"Test Test", test_width * 3, line_height, false, "Test Test"},
-      {"Test\nTest", test_width * 3, line_height * 2, false, "Test|Test"},
-      {"Te\nst Te", test_width, line_height * 3, false, "Te|st|Te"},
-      {"\nTest", test_width, line_height * 2, false, "|Test"},
-      {"\nTest", test_width, line_height, true, ""},
-      {"\n\nTest", test_width, line_height * 3, false, "||Test"},
-      {"\n\nTest", test_width, line_height * 2, true, "|"},
-      {"Test\n", 2 * test_width, line_height * 5, false, "Test|"},
-      {"Test\n\n", 2 * test_width, line_height * 5, false, "Test||"},
-      {"Test\n\n\n", 2 * test_width, line_height * 5, false, "Test|||"},
-      {"Test\nTest\n\n", 2 * test_width, line_height * 5, false, "Test|Test||"},
-      {"Test\n\nTest\n", 2 * test_width, line_height * 5, false, "Test||Test|"},
-      {"Test\n\n\nTest", 2 * test_width, line_height * 5, false, "Test|||Test"},
-      {"Te ", test_width, line_height, false, "Te"},
-      {"Te  Te Test", test_width, 3 * line_height, false, "Te|Te|Test"},
+      {u"", 0, 0, false, nullptr},
+      {u"", 1, 1, false, nullptr},
+      {u"Test", test_width, 0, true, nullptr},
+      {u"Test", test_width, 1, false, u"Test"},
+      {u"Test", test_width, line_height, false, u"Test"},
+      {u"Test Test", test_width, line_height, true, u"Test"},
+      {u"Test Test", test_width, line_height + 1, false, u"Test|Test"},
+      {u"Test Test", test_width, line_height * 2, false, u"Test|Test"},
+      {u"Test Test", test_width, line_height * 3, false, u"Test|Test"},
+      {u"Test Test", test_width * 2, line_height * 2, false, u"Test|Test"},
+      {u"Test Test", test_width * 3, line_height, false, u"Test Test"},
+      {u"Test\nTest", test_width * 3, line_height * 2, false, u"Test|Test"},
+      {u"Te\nst Te", test_width, line_height * 3, false, u"Te|st|Te"},
+      {u"\nTest", test_width, line_height * 2, false, u"|Test"},
+      {u"\nTest", test_width, line_height, true, u""},
+      {u"\n\nTest", test_width, line_height * 3, false, u"||Test"},
+      {u"\n\nTest", test_width, line_height * 2, true, u"|"},
+      {u"Test\n", 2 * test_width, line_height * 5, false, u"Test|"},
+      {u"Test\n\n", 2 * test_width, line_height * 5, false, u"Test||"},
+      {u"Test\n\n\n", 2 * test_width, line_height * 5, false, u"Test|||"},
+      {u"Test\nTest\n\n", 2 * test_width, line_height * 5, false,
+       u"Test|Test||"},
+      {u"Test\n\nTest\n", 2 * test_width, line_height * 5, false,
+       u"Test||Test|"},
+      {u"Test\n\n\nTest", 2 * test_width, line_height * 5, false,
+       u"Test|||Test"},
+      {u"Te ", test_width, line_height, false, u"Te"},
+      {u"Te  Te Test", test_width, 3 * line_height, false, u"Te|Te|Test"},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     std::vector<std::u16string> lines;
     EXPECT_EQ(cases[i].truncated_y ? INSUFFICIENT_SPACE_VERTICAL : 0,
-              ElideRectangleText(UTF8ToUTF16(cases[i].input),
-                                 font_list,
+              ElideRectangleText(cases[i].input, font_list,
                                  cases[i].available_pixel_width,
                                  cases[i].available_pixel_height,
-                                 TRUNCATE_LONG_WORDS,
-                                 &lines));
+                                 TRUNCATE_LONG_WORDS, &lines));
     if (cases[i].output) {
-      const std::string result = UTF16ToUTF8(base::JoinString(lines, u"|"));
+      const std::u16string result = base::JoinString(lines, u"|");
       EXPECT_EQ(cases[i].output, result) << "Case " << i << " failed!";
     } else {
       EXPECT_TRUE(lines.empty()) << "Case " << i << " failed!";
@@ -776,27 +731,27 @@
 
   std::vector<std::u16string> lines;
 
-  auto result_for_width = [&](const char* input, float width) {
+  auto result_for_width = [&](const char16_t* input, float width) {
     lines.clear();
-    return ElideRectangleText(ASCIIToUTF16(input), font_list, width,
-                              line_height * 4, WRAP_LONG_WORDS, &lines);
+    return ElideRectangleText(input, font_list, width, line_height * 4,
+                              WRAP_LONG_WORDS, &lines);
   };
 
   // Test base case.
-  EXPECT_EQ(0, result_for_width("Test", test_width));
+  EXPECT_EQ(0, result_for_width(u"Test", test_width));
   EXPECT_EQ(1u, lines.size());
   EXPECT_EQ(u"Test", lines[0]);
 
   // First word truncated.
   EXPECT_EQ(INSUFFICIENT_SPACE_FOR_FIRST_WORD,
-            result_for_width("Test", tes_width));
+            result_for_width(u"Test", tes_width));
   EXPECT_EQ(2u, lines.size());
   EXPECT_EQ(u"Tes", lines[0]);
   EXPECT_EQ(u"t", lines[1]);
 
   // Two words truncated.
   EXPECT_EQ(INSUFFICIENT_SPACE_FOR_FIRST_WORD,
-            result_for_width("Test\nTest", tes_width));
+            result_for_width(u"Test\nTest", tes_width));
   EXPECT_EQ(4u, lines.size());
   EXPECT_EQ(u"Tes", lines[0]);
   EXPECT_EQ(u"t", lines[1]);
@@ -804,14 +759,14 @@
   EXPECT_EQ(u"t", lines[3]);
 
   // Word truncated, but not the first.
-  EXPECT_EQ(0, result_for_width("T Test", tes_width));
+  EXPECT_EQ(0, result_for_width(u"T Test", tes_width));
   EXPECT_EQ(3u, lines.size());
   EXPECT_EQ(u"T", lines[0]);
   EXPECT_EQ(u"Tes", lines[1]);
   EXPECT_EQ(u"t", lines[2]);
 
   // Leading \n.
-  EXPECT_EQ(0, result_for_width("\nTest", tes_width));
+  EXPECT_EQ(0, result_for_width(u"\nTest", tes_width));
   EXPECT_EQ(3u, lines.size());
   EXPECT_EQ(u"", lines[0]);
   EXPECT_EQ(u"Tes", lines[1]);
@@ -827,17 +782,17 @@
       INSUFFICIENT_SPACE_HORIZONTAL | INSUFFICIENT_SPACE_VERTICAL;
 
   struct TestData {
-    const char* input;
+    const char16_t* input;
     float available_pixel_width;
     int available_pixel_height;
     bool wrap_words;
     bool truncated_x;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "Test T.", test_t_width, line_height * 2, false, false, "Test|T." },
-    { "Test T ?", test_t_width, line_height * 2, false, false, "Test|T ?" },
-    { "Test. Test", test_width, line_height * 3, false, true, "Test|Test" },
-    { "Test. Test", test_width, line_height * 3, true, false, "Test|.|Test" },
+      {u"Test T.", test_t_width, line_height * 2, false, false, u"Test|T."},
+      {u"Test T ?", test_t_width, line_height * 2, false, false, u"Test|T ?"},
+      {u"Test. Test", test_width, line_height * 3, false, true, u"Test|Test"},
+      {u"Test. Test", test_width, line_height * 3, true, false, u"Test|.|Test"},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
@@ -845,13 +800,12 @@
     const WordWrapBehavior wrap_behavior =
         (cases[i].wrap_words ? WRAP_LONG_WORDS : TRUNCATE_LONG_WORDS);
     EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0,
-              ElideRectangleText(UTF8ToUTF16(cases[i].input), font_list,
-                                 cases[i].available_pixel_width,
-                                 cases[i].available_pixel_height, wrap_behavior,
-                                 &lines) &
+              ElideRectangleText(
+                  cases[i].input, font_list, cases[i].available_pixel_width,
+                  cases[i].available_pixel_height, wrap_behavior, &lines) &
                   kResultMask);
     if (cases[i].output) {
-      const std::string result = UTF16ToUTF8(base::JoinString(lines, u"|"));
+      const std::u16string result = base::JoinString(lines, u"|");
       EXPECT_EQ(cases[i].output, result) << "Case " << i << " failed!";
     } else {
       EXPECT_TRUE(lines.empty()) << "Case " << i << " failed!";
@@ -862,60 +816,58 @@
 TEST(TextEliderTest, ElideRectangleTextLongWords) {
   const FontList font_list;
   const int kAvailableHeight = 1000;
-  const std::u16string kElidedTesting =
-      UTF8ToUTF16(std::string("Tes") + kEllipsis);
+  const std::u16string kElidedTesting = u"Tes…";
   const float elided_width = GetStringWidthF(kElidedTesting, font_list);
   const float test_width = GetStringWidthF(u"Test", font_list);
   constexpr int kResultMask =
       INSUFFICIENT_SPACE_HORIZONTAL | INSUFFICIENT_SPACE_VERTICAL;
 
   struct TestData {
-    const char* input;
+    const char16_t* input;
     float available_pixel_width;
     WordWrapBehavior wrap_behavior;
     bool truncated_x;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "Testing", test_width, IGNORE_LONG_WORDS, false, "Testing" },
-    { "X Testing", test_width, IGNORE_LONG_WORDS, false, "X|Testing" },
-    { "Test Testing", test_width, IGNORE_LONG_WORDS, false, "Test|Testing" },
-    { "Test\nTesting", test_width, IGNORE_LONG_WORDS, false, "Test|Testing" },
-    { "Test Tests ", test_width, IGNORE_LONG_WORDS, false, "Test|Tests" },
-    { "Test Tests T", test_width, IGNORE_LONG_WORDS, false, "Test|Tests|T" },
+      {u"Testing", test_width, IGNORE_LONG_WORDS, false, u"Testing"},
+      {u"X Testing", test_width, IGNORE_LONG_WORDS, false, u"X|Testing"},
+      {u"Test Testing", test_width, IGNORE_LONG_WORDS, false, u"Test|Testing"},
+      {u"Test\nTesting", test_width, IGNORE_LONG_WORDS, false, u"Test|Testing"},
+      {u"Test Tests ", test_width, IGNORE_LONG_WORDS, false, u"Test|Tests"},
+      {u"Test Tests T", test_width, IGNORE_LONG_WORDS, false, u"Test|Tests|T"},
 
-    { "Testing", elided_width, ELIDE_LONG_WORDS, true, "Tes..." },
-    { "X Testing", elided_width, ELIDE_LONG_WORDS, true, "X|Tes..." },
-    { "Test Testing", elided_width, ELIDE_LONG_WORDS, true, "Test|Tes..." },
-    { "Test\nTesting", elided_width, ELIDE_LONG_WORDS, true, "Test|Tes..." },
+      {u"Testing", elided_width, ELIDE_LONG_WORDS, true, u"Tes…"},
+      {u"X Testing", elided_width, ELIDE_LONG_WORDS, true, u"X|Tes…"},
+      {u"Test Testing", elided_width, ELIDE_LONG_WORDS, true, u"Test|Tes…"},
+      {u"Test\nTesting", elided_width, ELIDE_LONG_WORDS, true, u"Test|Tes…"},
 
-    { "Testing", test_width, TRUNCATE_LONG_WORDS, true, "Test" },
-    { "X Testing", test_width, TRUNCATE_LONG_WORDS, true, "X|Test" },
-    { "Test Testing", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test" },
-    { "Test\nTesting", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test" },
-    { "Test Tests ", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test" },
-    { "Test Tests T", test_width, TRUNCATE_LONG_WORDS, true, "Test|Test|T" },
+      {u"Testing", test_width, TRUNCATE_LONG_WORDS, true, u"Test"},
+      {u"X Testing", test_width, TRUNCATE_LONG_WORDS, true, u"X|Test"},
+      {u"Test Testing", test_width, TRUNCATE_LONG_WORDS, true, u"Test|Test"},
+      {u"Test\nTesting", test_width, TRUNCATE_LONG_WORDS, true, u"Test|Test"},
+      {u"Test Tests ", test_width, TRUNCATE_LONG_WORDS, true, u"Test|Test"},
+      {u"Test Tests T", test_width, TRUNCATE_LONG_WORDS, true, u"Test|Test|T"},
 
-    { "Testing", test_width, WRAP_LONG_WORDS, false, "Test|ing" },
-    { "X Testing", test_width, WRAP_LONG_WORDS, false, "X|Test|ing" },
-    { "Test Testing", test_width, WRAP_LONG_WORDS, false, "Test|Test|ing" },
-    { "Test\nTesting", test_width, WRAP_LONG_WORDS, false, "Test|Test|ing" },
-    { "Test Tests ", test_width, WRAP_LONG_WORDS, false, "Test|Test|s" },
-    { "Test Tests T", test_width, WRAP_LONG_WORDS, false, "Test|Test|s T" },
-    { "TestTestTest", test_width, WRAP_LONG_WORDS, false, "Test|Test|Test" },
-    { "TestTestTestT", test_width, WRAP_LONG_WORDS, false, "Test|Test|Test|T" },
+      {u"Testing", test_width, WRAP_LONG_WORDS, false, u"Test|ing"},
+      {u"X Testing", test_width, WRAP_LONG_WORDS, false, u"X|Test|ing"},
+      {u"Test Testing", test_width, WRAP_LONG_WORDS, false, u"Test|Test|ing"},
+      {u"Test\nTesting", test_width, WRAP_LONG_WORDS, false, u"Test|Test|ing"},
+      {u"Test Tests ", test_width, WRAP_LONG_WORDS, false, u"Test|Test|s"},
+      {u"Test Tests T", test_width, WRAP_LONG_WORDS, false, u"Test|Test|s T"},
+      {u"TestTestTest", test_width, WRAP_LONG_WORDS, false, u"Test|Test|Test"},
+      {u"TestTestTestT", test_width, WRAP_LONG_WORDS, false,
+       u"Test|Test|Test|T"},
   };
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     std::vector<std::u16string> lines;
-    EXPECT_EQ(
-        cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0,
-        ElideRectangleText(UTF8ToUTF16(cases[i].input), font_list,
-                           cases[i].available_pixel_width, kAvailableHeight,
-                           cases[i].wrap_behavior, &lines) &
-            kResultMask);
-    std::string expected_output(cases[i].output);
-    base::ReplaceSubstringsAfterOffset(&expected_output, 0, "...", kEllipsis);
-    const std::string result = UTF16ToUTF8(base::JoinString(lines, u"|"));
+    EXPECT_EQ(cases[i].truncated_x ? INSUFFICIENT_SPACE_HORIZONTAL : 0,
+              ElideRectangleText(
+                  cases[i].input, font_list, cases[i].available_pixel_width,
+                  kAvailableHeight, cases[i].wrap_behavior, &lines) &
+                  kResultMask);
+    std::u16string expected_output(cases[i].output);
+    const std::u16string result = base::JoinString(lines, u"|");
     EXPECT_EQ(expected_output, result) << "Case " << i << " failed!";
   }
 }
@@ -933,14 +885,10 @@
 #endif
   const float kAvailableWidth = 235;
   const int kAvailableHeight = 1000;
-  const char text[] = "that Russian place we used to go to after fencing";
+  const char16_t text[] = u"that Russian place we used to go to after fencing";
   std::vector<std::u16string> lines;
-  EXPECT_EQ(0, ElideRectangleText(UTF8ToUTF16(text),
-                                  font_list,
-                                  kAvailableWidth,
-                                  kAvailableHeight,
-                                  WRAP_LONG_WORDS,
-                                  &lines));
+  EXPECT_EQ(0, ElideRectangleText(text, font_list, kAvailableWidth,
+                                  kAvailableHeight, WRAP_LONG_WORDS, &lines));
   ASSERT_EQ(2u, lines.size());
   EXPECT_LE(GetStringWidthF(lines[0], font_list), kAvailableWidth);
   EXPECT_LE(GetStringWidthF(lines[1], font_list), kAvailableWidth);
@@ -954,10 +902,10 @@
   FontList font_list;
   font_list = FontList("Noto Sans UI,ui-sans, 12px");
   SetFontRenderParamsDeviceScaleFactor(1.25f);
-#define WIDTH(x) GetStringWidthF(UTF8ToUTF16(x), font_list)
-  EXPECT_EQ(WIDTH("The administrator for this account has"),
-            WIDTH("The ") + WIDTH("administrator ") + WIDTH("for ") +
-                WIDTH("this ") + WIDTH("account ") + WIDTH("has"));
+#define WIDTH(x) GetStringWidthF((x), font_list)
+  EXPECT_EQ(WIDTH(u"The administrator for this account has"),
+            WIDTH(u"The ") + WIDTH(u"administrator ") + WIDTH(u"for ") +
+                WIDTH(u"this ") + WIDTH(u"account ") + WIDTH(u"has"));
 #undef WIDTH
   SetFontRenderParamsDeviceScaleFactor(1.0f);
 }
@@ -965,181 +913,172 @@
 
 TEST(TextEliderTest, ElideRectangleString) {
   struct TestData {
-    const char* input;
+    const char16_t* input;
     int max_rows;
     int max_cols;
     bool result;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "", 0, 0, false, "" },
-    { "", 1, 1, false, "" },
-    { "Hi, my name is\nTom", 0, 0,  true,  "..." },
-    { "Hi, my name is\nTom", 1, 0,  true,  "\n..." },
-    { "Hi, my name is\nTom", 0, 1,  true,  "..." },
-    { "Hi, my name is\nTom", 1, 1,  true,  "H\n..." },
-    { "Hi, my name is\nTom", 2, 1,  true,  "H\ni\n..." },
-    { "Hi, my name is\nTom", 3, 1,  true,  "H\ni\n,\n..." },
-    { "Hi, my name is\nTom", 4, 1,  true,  "H\ni\n,\n \n..." },
-    { "Hi, my name is\nTom", 5, 1,  true,  "H\ni\n,\n \nm\n..." },
-    { "Hi, my name is\nTom", 0, 2,  true,  "..." },
-    { "Hi, my name is\nTom", 1, 2,  true,  "Hi\n..." },
-    { "Hi, my name is\nTom", 2, 2,  true,  "Hi\n, \n..." },
-    { "Hi, my name is\nTom", 3, 2,  true,  "Hi\n, \nmy\n..." },
-    { "Hi, my name is\nTom", 4, 2,  true,  "Hi\n, \nmy\n n\n..." },
-    { "Hi, my name is\nTom", 5, 2,  true,  "Hi\n, \nmy\n n\nam\n..." },
-    { "Hi, my name is\nTom", 0, 3,  true,  "..." },
-    { "Hi, my name is\nTom", 1, 3,  true,  "Hi,\n..." },
-    { "Hi, my name is\nTom", 2, 3,  true,  "Hi,\n my\n..." },
-    { "Hi, my name is\nTom", 3, 3,  true,  "Hi,\n my\n na\n..." },
-    { "Hi, my name is\nTom", 4, 3,  true,  "Hi,\n my\n na\nme \n..." },
-    { "Hi, my name is\nTom", 5, 3,  true,  "Hi,\n my\n na\nme \nis\n..." },
-    { "Hi, my name is\nTom", 1, 4,  true,  "Hi, \n..." },
-    { "Hi, my name is\nTom", 2, 4,  true,  "Hi, \nmy n\n..." },
-    { "Hi, my name is\nTom", 3, 4,  true,  "Hi, \nmy n\name \n..." },
-    { "Hi, my name is\nTom", 4, 4,  true,  "Hi, \nmy n\name \nis\n..." },
-    { "Hi, my name is\nTom", 5, 4,  false, "Hi, \nmy n\name \nis\nTom" },
-    { "Hi, my name is\nTom", 1, 5,  true,  "Hi, \n..." },
-    { "Hi, my name is\nTom", 2, 5,  true,  "Hi, \nmy na\n..." },
-    { "Hi, my name is\nTom", 3, 5,  true,  "Hi, \nmy na\nme \n..." },
-    { "Hi, my name is\nTom", 4, 5,  true,  "Hi, \nmy na\nme \nis\n..." },
-    { "Hi, my name is\nTom", 5, 5,  false, "Hi, \nmy na\nme \nis\nTom" },
-    { "Hi, my name is\nTom", 1, 6,  true,  "Hi, \n..." },
-    { "Hi, my name is\nTom", 2, 6,  true,  "Hi, \nmy \n..." },
-    { "Hi, my name is\nTom", 3, 6,  true,  "Hi, \nmy \nname \n..." },
-    { "Hi, my name is\nTom", 4, 6,  true,  "Hi, \nmy \nname \nis\n..." },
-    { "Hi, my name is\nTom", 5, 6,  false, "Hi, \nmy \nname \nis\nTom" },
-    { "Hi, my name is\nTom", 1, 7,  true,  "Hi, \n..." },
-    { "Hi, my name is\nTom", 2, 7,  true,  "Hi, \nmy \n..." },
-    { "Hi, my name is\nTom", 3, 7,  true,  "Hi, \nmy \nname \n..." },
-    { "Hi, my name is\nTom", 4, 7,  true,  "Hi, \nmy \nname \nis\n..." },
-    { "Hi, my name is\nTom", 5, 7,  false, "Hi, \nmy \nname \nis\nTom" },
-    { "Hi, my name is\nTom", 1, 8,  true,  "Hi, my \n..." },
-    { "Hi, my name is\nTom", 2, 8,  true,  "Hi, my \nname \n..." },
-    { "Hi, my name is\nTom", 3, 8,  true,  "Hi, my \nname \nis\n..." },
-    { "Hi, my name is\nTom", 4, 8,  false, "Hi, my \nname \nis\nTom" },
-    { "Hi, my name is\nTom", 1, 9,  true,  "Hi, my \n..." },
-    { "Hi, my name is\nTom", 2, 9,  true,  "Hi, my \nname is\n..." },
-    { "Hi, my name is\nTom", 3, 9,  false, "Hi, my \nname is\nTom" },
-    { "Hi, my name is\nTom", 1, 10, true,  "Hi, my \n..." },
-    { "Hi, my name is\nTom", 2, 10, true,  "Hi, my \nname is\n..." },
-    { "Hi, my name is\nTom", 3, 10, false, "Hi, my \nname is\nTom" },
-    { "Hi, my name is\nTom", 1, 11, true,  "Hi, my \n..." },
-    { "Hi, my name is\nTom", 2, 11, true,  "Hi, my \nname is\n..." },
-    { "Hi, my name is\nTom", 3, 11, false, "Hi, my \nname is\nTom" },
-    { "Hi, my name is\nTom", 1, 12, true,  "Hi, my \n..." },
-    { "Hi, my name is\nTom", 2, 12, true,  "Hi, my \nname is\n..." },
-    { "Hi, my name is\nTom", 3, 12, false, "Hi, my \nname is\nTom" },
-    { "Hi, my name is\nTom", 1, 13, true,  "Hi, my name \n..." },
-    { "Hi, my name is\nTom", 2, 13, true,  "Hi, my name \nis\n..." },
-    { "Hi, my name is\nTom", 3, 13, false, "Hi, my name \nis\nTom" },
-    { "Hi, my name is\nTom", 1, 20, true,  "Hi, my name is\n..." },
-    { "Hi, my name is\nTom", 2, 20, false, "Hi, my name is\nTom" },
-    { "Hi, my name is Tom",  1, 40, false, "Hi, my name is Tom" },
+      {u"", 0, 0, false, u""},
+      {u"", 1, 1, false, u""},
+      {u"Hi, my name is\nTom", 0, 0, true, u"..."},
+      {u"Hi, my name is\nTom", 1, 0, true, u"\n..."},
+      {u"Hi, my name is\nTom", 0, 1, true, u"..."},
+      {u"Hi, my name is\nTom", 1, 1, true, u"H\n..."},
+      {u"Hi, my name is\nTom", 2, 1, true, u"H\ni\n..."},
+      {u"Hi, my name is\nTom", 3, 1, true, u"H\ni\n,\n..."},
+      {u"Hi, my name is\nTom", 4, 1, true, u"H\ni\n,\n \n..."},
+      {u"Hi, my name is\nTom", 5, 1, true, u"H\ni\n,\n \nm\n..."},
+      {u"Hi, my name is\nTom", 0, 2, true, u"..."},
+      {u"Hi, my name is\nTom", 1, 2, true, u"Hi\n..."},
+      {u"Hi, my name is\nTom", 2, 2, true, u"Hi\n, \n..."},
+      {u"Hi, my name is\nTom", 3, 2, true, u"Hi\n, \nmy\n..."},
+      {u"Hi, my name is\nTom", 4, 2, true, u"Hi\n, \nmy\n n\n..."},
+      {u"Hi, my name is\nTom", 5, 2, true, u"Hi\n, \nmy\n n\nam\n..."},
+      {u"Hi, my name is\nTom", 0, 3, true, u"..."},
+      {u"Hi, my name is\nTom", 1, 3, true, u"Hi,\n..."},
+      {u"Hi, my name is\nTom", 2, 3, true, u"Hi,\n my\n..."},
+      {u"Hi, my name is\nTom", 3, 3, true, u"Hi,\n my\n na\n..."},
+      {u"Hi, my name is\nTom", 4, 3, true, u"Hi,\n my\n na\nme \n..."},
+      {u"Hi, my name is\nTom", 5, 3, true, u"Hi,\n my\n na\nme \nis\n..."},
+      {u"Hi, my name is\nTom", 1, 4, true, u"Hi, \n..."},
+      {u"Hi, my name is\nTom", 2, 4, true, u"Hi, \nmy n\n..."},
+      {u"Hi, my name is\nTom", 3, 4, true, u"Hi, \nmy n\name \n..."},
+      {u"Hi, my name is\nTom", 4, 4, true, u"Hi, \nmy n\name \nis\n..."},
+      {u"Hi, my name is\nTom", 5, 4, false, u"Hi, \nmy n\name \nis\nTom"},
+      {u"Hi, my name is\nTom", 1, 5, true, u"Hi, \n..."},
+      {u"Hi, my name is\nTom", 2, 5, true, u"Hi, \nmy na\n..."},
+      {u"Hi, my name is\nTom", 3, 5, true, u"Hi, \nmy na\nme \n..."},
+      {u"Hi, my name is\nTom", 4, 5, true, u"Hi, \nmy na\nme \nis\n..."},
+      {u"Hi, my name is\nTom", 5, 5, false, u"Hi, \nmy na\nme \nis\nTom"},
+      {u"Hi, my name is\nTom", 1, 6, true, u"Hi, \n..."},
+      {u"Hi, my name is\nTom", 2, 6, true, u"Hi, \nmy \n..."},
+      {u"Hi, my name is\nTom", 3, 6, true, u"Hi, \nmy \nname \n..."},
+      {u"Hi, my name is\nTom", 4, 6, true, u"Hi, \nmy \nname \nis\n..."},
+      {u"Hi, my name is\nTom", 5, 6, false, u"Hi, \nmy \nname \nis\nTom"},
+      {u"Hi, my name is\nTom", 1, 7, true, u"Hi, \n..."},
+      {u"Hi, my name is\nTom", 2, 7, true, u"Hi, \nmy \n..."},
+      {u"Hi, my name is\nTom", 3, 7, true, u"Hi, \nmy \nname \n..."},
+      {u"Hi, my name is\nTom", 4, 7, true, u"Hi, \nmy \nname \nis\n..."},
+      {u"Hi, my name is\nTom", 5, 7, false, u"Hi, \nmy \nname \nis\nTom"},
+      {u"Hi, my name is\nTom", 1, 8, true, u"Hi, my \n..."},
+      {u"Hi, my name is\nTom", 2, 8, true, u"Hi, my \nname \n..."},
+      {u"Hi, my name is\nTom", 3, 8, true, u"Hi, my \nname \nis\n..."},
+      {u"Hi, my name is\nTom", 4, 8, false, u"Hi, my \nname \nis\nTom"},
+      {u"Hi, my name is\nTom", 1, 9, true, u"Hi, my \n..."},
+      {u"Hi, my name is\nTom", 2, 9, true, u"Hi, my \nname is\n..."},
+      {u"Hi, my name is\nTom", 3, 9, false, u"Hi, my \nname is\nTom"},
+      {u"Hi, my name is\nTom", 1, 10, true, u"Hi, my \n..."},
+      {u"Hi, my name is\nTom", 2, 10, true, u"Hi, my \nname is\n..."},
+      {u"Hi, my name is\nTom", 3, 10, false, u"Hi, my \nname is\nTom"},
+      {u"Hi, my name is\nTom", 1, 11, true, u"Hi, my \n..."},
+      {u"Hi, my name is\nTom", 2, 11, true, u"Hi, my \nname is\n..."},
+      {u"Hi, my name is\nTom", 3, 11, false, u"Hi, my \nname is\nTom"},
+      {u"Hi, my name is\nTom", 1, 12, true, u"Hi, my \n..."},
+      {u"Hi, my name is\nTom", 2, 12, true, u"Hi, my \nname is\n..."},
+      {u"Hi, my name is\nTom", 3, 12, false, u"Hi, my \nname is\nTom"},
+      {u"Hi, my name is\nTom", 1, 13, true, u"Hi, my name \n..."},
+      {u"Hi, my name is\nTom", 2, 13, true, u"Hi, my name \nis\n..."},
+      {u"Hi, my name is\nTom", 3, 13, false, u"Hi, my name \nis\nTom"},
+      {u"Hi, my name is\nTom", 1, 20, true, u"Hi, my name is\n..."},
+      {u"Hi, my name is\nTom", 2, 20, false, u"Hi, my name is\nTom"},
+      {u"Hi, my name is Tom", 1, 40, false, u"Hi, my name is Tom"},
   };
   std::u16string output;
   for (size_t i = 0; i < base::size(cases); ++i) {
     EXPECT_EQ(cases[i].result,
-              ElideRectangleString(UTF8ToUTF16(cases[i].input),
-                                   cases[i].max_rows, cases[i].max_cols,
-                                   true, &output));
-    EXPECT_EQ(cases[i].output, UTF16ToUTF8(output));
+              ElideRectangleString(cases[i].input, cases[i].max_rows,
+                                   cases[i].max_cols, true, &output));
+    EXPECT_EQ(cases[i].output, output);
   }
 }
 
 TEST(TextEliderTest, ElideRectangleStringNotStrict) {
   struct TestData {
-    const char* input;
+    const char16_t* input;
     int max_rows;
     int max_cols;
     bool result;
-    const char* output;
+    const char16_t* output;
   } cases[] = {
-    { "", 0, 0, false, "" },
-    { "", 1, 1, false, "" },
-    { "Hi, my name_is\nDick", 0, 0,  true,  "..." },
-    { "Hi, my name_is\nDick", 1, 0,  true,  "\n..." },
-    { "Hi, my name_is\nDick", 0, 1,  true,  "..." },
-    { "Hi, my name_is\nDick", 1, 1,  true,  "H\n..." },
-    { "Hi, my name_is\nDick", 2, 1,  true,  "H\ni\n..." },
-    { "Hi, my name_is\nDick", 3, 1,  true,  "H\ni\n,\n..." },
-    { "Hi, my name_is\nDick", 4, 1,  true,  "H\ni\n,\n \n..." },
-    { "Hi, my name_is\nDick", 5, 1,  true,  "H\ni\n,\n \nm\n..." },
-    { "Hi, my name_is\nDick", 0, 2,  true,  "..." },
-    { "Hi, my name_is\nDick", 1, 2,  true,  "Hi\n..." },
-    { "Hi, my name_is\nDick", 2, 2,  true,  "Hi\n, \n..." },
-    { "Hi, my name_is\nDick", 3, 2,  true,  "Hi\n, \nmy\n..." },
-    { "Hi, my name_is\nDick", 4, 2,  true,  "Hi\n, \nmy\n n\n..." },
-    { "Hi, my name_is\nDick", 5, 2,  true,  "Hi\n, \nmy\n n\nam\n..." },
-    { "Hi, my name_is\nDick", 0, 3,  true,  "..." },
-    { "Hi, my name_is\nDick", 1, 3,  true,  "Hi,\n..." },
-    { "Hi, my name_is\nDick", 2, 3,  true,  "Hi,\n my\n..." },
-    { "Hi, my name_is\nDick", 3, 3,  true,  "Hi,\n my\n na\n..." },
-    { "Hi, my name_is\nDick", 4, 3,  true,  "Hi,\n my\n na\nme_\n..." },
-    { "Hi, my name_is\nDick", 5, 3,  true,  "Hi,\n my\n na\nme_\nis\n..." },
-    { "Hi, my name_is\nDick", 1, 4,  true,  "Hi, ..." },
-    { "Hi, my name_is\nDick", 2, 4,  true,  "Hi, my n\n..." },
-    { "Hi, my name_is\nDick", 3, 4,  true,  "Hi, my n\name_\n..." },
-    { "Hi, my name_is\nDick", 4, 4,  true,  "Hi, my n\name_\nis\n..." },
-    { "Hi, my name_is\nDick", 5, 4,  false, "Hi, my n\name_\nis\nDick" },
-    { "Hi, my name_is\nDick", 1, 5,  true,  "Hi, ..." },
-    { "Hi, my name_is\nDick", 2, 5,  true,  "Hi, my na\n..." },
-    { "Hi, my name_is\nDick", 3, 5,  true,  "Hi, my na\nme_is\n..." },
-    { "Hi, my name_is\nDick", 4, 5,  true,  "Hi, my na\nme_is\n\n..." },
-    { "Hi, my name_is\nDick", 5, 5,  false, "Hi, my na\nme_is\n\nDick" },
-    { "Hi, my name_is\nDick", 1, 6,  true,  "Hi, ..." },
-    { "Hi, my name_is\nDick", 2, 6,  true,  "Hi, my nam\n..." },
-    { "Hi, my name_is\nDick", 3, 6,  true,  "Hi, my nam\ne_is\n..." },
-    { "Hi, my name_is\nDick", 4, 6,  false, "Hi, my nam\ne_is\nDick" },
-    { "Hi, my name_is\nDick", 5, 6,  false, "Hi, my nam\ne_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 7,  true,  "Hi, ..." },
-    { "Hi, my name_is\nDick", 2, 7,  true,  "Hi, my name\n..." },
-    { "Hi, my name_is\nDick", 3, 7,  true,  "Hi, my name\n_is\n..." },
-    { "Hi, my name_is\nDick", 4, 7,  false, "Hi, my name\n_is\nDick" },
-    { "Hi, my name_is\nDick", 5, 7,  false, "Hi, my name\n_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 8,  true,  "Hi, my n\n..." },
-    { "Hi, my name_is\nDick", 2, 8,  true,  "Hi, my n\name_is\n..." },
-    { "Hi, my name_is\nDick", 3, 8,  false, "Hi, my n\name_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 9,  true,  "Hi, my ..." },
-    { "Hi, my name_is\nDick", 2, 9,  true,  "Hi, my name_is\n..." },
-    { "Hi, my name_is\nDick", 3, 9,  false, "Hi, my name_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 10, true,  "Hi, my ..." },
-    { "Hi, my name_is\nDick", 2, 10, true,  "Hi, my name_is\n..." },
-    { "Hi, my name_is\nDick", 3, 10, false, "Hi, my name_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 11, true,  "Hi, my ..." },
-    { "Hi, my name_is\nDick", 2, 11, true,  "Hi, my name_is\n..." },
-    { "Hi, my name_is\nDick", 3, 11, false, "Hi, my name_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 12, true,  "Hi, my ..." },
-    { "Hi, my name_is\nDick", 2, 12, true,  "Hi, my name_is\n..." },
-    { "Hi, my name_is\nDick", 3, 12, false, "Hi, my name_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 13, true,  "Hi, my ..." },
-    { "Hi, my name_is\nDick", 2, 13, true,  "Hi, my name_is\n..." },
-    { "Hi, my name_is\nDick", 3, 13, false, "Hi, my name_is\nDick" },
-    { "Hi, my name_is\nDick", 1, 20, true,  "Hi, my name_is\n..." },
-    { "Hi, my name_is\nDick", 2, 20, false, "Hi, my name_is\nDick" },
-    { "Hi, my name_is Dick",  1, 40, false, "Hi, my name_is Dick" },
+      {u"", 0, 0, false, u""},
+      {u"", 1, 1, false, u""},
+      {u"Hi, my name_is\nDick", 0, 0, true, u"..."},
+      {u"Hi, my name_is\nDick", 1, 0, true, u"\n..."},
+      {u"Hi, my name_is\nDick", 0, 1, true, u"..."},
+      {u"Hi, my name_is\nDick", 1, 1, true, u"H\n..."},
+      {u"Hi, my name_is\nDick", 2, 1, true, u"H\ni\n..."},
+      {u"Hi, my name_is\nDick", 3, 1, true, u"H\ni\n,\n..."},
+      {u"Hi, my name_is\nDick", 4, 1, true, u"H\ni\n,\n \n..."},
+      {u"Hi, my name_is\nDick", 5, 1, true, u"H\ni\n,\n \nm\n..."},
+      {u"Hi, my name_is\nDick", 0, 2, true, u"..."},
+      {u"Hi, my name_is\nDick", 1, 2, true, u"Hi\n..."},
+      {u"Hi, my name_is\nDick", 2, 2, true, u"Hi\n, \n..."},
+      {u"Hi, my name_is\nDick", 3, 2, true, u"Hi\n, \nmy\n..."},
+      {u"Hi, my name_is\nDick", 4, 2, true, u"Hi\n, \nmy\n n\n..."},
+      {u"Hi, my name_is\nDick", 5, 2, true, u"Hi\n, \nmy\n n\nam\n..."},
+      {u"Hi, my name_is\nDick", 0, 3, true, u"..."},
+      {u"Hi, my name_is\nDick", 1, 3, true, u"Hi,\n..."},
+      {u"Hi, my name_is\nDick", 2, 3, true, u"Hi,\n my\n..."},
+      {u"Hi, my name_is\nDick", 3, 3, true, u"Hi,\n my\n na\n..."},
+      {u"Hi, my name_is\nDick", 4, 3, true, u"Hi,\n my\n na\nme_\n..."},
+      {u"Hi, my name_is\nDick", 5, 3, true, u"Hi,\n my\n na\nme_\nis\n..."},
+      {u"Hi, my name_is\nDick", 1, 4, true, u"Hi, ..."},
+      {u"Hi, my name_is\nDick", 2, 4, true, u"Hi, my n\n..."},
+      {u"Hi, my name_is\nDick", 3, 4, true, u"Hi, my n\name_\n..."},
+      {u"Hi, my name_is\nDick", 4, 4, true, u"Hi, my n\name_\nis\n..."},
+      {u"Hi, my name_is\nDick", 5, 4, false, u"Hi, my n\name_\nis\nDick"},
+      {u"Hi, my name_is\nDick", 1, 5, true, u"Hi, ..."},
+      {u"Hi, my name_is\nDick", 2, 5, true, u"Hi, my na\n..."},
+      {u"Hi, my name_is\nDick", 3, 5, true, u"Hi, my na\nme_is\n..."},
+      {u"Hi, my name_is\nDick", 4, 5, true, u"Hi, my na\nme_is\n\n..."},
+      {u"Hi, my name_is\nDick", 5, 5, false, u"Hi, my na\nme_is\n\nDick"},
+      {u"Hi, my name_is\nDick", 1, 6, true, u"Hi, ..."},
+      {u"Hi, my name_is\nDick", 2, 6, true, u"Hi, my nam\n..."},
+      {u"Hi, my name_is\nDick", 3, 6, true, u"Hi, my nam\ne_is\n..."},
+      {u"Hi, my name_is\nDick", 4, 6, false, u"Hi, my nam\ne_is\nDick"},
+      {u"Hi, my name_is\nDick", 5, 6, false, u"Hi, my nam\ne_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 7, true, u"Hi, ..."},
+      {u"Hi, my name_is\nDick", 2, 7, true, u"Hi, my name\n..."},
+      {u"Hi, my name_is\nDick", 3, 7, true, u"Hi, my name\n_is\n..."},
+      {u"Hi, my name_is\nDick", 4, 7, false, u"Hi, my name\n_is\nDick"},
+      {u"Hi, my name_is\nDick", 5, 7, false, u"Hi, my name\n_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 8, true, u"Hi, my n\n..."},
+      {u"Hi, my name_is\nDick", 2, 8, true, u"Hi, my n\name_is\n..."},
+      {u"Hi, my name_is\nDick", 3, 8, false, u"Hi, my n\name_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 9, true, u"Hi, my ..."},
+      {u"Hi, my name_is\nDick", 2, 9, true, u"Hi, my name_is\n..."},
+      {u"Hi, my name_is\nDick", 3, 9, false, u"Hi, my name_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 10, true, u"Hi, my ..."},
+      {u"Hi, my name_is\nDick", 2, 10, true, u"Hi, my name_is\n..."},
+      {u"Hi, my name_is\nDick", 3, 10, false, u"Hi, my name_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 11, true, u"Hi, my ..."},
+      {u"Hi, my name_is\nDick", 2, 11, true, u"Hi, my name_is\n..."},
+      {u"Hi, my name_is\nDick", 3, 11, false, u"Hi, my name_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 12, true, u"Hi, my ..."},
+      {u"Hi, my name_is\nDick", 2, 12, true, u"Hi, my name_is\n..."},
+      {u"Hi, my name_is\nDick", 3, 12, false, u"Hi, my name_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 13, true, u"Hi, my ..."},
+      {u"Hi, my name_is\nDick", 2, 13, true, u"Hi, my name_is\n..."},
+      {u"Hi, my name_is\nDick", 3, 13, false, u"Hi, my name_is\nDick"},
+      {u"Hi, my name_is\nDick", 1, 20, true, u"Hi, my name_is\n..."},
+      {u"Hi, my name_is\nDick", 2, 20, false, u"Hi, my name_is\nDick"},
+      {u"Hi, my name_is Dick", 1, 40, false, u"Hi, my name_is Dick"},
   };
   std::u16string output;
   for (size_t i = 0; i < base::size(cases); ++i) {
     EXPECT_EQ(cases[i].result,
-              ElideRectangleString(UTF8ToUTF16(cases[i].input),
-                                   cases[i].max_rows, cases[i].max_cols,
-                                   false, &output));
-    EXPECT_EQ(cases[i].output, UTF16ToUTF8(output));
+              ElideRectangleString(cases[i].input, cases[i].max_rows,
+                                   cases[i].max_cols, false, &output));
+    EXPECT_EQ(cases[i].output, output);
   }
 }
 
 TEST(TextEliderTest, ElideRectangleWide16) {
   // Two greek words separated by space.
-  const std::u16string str(
-      u"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
-      u"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2");
-  const std::u16string out1(
-      u"\x03a0\x03b1\x03b3\x03ba\n"
-      u"\x03cc\x03c3\x03bc\x03b9\n"
-      u"...");
-  const std::u16string out2(
-      u"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9\x03bf\x03c2\x0020\n"
-      u"\x0399\x03c3\x03c4\x03cc\x03c2");
+  const std::u16string str(u"Παγκόσμιος Ιστός");
+  const std::u16string out1(u"Παγκ\nόσμι\n...");
+  const std::u16string out2(u"Παγκόσμιος \nΙστός");
   std::u16string output;
   EXPECT_TRUE(ElideRectangleString(str, 2, 4, true, &output));
   EXPECT_EQ(out1, output);
@@ -1148,13 +1087,8 @@
 }
 
 TEST(TextEliderTest, ElideRectangleWide32) {
-  // Four U+1D49C MATHEMATICAL SCRIPT CAPITAL A followed by space "aaaaa".
-  const std::u16string str(UTF8ToUTF16(
-      "\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C"
-      " aaaaa"));
-  const std::u16string out(
-      UTF8ToUTF16("\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\xF0\x9D\x92\x9C\n"
-                  "\xF0\x9D\x92\x9C \naaa\n..."));
+  const std::u16string str(u"𝒜𝒜𝒜𝒜 aaaaa");
+  const std::u16string out(u"𝒜𝒜𝒜\n𝒜 \naaa\n...");
   std::u16string output;
   EXPECT_TRUE(ElideRectangleString(str, 3, 3, true, &output));
   EXPECT_EQ(out, output);
@@ -1168,36 +1102,31 @@
   EXPECT_EQ(std::u16string(), TruncateString(str, 0, CHARACTER_BREAK));
 
   // Test breaking at character 1.
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str, 1, WORD_BREAK)));
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str, 1, CHARACTER_BREAK)));
+  EXPECT_EQ(u"…", TruncateString(str, 1, WORD_BREAK));
+  EXPECT_EQ(u"…", TruncateString(str, 1, CHARACTER_BREAK));
 
   // Test breaking in the middle of the first word.
-  EXPECT_EQ(L"f\x2026", UTF16ToWide(TruncateString(str, 2, WORD_BREAK)));
-  EXPECT_EQ(L"f\x2026", UTF16ToWide(TruncateString(str, 2, CHARACTER_BREAK)));
+  EXPECT_EQ(u"f…", TruncateString(str, 2, WORD_BREAK));
+  EXPECT_EQ(u"f…", TruncateString(str, 2, CHARACTER_BREAK));
 
   // Test breaking in between words.
-  EXPECT_EQ(L"fooooey\x2026", UTF16ToWide(TruncateString(str, 9, WORD_BREAK)));
-  EXPECT_EQ(L"fooooey\x2026",
-            UTF16ToWide(TruncateString(str, 9, CHARACTER_BREAK)));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 9, WORD_BREAK));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 9, CHARACTER_BREAK));
 
   // Test breaking at the start of a later word.
-  EXPECT_EQ(L"fooooey\x2026", UTF16ToWide(TruncateString(str, 11, WORD_BREAK)));
-  EXPECT_EQ(L"fooooey\x2026",
-            UTF16ToWide(TruncateString(str, 11, CHARACTER_BREAK)));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 11, WORD_BREAK));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 11, CHARACTER_BREAK));
 
   // Test breaking in the middle of a word.
-  EXPECT_EQ(L"fooooey\x2026", UTF16ToWide(TruncateString(str, 12, WORD_BREAK)));
-  EXPECT_EQ(L"fooooey\x2026",
-            UTF16ToWide(TruncateString(str, 12, CHARACTER_BREAK)));
-  EXPECT_EQ(L"fooooey\x2026", UTF16ToWide(TruncateString(str, 14, WORD_BREAK)));
-  EXPECT_EQ(L"fooooey    bx\x2026",
-            UTF16ToWide(TruncateString(str, 14, CHARACTER_BREAK)));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 12, WORD_BREAK));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 12, CHARACTER_BREAK));
+  EXPECT_EQ(u"fooooey…", TruncateString(str, 14, WORD_BREAK));
+  EXPECT_EQ(u"fooooey    bx…", TruncateString(str, 14, CHARACTER_BREAK));
 
   // Test breaking in whitespace at the end of the string.
-  EXPECT_EQ(L"fooooey    bxxxar baz\x2026",
-            UTF16ToWide(TruncateString(str, 22, WORD_BREAK)));
-  EXPECT_EQ(L"fooooey    bxxxar baz\x2026",
-            UTF16ToWide(TruncateString(str, 22, CHARACTER_BREAK)));
+  EXPECT_EQ(u"fooooey    bxxxar baz…", TruncateString(str, 22, WORD_BREAK));
+  EXPECT_EQ(u"fooooey    bxxxar baz…",
+            TruncateString(str, 22, CHARACTER_BREAK));
 
   // Test breaking at the end of the string.
   EXPECT_EQ(str, TruncateString(str, str.length(), WORD_BREAK));
@@ -1212,19 +1141,18 @@
   std::u16string str2 = u"   foo";
 
   // Test breaking in leading whitespace.
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str2, 2, WORD_BREAK)));
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str2, 2, CHARACTER_BREAK)));
+  EXPECT_EQ(u"…", TruncateString(str2, 2, WORD_BREAK));
+  EXPECT_EQ(u"…", TruncateString(str2, 2, CHARACTER_BREAK));
 
   // Test breaking at the beginning of the first word, with leading whitespace.
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str2, 3, WORD_BREAK)));
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str2, 3, CHARACTER_BREAK)));
+  EXPECT_EQ(u"…", TruncateString(str2, 3, WORD_BREAK));
+  EXPECT_EQ(u"…", TruncateString(str2, 3, CHARACTER_BREAK));
 
   // Test breaking in the middle of the first word, with leading whitespace.
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str2, 4, WORD_BREAK)));
-  EXPECT_EQ(L"\x2026", UTF16ToWide(TruncateString(str2, 4, CHARACTER_BREAK)));
-  EXPECT_EQ(L"   f\x2026", UTF16ToWide(TruncateString(str2, 5, WORD_BREAK)));
-  EXPECT_EQ(L"   f\x2026",
-            UTF16ToWide(TruncateString(str2, 5, CHARACTER_BREAK)));
+  EXPECT_EQ(u"…", TruncateString(str2, 4, WORD_BREAK));
+  EXPECT_EQ(u"…", TruncateString(str2, 4, CHARACTER_BREAK));
+  EXPECT_EQ(u"   f…", TruncateString(str2, 5, WORD_BREAK));
+  EXPECT_EQ(u"   f…", TruncateString(str2, 5, CHARACTER_BREAK));
 }
 
 }  // namespace gfx
diff --git a/ui/message_center/notification_list_unittest.cc b/ui/message_center/notification_list_unittest.cc
index 5c181f1..c2e019dc 100644
--- a/ui/message_center/notification_list_unittest.cc
+++ b/ui/message_center/notification_list_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/i18n/time_formatting.h"
 #include "base/macros.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
@@ -21,8 +22,6 @@
 #include "ui/message_center/public/cpp/notification_types.h"
 #include "ui/message_center/public/cpp/notifier_id.h"
 
-using base::UTF8ToUTF16;
-
 namespace message_center {
 
 using NotificationState = NotificationList::NotificationState;
@@ -61,13 +60,13 @@
       const RichNotificationData& optional_fields,
       std::string* id_out) {
     *id_out = base::StringPrintf(kIdFormat, counter_);
-    std::unique_ptr<Notification> notification(new Notification(
-        NOTIFICATION_TYPE_SIMPLE, *id_out,
-        UTF8ToUTF16(base::StringPrintf(kTitleFormat, counter_)),
-        UTF8ToUTF16(base::StringPrintf(kMessageFormat, counter_)), gfx::Image(),
-        UTF8ToUTF16(kDisplaySource), GURL(),
-        NotifierId(NotifierType::APPLICATION, kExtensionId), optional_fields,
-        nullptr));
+    std::unique_ptr<Notification> notification(
+        new Notification(NOTIFICATION_TYPE_SIMPLE, *id_out,
+                         u"id" + base::NumberToString16(counter_),
+                         u"message" + base::NumberToString16(counter_),
+                         gfx::Image(), kDisplaySource, GURL(),
+                         NotifierId(NotifierType::APPLICATION, kExtensionId),
+                         optional_fields, nullptr));
     return notification;
   }
 
@@ -104,9 +103,7 @@
   }
 
   static const char kIdFormat[];
-  static const char kTitleFormat[];
-  static const char kMessageFormat[];
-  static const char kDisplaySource[];
+  static const char16_t kDisplaySource[];
   static const char kExtensionId[];
 
   std::unique_ptr<FakeMessageCenter> message_center_;
@@ -128,9 +125,7 @@
 }
 
 const char NotificationListTest::kIdFormat[] = "id%ld";
-const char NotificationListTest::kTitleFormat[] = "id%ld";
-const char NotificationListTest::kMessageFormat[] = "message%ld";
-const char NotificationListTest::kDisplaySource[] = "source";
+const char16_t NotificationListTest::kDisplaySource[] = u"source";
 const char NotificationListTest::kExtensionId[] = "ext";
 
 TEST_F(NotificationListTest, Basic) {
@@ -174,11 +169,11 @@
   std::string id0 = AddNotification();
   std::string replaced = id0 + "_replaced";
   EXPECT_EQ(1u, notification_list_->NotificationCount(blockers_));
-  std::unique_ptr<Notification> notification(new Notification(
-      NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle", u"newbody", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
-      NotifierId(NotifierType::APPLICATION, kExtensionId),
-      RichNotificationData(), nullptr));
+  std::unique_ptr<Notification> notification(
+      new Notification(NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle",
+                       u"newbody", gfx::Image(), kDisplaySource, GURL(),
+                       NotifierId(NotifierType::APPLICATION, kExtensionId),
+                       RichNotificationData(), nullptr));
   notification_list_->UpdateNotificationMessage(id0, std::move(notification));
   EXPECT_EQ(1u, notification_list_->NotificationCount(blockers_));
   const NotificationList::Notifications notifications =
@@ -465,11 +460,11 @@
     EXPECT_EQ(0u, GetPopupCounts());
 
     RichNotificationData optional;
-    std::unique_ptr<Notification> notification(new Notification(
-        NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle", u"newbody",
-        gfx::Image(), UTF8ToUTF16(kDisplaySource), GURL(),
-        NotifierId(NotifierType::APPLICATION, kExtensionId), optional,
-        nullptr));
+    std::unique_ptr<Notification> notification(
+        new Notification(NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle",
+                         u"newbody", gfx::Image(), kDisplaySource, GURL(),
+                         NotifierId(NotifierType::APPLICATION, kExtensionId),
+                         optional, nullptr));
     notification_list_->UpdateNotificationMessage(id0, std::move(notification));
     EXPECT_EQ(1u, notification_list_->NotificationCount(blockers_));
     EXPECT_EQ(has_message_center_view ? 0U : 1U, GetPopupCounts());
@@ -498,7 +493,7 @@
   optional.renotify = true;
   std::unique_ptr<Notification> notification(new Notification(
       NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle", u"newbody", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
+      kDisplaySource, GURL(),
       NotifierId(NotifierType::APPLICATION, kExtensionId), optional, nullptr));
   notification_list_->UpdateNotificationMessage(id0, std::move(notification));
   EXPECT_EQ(1u, notification_list_->NotificationCount(blockers_));
@@ -522,7 +517,7 @@
   priority.priority = DEFAULT_PRIORITY;
   std::unique_ptr<Notification> notification(new Notification(
       NOTIFICATION_TYPE_SIMPLE, id0, u"newtitle", u"newbody", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
+      kDisplaySource, GURL(),
       NotifierId(NotifierType::APPLICATION, kExtensionId), priority, nullptr));
   notification_list_->UpdateNotificationMessage(id0, std::move(notification));
   EXPECT_EQ(1u, GetPopupCounts());
@@ -532,7 +527,7 @@
   // update with no promotion change for id0, it won't appear as a toast.
   notification = std::make_unique<Notification>(
       NOTIFICATION_TYPE_SIMPLE, id0, u"newtitle2", u"newbody2", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
+      kDisplaySource, GURL(),
       NotifierId(NotifierType::APPLICATION, kExtensionId), priority, nullptr);
   notification_list_->UpdateNotificationMessage(id0, std::move(notification));
   EXPECT_EQ(0u, GetPopupCounts());
@@ -541,7 +536,7 @@
   priority.priority = HIGH_PRIORITY;
   notification = std::make_unique<Notification>(
       NOTIFICATION_TYPE_SIMPLE, id1, u"newtitle", u"newbody", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
+      kDisplaySource, GURL(),
       NotifierId(NotifierType::APPLICATION, kExtensionId), priority, nullptr);
   notification_list_->UpdateNotificationMessage(id1, std::move(notification));
   EXPECT_EQ(0u, GetPopupCounts());
@@ -550,7 +545,7 @@
   priority.renotify = true;
   notification = std::make_unique<Notification>(
       NOTIFICATION_TYPE_SIMPLE, id1, u"newtitle", u"newbody", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
+      kDisplaySource, GURL(),
       NotifierId(NotifierType::APPLICATION, kExtensionId), priority, nullptr);
   notification_list_->UpdateNotificationMessage(id1, std::move(notification));
   EXPECT_EQ(1u, GetPopupCounts());
@@ -648,11 +643,11 @@
   EXPECT_TRUE(n1_state.is_read);
 
   const std::string replaced("test-replaced-id");
-  std::unique_ptr<Notification> notification(new Notification(
-      NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle", u"newbody", gfx::Image(),
-      UTF8ToUTF16(kDisplaySource), GURL(),
-      NotifierId(NotifierType::APPLICATION, kExtensionId),
-      RichNotificationData(), nullptr));
+  std::unique_ptr<Notification> notification(
+      new Notification(NOTIFICATION_TYPE_SIMPLE, replaced, u"newtitle",
+                       u"newbody", gfx::Image(), kDisplaySource, GURL(),
+                       NotifierId(NotifierType::APPLICATION, kExtensionId),
+                       RichNotificationData(), nullptr));
   notification_list_->UpdateNotificationMessage(id1, std::move(notification));
   Notification* n1 = GetNotification(id1);
   EXPECT_TRUE(n1 == nullptr);
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc
index 78a5ddd5..f27858b8 100644
--- a/ui/message_center/views/notification_view_md.cc
+++ b/ui/message_center/views/notification_view_md.cc
@@ -357,9 +357,9 @@
   auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
 
-  SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
-  SetInkDropVisibleOpacity(1);
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
+  ink_drop()->SetVisibleOpacity(1);
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](views::View* host) {
         return host->GetNativeTheme()->GetSystemColor(
             ui::NativeTheme::kColorId_NotificationInkDropBase);
@@ -387,8 +387,9 @@
 void NotificationInputContainerMD::AnimateBackground(const ui::Event& event) {
   std::unique_ptr<ui::Event> located_event =
       ConvertToBoundedLocatedEvent(event, this);
-  AnimateInkDrop(views::InkDropState::ACTION_PENDING,
-                 ui::LocatedEvent::FromIfValid(located_event.get()));
+  ink_drop()->AnimateToState(
+      views::InkDropState::ACTION_PENDING,
+      ui::LocatedEvent::FromIfValid(located_event.get()));
 }
 
 void NotificationInputContainerMD::AddLayerBeneathView(ui::Layer* layer) {
@@ -489,7 +490,7 @@
  public:
   NotificationInkDropImpl(views::InkDropHostView* ink_drop_host,
                           const gfx::Size& host_size)
-      : views::InkDropImpl(ink_drop_host, host_size) {
+      : views::InkDropImpl(ink_drop_host->ink_drop(), host_size) {
     SetAutoHighlightMode(views::InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE);
   }
 
@@ -567,20 +568,21 @@
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
 
-  SetInkDropVisibleOpacity(1.0f);
-  SetCreateInkDropCallback(base::BindRepeating(
+  ink_drop()->SetVisibleOpacity(1.0f);
+  ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
       [](InkDropHostView* host) -> std::unique_ptr<views::InkDrop> {
         return std::make_unique<NotificationInkDropImpl>(host, host->size());
       },
       this));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
         return std::make_unique<views::FloodFillInkDropRipple>(
-            host->size(), host->GetInkDropCenterBasedOnLastEvent(),
-            host->GetInkDropBaseColor(), host->GetInkDropVisibleOpacity());
+            host->size(), host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetBaseColor(),
+            host->ink_drop()->GetVisibleOpacity());
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](views::View* host) {
         return host->GetNativeTheme()->GetSystemColor(
             ui::NativeTheme::kColorId_NotificationBackgroundActive);
@@ -672,7 +674,7 @@
 }
 
 void NotificationViewMD::AddLayerBeneathView(ui::Layer* layer) {
-  GetInkDrop()->AddObserver(this);
+  ink_drop()->GetInkDrop()->AddObserver(this);
   for (auto* child : GetChildrenForLayerAdjustment()) {
     child->SetPaintToLayer();
     child->layer()->SetFillsBoundsOpaquely(false);
@@ -684,7 +686,7 @@
   ink_drop_container_->RemoveLayerBeneathView(layer);
   for (auto* child : GetChildrenForLayerAdjustment())
     child->DestroyLayer();
-  GetInkDrop()->RemoveObserver(this);
+  ink_drop()->GetInkDrop()->RemoveObserver(this);
 }
 
 void NotificationViewMD::Layout() {
@@ -1474,16 +1476,17 @@
 }
 
 void NotificationViewMD::AddBackgroundAnimation(const ui::Event& event) {
-  SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
   std::unique_ptr<ui::Event> located_event =
       ConvertToBoundedLocatedEvent(event, this);
-  AnimateInkDrop(views::InkDropState::ACTION_PENDING,
-                 ui::LocatedEvent::FromIfValid(located_event.get()));
+  ink_drop()->AnimateToState(
+      views::InkDropState::ACTION_PENDING,
+      ui::LocatedEvent::FromIfValid(located_event.get()));
 }
 
 void NotificationViewMD::RemoveBackgroundAnimation() {
-  SetInkDropMode(InkDropMode::OFF);
-  AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
+  ink_drop()->AnimateToState(views::InkDropState::HIDDEN, nullptr);
 }
 
 std::vector<views::View*> NotificationViewMD::GetChildrenForLayerAdjustment()
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc
index 334981b..d80b651 100644
--- a/ui/message_center/views/notification_view_md_unittest.cc
+++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -226,7 +226,8 @@
   DCHECK(notification_view_ || delete_on_preferred_size_changed_ ||
          delete_on_notification_removed_);
   if (notification_view_) {
-    notification_view_->SetInkDropMode(MessageView::InkDropMode::OFF);
+    notification_view_->ink_drop()->SetMode(
+        views::InkDropHost::InkDropMode::OFF);
     static_cast<views::View*>(notification_view_)->RemoveObserver(this);
     notification_view_->GetWidget()->Close();
     notification_view_ = nullptr;
@@ -1274,14 +1275,14 @@
   generator.ClickLeftButton();
   EXPECT_TRUE(notification_view()->settings_row_->GetVisible());
 
-  notification_view()->GetInkDrop()->AddObserver(this);
+  notification_view()->ink_drop()->GetInkDrop()->AddObserver(this);
 
   // Resize the widget by 1px to simulate the expand animation.
   gfx::Rect size = notification_view()->GetWidget()->GetWindowBoundsInScreen();
   size.Inset(0, 0, 0, 1);
   notification_view()->GetWidget()->SetBounds(size);
 
-  notification_view()->GetInkDrop()->RemoveObserver(this);
+  notification_view()->ink_drop()->GetInkDrop()->RemoveObserver(this);
 
   // The ink drop animation should still be running.
   EXPECT_FALSE(ink_drop_stopped());
@@ -1311,8 +1312,8 @@
   // Toggle inline settings to show ink drop background.
   notification_view()->ToggleInlineSettings(DummyEvent());
 
-  auto* ink_drop =
-      static_cast<views::InkDropImpl*>(notification_view()->GetInkDrop());
+  auto* ink_drop = static_cast<views::InkDropImpl*>(
+      notification_view()->ink_drop()->GetInkDrop());
   views::test::InkDropImplTestApi ink_drop_test_api(ink_drop);
   gfx::Rect clip_rect = ink_drop_test_api.GetRootLayer()->clip_rect();
 
diff --git a/ui/message_center/views/padded_button.cc b/ui/message_center/views/padded_button.cc
index 661624a9..515af01 100644
--- a/ui/message_center/views/padded_button.cc
+++ b/ui/message_center/views/padded_button.cc
@@ -26,10 +26,10 @@
   SetBorder(views::CreateEmptyBorder(gfx::Insets(kControlButtonBorderSize)));
   SetAnimateOnStateChange(false);
 
-  SetInkDropMode(InkDropMode::ON);
-  SetInkDropVisibleOpacity(0.12f);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetVisibleOpacity(0.12f);
   SetHasInkDropActionOnClick(true);
-  views::InkDrop::UseInkDropForSquareRipple(this,
+  views::InkDrop::UseInkDropForSquareRipple(ink_drop(),
                                             /*highlight_on_hover=*/false);
 }
 
@@ -44,7 +44,8 @@
   SkColor background_color =
       theme->GetSystemColor(ui::NativeTheme::kColorId_WindowBackground);
 #endif
-  SetInkDropBaseColor(color_utils::GetColorWithMaxContrast(background_color));
+  ink_drop()->SetBaseColor(
+      color_utils::GetColorWithMaxContrast(background_color));
 }
 
 BEGIN_METADATA(PaddedButton, views::ImageButton)
diff --git a/ui/views/animation/ink_drop.cc b/ui/views/animation/ink_drop.cc
index 823198e..7915c9b1 100644
--- a/ui/views/animation/ink_drop.cc
+++ b/ui/views/animation/ink_drop.cc
@@ -21,11 +21,12 @@
 // TODO(pbos): Remove this by changing the constructor parameters to
 // InkDropImpl.
 std::unique_ptr<InkDrop> CreateInkDropImpl(
-    InkDropHostView* host,
+    InkDropHost* host,
     InkDropImpl::AutoHighlightMode auto_highlight_mode,
     bool highlight_on_hover,
     bool highlight_on_focus) {
-  auto ink_drop = std::make_unique<InkDropImpl>(host, host->size());
+  auto ink_drop =
+      std::make_unique<InkDropImpl>(host, host->host_view()->size());
   ink_drop->SetAutoHighlightMode(auto_highlight_mode);
   ink_drop->SetShowHighlightOnHover(highlight_on_hover);
   ink_drop->SetShowHighlightOnFocus(highlight_on_focus);
@@ -37,14 +38,14 @@
 InkDrop::~InkDrop() = default;
 
 std::unique_ptr<InkDrop> InkDrop::CreateInkDropForSquareRipple(
-    InkDropHostView* host,
+    InkDropHost* host,
     bool highlight_on_hover,
     bool highlight_on_focus) {
   return CreateInkDropImpl(host, InkDropImpl::AutoHighlightMode::HIDE_ON_RIPPLE,
                            highlight_on_hover, highlight_on_focus);
 }
 
-void InkDrop::UseInkDropForSquareRipple(InkDropHostView* host,
+void InkDrop::UseInkDropForSquareRipple(InkDropHost* host,
                                         bool highlight_on_hover,
                                         bool highlight_on_focus) {
   host->SetCreateInkDropCallback(
@@ -53,14 +54,14 @@
 }
 
 std::unique_ptr<InkDrop> InkDrop::CreateInkDropForFloodFillRipple(
-    InkDropHostView* host,
+    InkDropHost* host,
     bool highlight_on_hover,
     bool highlight_on_focus) {
   return CreateInkDropImpl(host, InkDropImpl::AutoHighlightMode::SHOW_ON_RIPPLE,
                            highlight_on_hover, highlight_on_focus);
 }
 
-void InkDrop::UseInkDropForFloodFillRipple(InkDropHostView* host,
+void InkDrop::UseInkDropForFloodFillRipple(InkDropHost* host,
                                            bool highlight_on_hover,
                                            bool highlight_on_focus) {
   host->SetCreateInkDropCallback(
@@ -69,14 +70,14 @@
 }
 
 std::unique_ptr<InkDrop> InkDrop::CreateInkDropWithoutAutoHighlight(
-    InkDropHostView* host,
+    InkDropHost* host,
     bool highlight_on_hover,
     bool highlight_on_focus) {
   return CreateInkDropImpl(host, InkDropImpl::AutoHighlightMode::NONE,
                            highlight_on_hover, highlight_on_focus);
 }
 
-void InkDrop::UseInkDropWithoutAutoHighlight(InkDropHostView* host,
+void InkDrop::UseInkDropWithoutAutoHighlight(InkDropHost* host,
                                              bool highlight_on_hover,
                                              bool highlight_on_focus) {
   host->SetCreateInkDropCallback(
diff --git a/ui/views/animation/ink_drop.h b/ui/views/animation/ink_drop.h
index 91e6e4b5..50e171c 100644
--- a/ui/views/animation/ink_drop.h
+++ b/ui/views/animation/ink_drop.h
@@ -20,8 +20,8 @@
 
 namespace views {
 
+class InkDropHost;
 class InkDropObserver;
-class InkDropHostView;
 
 // Base class that manages the lifetime and state of an ink drop ripple as
 // well as visual hover state feedback.
@@ -35,35 +35,35 @@
   // InkDrop hides when the ripple effect is active instead of layering
   // underneath it.
   static std::unique_ptr<InkDrop> CreateInkDropForSquareRipple(
-      InkDropHostView* host,
+      InkDropHost* host,
       bool highlight_on_hover = true,
       bool highlight_on_focus = false);
 
   // Configure `host` to use CreateInkDropForSquareRipple().
-  static void UseInkDropForSquareRipple(InkDropHostView* host,
+  static void UseInkDropForSquareRipple(InkDropHost* host,
                                         bool highlight_on_hover = true,
                                         bool highlight_on_focus = false);
 
   // Create an InkDrop appropriate for the "flood-fill" InkDropRipple effect.
   // This InkDrop shows as a response to the ripple effect.
   static std::unique_ptr<InkDrop> CreateInkDropForFloodFillRipple(
-      InkDropHostView* host,
+      InkDropHost* host,
       bool highlight_on_hover = true,
       bool highlight_on_focus = false);
 
   // Configure `host` to use CreateInkDropForFloodFillRipple().
-  static void UseInkDropForFloodFillRipple(InkDropHostView* host,
+  static void UseInkDropForFloodFillRipple(InkDropHost* host,
                                            bool highlight_on_hover = true,
                                            bool highlight_on_focus = false);
 
   // Create an InkDrop whose highlight does not react to its ripple.
   static std::unique_ptr<InkDrop> CreateInkDropWithoutAutoHighlight(
-      InkDropHostView* host,
+      InkDropHost* host,
       bool highlight_on_hover = true,
       bool highlight_on_focus = false);
 
   // Configure `host` to use CreateInkDropWithoutAutoHighlight().
-  static void UseInkDropWithoutAutoHighlight(InkDropHostView* host,
+  static void UseInkDropWithoutAutoHighlight(InkDropHost* host,
                                              bool highlight_on_hover = true,
                                              bool highlight_on_focus = false);
 
diff --git a/ui/views/animation/ink_drop_event_handler.cc b/ui/views/animation/ink_drop_event_handler.cc
index d0771c2..5e89d08 100644
--- a/ui/views/animation/ink_drop_event_handler.cc
+++ b/ui/views/animation/ink_drop_event_handler.cc
@@ -23,7 +23,7 @@
 
 InkDropEventHandler::~InkDropEventHandler() = default;
 
-void InkDropEventHandler::AnimateInkDrop(InkDropState state,
+void InkDropEventHandler::AnimateToState(InkDropState state,
                                          const ui::LocatedEvent* event) {
 #if defined(OS_WIN)
   // On Windows, don't initiate ink-drops for touch/gesture events.
@@ -93,7 +93,7 @@
     // case would prematurely pre-empt these animations.
     return;
   }
-  AnimateInkDrop(ink_drop_state, event);
+  AnimateToState(ink_drop_state, event);
 }
 
 void InkDropEventHandler::OnMouseEvent(ui::MouseEvent* event) {
diff --git a/ui/views/animation/ink_drop_event_handler.h b/ui/views/animation/ink_drop_event_handler.h
index ebc95790..51e97dff 100644
--- a/ui/views/animation/ink_drop_event_handler.h
+++ b/ui/views/animation/ink_drop_event_handler.h
@@ -44,7 +44,7 @@
   InkDropEventHandler(View* host_view, Delegate* delegate);
   ~InkDropEventHandler() override;
 
-  void AnimateInkDrop(InkDropState state, const ui::LocatedEvent* event);
+  void AnimateToState(InkDropState state, const ui::LocatedEvent* event);
   ui::LocatedEvent* GetLastRippleTriggeringEvent() const;
 
  private:
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index 9883bdb..ab0b9eb 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -26,122 +26,117 @@
 namespace views {
 
 // static
-constexpr gfx::Size InkDropHostView::kDefaultSquareInkDropSize;
+constexpr gfx::Size InkDropHost::kDefaultSquareInkDropSize;
 
-InkDropHostView::InkDropHostViewEventHandlerDelegate::
-    InkDropHostViewEventHandlerDelegate(InkDropHostView* host_view)
-    : host_view_(host_view) {}
+InkDropHost::InkDropHostEventHandlerDelegate::InkDropHostEventHandlerDelegate(
+    InkDropHost* ink_drop_host)
+    : ink_drop_host_(ink_drop_host) {}
 
-bool InkDropHostView::InkDropHostViewEventHandlerDelegate::HasInkDrop() const {
-  return host_view_->HasInkDrop();
+bool InkDropHost::InkDropHostEventHandlerDelegate::HasInkDrop() const {
+  return ink_drop_host_->HasInkDrop();
 }
 
-InkDrop* InkDropHostView::InkDropHostViewEventHandlerDelegate::GetInkDrop() {
-  return host_view_->GetInkDrop();
+InkDrop* InkDropHost::InkDropHostEventHandlerDelegate::GetInkDrop() {
+  return ink_drop_host_->GetInkDrop();
 }
 
-bool InkDropHostView::InkDropHostViewEventHandlerDelegate::
-    SupportsGestureEvents() const {
-  return host_view_->ink_drop_mode_ == InkDropMode::ON;
+bool InkDropHost::InkDropHostEventHandlerDelegate::SupportsGestureEvents()
+    const {
+  return ink_drop_host_->ink_drop_mode_ == InkDropMode::ON;
 }
 
-InkDropHostView::ViewLayerTransformObserver::ViewLayerTransformObserver(
-    InkDropHostView* host)
-    : host_(host) {
-  observation_.Observe(host);
+InkDropHost::ViewLayerTransformObserver::ViewLayerTransformObserver(
+    InkDropHost* ink_drop_host,
+    View* host_view)
+    : ink_drop_host_(ink_drop_host) {
+  observation_.Observe(host_view);
 }
 
-InkDropHostView::ViewLayerTransformObserver::~ViewLayerTransformObserver() =
+InkDropHost::ViewLayerTransformObserver::~ViewLayerTransformObserver() =
     default;
 
-void InkDropHostView::ViewLayerTransformObserver::OnViewLayerTransformed(
+void InkDropHost::ViewLayerTransformObserver::OnViewLayerTransformed(
     View* observed_view) {
-  DCHECK_EQ(observed_view, host_);
   // Notify the ink drop that we have transformed so it can adapt
   // accordingly.
-  if (host_->HasInkDrop())
-    host_->GetInkDrop()->HostTransformChanged(host_->GetTransform());
+  if (ink_drop_host_->HasInkDrop()) {
+    ink_drop_host_->GetInkDrop()->HostTransformChanged(
+        observed_view->GetTransform());
+  }
 }
 
-InkDropHostView::InkDropHostView()
-    : host_view_transform_observer_(this),
+InkDropHost::InkDropHost(View* view)
+    : host_view_(view),
+      host_view_transform_observer_(this, view),
       ink_drop_event_handler_delegate_(this),
-      ink_drop_event_handler_(this, &ink_drop_event_handler_delegate_) {}
+      ink_drop_event_handler_(view, &ink_drop_event_handler_delegate_) {}
 
-InkDropHostView::~InkDropHostView() {
+InkDropHost::~InkDropHost() {
   // TODO(bruthig): Improve InkDropImpl to be safer about calling back to
   // potentially destroyed InkDropHosts and remove |destroying_|.
   destroying_ = true;
 }
 
-void InkDropHostView::SetAddInkDropLayerCallback(
+void InkDropHost::SetAddLayerCallback(
     base::RepeatingCallback<void(ui::Layer*)> callback) {
   add_ink_drop_layer_callback_ = std::move(callback);
 }
 
 const base::RepeatingCallback<void(ui::Layer*)>&
-InkDropHostView::GetAddInkDropLayerCallback() const {
+InkDropHost::GetAddLayerCallbackForTesting() const {
   return add_ink_drop_layer_callback_;
 }
 
-void InkDropHostView::SetRemoveInkDropLayerCallback(
+void InkDropHost::SetRemoveLayerCallback(
     base::RepeatingCallback<void(ui::Layer*)> callback) {
   remove_ink_drop_layer_callback_ = std::move(callback);
 }
 
 const base::RepeatingCallback<void(ui::Layer*)>&
-InkDropHostView::GetRemoveInkDropLayerCallback() const {
+InkDropHost::GetRemoveLayerCallbackForTesting() const {
   return remove_ink_drop_layer_callback_;
 }
 
-std::unique_ptr<InkDrop> InkDropHostView::CreateInkDrop() {
+std::unique_ptr<InkDrop> InkDropHost::CreateInkDrop() {
   if (create_ink_drop_callback_)
     return create_ink_drop_callback_.Run();
   return InkDrop::CreateInkDropForFloodFillRipple(this);
 }
 
-void InkDropHostView::SetCreateInkDropCallback(
+void InkDropHost::SetCreateInkDropCallback(
     base::RepeatingCallback<std::unique_ptr<InkDrop>()> callback) {
   create_ink_drop_callback_ = std::move(callback);
 }
 
-const base::RepeatingCallback<std::unique_ptr<InkDrop>()>&
-InkDropHostView::GetCreateInkDropCallback() const {
-  return create_ink_drop_callback_;
-}
-
-std::unique_ptr<InkDropRipple> InkDropHostView::CreateInkDropRipple() const {
+std::unique_ptr<InkDropRipple> InkDropHost::CreateInkDropRipple() const {
   if (create_ink_drop_ripple_callback_)
     return create_ink_drop_ripple_callback_.Run();
   return std::make_unique<views::FloodFillInkDropRipple>(
-      size(), gfx::Insets(), GetInkDropCenterBasedOnLastEvent(),
-      GetInkDropBaseColor(), GetInkDropVisibleOpacity());
+      host_view_->size(), gfx::Insets(), GetInkDropCenterBasedOnLastEvent(),
+      GetBaseColor(), GetVisibleOpacity());
 }
 
-void InkDropHostView::SetCreateInkDropRippleCallback(
+void InkDropHost::SetCreateRippleCallback(
     base::RepeatingCallback<std::unique_ptr<InkDropRipple>()> callback) {
   create_ink_drop_ripple_callback_ = std::move(callback);
 }
 
-const base::RepeatingCallback<std::unique_ptr<InkDropRipple>()>&
-InkDropHostView::GetCreateInkDropRippleCallback() const {
-  return create_ink_drop_ripple_callback_;
-}
-
-gfx::Point InkDropHostView::GetInkDropCenterBasedOnLastEvent() const {
+gfx::Point InkDropHost::GetInkDropCenterBasedOnLastEvent() const {
   return GetEventHandler()->GetLastRippleTriggeringEvent()
              ? GetEventHandler()->GetLastRippleTriggeringEvent()->location()
-             : GetMirroredRect(GetContentsBounds()).CenterPoint();
+             : host_view_->GetMirroredRect(host_view_->GetContentsBounds())
+                   .CenterPoint();
 }
 
-std::unique_ptr<InkDropHighlight> InkDropHostView::CreateInkDropHighlight()
-    const {
+std::unique_ptr<InkDropHighlight> InkDropHost::CreateInkDropHighlight() const {
   if (create_ink_drop_highlight_callback_)
     return create_ink_drop_highlight_callback_.Run();
 
   auto highlight = std::make_unique<views::InkDropHighlight>(
-      size(), 0, gfx::RectF(GetMirroredRect(GetLocalBounds())).CenterPoint(),
-      GetInkDropBaseColor());
+      host_view_->size(), 0,
+      gfx::RectF(host_view_->GetMirroredRect(host_view_->GetLocalBounds()))
+          .CenterPoint(),
+      GetBaseColor());
   // TODO(pbos): Once |ink_drop_highlight_opacity_| is either always set or
   // callers are using the default InkDropHighlight value then make this a
   // constructor argument to InkDropHighlight.
@@ -151,114 +146,90 @@
   return highlight;
 }
 
-void InkDropHostView::SetCreateInkDropHighlightCallback(
+void InkDropHost::SetCreateHighlightCallback(
     base::RepeatingCallback<std::unique_ptr<InkDropHighlight>()> callback) {
   create_ink_drop_highlight_callback_ = std::move(callback);
 }
 
-const base::RepeatingCallback<std::unique_ptr<InkDropHighlight>()>&
-InkDropHostView::GetCreateInkDropHighlightCallback() const {
-  return create_ink_drop_highlight_callback_;
-}
-
-std::unique_ptr<views::InkDropMask> InkDropHostView::CreateInkDropMask() const {
+std::unique_ptr<views::InkDropMask> InkDropHost::CreateInkDropMask() const {
   if (create_ink_drop_mask_callback_)
     return create_ink_drop_mask_callback_.Run();
-  return std::make_unique<views::PathInkDropMask>(size(),
-                                                  GetHighlightPath(this));
+  return std::make_unique<views::PathInkDropMask>(host_view_->size(),
+                                                  GetHighlightPath(host_view_));
 }
 
-void InkDropHostView::SetCreateInkDropMaskCallback(
+void InkDropHost::SetCreateMaskCallback(
     base::RepeatingCallback<std::unique_ptr<InkDropMask>()> callback) {
   create_ink_drop_mask_callback_ = std::move(callback);
 }
 
-const base::RepeatingCallback<std::unique_ptr<InkDropMask>()>&
-InkDropHostView::GetCreateInkDropMaskCallback() const {
-  return create_ink_drop_mask_callback_;
-}
-
-SkColor InkDropHostView::GetInkDropBaseColor() const {
+SkColor InkDropHost::GetBaseColor() const {
   if (ink_drop_base_color_callback_)
     return ink_drop_base_color_callback_.Run();
   DCHECK(ink_drop_base_color_);
   return ink_drop_base_color_.value_or(gfx::kPlaceholderColor);
 }
 
-void InkDropHostView::SetInkDropBaseColor(SkColor color) {
+void InkDropHost::SetBaseColor(SkColor color) {
   ink_drop_base_color_ = color;
 }
 
-void InkDropHostView::SetInkDropBaseColorCallback(
+void InkDropHost::SetBaseColorCallback(
     base::RepeatingCallback<SkColor()> callback) {
   ink_drop_base_color_callback_ = std::move(callback);
 }
 
-const base::RepeatingCallback<SkColor()>&
-InkDropHostView::GetInkDropBaseColorCallback() const {
-  return ink_drop_base_color_callback_;
-}
-
-void InkDropHostView::SetInkDropMode(InkDropMode ink_drop_mode) {
+void InkDropHost::SetMode(InkDropMode ink_drop_mode) {
   ink_drop_mode_ = ink_drop_mode;
   ink_drop_ = nullptr;
 }
 
-void InkDropHostView::SetInkDropVisibleOpacity(float visible_opacity) {
+void InkDropHost::SetVisibleOpacity(float visible_opacity) {
   if (visible_opacity == ink_drop_visible_opacity_)
     return;
   ink_drop_visible_opacity_ = visible_opacity;
-  OnPropertyChanged(&ink_drop_visible_opacity_, kPropertyEffectsPaint);
 }
 
-float InkDropHostView::GetInkDropVisibleOpacity() const {
+float InkDropHost::GetVisibleOpacity() const {
   return ink_drop_visible_opacity_;
 }
 
-void InkDropHostView::SetInkDropHighlightOpacity(
-    base::Optional<float> opacity) {
+void InkDropHost::SetHighlightOpacity(base::Optional<float> opacity) {
   if (opacity == ink_drop_highlight_opacity_)
     return;
   ink_drop_highlight_opacity_ = opacity;
-  OnPropertyChanged(&ink_drop_highlight_opacity_, kPropertyEffectsPaint);
 }
 
-base::Optional<float> InkDropHostView::GetInkDropHighlightOpacity() const {
-  return ink_drop_highlight_opacity_;
-}
-
-void InkDropHostView::SetInkDropSmallCornerRadius(int small_radius) {
+void InkDropHost::SetSmallCornerRadius(int small_radius) {
   if (small_radius == ink_drop_small_corner_radius_)
     return;
   ink_drop_small_corner_radius_ = small_radius;
-  OnPropertyChanged(&ink_drop_small_corner_radius_, kPropertyEffectsLayout);
 }
 
-int InkDropHostView::GetInkDropSmallCornerRadius() const {
+int InkDropHost::GetSmallCornerRadius() const {
   return ink_drop_small_corner_radius_;
 }
 
-void InkDropHostView::SetInkDropLargeCornerRadius(int large_radius) {
+void InkDropHost::SetLargeCornerRadius(int large_radius) {
   if (large_radius == ink_drop_large_corner_radius_)
     return;
   ink_drop_large_corner_radius_ = large_radius;
-  OnPropertyChanged(&ink_drop_large_corner_radius_, kPropertyEffectsLayout);
 }
 
-int InkDropHostView::GetInkDropLargeCornerRadius() const {
+int InkDropHost::GetLargeCornerRadius() const {
   return ink_drop_large_corner_radius_;
 }
 
-void InkDropHostView::AnimateInkDrop(InkDropState state,
-                                     const ui::LocatedEvent* event) {
-  GetEventHandler()->AnimateInkDrop(state, event);
+void InkDropHost::AnimateToState(InkDropState state,
+                                 const ui::LocatedEvent* event) {
+  GetEventHandler()->AnimateToState(state, event);
 }
 
-bool InkDropHostView::HasInkDrop() const {
+bool InkDropHost::HasInkDrop() const {
   return !!ink_drop_;
 }
 
-InkDrop* InkDropHostView::GetInkDrop() {
+InkDrop* InkDropHost::GetInkDrop() {
   if (!ink_drop_) {
     if (ink_drop_mode_ == InkDropMode::OFF)
       ink_drop_ = std::make_unique<InkDropStub>();
@@ -268,25 +239,20 @@
   return ink_drop_.get();
 }
 
-bool InkDropHostView::GetHighlighted() const {
+bool InkDropHost::GetHighlighted() const {
   return ink_drop_ && ink_drop_->IsHighlightFadingInOrVisible();
 }
 
-base::CallbackListSubscription InkDropHostView::AddHighlightedChangedCallback(
-    PropertyChangedCallback callback) {
-  // Since the highlight state is not directly represented by a member, use the
-  // applicable member (|ink_drop_|) as the property key.  Note that this won't
-  // suffice if a future InkDrop-related property is added.
-  return AddPropertyChangedCallback(&ink_drop_, std::move(callback));
+base::CallbackListSubscription InkDropHost::AddHighlightedChangedCallback(
+    base::RepeatingClosure callback) {
+  return highlighted_changed_callbacks_.Add(std::move(callback));
 }
 
-void InkDropHostView::OnInkDropHighlightedChanged() {
-  // See comments in AddHighlightedChangedCallback() re: using |ink_drop_| as
-  // the key.
-  OnPropertyChanged(&ink_drop_, kPropertyEffectsNone);
+void InkDropHost::OnInkDropHighlightedChanged() {
+  highlighted_changed_callbacks_.Notify();
 }
 
-void InkDropHostView::AddInkDropLayer(ui::Layer* ink_drop_layer) {
+void InkDropHost::AddInkDropLayer(ui::Layer* ink_drop_layer) {
   if (add_ink_drop_layer_callback_) {
     add_ink_drop_layer_callback_.Run(ink_drop_layer);
     return;
@@ -295,10 +261,10 @@
   // If a clip is provided, use that as it is more performant than a mask.
   if (!AddInkDropClip(ink_drop_layer))
     InstallInkDropMask(ink_drop_layer);
-  AddLayerBeneathView(ink_drop_layer);
+  host_view_->AddLayerBeneathView(ink_drop_layer);
 }
 
-void InkDropHostView::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
+void InkDropHost::RemoveInkDropLayer(ui::Layer* ink_drop_layer) {
   // No need to do anything when called during shutdown, and if a derived
   // class has set `remove_ink_drop_layer_callback_` then running that callback
   // is very likely to be a use-after-free.
@@ -310,7 +276,7 @@
     return;
   }
 
-  RemoveLayerBeneathView(ink_drop_layer);
+  host_view_->RemoveLayerBeneathView(ink_drop_layer);
 
   // Remove clipping.
   ink_drop_layer->SetClipRect(gfx::Rect());
@@ -320,32 +286,32 @@
   ink_drop_mask_.reset();
 }
 
-std::unique_ptr<InkDropRipple> InkDropHostView::CreateSquareInkDropRipple(
+std::unique_ptr<InkDropRipple> InkDropHost::CreateSquareRipple(
     const gfx::Point& center_point,
     const gfx::Size& size) const {
   constexpr float kLargeInkDropScale = 1.333f;
   const gfx::Size large_size = gfx::ScaleToCeiledSize(size, kLargeInkDropScale);
   auto ripple = std::make_unique<SquareInkDropRipple>(
       large_size, ink_drop_large_corner_radius_, size,
-      ink_drop_small_corner_radius_, center_point, GetInkDropBaseColor(),
-      GetInkDropVisibleOpacity());
+      ink_drop_small_corner_radius_, center_point, GetBaseColor(),
+      GetVisibleOpacity());
   return ripple;
 }
 
-const InkDropEventHandler* InkDropHostView::GetEventHandler() const {
+const InkDropEventHandler* InkDropHost::GetEventHandler() const {
   if (ink_drop_event_handler_override_)
     return ink_drop_event_handler_override_;
   return &ink_drop_event_handler_;
 }
 
-InkDropEventHandler* InkDropHostView::GetEventHandler() {
+InkDropEventHandler* InkDropHost::GetEventHandler() {
   return const_cast<InkDropEventHandler*>(
-      const_cast<const InkDropHostView*>(this)->GetEventHandler());
+      const_cast<const InkDropHost*>(this)->GetEventHandler());
 }
 
-bool InkDropHostView::AddInkDropClip(ui::Layer* ink_drop_layer) {
+bool InkDropHost::AddInkDropClip(ui::Layer* ink_drop_layer) {
   base::Optional<gfx::RRectF> clipping_data =
-      HighlightPathGenerator::GetRoundRectForView(this);
+      HighlightPathGenerator::GetRoundRectForView(host_view_);
   if (!clipping_data)
     return false;
 
@@ -368,34 +334,16 @@
   return true;
 }
 
-void InkDropHostView::InstallInkDropMask(ui::Layer* ink_drop_layer) {
+void InkDropHost::InstallInkDropMask(ui::Layer* ink_drop_layer) {
   ink_drop_mask_ = CreateInkDropMask();
   DCHECK(ink_drop_mask_);
   ink_drop_layer->SetMaskLayer(ink_drop_mask_->layer());
 }
 
+InkDropHostView::InkDropHostView() = default;
+InkDropHostView::~InkDropHostView() = default;
+
 BEGIN_METADATA(InkDropHostView, View)
-ADD_PROPERTY_METADATA(base::RepeatingCallback<void(ui::Layer*)>,
-                      AddInkDropLayerCallback)
-ADD_PROPERTY_METADATA(base::RepeatingCallback<void(ui::Layer*)>,
-                      RemoveInkDropLayerCallback)
-ADD_PROPERTY_METADATA(base::RepeatingCallback<std::unique_ptr<InkDrop>()>,
-                      CreateInkDropCallback)
-ADD_PROPERTY_METADATA(base::RepeatingCallback<std::unique_ptr<InkDropRipple>()>,
-                      CreateInkDropRippleCallback)
-ADD_PROPERTY_METADATA(
-    base::RepeatingCallback<std::unique_ptr<InkDropHighlight>()>,
-    CreateInkDropHighlightCallback)
-ADD_PROPERTY_METADATA(base::RepeatingCallback<std::unique_ptr<InkDropMask>()>,
-                      CreateInkDropMaskCallback)
-ADD_PROPERTY_METADATA(base::RepeatingCallback<SkColor()>,
-                      InkDropBaseColorCallback)
-ADD_READONLY_PROPERTY_METADATA(bool, Highlighted)
-ADD_PROPERTY_METADATA(SkColor, InkDropBaseColor, ui::metadata::SkColorConverter)
-ADD_PROPERTY_METADATA(float, InkDropVisibleOpacity)
-ADD_PROPERTY_METADATA(base::Optional<float>, InkDropHighlightOpacity)
-ADD_PROPERTY_METADATA(int, InkDropLargeCornerRadius)
-ADD_PROPERTY_METADATA(int, InkDropSmallCornerRadius)
 END_METADATA
 
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_host_view.h b/ui/views/animation/ink_drop_host_view.h
index 4a62b1cc..ddd2f1c 100644
--- a/ui/views/animation/ink_drop_host_view.h
+++ b/ui/views/animation/ink_drop_host_view.h
@@ -29,15 +29,24 @@
 enum class InkDropState;
 
 namespace test {
-class InkDropHostViewTestApi;
+class InkDropHostTestApi;
 }  // namespace test
 
-// A view that provides InkDropHost functionality.
-class VIEWS_EXPORT InkDropHostView : public View {
+// TODO(crbug.com/931964): Rename this type and move this header. Also consider
+// if InkDropHost should be what implements the InkDrop interface and have that
+// be the public interface.
+// The current division of labor is roughly as follows:
+// * InkDropHost manages an InkDrop and is responsible for a lot of its
+//   configuration and creating the parts of the InkDrop.
+// * InkDrop manages the parts of the ink-drop effect once it's up and running.
+// * InkDropRipple is a ripple effect that usually triggers as a result of
+//   clicking or activating the button / similar which hosts this.
+// * InkDropHighlight manages the hover/focus highlight layer.
+// TODO(pbos): See if this can be the only externally visible surface for an
+// ink-drop effect, and rename this InkDrop, or consolidate with InkDrop.
+class VIEWS_EXPORT InkDropHost {
  public:
-  METADATA_HEADER(InkDropHostView);
-
-  // Used in SetInkDropMode() to specify whether the ink drop effect is enabled
+  // Used in SetMode() to specify whether the ink drop effect is enabled
   // or not for the view. In case of having an ink drop, it also specifies
   // whether the default event handler for the ink drop should be installed or
   // the subclass will handle ink drop events itself.
@@ -47,10 +56,10 @@
     ON_NO_GESTURE_HANDLER,
   };
 
-  InkDropHostView();
-  InkDropHostView(const InkDropHostView&) = delete;
-  InkDropHostView& operator=(const InkDropHostView&) = delete;
-  ~InkDropHostView() override;
+  explicit InkDropHost(View* host);
+  InkDropHost(const InkDropHost&) = delete;
+  InkDropHost& operator=(const InkDropHost&) = delete;
+  virtual ~InkDropHost();
 
   // TODO(pbos): Re-think this API, we may want to expose adding a Layer beneath
   // a child view to add the effect in the middle of the layer stack. See
@@ -60,12 +69,11 @@
   //
   // Do not call from new code. Most uses for this API should be overriding
   // View::AddLayerBeneathView instead. New ones should re-think the API.
-  void SetAddInkDropLayerCallback(
-      base::RepeatingCallback<void(ui::Layer*)> callback);
+  void SetAddLayerCallback(base::RepeatingCallback<void(ui::Layer*)> callback);
 
-  // Only here to support metadata.
-  const base::RepeatingCallback<void(ui::Layer*)>& GetAddInkDropLayerCallback()
-      const;
+  // TODO(pbos): Remove, only used in toggle_button_unittest.cc.
+  const base::RepeatingCallback<void(ui::Layer*)>&
+  GetAddLayerCallbackForTesting() const;
 
   // TODO(pbos): Re-think this API, we may want to expose adding a Layer beneath
   // a child view to add the effect in the middle of the layer stack. See
@@ -75,11 +83,11 @@
   //
   // Do not call from new code. Most uses for this API should be overriding
   // View::AddLayerBeneathView instead. New ones should re-think the API.
-  void SetRemoveInkDropLayerCallback(
+  void SetRemoveLayerCallback(
       base::RepeatingCallback<void(ui::Layer*)> callback);
-  // Only here to support metadata.
+  // TODO(pbos): Remove, only used in toggle_button_unittest.cc.
   const base::RepeatingCallback<void(ui::Layer*)>&
-  GetRemoveInkDropLayerCallback() const;
+  GetRemoveLayerCallbackForTesting() const;
 
   // Returns a configured InkDrop. To override default behavior call
   // SetCreateInkDropCallback().
@@ -89,22 +97,14 @@
   void SetCreateInkDropCallback(
       base::RepeatingCallback<std::unique_ptr<InkDrop>()> callback);
 
-  // Only here to support metadata.
-  const base::RepeatingCallback<std::unique_ptr<InkDrop>()>&
-  GetCreateInkDropCallback() const;
-
   // Creates and returns the visual effect used for press. Used by InkDropImpl
   // instances.
   std::unique_ptr<InkDropRipple> CreateInkDropRipple() const;
 
   // Replaces CreateInkDropRipple() behavior.
-  void SetCreateInkDropRippleCallback(
+  void SetCreateRippleCallback(
       base::RepeatingCallback<std::unique_ptr<InkDropRipple>()> callback);
 
-  // Only here to support metadata.
-  const base::RepeatingCallback<std::unique_ptr<InkDropRipple>()>&
-  GetCreateInkDropRippleCallback() const;
-
   // Returns the point of the |last_ripple_triggering_event_| if it was a
   // LocatedEvent, otherwise the center point of the local bounds is returned.
   // This is nominally used by the InkDropRipple.
@@ -112,42 +112,31 @@
 
   // Creates and returns the visual effect used for hover and focus. Used by
   // InkDropImpl instances. To override behavior call
-  // SetCreateInkDropHighlightCallback().
+  // SetCreateHighlightCallback().
   std::unique_ptr<InkDropHighlight> CreateInkDropHighlight() const;
 
   // Replaces CreateInkDropHighlight() behavior.
-  void SetCreateInkDropHighlightCallback(
+  void SetCreateHighlightCallback(
       base::RepeatingCallback<std::unique_ptr<InkDropHighlight>()> callback);
 
-  // Only here to support metadata.
-  const base::RepeatingCallback<std::unique_ptr<InkDropHighlight>()>&
-  GetCreateInkDropHighlightCallback() const;
-
   // Callback replacement of CreateInkDropMask().
   // TODO(pbos): Investigate removing this. It currently is only used by
   // ToolbarButton.
-  void SetCreateInkDropMaskCallback(
+  void SetCreateMaskCallback(
       base::RepeatingCallback<std::unique_ptr<InkDropMask>()> callback);
 
-  // Only here to support metadata.
-  const base::RepeatingCallback<std::unique_ptr<InkDropMask>()>&
-  GetCreateInkDropMaskCallback() const;
-
   // Returns the base color for the ink drop.
-  SkColor GetInkDropBaseColor() const;
+  SkColor GetBaseColor() const;
 
   // Sets the base color for the ink drop.
-  void SetInkDropBaseColor(SkColor color);
+  void SetBaseColor(SkColor color);
 
-  // Callback version of GetInkDropBaseColor(). If possible, prefer using
-  // SetInkDropBaseColor(). If a callback has been set by previous configuration
-  // and you want to use the base version of GetInkDropBaseColor() that's
-  // reading SetInkDropBaseColor(), you need to reset the callback by calling
-  // SetInkDropBaseColorCallback({}).
-  void SetInkDropBaseColorCallback(base::RepeatingCallback<SkColor()> callback);
-
-  // Only here to support metadata.
-  const base::RepeatingCallback<SkColor()>& GetInkDropBaseColorCallback() const;
+  // Callback version of GetBaseColor(). If possible, prefer using
+  // SetBaseColor(). If a callback has been set by previous configuration
+  // and you want to use the base version of GetBaseColor() that's reading
+  // SetBaseColor(), you need to reset the callback by calling
+  // SetBaseColorCallback({}).
+  void SetBaseColorCallback(base::RepeatingCallback<SkColor()> callback);
 
   // Toggle to enable/disable an InkDrop on this View.  Descendants can override
   // CreateInkDropHighlight() and CreateInkDropRipple() to change the look/feel
@@ -155,19 +144,18 @@
   //
   // TODO(bruthig): Add an easier mechanism than overriding functions to allow
   // subclasses/clients to specify the flavor of ink drop.
-  void SetInkDropMode(InkDropMode ink_drop_mode);
+  void SetMode(InkDropMode ink_drop_mode);
 
-  void SetInkDropVisibleOpacity(float visible_opacity);
-  float GetInkDropVisibleOpacity() const;
+  void SetVisibleOpacity(float visible_opacity);
+  float GetVisibleOpacity() const;
 
-  void SetInkDropHighlightOpacity(base::Optional<float> opacity);
-  base::Optional<float> GetInkDropHighlightOpacity() const;
+  void SetHighlightOpacity(base::Optional<float> opacity);
 
-  void SetInkDropSmallCornerRadius(int small_radius);
-  int GetInkDropSmallCornerRadius() const;
+  void SetSmallCornerRadius(int small_radius);
+  int GetSmallCornerRadius() const;
 
-  void SetInkDropLargeCornerRadius(int large_radius);
-  int GetInkDropLargeCornerRadius() const;
+  void SetLargeCornerRadius(int large_radius);
+  int GetLargeCornerRadius() const;
 
   // Allows InstallableInkDrop to override our InkDropEventHandler
   // instance.
@@ -186,7 +174,7 @@
   // the purposes of centering ink drop ripples on located Events.  Thus nullptr
   // has been used by clients who do not have an Event instance available to
   // them.
-  void AnimateInkDrop(InkDropState state, const ui::LocatedEvent* event);
+  void AnimateToState(InkDropState state, const ui::LocatedEvent* event);
 
   // Returns true if an ink drop instance has been created.
   bool HasInkDrop() const;
@@ -194,7 +182,7 @@
   // Provides public access to |ink_drop_| so that factory methods can configure
   // the inkdrop. Implements lazy initialization of |ink_drop_| so as to avoid
   // virtual method calls during construction since subclasses should be able to
-  // call SetInkDropMode() during construction.
+  // call SetMode() during construction.
   InkDrop* GetInkDrop();
 
   // Returns whether the ink drop should be considered "highlighted" (in or
@@ -202,7 +190,7 @@
   bool GetHighlighted() const;
 
   base::CallbackListSubscription AddHighlightedChangedCallback(
-      PropertyChangedCallback callback);
+      base::RepeatingClosure callback);
 
   // Should be called by InkDrop implementations when their highlight state
   // changes, to trigger the corresponding property change notification here.
@@ -217,39 +205,41 @@
   static constexpr gfx::Size kDefaultSquareInkDropSize = gfx::Size(24, 24);
 
   // Creates a SquareInkDropRipple centered on |center_point|.
-  std::unique_ptr<InkDropRipple> CreateSquareInkDropRipple(
+  std::unique_ptr<InkDropRipple> CreateSquareRipple(
       const gfx::Point& center_point,
       const gfx::Size& size = kDefaultSquareInkDropSize) const;
 
+  View* host_view() { return host_view_; }
+  const View* host_view() const { return host_view_; }
+
  private:
-  friend class test::InkDropHostViewTestApi;
+  friend class test::InkDropHostTestApi;
 
   class ViewLayerTransformObserver : public ViewObserver {
    public:
-    explicit ViewLayerTransformObserver(InkDropHostView* host);
+    ViewLayerTransformObserver(InkDropHost* ink_drop_host, View* host);
     ~ViewLayerTransformObserver() override;
 
     void OnViewLayerTransformed(View* observed_view) override;
 
    private:
     base::ScopedObservation<View, ViewObserver> observation_{this};
-    InkDropHostView* const host_;
+    InkDropHost* const ink_drop_host_;
   };
 
-  class InkDropHostViewEventHandlerDelegate
-      : public InkDropEventHandler::Delegate {
+  class InkDropHostEventHandlerDelegate : public InkDropEventHandler::Delegate {
    public:
-    explicit InkDropHostViewEventHandlerDelegate(InkDropHostView* host_view);
+    explicit InkDropHostEventHandlerDelegate(InkDropHost* host);
 
-    // InkDropEventHandler:
+    // InkDropEventHandler::Delegate:
     InkDrop* GetInkDrop() override;
     bool HasInkDrop() const override;
 
     bool SupportsGestureEvents() const override;
 
    private:
-    // The host view.
-    InkDropHostView* const host_view_;
+    // The host.
+    InkDropHost* const ink_drop_host_;
   };
 
   const InkDropEventHandler* GetEventHandler() const;
@@ -268,8 +258,10 @@
   // AddInkDropLayer().
   void InstallInkDropMask(ui::Layer* ink_drop_layer);
 
+  View* const host_view_;
+
   // Defines what type of |ink_drop_| to create.
-  InkDropMode ink_drop_mode_ = InkDropMode::OFF;
+  InkDropMode ink_drop_mode_ = views::InkDropHost::InkDropMode::OFF;
 
   // Used to observe View and inform the InkDrop of host-transform changes.
   ViewLayerTransformObserver host_view_transform_observer_;
@@ -279,7 +271,7 @@
 
   // Intentionally declared after |ink_drop_| so that it doesn't access a
   // destroyed |ink_drop_| during destruction.
-  InkDropHostViewEventHandlerDelegate ink_drop_event_handler_delegate_;
+  InkDropHostEventHandlerDelegate ink_drop_event_handler_delegate_;
   InkDropEventHandler ink_drop_event_handler_;
 
   InkDropEventHandler* ink_drop_event_handler_override_ = nullptr;
@@ -312,15 +304,34 @@
   base::RepeatingCallback<std::unique_ptr<InkDropMask>()>
       create_ink_drop_mask_callback_;
   base::RepeatingCallback<SkColor()> ink_drop_base_color_callback_;
+
+  base::RepeatingClosureList highlighted_changed_callbacks_;
+};
+
+// A view that provides InkDropHost functionality.
+// TODO(crbug.com/931964): Remove this type and have child classes create their
+// own ink_drop() implementations. See Button::focus_ring() for instance.
+class VIEWS_EXPORT InkDropHostView : public View {
+ public:
+  METADATA_HEADER(InkDropHostView);
+  InkDropHostView();
+  InkDropHostView(const InkDropHostView&) = delete;
+  InkDropHostView& operator=(const InkDropHostView&) = delete;
+  ~InkDropHostView() override;
+
+  // Note that this is called ink_drop() to have most call sites interact with
+  // this as `ink_drop()`. The type for InkDropHost should instead be renamed.
+
+  // TODO(pbos): Consider having "InkDrop" be the public interface for these
+  // effects and have ink_drop() return that.
+  InkDropHost* ink_drop() { return ink_drop_.get(); }
+  const InkDropHost* ink_drop() const { return ink_drop_.get(); }
+
+ private:
+  std::unique_ptr<InkDropHost> ink_drop_{std::make_unique<InkDropHost>(this)};
 };
 
 BEGIN_VIEW_BUILDER(VIEWS_EXPORT, InkDropHostView, View)
-VIEW_BUILDER_PROPERTY(base::Optional<float>, InkDropHighlightOpacity)
-VIEW_BUILDER_PROPERTY(int, InkDropLargeCornerRadius)
-VIEW_BUILDER_PROPERTY(InkDropHostView::InkDropMode, InkDropMode)
-VIEW_BUILDER_PROPERTY(int, InkDropSmallCornerRadius)
-VIEW_BUILDER_PROPERTY(SkColor, InkDropBaseColor)
-VIEW_BUILDER_PROPERTY(float, InkDropVisibleOpacity)
 END_VIEW_BUILDER
 
 }  // namespace views
diff --git a/ui/views/animation/ink_drop_host_view_unittest.cc b/ui/views/animation/ink_drop_host_view_unittest.cc
index 39ee1c5..bd749de 100644
--- a/ui/views/animation/ink_drop_host_view_unittest.cc
+++ b/ui/views/animation/ink_drop_host_view_unittest.cc
@@ -30,19 +30,19 @@
 
 namespace views {
 namespace test {
-using InkDropMode = InkDropHostViewTestApi::InkDropMode;
+using InkDropMode = InkDropHostTestApi::InkDropMode;
 
 class TestInkDropHostView : public InkDropHostView {
  public:
   TestInkDropHostView() {
-    SetCreateInkDropCallback(base::BindRepeating(
+    ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
         [](TestInkDropHostView* host) -> std::unique_ptr<InkDrop> {
           auto ink_drop = std::make_unique<TestInkDrop>();
           host->last_created_inkdrop_ = ink_drop.get();
           return ink_drop;
         },
         this));
-    SetInkDropBaseColor(gfx::kPlaceholderColor);
+    ink_drop()->SetBaseColor(gfx::kPlaceholderColor);
   }
 
   // Accessors to InkDropHostView internals.
@@ -69,7 +69,7 @@
   TestInkDropHostView host_view_;
 
   // Provides internal access to |host_view_| test target.
-  InkDropHostViewTestApi test_api_;
+  InkDropHostTestApi test_api_;
 
   std::unique_ptr<base::AutoReset<gfx::Animation::RichAnimationRenderMode>>
       animation_mode_reset_;
@@ -81,7 +81,7 @@
 };
 
 InkDropHostViewTest::InkDropHostViewTest()
-    : test_api_(&host_view_),
+    : test_api_(host_view_.ink_drop()),
       animation_mode_reset_(gfx::AnimationTestApi::SetRichAnimationRenderMode(
           gfx::Animation::RichAnimationRenderMode::FORCE_DISABLED)) {}
 
@@ -92,9 +92,10 @@
   test_api_.SetInkDropMode(ink_drop_mode);
   host_view_.SetEnabled(true);
 
-  // Call GetInkDrop() to make sure the test CreateInkDrop() is created.
+  // Call ink_drop()->GetInkDrop() to make sure the test CreateInkDrop() is
+  // created.
   test_api_.GetInkDrop();
-  if (ink_drop_mode != InkDropMode::OFF)
+  if (ink_drop_mode != views::InkDropHost::InkDropMode::OFF)
     EXPECT_FALSE(host_view_.last_created_inkdrop()->is_hovered());
   else
     EXPECT_EQ(host_view_.last_created_inkdrop(), nullptr);
@@ -105,7 +106,7 @@
 
   host_view_.GetTargetHandler()->OnEvent(&mouse_event);
 
-  if (ink_drop_mode != InkDropMode::OFF)
+  if (ink_drop_mode != views::InkDropHost::InkDropMode::OFF)
     EXPECT_TRUE(host_view_.last_created_inkdrop()->is_hovered());
   else
     EXPECT_EQ(host_view_.last_created_inkdrop(), nullptr);
@@ -115,8 +116,9 @@
 // Event.
 TEST_F(InkDropHostViewTest, GetInkDropCenterBasedOnLastEventForNullEvent) {
   host_view_.SetSize(gfx::Size(20, 20));
-  test_api_.AnimateInkDrop(InkDropState::ACTION_PENDING, nullptr);
-  EXPECT_EQ(gfx::Point(10, 10), host_view_.GetInkDropCenterBasedOnLastEvent());
+  test_api_.AnimateToState(InkDropState::ACTION_PENDING, nullptr);
+  EXPECT_EQ(gfx::Point(10, 10),
+            host_view_.ink_drop()->GetInkDropCenterBasedOnLastEvent());
 }
 
 // Verifies the return value of GetInkDropCenterBasedOnLastEvent() for a located
@@ -128,8 +130,9 @@
                                gfx::Point(5, 6), ui::EventTimeForNow(),
                                ui::EF_LEFT_MOUSE_BUTTON, 0);
 
-  test_api_.AnimateInkDrop(InkDropState::ACTION_PENDING, &located_event);
-  EXPECT_EQ(gfx::Point(5, 6), host_view_.GetInkDropCenterBasedOnLastEvent());
+  test_api_.AnimateToState(InkDropState::ACTION_PENDING, &located_event);
+  EXPECT_EQ(gfx::Point(5, 6),
+            host_view_.ink_drop()->GetInkDropCenterBasedOnLastEvent());
 }
 
 TEST_F(InkDropHostViewTest, HasInkDrop) {
@@ -138,32 +141,33 @@
   test_api_.GetInkDrop();
   EXPECT_TRUE(test_api_.HasInkDrop());
 
-  test_api_.SetInkDropMode(InkDropMode::OFF);
+  test_api_.SetInkDropMode(views::InkDropHost::InkDropMode::OFF);
   EXPECT_FALSE(test_api_.HasInkDrop());
 }
 
 // Verifies that mouse events trigger ink drops when ink drop mode is ON.
 TEST_F(InkDropHostViewTest, MouseEventsTriggerInkDropsWhenInkDropIsOn) {
-  MouseEventTriggersInkDropHelper(InkDropMode::ON);
+  MouseEventTriggersInkDropHelper(views::InkDropHost::InkDropMode::ON);
 }
 
 // Verifies that mouse events trigger ink drops when ink drop mode is
 // ON_NO_GESTURE_HANDLER.
 TEST_F(InkDropHostViewTest,
        MouseEventsTriggerInkDropsWhenInkDropIsOnNoGestureHandler) {
-  MouseEventTriggersInkDropHelper(InkDropMode::ON_NO_GESTURE_HANDLER);
+  MouseEventTriggersInkDropHelper(
+      views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
 }
 
 // Verifies that mouse events do not trigger ink drops when ink drop mode is
 // OFF.
 TEST_F(InkDropHostViewTest, MouseEventsDontTriggerInkDropsWhenInkDropIsOff) {
-  MouseEventTriggersInkDropHelper(InkDropMode::OFF);
+  MouseEventTriggersInkDropHelper(views::InkDropHost::InkDropMode::OFF);
 }
 
 // Verifies that ink drops are not shown when the host is disabled.
 TEST_F(InkDropHostViewTest,
        GestureEventsDontTriggerInkDropsWhenHostIsDisabled) {
-  test_api_.SetInkDropMode(InkDropMode::ON);
+  test_api_.SetInkDropMode(views::InkDropHost::InkDropMode::ON);
   host_view_.SetEnabled(false);
 
   ui::GestureEvent gesture_event(
@@ -181,7 +185,8 @@
 TEST_F(InkDropHostViewTest,
        GestureEventsDontTriggerInkDropsWhenInkDropModeIsNotOn) {
   for (auto ink_drop_mode :
-       {InkDropMode::ON_NO_GESTURE_HANDLER, InkDropMode::OFF}) {
+       {views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER,
+        views::InkDropHost::InkDropMode::OFF}) {
     test_api_.SetInkDropMode(ink_drop_mode);
     ui::GestureEvent gesture_event(
         0.f, 0.f, 0, ui::EventTimeForNow(),
@@ -198,7 +203,8 @@
 TEST_F(InkDropHostViewTest, NoInkDropOnTouchOrGestureEvents) {
   host_view_.SetSize(gfx::Size(20, 20));
 
-  test_api_.SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+  test_api_.SetInkDropMode(
+      views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
 
   // Ensure the target ink drop is in the expected state.
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
@@ -208,11 +214,11 @@
       ui::ET_TOUCH_PRESSED, gfx::Point(5, 6), ui::EventTimeForNow(),
       ui::PointerDetails(ui::EventPointerType::kTouch, 1));
 
-  test_api_.AnimateInkDrop(InkDropState::ACTION_PENDING, &touch_event);
+  test_api_.AnimateToState(InkDropState::ACTION_PENDING, &touch_event);
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
             InkDropState::HIDDEN);
 
-  test_api_.AnimateInkDrop(InkDropState::ALTERNATE_ACTION_PENDING,
+  test_api_.AnimateToState(InkDropState::ALTERNATE_ACTION_PENDING,
                            &touch_event);
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
             InkDropState::HIDDEN);
@@ -220,11 +226,11 @@
   ui::GestureEvent gesture_event(5.0f, 6.0f, 0, ui::EventTimeForNow(),
                                  ui::GestureEventDetails(ui::ET_GESTURE_TAP));
 
-  test_api_.AnimateInkDrop(InkDropState::ACTION_PENDING, &gesture_event);
+  test_api_.AnimateToState(InkDropState::ACTION_PENDING, &gesture_event);
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
             InkDropState::HIDDEN);
 
-  test_api_.AnimateInkDrop(InkDropState::ALTERNATE_ACTION_PENDING,
+  test_api_.AnimateToState(InkDropState::ALTERNATE_ACTION_PENDING,
                            &gesture_event);
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
             InkDropState::HIDDEN);
@@ -238,7 +244,8 @@
 
   host_view_.SetSize(gfx::Size(20, 20));
 
-  test_api_.SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+  test_api_.SetInkDropMode(
+      views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
 
   // Ensure the target ink drop is in the expected state.
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
@@ -248,7 +255,7 @@
                              gfx::Point(5, 6), ui::EventTimeForNow(),
                              ui::EF_LEFT_MOUSE_BUTTON, 0);
 
-  test_api_.AnimateInkDrop(InkDropState::ACTION_PENDING, &mouse_event);
+  test_api_.AnimateToState(InkDropState::ACTION_PENDING, &mouse_event);
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
             InkDropState::ACTION_PENDING);
 
@@ -256,7 +263,7 @@
       ui::ET_TOUCH_PRESSED, gfx::Point(5, 6), ui::EventTimeForNow(),
       ui::PointerDetails(ui::EventPointerType::kTouch, 1));
 
-  test_api_.AnimateInkDrop(InkDropState::ACTION_TRIGGERED, &touch_event);
+  test_api_.AnimateToState(InkDropState::ACTION_TRIGGERED, &touch_event);
   EXPECT_EQ(test_api_.GetInkDrop()->GetTargetInkDropState(),
             InkDropState::ACTION_TRIGGERED);
 }
@@ -267,17 +274,19 @@
 TEST_F(InkDropHostViewTest, HighlightedChangedFired) {
   bool callback_called = false;
   auto subscription =
-      host_view_.AddHighlightedChangedCallback(base::BindRepeating(
+      host_view_.ink_drop()->AddHighlightedChangedCallback(base::BindRepeating(
           [](bool* called) { *called = true; }, &callback_called));
-  host_view_.OnInkDropHighlightedChanged();
+  host_view_.ink_drop()->OnInkDropHighlightedChanged();
   EXPECT_TRUE(callback_called);
 }
 
-// A very basic InkDropHostView that only calls SetInkDropBaseColor to
-// avoid hitting a NOTREACHED.
+// A very basic InkDropHostView that only calls SetBaseColor to avoid
+// hitting a NOTREACHED.
 class BasicTestInkDropHostView : public InkDropHostView {
  public:
-  BasicTestInkDropHostView() { SetInkDropBaseColor(gfx::kPlaceholderColor); }
+  BasicTestInkDropHostView() {
+    ink_drop()->SetBaseColor(gfx::kPlaceholderColor);
+  }
   BasicTestInkDropHostView(const BasicTestInkDropHostView&) = delete;
   BasicTestInkDropHostView& operator=(const BasicTestInkDropHostView&) = delete;
   ~BasicTestInkDropHostView() override = default;
@@ -287,15 +296,15 @@
 // generators are applied on an InkDropHostView.
 class InkDropHostViewClippingTest : public testing::Test {
  public:
-  InkDropHostViewClippingTest() : host_view_test_api_(&host_view_) {
+  InkDropHostViewClippingTest() : host_view_test_api_(host_view_.ink_drop()) {
     // Set up an InkDropHostView. Clipping is based on the size of the view, so
     // make sure the size is non empty.
-    host_view_test_api_.SetInkDropMode(InkDropMode::ON);
+    host_view_test_api_.SetInkDropMode(views::InkDropHost::InkDropMode::ON);
     host_view_.SetSize(gfx::Size(20, 20));
 
     // The root layer of the ink drop is created the first time GetInkDrop is
     // called and then kept alive until the host view is destroyed.
-    ink_drop_ = static_cast<InkDropImpl*>(host_view_.GetInkDrop());
+    ink_drop_ = static_cast<InkDropImpl*>(host_view_.ink_drop()->GetInkDrop());
     ink_drop_test_api_ = std::make_unique<test::InkDropImplTestApi>(ink_drop_);
   }
   InkDropHostViewClippingTest(const InkDropHostViewClippingTest&) = delete;
@@ -310,7 +319,7 @@
   BasicTestInkDropHostView host_view_;
 
   // Provides internal access to |host_view_| test target.
-  InkDropHostViewTestApi host_view_test_api_;
+  InkDropHostTestApi host_view_test_api_;
 
   InkDropImpl* ink_drop_ = nullptr;
 
diff --git a/ui/views/animation/ink_drop_impl.cc b/ui/views/animation/ink_drop_impl.cc
index c665d06..96f8de0 100644
--- a/ui/views/animation/ink_drop_impl.cc
+++ b/ui/views/animation/ink_drop_impl.cc
@@ -562,8 +562,7 @@
   return nullptr;
 }
 
-InkDropImpl::InkDropImpl(InkDropHostView* ink_drop_host,
-                         const gfx::Size& host_size)
+InkDropImpl::InkDropImpl(InkDropHost* ink_drop_host, const gfx::Size& host_size)
     : ink_drop_host_(ink_drop_host),
       root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)) {
   root_layer_->SetBounds(gfx::Rect(host_size));
@@ -733,7 +732,7 @@
 
   // If the platform provides HC colors, we need to show them fully on hover and
   // press.
-  if (views::UsingPlatformHighContrastInkDrop(ink_drop_host_))
+  if (views::UsingPlatformHighContrastInkDrop(ink_drop_host_->host_view()))
     highlight_->set_visible_opacity(1.0f);
 
   highlight_->set_observer(this);
diff --git a/ui/views/animation/ink_drop_impl.h b/ui/views/animation/ink_drop_impl.h
index 7bc842d..9a598ee6 100644
--- a/ui/views/animation/ink_drop_impl.h
+++ b/ui/views/animation/ink_drop_impl.h
@@ -22,7 +22,7 @@
 }  // namespace test
 
 class InkDropRipple;
-class InkDropHostView;
+class InkDropHost;
 class InkDropHighlight;
 
 // A functional implementation of an InkDrop.
@@ -50,7 +50,7 @@
   //
   // By default the highlight will be made visible while |this| is hovered but
   // not focused and the NONE AutoHighlightMode will be used.
-  InkDropImpl(InkDropHostView* ink_drop_host, const gfx::Size& host_size);
+  InkDropImpl(InkDropHost* ink_drop_host, const gfx::Size& host_size);
   ~InkDropImpl() override;
 
   // Auto highlighting is a mechanism to show/hide the highlight based on the
@@ -267,7 +267,7 @@
 
   // The host of the ink drop. Used to create the ripples and highlights, and to
   // add/remove the root layer to/from it.
-  InkDropHostView* const ink_drop_host_;
+  InkDropHost* const ink_drop_host_;
 
   // The root Layer that parents the InkDropRipple layers and the
   // InkDropHighlight layers. The |root_layer_| is the one that is added and
diff --git a/ui/views/animation/ink_drop_impl_unittest.cc b/ui/views/animation/ink_drop_impl_unittest.cc
index 163a8a9..e4a3f2d 100644
--- a/ui/views/animation/ink_drop_impl_unittest.cc
+++ b/ui/views/animation/ink_drop_impl_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "ui/views/animation/ink_drop_impl.h"
 
+#include "base/bind.h"
 #include "base/macros.h"
 #include "base/test/gtest_util.h"
 #include "base/test/test_simple_task_runner.h"
@@ -33,7 +34,7 @@
   const TestInkDropHost* ink_drop_host() const { return &ink_drop_host_; }
 
   InkDropImpl* ink_drop() {
-    return static_cast<InkDropImpl*>(ink_drop_host()->GetInkDrop());
+    return static_cast<InkDropImpl*>(ink_drop_host()->ink_drop()->GetInkDrop());
   }
 
   InkDropRipple* ink_drop_ripple() {
@@ -79,7 +80,7 @@
 };
 
 InkDropImplTest::InkDropImplTest() {
-  ink_drop_host()->SetInkDropMode(InkDropHostView::InkDropMode::ON);
+  ink_drop_host()->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   test_api_ = std::make_unique<test::InkDropImplTestApi>(ink_drop());
   ink_drop_host()->set_disable_timers_for_test(true);
 }
@@ -97,7 +98,7 @@
 
 void InkDropImplTest::DestroyInkDrop() {
   test_api_.reset();
-  ink_drop_host()->SetInkDropMode(InkDropHostView::InkDropMode::OFF);
+  ink_drop_host()->ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
 }
 
 // AutoHighlightMode parameterized test fixture.
@@ -316,18 +317,19 @@
 TEST_F(InkDropImplTest, HostTracksHighlightState) {
   bool callback_called = false;
   auto subscription =
-      ink_drop_host()->AddHighlightedChangedCallback(base::BindRepeating(
-          [](bool* called) { *called = true; }, &callback_called));
-  EXPECT_FALSE(ink_drop_host()->GetHighlighted());
+      ink_drop_host()->ink_drop()->AddHighlightedChangedCallback(
+          base::BindRepeating([](bool* called) { *called = true; },
+                              &callback_called));
+  EXPECT_FALSE(ink_drop_host()->ink_drop()->GetHighlighted());
 
   test_api()->SetShouldHighlight(true);
   EXPECT_TRUE(callback_called);
-  EXPECT_TRUE(ink_drop_host()->GetHighlighted());
+  EXPECT_TRUE(ink_drop_host()->ink_drop()->GetHighlighted());
   callback_called = false;
 
   test_api()->SetShouldHighlight(false);
   EXPECT_TRUE(callback_called);
-  EXPECT_FALSE(ink_drop_host()->GetHighlighted());
+  EXPECT_FALSE(ink_drop_host()->ink_drop()->GetHighlighted());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/ui/views/animation/ink_drop_unittest.cc b/ui/views/animation/ink_drop_unittest.cc
index d9cd80c..9c29f33 100644
--- a/ui/views/animation/ink_drop_unittest.cc
+++ b/ui/views/animation/ink_drop_unittest.cc
@@ -56,8 +56,8 @@
       ink_drop_ = std::make_unique<InkDropStub>();
       break;
     case INK_DROP_IMPL:
-      ink_drop_ =
-          std::make_unique<InkDropImpl>(&test_ink_drop_host_, gfx::Size());
+      ink_drop_ = std::make_unique<InkDropImpl>(test_ink_drop_host_.ink_drop(),
+                                                gfx::Size());
       // The Timer's used by the InkDropImpl class require a
       // base::ThreadTaskRunnerHandle instance.
       scoped_refptr<base::TestMockTimeTaskRunner> task_runner(
diff --git a/ui/views/animation/installable_ink_drop.cc b/ui/views/animation/installable_ink_drop.cc
index dd71277..6f38481 100644
--- a/ui/views/animation/installable_ink_drop.cc
+++ b/ui/views/animation/installable_ink_drop.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/check_op.h"
 #include "base/memory/ptr_util.h"
 #include "base/notreached.h"
@@ -82,24 +83,24 @@
   }
 }
 
-InstallableInkDrop::InstallableInkDrop(InkDropHostView* ink_drop_host_view)
-    : InstallableInkDrop(static_cast<View*>(ink_drop_host_view)) {
-  // To get all events, we must override InkDropHostView's event handler.
-  ink_drop_host_view->set_ink_drop_event_handler_override(&event_handler_);
-  ink_drop_host_view_ = ink_drop_host_view;
+InstallableInkDrop::InstallableInkDrop(InkDropHost* ink_drop_host)
+    : InstallableInkDrop(ink_drop_host->host_view()) {
+  // To get all events, we must override InkDropHost's event handler.
+  ink_drop_host->set_ink_drop_event_handler_override(&event_handler_);
+  ink_drop_host_ = ink_drop_host;
 
   // TODO(crbug.com/931964): When this is removed, classes relying on property
-  // changed notifications from InkDropHostView for the highlighted state will
+  // changed notifications from InkDropHost for the highlighted state will
   // need to register here instead.
   RegisterHighlightedChangedCallback(
-      base::BindRepeating(&InkDropHostView::OnInkDropHighlightedChanged,
-                          base::Unretained(ink_drop_host_view_)));
+      base::BindRepeating(&InkDropHost::OnInkDropHighlightedChanged,
+                          base::Unretained(ink_drop_host_)));
 }
 
 InstallableInkDrop::~InstallableInkDrop() {
   view_->RemoveLayerBeneathView(layer_.get());
-  if (ink_drop_host_view_)
-    ink_drop_host_view_->set_ink_drop_event_handler_override(nullptr);
+  if (ink_drop_host_)
+    ink_drop_host_->set_ink_drop_event_handler_override(nullptr);
   if (DCHECK_IS_ON())
     view_->RemoveObserver(this);
 }
diff --git a/ui/views/animation/installable_ink_drop.h b/ui/views/animation/installable_ink_drop.h
index 1b91ea19..512bb92 100644
--- a/ui/views/animation/installable_ink_drop.h
+++ b/ui/views/animation/installable_ink_drop.h
@@ -31,7 +31,7 @@
 
 namespace views {
 
-class InkDropHostView;
+class InkDropHost;
 class View;
 
 extern const VIEWS_EXPORT base::Feature kInstallableInkDropFeature;
@@ -48,11 +48,11 @@
   // Create ink drop for |view|. Note that |view| must live longer than us.
   explicit InstallableInkDrop(View* view);
 
-  // Overload for working within the InkDropHostView hierarchy. Similar to
-  // above, |ink_drop_host_view| must outlive us.
+  // Overload for working within an InkDropHost structure. Similar to above,
+  // |ink_drop_host| must outlive us.
   //
   // TODO(crbug.com/931964): Remove this.
-  explicit InstallableInkDrop(InkDropHostView* ink_drop_host_view);
+  explicit InstallableInkDrop(InkDropHost* ink_drop_host);
 
   InstallableInkDrop(const InstallableInkDrop&) = delete;
   InstallableInkDrop(InstallableInkDrop&&) = delete;
@@ -106,9 +106,9 @@
   // visual state.
   View* const view_;
 
-  // If we were installed on an InkDropHostView, this will be non-null. We store
+  // If we were installed on an InkDropHost, this will be non-null. We store
   // this to to remove our InkDropEventHandler override.
-  InkDropHostView* ink_drop_host_view_ = nullptr;
+  InkDropHost* ink_drop_host_ = nullptr;
 
   // Contains the colors and opacities used to paint.
   InstallableInkDropConfig config_;
diff --git a/ui/views/animation/test/ink_drop_host_view_test_api.cc b/ui/views/animation/test/ink_drop_host_view_test_api.cc
index a4aad4b..6f70187 100644
--- a/ui/views/animation/test/ink_drop_host_view_test_api.cc
+++ b/ui/views/animation/test/ink_drop_host_view_test_api.cc
@@ -9,38 +9,39 @@
 namespace views {
 namespace test {
 
-InkDropHostViewTestApi::InkDropHostViewTestApi(InkDropHostView* host_view)
-    : host_view_(host_view) {}
+InkDropHostTestApi::InkDropHostTestApi(InkDropHost* ink_drop_host)
+    : ink_drop_host_(ink_drop_host) {}
 
-InkDropHostViewTestApi::~InkDropHostViewTestApi() = default;
+InkDropHostTestApi::~InkDropHostTestApi() = default;
 
-void InkDropHostViewTestApi::SetInkDropMode(InkDropMode ink_drop_mode) {
-  host_view_->SetInkDropMode(ink_drop_mode);
+void InkDropHostTestApi::SetInkDropMode(InkDropMode ink_drop_mode) {
+  ink_drop_host_->SetMode(ink_drop_mode);
 }
 
-void InkDropHostViewTestApi::SetInkDrop(std::unique_ptr<InkDrop> ink_drop,
-                                        bool handles_gesture_events) {
-  host_view_->SetInkDropMode(handles_gesture_events
-                                 ? InkDropMode::ON
-                                 : InkDropMode::ON_NO_GESTURE_HANDLER);
-  host_view_->ink_drop_ = std::move(ink_drop);
+void InkDropHostTestApi::SetInkDrop(std::unique_ptr<InkDrop> ink_drop,
+                                    bool handles_gesture_events) {
+  ink_drop_host_->SetMode(
+      handles_gesture_events
+          ? views::InkDropHost::InkDropMode::ON
+          : views::InkDropHost::InkDropMode::ON_NO_GESTURE_HANDLER);
+  ink_drop_host_->ink_drop_ = std::move(ink_drop);
 }
 
-void InkDropHostViewTestApi::SetInkDrop(std::unique_ptr<InkDrop> ink_drop) {
+void InkDropHostTestApi::SetInkDrop(std::unique_ptr<InkDrop> ink_drop) {
   SetInkDrop(std::move(ink_drop), true);
 }
 
-bool InkDropHostViewTestApi::HasInkDrop() const {
-  return host_view_->HasInkDrop();
+bool InkDropHostTestApi::HasInkDrop() const {
+  return ink_drop_host_->HasInkDrop();
 }
 
-InkDrop* InkDropHostViewTestApi::GetInkDrop() {
-  return host_view_->GetInkDrop();
+InkDrop* InkDropHostTestApi::GetInkDrop() {
+  return ink_drop_host_->GetInkDrop();
 }
 
-void InkDropHostViewTestApi::AnimateInkDrop(InkDropState state,
-                                            const ui::LocatedEvent* event) {
-  host_view_->AnimateInkDrop(state, event);
+void InkDropHostTestApi::AnimateToState(InkDropState state,
+                                        const ui::LocatedEvent* event) {
+  ink_drop_host_->AnimateToState(state, event);
 }
 
 }  // namespace test
diff --git a/ui/views/animation/test/ink_drop_host_view_test_api.h b/ui/views/animation/test/ink_drop_host_view_test_api.h
index 47b2745..d37efb6a 100644
--- a/ui/views/animation/test/ink_drop_host_view_test_api.h
+++ b/ui/views/animation/test/ink_drop_host_view_test_api.h
@@ -15,14 +15,14 @@
 namespace views {
 namespace test {
 
-// Test API to provide internal access to an InkDropHostView instance.
-class InkDropHostViewTestApi {
+// Test API to provide internal access to an InkDropHost instance.
+class InkDropHostTestApi {
  public:
-  // Make the protected enum accessbile.
-  using InkDropMode = InkDropHostView::InkDropMode;
+  // Make the protected enum accessible.
+  using InkDropMode = views::InkDropHost::InkDropMode;
 
-  explicit InkDropHostViewTestApi(InkDropHostView* host_view);
-  ~InkDropHostViewTestApi();
+  explicit InkDropHostTestApi(InkDropHost* ink_drop_host);
+  ~InkDropHostTestApi();
 
   void SetInkDropMode(InkDropMode ink_drop_mode);
 
@@ -30,28 +30,28 @@
                   bool handles_gesture_events);
   void SetInkDrop(std::unique_ptr<InkDrop> ink_drop);
 
-  InkDrop* ink_drop() { return host_view_->ink_drop_.get(); }
+  InkDrop* ink_drop() { return ink_drop_host_->ink_drop_.get(); }
 
-  // Wrapper for InkDropHostView::HasInkDrop().
+  // Wrapper for InkDropHost::HasInkDrop().
   bool HasInkDrop() const;
 
-  // Wrapper for InkDropHostView::GetInkDrop() which lazily creates the ink drop
+  // Wrapper for InkDropHost::GetInkDrop() which lazily creates the ink drop
   // instance if it doesn't already exist. If you need direct access to
-  // InkDropHostView::ink_drop_ use ink_drop() instead.
+  // InkDropHost::ink_drop_ use ink_drop() instead.
   InkDrop* GetInkDrop();
 
   bool HasInkdropEventHandler() const;
 
-  // Wrapper for InkDropHostView::AnimateInkDrop().
-  void AnimateInkDrop(InkDropState state, const ui::LocatedEvent* event);
+  // Wrapper for InkDropHost::AnimateToState().
+  void AnimateToState(InkDropState state, const ui::LocatedEvent* event);
 
-  InkDropMode ink_drop_mode() const { return host_view_->ink_drop_mode_; }
+  InkDropMode ink_drop_mode() const { return ink_drop_host_->ink_drop_mode_; }
 
  private:
-  // The InkDropHostView to provide internal access to.
-  InkDropHostView* host_view_;
+  // The InkDropHost to provide internal access to.
+  InkDropHost* ink_drop_host_;
 
-  DISALLOW_COPY_AND_ASSIGN(InkDropHostViewTestApi);
+  DISALLOW_COPY_AND_ASSIGN(InkDropHostTestApi);
 };
 
 }  // namespace test
diff --git a/ui/views/animation/test/test_ink_drop_host.cc b/ui/views/animation/test/test_ink_drop_host.cc
index b820164..24f402e 100644
--- a/ui/views/animation/test/test_ink_drop_host.cc
+++ b/ui/views/animation/test/test_ink_drop_host.cc
@@ -78,19 +78,19 @@
 }  // namespace
 
 TestInkDropHost::TestInkDropHost() {
-  InkDrop::UseInkDropWithoutAutoHighlight(this);
+  InkDrop::UseInkDropWithoutAutoHighlight(ink_drop());
 
-  SetAddInkDropLayerCallback(base::BindRepeating(
+  ink_drop()->SetAddLayerCallback(base::BindRepeating(
       [](TestInkDropHost* host, ui::Layer*) {
         ++host->num_ink_drop_layers_added_;
       },
       this));
-  SetRemoveInkDropLayerCallback(base::BindRepeating(
+  ink_drop()->SetRemoveLayerCallback(base::BindRepeating(
       [](TestInkDropHost* host, ui::Layer*) {
         ++host->num_ink_drop_layers_removed_;
       },
       this));
-  SetCreateInkDropHighlightCallback(base::BindRepeating(
+  ink_drop()->SetCreateHighlightCallback(base::BindRepeating(
       [](TestInkDropHost* host) -> std::unique_ptr<views::InkDropHighlight> {
         auto highlight = std::make_unique<TestInkDropHighlight>(
             host->size(), 0, gfx::PointF(), SK_ColorBLACK);
@@ -101,7 +101,7 @@
         return highlight;
       },
       this));
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](TestInkDropHost* host) -> std::unique_ptr<views::InkDropRipple> {
         auto ripple = std::make_unique<TestInkDropRipple>(
             host->size(), 0, host->size(), 0, gfx::Point(), SK_ColorBLACK,
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index ca033721..31647fb 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -565,7 +565,8 @@
       anchor_widget->SetContentsView(std::make_unique<LabelButton>(
           Button::PressedCallback(), std::u16string()));
   TestInkDrop* ink_drop = new TestInkDrop();
-  test::InkDropHostViewTestApi(button).SetInkDrop(base::WrapUnique(ink_drop));
+  test::InkDropHostTestApi(button->ink_drop())
+      .SetInkDrop(base::WrapUnique(ink_drop));
   TestBubbleDialogDelegateView* bubble_delegate =
       new TestBubbleDialogDelegateView(nullptr);
   bubble_delegate->set_parent_window(anchor_widget->GetNativeView());
@@ -594,7 +595,8 @@
       anchor_widget->SetContentsView(std::make_unique<LabelButton>(
           Button::PressedCallback(), std::u16string()));
   TestInkDrop* ink_drop = new TestInkDrop();
-  test::InkDropHostViewTestApi(button).SetInkDrop(base::WrapUnique(ink_drop));
+  test::InkDropHostTestApi(button->ink_drop())
+      .SetInkDrop(base::WrapUnique(ink_drop));
   TestBubbleDialogDelegateView* bubble_delegate =
       new TestBubbleDialogDelegateView(nullptr);
   bubble_delegate->set_parent_window(anchor_widget->GetNativeView());
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc
index 6b861260..48d8ac04 100644
--- a/ui/views/controls/button/button.cc
+++ b/ui/views/controls/button/button.cc
@@ -84,7 +84,7 @@
 }
 
 InkDrop* Button::DefaultButtonControllerDelegate::GetInkDrop() {
-  return button()->GetInkDrop();
+  return button()->ink_drop()->GetInkDrop();
 }
 
 int Button::DefaultButtonControllerDelegate::GetDragOperations(
@@ -320,9 +320,9 @@
   if (state_ != STATE_DISABLED) {
     SetState(is_hot_tracked ? STATE_HOVERED : STATE_NORMAL);
     if (show_ink_drop_when_hot_tracked_) {
-      AnimateInkDrop(is_hot_tracked ? views::InkDropState::ACTIVATED
-                                    : views::InkDropState::HIDDEN,
-                     nullptr);
+      ink_drop()->AnimateToState(is_hot_tracked ? views::InkDropState::ACTIVATED
+                                                : views::InkDropState::HIDDEN,
+                                 nullptr);
     }
   }
 
@@ -339,9 +339,9 @@
 }
 
 void Button::SetHighlighted(bool bubble_visible) {
-  AnimateInkDrop(bubble_visible ? views::InkDropState::ACTIVATED
-                                : views::InkDropState::DEACTIVATED,
-                 nullptr);
+  ink_drop()->AnimateToState(bubble_visible ? views::InkDropState::ACTIVATED
+                                            : views::InkDropState::DEACTIVATED,
+                             nullptr);
 }
 
 base::CallbackListSubscription Button::AddStateChangedCallback(
@@ -408,15 +408,17 @@
         !InDrag();
     if (HitTestPoint(event.location())) {
       SetState(should_enter_pushed ? STATE_PRESSED : STATE_HOVERED);
-      if (should_show_pending && GetInkDrop()->GetTargetInkDropState() ==
-                                     views::InkDropState::HIDDEN) {
-        AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event);
+      if (should_show_pending &&
+          ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
+              views::InkDropState::HIDDEN) {
+        ink_drop()->AnimateToState(views::InkDropState::ACTION_PENDING, &event);
       }
     } else {
       SetState(STATE_NORMAL);
-      if (should_show_pending && GetInkDrop()->GetTargetInkDropState() ==
-                                     views::InkDropState::ACTION_PENDING) {
-        AnimateInkDrop(views::InkDropState::HIDDEN, &event);
+      if (should_show_pending &&
+          ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
+              views::InkDropState::ACTION_PENDING) {
+        ink_drop()->AnimateToState(views::InkDropState::HIDDEN, &event);
       }
     }
   }
@@ -433,8 +435,8 @@
   // applies everywhere so gather any feedback and update.
   if (state_ != STATE_DISABLED)
     SetState(STATE_NORMAL);
-  AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
-  GetInkDrop()->SetHovered(false);
+  ink_drop()->AnimateToState(views::InkDropState::HIDDEN, nullptr /* event */);
+  ink_drop()->GetInkDrop()->SetHovered(false);
   InkDropHostView::OnMouseCaptureLost();
 }
 
@@ -489,8 +491,8 @@
   if (state_ != STATE_DISABLED)
     SetState(STATE_NORMAL);
   if (hide_ink_drop_when_showing_context_menu_) {
-    GetInkDrop()->SetHovered(false);
-    AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */);
+    ink_drop()->GetInkDrop()->SetHovered(false);
+    ink_drop()->AnimateToState(InkDropState::HIDDEN, nullptr /* event */);
   }
   InkDropHostView::ShowContextMenu(p, source_type);
 }
@@ -500,7 +502,7 @@
   // (since disabled buttons may still be able to be dragged).
   if (state_ != STATE_DISABLED)
     SetState(STATE_NORMAL);
-  AnimateInkDrop(InkDropState::HIDDEN, nullptr /* event */);
+  ink_drop()->AnimateToState(InkDropState::HIDDEN, nullptr /* event */);
 }
 
 void Button::OnPaint(gfx::Canvas* canvas) {
@@ -559,8 +561,10 @@
   InkDropHostView::OnBlur();
   if (IsHotTracked() || state_ == STATE_PRESSED) {
     SetState(STATE_NORMAL);
-    if (GetInkDrop()->GetTargetInkDropState() != views::InkDropState::HIDDEN)
-      AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
+    if (ink_drop()->GetInkDrop()->GetTargetInkDropState() !=
+        views::InkDropState::HIDDEN)
+      ink_drop()->AnimateToState(views::InkDropState::HIDDEN,
+                                 nullptr /* event */);
     // TODO(bruthig) : Fix Buttons to work well when multiple input
     // methods are interacting with a button. e.g. By animating to HIDDEN here
     // it is possible for a Mouse Release to trigger an action however there
@@ -582,17 +586,17 @@
   SetInstallFocusRingOnFocus(true);
   button_controller_ = std::make_unique<ButtonController>(
       this, std::make_unique<DefaultButtonControllerDelegate>(this));
-  SetCreateInkDropCallback(base::BindRepeating(
+  ink_drop()->SetCreateInkDropCallback(base::BindRepeating(
       [](Button* button) {
         std::unique_ptr<InkDrop> ink_drop =
-            views::InkDrop::CreateInkDropForFloodFillRipple(button);
+            views::InkDrop::CreateInkDropForFloodFillRipple(button->ink_drop());
         ink_drop->SetShowHighlightOnFocus(!button->focus_ring());
         return ink_drop;
       },
       base::Unretained(this)));
   // TODO(pbos): Investigate not setting a default color so that we can DCHECK
   // if one hasn't been set.
-  SetInkDropBaseColor(gfx::kPlaceholderColor);
+  ink_drop()->SetBaseColor(gfx::kPlaceholderColor);
 }
 
 void Button::RequestFocusFromEvent() {
@@ -602,8 +606,8 @@
 
 void Button::NotifyClick(const ui::Event& event) {
   if (has_ink_drop_action_on_click_) {
-    AnimateInkDrop(InkDropState::ACTION_TRIGGERED,
-                   ui::LocatedEvent::FromIfValid(&event));
+    ink_drop()->AnimateToState(InkDropState::ACTION_TRIGGERED,
+                               ui::LocatedEvent::FromIfValid(&event));
   }
 
   // If we have an associated help context ID, notify that system that we have
@@ -620,12 +624,12 @@
 
 void Button::OnClickCanceled(const ui::Event& event) {
   if (ShouldUpdateInkDropOnClickCanceled()) {
-    if (GetInkDrop()->GetTargetInkDropState() ==
+    if (ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
             views::InkDropState::ACTION_PENDING ||
-        GetInkDrop()->GetTargetInkDropState() ==
+        ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
             views::InkDropState::ALTERNATE_ACTION_PENDING) {
-      AnimateInkDrop(views::InkDropState::HIDDEN,
-                     ui::LocatedEvent::FromIfValid(&event));
+      ink_drop()->AnimateToState(views::InkDropState::HIDDEN,
+                                 ui::LocatedEvent::FromIfValid(&event));
     }
   }
 }
@@ -679,10 +683,10 @@
   if (GetEnabled()) {
     bool should_enter_hover_state = ShouldEnterHoveredState();
     SetState(should_enter_hover_state ? STATE_HOVERED : STATE_NORMAL);
-    GetInkDrop()->SetHovered(should_enter_hover_state);
+    ink_drop()->GetInkDrop()->SetHovered(should_enter_hover_state);
   } else {
     SetState(STATE_DISABLED);
-    GetInkDrop()->SetHovered(false);
+    ink_drop()->GetInkDrop()->SetHovered(false);
   }
 }
 
diff --git a/ui/views/controls/button/button_controller.cc b/ui/views/controls/button/button_controller.cc
index e652cb11..77c71e4 100644
--- a/ui/views/controls/button/button_controller.cc
+++ b/ui/views/controls/button/button_controller.cc
@@ -27,7 +27,8 @@
       button_controller_delegate_->ShouldEnterPushedState(event) &&
       button_->HitTestPoint(event.location())) {
     button_->SetState(Button::STATE_PRESSED);
-    button_->AnimateInkDrop(views::InkDropState::ACTION_PENDING, &event);
+    button_->ink_drop()->AnimateToState(views::InkDropState::ACTION_PENDING,
+                                        &event);
   }
   button_controller_delegate_->RequestFocusFromEvent();
   if (button_controller_delegate_->IsTriggerableEvent(event) &&
@@ -87,8 +88,8 @@
       button_->SetState(Button::STATE_PRESSED);
       if (button_controller_delegate_->GetInkDrop()->GetTargetInkDropState() !=
           InkDropState::ACTION_PENDING) {
-        button_->AnimateInkDrop(InkDropState::ACTION_PENDING,
-                                nullptr /* event */);
+        button_->ink_drop()->AnimateToState(InkDropState::ACTION_PENDING,
+                                            nullptr /* event */);
       }
       return true;
     case Button::KeyClickAction::kOnKeyPress:
diff --git a/ui/views/controls/button/button_unittest.cc b/ui/views/controls/button/button_unittest.cc
index bb2cfcc..e64c3fc 100644
--- a/ui/views/controls/button/button_unittest.cc
+++ b/ui/views/controls/button/button_unittest.cc
@@ -49,7 +49,7 @@
 
 namespace views {
 
-using test::InkDropHostViewTestApi;
+using test::InkDropHostTestApi;
 using test::TestInkDrop;
 
 namespace {
@@ -116,7 +116,7 @@
  public:
   explicit TestButtonObserver(Button* button) {
     highlighted_changed_subscription_ =
-        button->AddHighlightedChangedCallback(base::BindRepeating(
+        button->ink_drop()->AddHighlightedChangedCallback(base::BindRepeating(
             [](TestButtonObserver* obs) { obs->highlighted_changed_ = true; },
             base::Unretained(this)));
     state_changed_subscription_ =
@@ -147,8 +147,8 @@
 TestInkDrop* AddTestInkDrop(TestButton* button) {
   auto owned_ink_drop = std::make_unique<TestInkDrop>();
   TestInkDrop* ink_drop = owned_ink_drop.get();
-  button->SetInkDropMode(InkDropHostView::InkDropMode::ON);
-  InkDropHostViewTestApi(button).SetInkDrop(std::move(owned_ink_drop));
+  button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  InkDropHostTestApi(button->ink_drop()).SetInkDrop(std::move(owned_ink_drop));
   return ink_drop;
 }
 
@@ -193,7 +193,7 @@
 
   void CreateButtonWithObserver() {
     button_ = widget()->SetContentsView(std::make_unique<TestButton>(false));
-    button_->SetInkDropMode(InkDropHostView::InkDropMode::ON);
+    button_->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     button_observer_ = std::make_unique<TestButtonObserver>(button_);
   }
 
@@ -737,8 +737,8 @@
       ADD_FAILURE();
     auto ink_drop_layer_add_failure_callback =
         base::BindRepeating([](ui::Layer*) { ADD_FAILURE(); });
-    SetAddInkDropLayerCallback(ink_drop_layer_add_failure_callback);
-    SetRemoveInkDropLayerCallback(ink_drop_layer_add_failure_callback);
+    ink_drop()->SetAddLayerCallback(ink_drop_layer_add_failure_callback);
+    ink_drop()->SetRemoveLayerCallback(ink_drop_layer_add_failure_callback);
   }
 };
 
@@ -847,19 +847,19 @@
 TEST_F(ButtonTest, ChangingHighlightStateNotifiesCallback) {
   CreateButtonWithObserver();
   EXPECT_FALSE(button_observer()->highlighted_changed());
-  EXPECT_FALSE(button()->GetHighlighted());
+  EXPECT_FALSE(button()->ink_drop()->GetHighlighted());
 
   button()->SetHighlighted(/*bubble_visible=*/true);
   EXPECT_TRUE(button_observer()->highlighted_changed());
-  EXPECT_TRUE(button()->GetHighlighted());
+  EXPECT_TRUE(button()->ink_drop()->GetHighlighted());
 
   button_observer()->Reset();
   EXPECT_FALSE(button_observer()->highlighted_changed());
-  EXPECT_TRUE(button()->GetHighlighted());
+  EXPECT_TRUE(button()->ink_drop()->GetHighlighted());
 
   button()->SetHighlighted(/*bubble_visible=*/false);
   EXPECT_TRUE(button_observer()->highlighted_changed());
-  EXPECT_FALSE(button()->GetHighlighted());
+  EXPECT_FALSE(button()->ink_drop()->GetHighlighted());
 }
 
 // Verifies that button state changes trigger property change callbacks.
diff --git a/ui/views/controls/button/checkbox.cc b/ui/views/controls/button/checkbox.cc
index b61ed837..5bcf4f7 100644
--- a/ui/views/controls/button/checkbox.cc
+++ b/ui/views/controls/button/checkbox.cc
@@ -52,19 +52,19 @@
   SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
   SetRequestFocusOnPress(false);
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
-  views::InkDrop::UseInkDropWithoutAutoHighlight(this,
+  views::InkDrop::UseInkDropWithoutAutoHighlight(ink_drop(),
                                                  /*highlight_on_hover=*/false);
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](Checkbox* host) {
         // The "small" size is 21dp, the large size is 1.33 * 21dp = 28dp.
-        return host->CreateSquareInkDropRipple(
+        return host->ink_drop()->CreateSquareRipple(
             host->image()->GetMirroredContentsBounds().CenterPoint(),
             gfx::Size(21, 21));
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](Checkbox* host) {
         // Usually ink-drop ripples match the text color. Checkboxes use the
         // color of the unchecked, enabled icon.
diff --git a/ui/views/controls/button/image_button_factory.cc b/ui/views/controls/button/image_button_factory.cc
index 66173e35..16f7751 100644
--- a/ui/views/controls/button/image_button_factory.cc
+++ b/ui/views/controls/button/image_button_factory.cc
@@ -69,7 +69,7 @@
 }
 
 void ConfigureVectorImageButton(ImageButton* button) {
-  button->SetInkDropMode(Button::InkDropMode::ON);
+  button->ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   button->SetHasInkDropActionOnClick(true);
   button->SetImageHorizontalAlignment(ImageButton::ALIGN_CENTER);
   button->SetImageVerticalAlignment(ImageButton::ALIGN_MIDDLE);
@@ -113,7 +113,7 @@
 
   button->SetImage(Button::STATE_NORMAL, normal_image);
   button->SetImage(Button::STATE_DISABLED, disabled_image);
-  button->SetInkDropBaseColor(icon_color);
+  button->ink_drop()->SetBaseColor(icon_color);
 }
 
 void SetToggledImageFromVectorIconWithColor(ToggleImageButton* button,
diff --git a/ui/views/controls/button/image_button_factory_unittest.cc b/ui/views/controls/button/image_button_factory_unittest.cc
index 40f4cc8..6dae681 100644
--- a/ui/views/controls/button/image_button_factory_unittest.cc
+++ b/ui/views/controls/button/image_button_factory_unittest.cc
@@ -33,7 +33,7 @@
   EXPECT_FALSE(button->GetImage(Button::STATE_NORMAL).isNull());
   EXPECT_FALSE(button->GetImage(Button::STATE_DISABLED).isNull());
   EXPECT_EQ(color_utils::DeriveDefaultIconColor(SK_ColorRED),
-            button->GetInkDropBaseColor());
+            button->ink_drop()->GetBaseColor());
 }
 
 class ImageButtonFactoryWidgetTest : public ViewsTestBase {
@@ -81,7 +81,7 @@
       Button::PressedCallback(), vector_icons::kCloseRoundedIcon));
   EXPECT_EQ(button()->GetNativeTheme()->GetSystemColor(
                 ui::NativeTheme::kColorId_DefaultIconColor),
-            button()->GetInkDropBaseColor());
+            button()->ink_drop()->GetBaseColor());
 }
 
 TEST_F(ImageButtonFactoryWidgetTest,
diff --git a/ui/views/controls/button/label_button_unittest.cc b/ui/views/controls/button/label_button_unittest.cc
index 26d5e94..d309a2d1 100644
--- a/ui/views/controls/button/label_button_unittest.cc
+++ b/ui/views/controls/button/label_button_unittest.cc
@@ -794,8 +794,8 @@
         Button::PressedCallback(), std::u16string()));
 
     test_ink_drop_ = new test::TestInkDrop();
-    test::InkDropHostViewTestApi(button_).SetInkDrop(
-        base::WrapUnique(test_ink_drop_));
+    test::InkDropHostTestApi(button_->ink_drop())
+        .SetInkDrop(base::WrapUnique(test_ink_drop_));
   }
 
   void TearDown() override {
diff --git a/ui/views/controls/button/md_text_button.cc b/ui/views/controls/button/md_text_button.cc
index 34f3def0..d781fa1 100644
--- a/ui/views/controls/button/md_text_button.cc
+++ b/ui/views/controls/button/md_text_button.cc
@@ -35,10 +35,10 @@
                            const std::u16string& text,
                            int button_context)
     : LabelButton(std::move(callback), text, button_context) {
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   SetHasInkDropActionOnClick(true);
   SetShowInkDropWhenHotTracked(true);
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](MdTextButton* host) {
         return color_utils::DeriveDefaultIconColor(
             host->label()->GetEnabledColor());
@@ -96,8 +96,8 @@
   if (corner_radius_ == radius)
     return;
   corner_radius_ = radius;
-  SetInkDropSmallCornerRadius(corner_radius_);
-  SetInkDropLargeCornerRadius(corner_radius_);
+  ink_drop()->SetSmallCornerRadius(corner_radius_);
+  ink_drop()->SetLargeCornerRadius(corner_radius_);
   OnPropertyChanged(&corner_radius_, kPropertyEffectsPaint);
 }
 
diff --git a/ui/views/controls/button/menu_button_controller.cc b/ui/views/controls/button/menu_button_controller.cc
index 6056c0a..785bba4b 100644
--- a/ui/views/controls/button/menu_button_controller.cc
+++ b/ui/views/controls/button/menu_button_controller.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
@@ -111,7 +112,7 @@
 
   // If this is an unintentional trigger do not display the inkdrop.
   if (!is_intentional_menu_trigger_)
-    button()->AnimateInkDrop(InkDropState::HIDDEN, &event);
+    button()->ink_drop()->AnimateToState(InkDropState::HIDDEN, &event);
   return true;
 }
 
@@ -122,7 +123,7 @@
     Activate(&event);
   } else {
     if (button()->GetHideInkDropWhenShowingContextMenu())
-      button()->AnimateInkDrop(InkDropState::HIDDEN, &event);
+      button()->ink_drop()->AnimateToState(InkDropState::HIDDEN, &event);
     ButtonController::OnMouseReleased(event);
   }
 }
@@ -264,8 +265,8 @@
     increment_pressed_lock_called_ = nullptr;
 
     if (!increment_pressed_lock_called && pressed_lock_count_ == 0) {
-      button()->AnimateInkDrop(InkDropState::ACTION_TRIGGERED,
-                               ui::LocatedEvent::FromIfValid(event));
+      button()->ink_drop()->AnimateToState(
+          InkDropState::ACTION_TRIGGERED, ui::LocatedEvent::FromIfValid(event));
     }
 
     // We must return false here so that the RootView does not get stuck
@@ -274,8 +275,8 @@
     return false;
   }
 
-  button()->AnimateInkDrop(InkDropState::HIDDEN,
-                           ui::LocatedEvent::FromIfValid(event));
+  button()->ink_drop()->AnimateToState(InkDropState::HIDDEN,
+                                       ui::LocatedEvent::FromIfValid(event));
   return true;
 }
 
@@ -318,7 +319,7 @@
     if (snap_ink_drop_to_activated)
       delegate()->GetInkDrop()->SnapToActivated();
     else
-      button()->AnimateInkDrop(InkDropState::ACTIVATED, event);
+      button()->ink_drop()->AnimateToState(InkDropState::ACTIVATED, event);
   }
   button()->SetState(Button::STATE_PRESSED);
   delegate()->GetInkDrop()->SetHovered(false);
@@ -346,7 +347,8 @@
     // The widget may be null during shutdown. If so, it doesn't make sense to
     // try to add an ink drop effect.
     if (button()->GetWidget() && button()->GetState() != Button::STATE_PRESSED)
-      button()->AnimateInkDrop(InkDropState::DEACTIVATED, nullptr /* event */);
+      button()->ink_drop()->AnimateToState(InkDropState::DEACTIVATED,
+                                           nullptr /* event */);
   }
 }
 
diff --git a/ui/views/controls/button/menu_button_unittest.cc b/ui/views/controls/button/menu_button_unittest.cc
index 6c40ffd6..994d0841 100644
--- a/ui/views/controls/button/menu_button_unittest.cc
+++ b/ui/views/controls/button/menu_button_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
@@ -117,7 +118,8 @@
 
     auto ink_drop = std::make_unique<test::TestInkDrop>();
     ink_drop_ = ink_drop.get();
-    test::InkDropHostViewTestApi(button_).SetInkDrop(std::move(ink_drop));
+    test::InkDropHostTestApi(button_->ink_drop())
+        .SetInkDrop(std::move(ink_drop));
 
     widget_->Show();
   }
@@ -296,7 +298,7 @@
 
   EXPECT_TRUE(button()->clicked());
   gfx::Point inkdrop_center_point =
-      button()->GetInkDropCenterBasedOnLastEvent();
+      button()->ink_drop()->GetInkDropCenterBasedOnLastEvent();
   View::ConvertPointToScreen(button(), &inkdrop_center_point);
   EXPECT_EQ(click_point, inkdrop_center_point);
 }
@@ -312,7 +314,8 @@
                                                  false, &click_event);
 
   EXPECT_EQ(Button::STATE_PRESSED, button()->GetState());
-  EXPECT_EQ(click_point, button()->GetInkDropCenterBasedOnLastEvent());
+  EXPECT_EQ(click_point,
+            button()->ink_drop()->GetInkDropCenterBasedOnLastEvent());
 }
 
 // Test that the MenuButton stays pressed while there are any PressedLocks.
diff --git a/ui/views/controls/button/toggle_button.cc b/ui/views/controls/button/toggle_button.cc
index 29770ea..b397674 100644
--- a/ui/views/controls/button/toggle_button.cc
+++ b/ui/views/controls/button/toggle_button.cc
@@ -128,37 +128,39 @@
   slide_animation_.SetSlideDuration(base::TimeDelta::FromMilliseconds(80));
   slide_animation_.SetTweenType(gfx::Tween::LINEAR);
   thumb_view_ = AddChildView(std::make_unique<ThumbView>());
-  SetInkDropMode(InkDropMode::ON);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
   // TODO(pbos): Update the highlight-path shape so that a FocusRing can be used
   // on top of it to increase contrast. Disabling it for now addresses a
   // regression in crbug.com/1031983, but a matching FocusRing would probably be
   // desirable.
   SetInstallFocusRingOnFocus(false);
   SetHasInkDropActionOnClick(true);
-  views::InkDrop::UseInkDropForSquareRipple(this,
+  views::InkDrop::UseInkDropForSquareRipple(ink_drop(),
                                             /*highlight_on_hover=*/false);
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](ToggleButton* host) {
         gfx::Rect rect = host->thumb_view_->GetLocalBounds();
         rect.Inset(-ThumbView::GetShadowOutsets());
-        return host->CreateSquareInkDropRipple(rect.CenterPoint());
+        return host->ink_drop()->CreateSquareRipple(rect.CenterPoint());
       },
       this));
-  SetInkDropBaseColorCallback(base::BindRepeating(
+  ink_drop()->SetBaseColorCallback(base::BindRepeating(
       [](ToggleButton* host) {
         return host->GetTrackColor(host->GetIsOn() || host->HasFocus());
       },
       this));
 
-  SetAddInkDropLayerCallback(base::BindRepeating(
-      &InkDropHostView::AddInkDropLayer, base::Unretained(thumb_view_)));
-  SetRemoveInkDropLayerCallback(base::BindRepeating(
-      &InkDropHostView::RemoveInkDropLayer, base::Unretained(thumb_view_)));
+  ink_drop()->SetAddLayerCallback(
+      base::BindRepeating(&InkDropHost::AddInkDropLayer,
+                          base::Unretained(thumb_view_->ink_drop())));
+  ink_drop()->SetRemoveLayerCallback(
+      base::BindRepeating(&InkDropHost::RemoveInkDropLayer,
+                          base::Unretained(thumb_view_->ink_drop())));
 }
 
 ToggleButton::~ToggleButton() {
   // Destroying ink drop early allows ink drop layer to be properly removed,
-  SetInkDropMode(InkDropMode::OFF);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
 }
 
 void ToggleButton::AnimateIsOn(bool is_on) {
@@ -291,16 +293,16 @@
 
 void ToggleButton::OnFocus() {
   Button::OnFocus();
-  AnimateInkDrop(views::InkDropState::ACTION_PENDING, nullptr);
+  ink_drop()->AnimateToState(views::InkDropState::ACTION_PENDING, nullptr);
 }
 
 void ToggleButton::OnBlur() {
   Button::OnBlur();
 
   // The ink drop may have already gone away if the user clicked after focusing.
-  if (GetInkDrop()->GetTargetInkDropState() ==
+  if (ink_drop()->GetInkDrop()->GetTargetInkDropState() ==
       views::InkDropState::ACTION_PENDING) {
-    AnimateInkDrop(views::InkDropState::ACTION_TRIGGERED, nullptr);
+    ink_drop()->AnimateToState(views::InkDropState::ACTION_TRIGGERED, nullptr);
   }
 }
 
@@ -310,8 +312,8 @@
   // Skip over Button::NotifyClick, to customize the ink drop animation.
   // Leave the ripple in place when the button is activated via the keyboard.
   if (!event.IsKeyEvent()) {
-    AnimateInkDrop(InkDropState::ACTION_TRIGGERED,
-                   ui::LocatedEvent::FromIfValid(&event));
+    ink_drop()->AnimateToState(InkDropState::ACTION_TRIGGERED,
+                               ui::LocatedEvent::FromIfValid(&event));
   }
 
   Button::NotifyClick(event);
diff --git a/ui/views/controls/button/toggle_button_unittest.cc b/ui/views/controls/button/toggle_button_unittest.cc
index a463d2ca..5b885ad 100644
--- a/ui/views/controls/button/toggle_button_unittest.cc
+++ b/ui/views/controls/button/toggle_button_unittest.cc
@@ -22,14 +22,14 @@
  public:
   explicit TestToggleButton(int* counter) {
     // TODO(pbos): Find a better testing strategy for this or throw out tests
-    // that rely on monitoring AddInkDropLayerCallbacks (which should hopefully
+    // that rely on monitoring AddLayerCallbacks (which should hopefully
     // go away). This is massively gross, but mimics virtual override of
     // ToggleButton's inkdrop behavior in order to monitor it.
     base::RepeatingCallback<void(ui::Layer*)> base_add_callback =
-        GetAddInkDropLayerCallback();
+        ink_drop()->GetAddLayerCallbackForTesting();
     base::RepeatingCallback<void(ui::Layer*)> base_remove_callback =
-        GetRemoveInkDropLayerCallback();
-    SetAddInkDropLayerCallback(base::BindRepeating(
+        ink_drop()->GetRemoveLayerCallbackForTesting();
+    ink_drop()->SetAddLayerCallback(base::BindRepeating(
         [](int* counter,
            base::RepeatingCallback<void(ui::Layer*)> base_callback,
            ui::Layer* layer) {
@@ -37,7 +37,7 @@
           base_callback.Run(layer);
         },
         counter, base_add_callback));
-    SetRemoveInkDropLayerCallback(base::BindRepeating(
+    ink_drop()->SetRemoveLayerCallback(base::BindRepeating(
         [](int* counter,
            base::RepeatingCallback<void(ui::Layer*)> base_callback,
            ui::Layer* layer) {
@@ -48,10 +48,10 @@
   }
 
   ~TestToggleButton() override {
-    // Calling SetInkDropMode() in this subclass allows this class's
+    // Calling ink_drop()->SetMode() in this subclass allows this class's
     // implementation of RemoveInkDropLayer() to be called. The same
     // call is made in ~ToggleButton() so this is testing the general technique.
-    SetInkDropMode(InkDropMode::OFF);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::OFF);
   }
 
   using View::Focus;
diff --git a/ui/views/controls/combobox/combobox.cc b/ui/views/controls/combobox/combobox.cc
index 7a0fc97a..babb10d6 100644
--- a/ui/views/controls/combobox/combobox.cc
+++ b/ui/views/controls/combobox/combobox.cc
@@ -66,17 +66,18 @@
     button_controller()->set_notify_action(
         ButtonController::NotifyAction::kOnPress);
 
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
-    InkDrop::UseInkDropForSquareRipple(this,
+    InkDrop::UseInkDropForSquareRipple(ink_drop(),
                                        /*highlight_on_hover=*/false);
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
           return std::make_unique<views::FloodFillInkDropRipple>(
-              host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+              host->size(),
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
               host->GetNativeTheme()->GetSystemColor(
                   ui::NativeTheme::kColorId_LabelEnabledColor),
-              host->GetInkDropVisibleOpacity());
+              host->ink_drop()->GetVisibleOpacity());
         },
         this));
   }
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc
index c8f85cc..e0dfcd33 100644
--- a/ui/views/controls/editable_combobox/editable_combobox.cc
+++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -76,17 +76,18 @@
     // TODO(pbos): Share ink-drop configuration code between here and
     // Combobox's TransparentButton.
     // Similar to Combobox's TransparentButton.
-    SetInkDropMode(InkDropMode::ON);
+    ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
     SetHasInkDropActionOnClick(true);
-    InkDrop::UseInkDropForSquareRipple(this,
+    InkDrop::UseInkDropForSquareRipple(ink_drop(),
                                        /*highlight_on_hover=*/false);
-    SetCreateInkDropRippleCallback(base::BindRepeating(
+    ink_drop()->SetCreateRippleCallback(base::BindRepeating(
         [](InkDropHostView* host) -> std::unique_ptr<views::InkDropRipple> {
           return std::make_unique<views::FloodFillInkDropRipple>(
-              host->size(), host->GetInkDropCenterBasedOnLastEvent(),
+              host->size(),
+              host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
               style::GetColor(*host, style::CONTEXT_TEXTFIELD,
                               style::STYLE_PRIMARY),
-              host->GetInkDropVisibleOpacity());
+              host->ink_drop()->GetVisibleOpacity());
         },
         this));
   }
diff --git a/ui/views/controls/label.cc b/ui/views/controls/label.cc
index 18b1e24..70d319e 100644
--- a/ui/views/controls/label.cc
+++ b/ui/views/controls/label.cc
@@ -629,16 +629,15 @@
     height = std::max(GetMaxLines(), 1) * base_line_height;
   } else {
     // SetDisplayRect() has a side effect for later calls of GetStringSize().
-    // Be careful to invoke |full_text_->SetDisplayRect(gfx::Rect())| to
+    // Be careful to invoke full_text_->SetDisplayRect(gfx::Rect()) to
     // cancel this effect before the next time GetStringSize() is called.
-    // It would be beneficial not to cancel here, considering that some layout
-    // managers invoke GetHeightForWidth() for the same width multiple times
-    // and |full_text_| can cache the height.
+    // It's beneficial not to cancel here, considering that some layout managers
+    // invoke GetHeightForWidth() for the same width multiple times and
+    // |full_text_| can cache the height.
     full_text_->SetDisplayRect(gfx::Rect(0, 0, w, 0));
     int string_height = full_text_->GetStringSize().height();
-    // Cap the number of lines to |GetMaxLines()| if multi-line and non-zero
-    // |GetMaxLines()|.
-    height = GetMultiLine() && GetMaxLines() > 0
+    // Cap the number of lines to GetMaxLines() if that's set.
+    height = GetMaxLines() > 0
                  ? std::min(GetMaxLines() * base_line_height, string_height)
                  : string_height;
   }
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc
index 3273b0a..b850a2e 100644
--- a/ui/views/controls/scroll_view.cc
+++ b/ui/views/controls/scroll_view.cc
@@ -499,7 +499,7 @@
   if (is_bounded()) {
     int content_width = available_rect.width();
     int content_height = contents_->GetHeightForWidth(content_width);
-    if (content_height > height()) {
+    if (content_height > available_rect.height()) {
       content_width = std::max(content_width - GetScrollBarLayoutWidth(), 0);
       content_height = contents_->GetHeightForWidth(content_width);
     }
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index c7f5d644..9125b66 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -77,9 +77,6 @@
 #include "ui/base/cocoa/text_services_context_menu.h"
 #endif
 
-using base::ASCIIToUTF16;
-using base::UTF8ToUTF16;
-
 namespace views {
 namespace test {
 
@@ -428,8 +425,8 @@
 }
 
 void TextfieldTest::SetClipboardText(ui::ClipboardBuffer clipboard_buffer,
-                                     const std::string& text) {
-  ui::ScopedClipboardWriter(clipboard_buffer).WriteText(ASCIIToUTF16(text));
+                                     const std::u16string& text) {
+  ui::ScopedClipboardWriter(clipboard_buffer).WriteText(text);
 }
 
 void TextfieldTest::ContentsChanged(Textfield* sender,
@@ -1430,7 +1427,7 @@
   EXPECT_EQ(u"password", textfield_->GetText());
   EXPECT_TRUE(last_contents_.empty());
   model_->SelectAll(false);
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "foo");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"foo");
 
   // Cut and copy should be disabled.
   EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCut));
@@ -1794,7 +1791,7 @@
   VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel());
 
   // Exercise the "paste enabled?" check in the verifier.
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"Test");
   VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel());
 }
 
@@ -1893,10 +1890,10 @@
 TEST_F(TextfieldTest, DragUpOrDownSelectsToEnd) {
   InitTextfield();
   textfield_->SetText(u"hello world");
-  const std::u16string expected_left = base::ASCIIToUTF16(
-      gfx::RenderText::kDragToEndIfOutsideVerticalBounds ? "hello" : "lo");
-  const std::u16string expected_right = base::ASCIIToUTF16(
-      gfx::RenderText::kDragToEndIfOutsideVerticalBounds ? " world" : " w");
+  const std::u16string expected_left =
+      gfx::RenderText::kDragToEndIfOutsideVerticalBounds ? u"hello" : u"lo";
+  const std::u16string expected_right =
+      gfx::RenderText::kDragToEndIfOutsideVerticalBounds ? u" world" : u" w";
   const int right_x = GetCursorPositionX(7);
   const int left_x = GetCursorPositionX(3);
 
@@ -2166,7 +2163,7 @@
   EXPECT_EQ(u"read only", textfield_->GetSelectedText());
 
   // Cut should be disabled.
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"Test");
   EXPECT_FALSE(textfield_->IsCommandIdEnabled(Textfield::kCut));
   textfield_->ExecuteCommand(Textfield::kCut, 0);
   SendKeyEvent(ui::VKEY_X, false, true);
@@ -2182,14 +2179,14 @@
   EXPECT_EQ(u"read only", textfield_->GetText());
 
   // Copy should work normally.
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"Test");
   EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kCopy));
   textfield_->ExecuteCommand(Textfield::kCopy, 0);
   EXPECT_EQ(u"read only", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"Test");
   SendKeyEvent(ui::VKEY_C, false, true);
   EXPECT_EQ(u"read only", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "Test");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"Test");
   SendAlternateCopy();
   EXPECT_EQ(u"read only", GetClipboardText(ui::ClipboardBuffer::kCopyPaste));
 
@@ -2530,7 +2527,7 @@
   EXPECT_EQ(ui::ClipboardBuffer::kCopyPaste, GetAndResetCopiedToClipboard());
 
   // Reset clipboard text.
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"");
 
   // Ensure [Shift]+[Delete] is a no-op in case there is no selection.
   textfield_->SetText(u"123");
@@ -2568,7 +2565,7 @@
 
   // Ensure kPaste, [Ctrl]+[V], and [Shift]+[Insert] pastes;
   // also ensure that [Ctrl]+[Alt]+[V] does nothing.
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "abc");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"abc");
   textfield_->SetText(std::u16string());
   EXPECT_TRUE(textfield_->IsCommandIdEnabled(Textfield::kPaste));
   textfield_->ExecuteCommand(Textfield::kPaste, 0);
@@ -3252,7 +3249,7 @@
   EXPECT_EQ(u"0123", GetClipboardText(ui::ClipboardBuffer::kSelection));
 
   // Middle clicking with an empty selection clipboard should still focus.
-  SetClipboardText(ui::ClipboardBuffer::kSelection, std::string());
+  SetClipboardText(ui::ClipboardBuffer::kSelection, std::u16string());
   textfield_->GetFocusManager()->ClearFocus();
   EXPECT_FALSE(textfield_->HasFocus());
   textfield_->OnMousePressed(middle);
@@ -3264,7 +3261,7 @@
   // Middle clicking in the selection should insert the selection clipboard
   // contents into the middle of the selection, and move the cursor to the end
   // of the pasted content.
-  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, "foo");
+  SetClipboardText(ui::ClipboardBuffer::kCopyPaste, u"foo");
   textfield_->SetSelectedRange(gfx::Range(2, 6));
   textfield_->OnMousePressed(middle);
   EXPECT_EQ(u"0123foo01230123", textfield_->GetText());
@@ -3304,7 +3301,7 @@
   EXPECT_EQ(u"ab cd ef", GetClipboardText(ui::ClipboardBuffer::kSelection));
   EXPECT_EQ(ui::ClipboardBuffer::kMaxValue, GetAndResetCopiedToClipboard());
 
-  SetClipboardText(ui::ClipboardBuffer::kSelection, "other");
+  SetClipboardText(ui::ClipboardBuffer::kSelection, u"other");
   textfield_->SelectAll(false);
   EXPECT_EQ(u"other", GetClipboardText(ui::ClipboardBuffer::kSelection));
   EXPECT_EQ(ui::ClipboardBuffer::kMaxValue, GetAndResetCopiedToClipboard());
@@ -3380,7 +3377,7 @@
 
   // Set text which may fall back to a font which has taller baseline than
   // the default font.
-  textfield_->SetText(UTF8ToUTF16("\xE0\xB9\x91"));
+  textfield_->SetText(u"๑");
   const int new_baseline = textfield_->GetBaseline();
 
   // Regardless of the text, the baseline must be the same.
@@ -3425,7 +3422,7 @@
   textfield_->GetAccessibleNodeData(&data);
   const std::string& name =
       data.GetStringAttribute(ax::mojom::StringAttribute::kName);
-  EXPECT_EQ(test_tooltip_text, ASCIIToUTF16(name));
+  EXPECT_EQ(test_tooltip_text, base::ASCIIToUTF16(name));
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -3768,12 +3765,12 @@
 }
 
 TEST_F(TextfieldTest, FocusChangesScrollToStart) {
-  const std::string& kText = "abcdef";
+  const std::u16string kText = u"abcdef";
   InitTextfield();
-  textfield_->SetText(ASCIIToUTF16(kText));
-  EXPECT_EQ(base::ASCIIToUTF16(std::string()), textfield_->GetSelectedText());
+  textfield_->SetText(kText);
+  EXPECT_EQ(std::u16string(), textfield_->GetSelectedText());
   textfield_->AboutToRequestFocusFromTabTraversal(false);
-  EXPECT_EQ(base::ASCIIToUTF16(kText), textfield_->GetSelectedText());
+  EXPECT_EQ(kText, textfield_->GetSelectedText());
   if (PlatformStyle::kTextfieldScrollsToStartOnFocusChange)
     EXPECT_EQ(0U, textfield_->GetCursorPosition());
   else
@@ -3929,9 +3926,9 @@
 #endif  // defined(OS_MAC)
 
 TEST_F(TextfieldTest, AccessibilitySelectionEvents) {
-  const std::string& kText = "abcdef";
+  const std::u16string kText = u"abcdef";
   InitTextfield();
-  textfield_->SetText(ASCIIToUTF16(kText));
+  textfield_->SetText(kText);
   EXPECT_TRUE(textfield_->HasFocus());
   int previous_selection_fired_count =
       textfield_->GetAccessibilitySelectionFiredCount();
diff --git a/ui/views/controls/textfield/textfield_unittest.h b/ui/views/controls/textfield/textfield_unittest.h
index 4d796ad..fef1348c 100644
--- a/ui/views/controls/textfield/textfield_unittest.h
+++ b/ui/views/controls/textfield/textfield_unittest.h
@@ -42,7 +42,7 @@
 
   ui::ClipboardBuffer GetAndResetCopiedToClipboard();
   std::u16string GetClipboardText(ui::ClipboardBuffer type);
-  void SetClipboardText(ui::ClipboardBuffer type, const std::string& text);
+  void SetClipboardText(ui::ClipboardBuffer type, const std::u16string& text);
 
   // TextfieldController:
   void ContentsChanged(Textfield* sender,
diff --git a/ui/views/examples/textarea_example.cc b/ui/views/examples/textarea_example.cc
index cf1ddf2..d3205e9 100644
--- a/ui/views/examples/textarea_example.cc
+++ b/ui/views/examples/textarea_example.cc
@@ -18,16 +18,16 @@
 TextareaExample::TextareaExample() : ExampleBase("Textarea") {}
 
 void TextareaExample::CreateExampleView(View* container) {
-  constexpr char kLongText[] =
-      "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod"
-      " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim "
-      "veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
-      "commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate "
-      "velit esse cillum dolore eu fugiat nulla pariatur.\n\nExcepteur sint "
-      "occaecat cupidatat non proident, sunt in culpa qui officia deserunt "
-      "mollit anim id est laborum.";
+  constexpr char16_t kLongText[] =
+      u"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+      u"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
+      u"minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
+      u"aliquip ex ea commodo consequat.\nDuis aute irure dolor in "
+      u"reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
+      u"pariatur.\n\nExcepteur sint occaecat cupidatat non proident, sunt in "
+      u"culpa qui officia deserunt mollit anim id est laborum.";
   auto textarea = std::make_unique<Textarea>();
-  textarea->SetText(base::UTF8ToUTF16(kLongText));
+  textarea->SetText(kLongText);
   container->SetLayoutManager(std::make_unique<views::FillLayout>());
   container->AddChildView(std::move(textarea));
 }
diff --git a/ui/views/window/frame_caption_button.cc b/ui/views/window/frame_caption_button.cc
index 4487c12..329021f 100644
--- a/ui/views/window/frame_caption_button.cc
+++ b/ui/views/window/frame_caption_button.cc
@@ -76,17 +76,18 @@
   swap_images_animation_->Reset(1);
 
   SetHasInkDropActionOnClick(true);
-  SetInkDropMode(InkDropMode::ON);
-  SetInkDropVisibleOpacity(kInkDropVisibleOpacity);
+  ink_drop()->SetMode(views::InkDropHost::InkDropMode::ON);
+  ink_drop()->SetVisibleOpacity(kInkDropVisibleOpacity);
   UpdateInkDropBaseColor();
-  views::InkDrop::UseInkDropWithoutAutoHighlight(this,
+  views::InkDrop::UseInkDropWithoutAutoHighlight(ink_drop(),
                                                  /*highlight_on_hover=*/false);
-  SetCreateInkDropRippleCallback(base::BindRepeating(
+  ink_drop()->SetCreateRippleCallback(base::BindRepeating(
       [](FrameCaptionButton* host) -> std::unique_ptr<views::InkDropRipple> {
         return std::make_unique<views::FloodFillInkDropRipple>(
             host->size(), host->GetInkdropInsets(host->size()),
-            host->GetInkDropCenterBasedOnLastEvent(),
-            host->GetInkDropBaseColor(), host->GetInkDropVisibleOpacity());
+            host->ink_drop()->GetInkDropCenterBasedOnLastEvent(),
+            host->ink_drop()->GetBaseColor(),
+            host->ink_drop()->GetVisibleOpacity());
       },
       this));
 
@@ -252,7 +253,7 @@
     // the window is moving as a result of the animation from normal to
     // maximized state or vice versa. https://crbug.com/840901.
     cc::PaintFlags flags;
-    flags.setColor(GetInkDropBaseColor());
+    flags.setColor(ink_drop()->GetBaseColor());
     flags.setAlpha(highlight_alpha);
     const gfx::Point center(GetMirroredRect(GetContentsBounds()).CenterPoint());
     canvas->DrawCircle(center, ink_drop_corner_radius_, flags);
@@ -326,7 +327,7 @@
   // TODO(pkasting): It would likely be better to make the button glyph always
   // be an alpha-blended version of GetColorWithMaxContrast(background_color_).
   const SkColor button_color = GetButtonColor(background_color_);
-  SetInkDropBaseColor(
+  ink_drop()->SetBaseColor(
       GetColorWithMaxContrast(GetColorWithMaxContrast(button_color)));
 }
 
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
index e4801a8..62f5983 100644
--- a/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
+++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/activation_code_page.html
@@ -174,6 +174,13 @@
       #pageBody {
         margin-top: -20px;
       }
+
+      #startScanningButton {
+        max-width: 470px;
+        min-width: 345px;
+        text-align: center;
+        width: auto;
+      }
     </style>
     <base-page>
       <div slot="page-body" id="pageBody" class="animate">
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
index 5d83e31c..255b6d8f 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.html
@@ -52,7 +52,7 @@
     <template is="dom-if" if="[[eq_(State.SIM_MISSING, state_)]]" restamp>
       <div id="simMissing" class="property-box two-line">
         <div class="start layout horizontal center">
-          <iron-icon icon="cr:sim-card-alert"></iron-icon>
+          <iron-icon id="simMissingIcon" icon="cr:sim-card-alert"></iron-icon>
           <div class="error">[[i18n('networkSimCardMissing')]]</div>
         </div>
       </div>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
index 359b2a9..b1c88f6 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_siminfo.js
@@ -127,6 +127,29 @@
     }
 
     this.delayUpdateLockEnabled_();
+    this.updateFocus_();
+  },
+
+  /**
+   * Sets default focus when dialog is closed.
+   * @private
+   */
+  updateFocus_() {
+    const state = this.computeState_();
+    // State.SIM_MISSING state is not needed here because it does not have a
+    // click event to open the sim lock dialogs
+    switch (state) {
+      case State.SIM_LOCKED:
+        if (this.$$('#unlockPinButton')) {
+          this.$$('#unlockPinButton').focus();
+        }
+        break;
+      case State.SIM_UNLOCKED:
+        if (this.$$('#simLockButton')) {
+          this.$$('#simLockButton').focus();
+        }
+        break;
+    }
   },
 
   /** @private */
diff --git a/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js b/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js
index 1973abe..7f723ae 100644
--- a/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js
+++ b/ui/webui/resources/cr_components/chromeos/network/sim_lock_dialogs.js
@@ -391,6 +391,13 @@
   },
 
   /**
+   * Used by test to simulate dialog cancel click.
+   */
+  closeDialogsForTest() {
+    this.closeDialogs_();
+  },
+
+  /**
    * @param {!Event} event
    * @private
    */
diff --git a/url/gurl_unittest.cc b/url/gurl_unittest.cc
index 6d23d65..35204704 100644
--- a/url/gurl_unittest.cc
+++ b/url/gurl_unittest.cc
@@ -117,8 +117,7 @@
 }
 
 TEST(GURLTest, Copy) {
-  GURL url(base::UTF8ToUTF16(
-      "http://user:pass@google.com:99/foo;bar?q=a#ref"));
+  GURL url(u"http://user:pass@google.com:99/foo;bar?q=a#ref");
 
   GURL url2(url);
   EXPECT_TRUE(url2.is_valid());
@@ -151,8 +150,7 @@
 }
 
 TEST(GURLTest, Assign) {
-  GURL url(base::UTF8ToUTF16(
-      "http://user:pass@google.com:99/foo;bar?q=a#ref"));
+  GURL url(u"http://user:pass@google.com:99/foo;bar?q=a#ref");
 
   GURL url2;
   url2 = url;
@@ -194,8 +192,7 @@
 }
 
 TEST(GURLTest, CopyFileSystem) {
-  GURL url(base::UTF8ToUTF16(
-      "filesystem:https://user:pass@google.com:99/t/foo;bar?q=a#ref"));
+  GURL url(u"filesystem:https://user:pass@google.com:99/t/foo;bar?q=a#ref");
 
   GURL url2(url);
   EXPECT_TRUE(url2.is_valid());
diff --git a/weblayer/browser/content_browser_client_impl.cc b/weblayer/browser/content_browser_client_impl.cc
index b5951d5..7e8c9eb 100644
--- a/weblayer/browser/content_browser_client_impl.cc
+++ b/weblayer/browser/content_browser_client_impl.cc
@@ -638,14 +638,10 @@
 
 void ContentBrowserClientImpl::PersistIsolatedOrigin(
     content::BrowserContext* context,
-    const url::Origin& origin) {
-  DCHECK(!context->IsOffTheRecord());
-  ListPrefUpdate update(user_prefs::UserPrefs::Get(context),
-                        site_isolation::prefs::kUserTriggeredIsolatedOrigins);
-  base::ListValue* list = update.Get();
-  base::Value value(origin.Serialize());
-  if (!base::Contains(list->GetList(), value))
-    list->Append(std::move(value));
+    const url::Origin& origin,
+    content::ChildProcessSecurityPolicy::IsolatedOriginSource source) {
+  site_isolation::SiteIsolationPolicy::PersistIsolatedOrigin(context, origin,
+                                                             source);
 }
 
 base::OnceClosure ContentBrowserClientImpl::SelectClientCertificate(
diff --git a/weblayer/browser/content_browser_client_impl.h b/weblayer/browser/content_browser_client_impl.h
index d0b6a97..f29c21f7d 100644
--- a/weblayer/browser/content_browser_client_impl.h
+++ b/weblayer/browser/content_browser_client_impl.h
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/content_browser_client.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
 
@@ -109,8 +110,11 @@
       content::PageVisibilityState* visibility_state) override;
   bool ShouldDisableSiteIsolation() override;
   std::vector<std::string> GetAdditionalSiteIsolationModes() override;
-  void PersistIsolatedOrigin(content::BrowserContext* context,
-                             const url::Origin& origin) override;
+  void PersistIsolatedOrigin(
+      content::BrowserContext* context,
+      const url::Origin& origin,
+      content::ChildProcessSecurityPolicy::IsolatedOriginSource source)
+      override;
   base::OnceClosure SelectClientCertificate(
       content::WebContents* web_contents,
       net::SSLCertRequestInfo* cert_request_info,
diff --git a/weblayer/browser/translate_browsertest.cc b/weblayer/browser/translate_browsertest.cc
index a46ac87..6dea4d9 100644
--- a/weblayer/browser/translate_browsertest.cc
+++ b/weblayer/browser/translate_browsertest.cc
@@ -225,11 +225,6 @@
 IN_PROC_BROWSER_TEST_F(TranslateBrowserTest, PageLanguageDetection) {
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   // Go to a page in English.
   ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
@@ -251,11 +246,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   // Navigate to a page in French.
   ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
@@ -291,11 +281,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   // Navigate to a page in French.
   ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
@@ -322,8 +307,12 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
+  // Navigate to a empty page to result in the model returning "und".
+  // An "und" result will result in "auto" as the source language
+  // in the translate script.
   ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
+  NavigateAndWaitForCompletion(
+      GURL(embedded_test_server()->GetURL("/clipboard.html")), shell());
   language_determination_waiter_->Wait();
   EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
 
@@ -348,11 +337,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   // Navigate to a page in French.
   ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
@@ -380,11 +364,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   // Navigate to a page in French.
   ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
@@ -412,11 +391,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   // Before browsing, set autotranslate from French to Chinese.
   translate_client->GetTranslatePrefs()->AddLanguagePairToAlwaysTranslateList(
       "fr", "zh-CN");
@@ -449,11 +423,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   TestInfoBarManagerObserver infobar_observer;
   infobar_manager->AddObserver(&infobar_observer);
 
@@ -504,13 +473,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
-  EXPECT_EQ(0u, infobar_manager->infobar_count());
-
   // Navigate to a page in French.
   ResetLanguageDeterminationWaiter();
   NavigateAndWaitForCompletion(
@@ -537,11 +499,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   TestInfoBarManagerObserver infobar_observer;
   infobar_manager->AddObserver(&infobar_observer);
 
@@ -611,11 +568,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   TestInfoBarManagerObserver infobar_observer;
   infobar_manager->AddObserver(&infobar_observer);
 
@@ -687,11 +639,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   TestInfoBarManagerObserver infobar_observer;
   infobar_manager->AddObserver(&infobar_observer);
 
@@ -758,11 +705,6 @@
 
   TranslateClientImpl* translate_client = GetTranslateClient(shell());
 
-  ResetLanguageDeterminationWaiter();
-  NavigateAndWaitForCompletion(GURL("about:blank"), shell());
-  language_determination_waiter_->Wait();
-  EXPECT_EQ("und", translate_client->GetLanguageState().source_language());
-
   TestInfoBarManagerObserver infobar_observer;
   infobar_manager->AddObserver(&infobar_observer);